/* cron.h */
#ifndef _DBCRON_COMM_H_
#define _DBCRON_COMM_H_
#define E_NORMAL            0x01
#define E_EVERY             0x02
#define E_AREA              0x04
#define E_SLASH             0x08
#define MINUTE_EVERY        0x01  
#define HOUR_EVERY          0x02
#define DAY_MON_EVERY       0x04
#define MON_YEAR_EVERY      0x08
#define DAY_WEEK_EVERY      0x10
#define FIRST_MINUTE        0
#define LAST_MINUTE         59
#define MINUTE_COUNT        (LAST_MINUTE - FIRST_MINUTE + 1)
#define FIRST_HOUR          0
#define LAST_HOUR           23
#define HOUR_COUNT          (LAST_HOUR - FIRST_HOUR + 1)
#define FIRST_DAYOFMON      1
#define LAST_DAYOFMON       31
#define DAYOFMON_COUNT      (LAST_DAYOFMON - FIRST_DAYOFMON + 1)
#define FIRST_MONTH         1
#define LAST_MONTH          12
#define MONTH_COUNT         (LAST_MONTH - FIRST_MONTH + 1)
/* note on DOW: 0 and 7 are both Sunday */
#define FIRST_DAYOFWEEK     0
#define LAST_DAYOFWEEK      7
#define DAYOFWEEK_COUNT     (LAST_DAYOFWEEK - FIRST_DAYOFWEEK + 1)
#define MAX_BUF             256
#define _bit_byte(bit) \
    ((bit) >> 3)
#define _bit_mask(bit) \
    (1 << ((bit)&0x7))
#define bitstr_size(nbits) \
    ((((nbits) - 1) >> 3) + 1)
#define bit_decl(name, nbits) \
    (name)[bitstr_size(nbits)]
#define bit_test(name, bit) \
    ((name)[_bit_byte(bit)] & _bit_mask(bit))
#define bit_set(name, bit) \
    (name)[_bit_byte(bit)] |= _bit_mask(bit)
#define bit_clear(name, bit) \
    (name)[_bit_byte(bit)] &= ~_bit_mask(bit)
#define bit_nclear(name, start, stop) { \
    register unsigned char *_name = name; \
    register int _start = start, _stop = stop; \
    register int _startbyte = _bit_byte(_start); \
    register int _stopbyte = _bit_byte(_stop); \
    if (_startbyte == _stopbyte) { \
        _name[_startbyte] &= ((0xff >> (8 - (_start&0x7))) | \
                        (0xff << ((_stop&0x7) + 1))); \
    } else { \
        _name[_startbyte] &= 0xff >> (8 - (_start&0x7)); \
        while (++_startbyte < _stopbyte) \
            name[_startbyte] = 0; \
        _name[_stopbyte] &= 0xff << ((_stop&0x7) + 1); \
    } \
}
#define bit_nset(name, start, stop) { \
    register unsigned char *_name = name; \
    register int _start = start, _stop = stop; \
    register int _startbyte = _bit_byte(_start); \
    register int _stopbyte = _bit_byte(_stop); \
    if (_startbyte == _stopbyte) { \
        _name[_startbyte] |= ((0xff << (_start&0x7)) & \
                    (0xff >> (7 - (_stop&0x7)))); \
    } else { \
        _name[_startbyte] |= 0xff << ((_start)&0x7); \
        while (++_startbyte < _stopbyte) \
                _name[_startbyte] = 0xff; \
        _name[_stopbyte] |= 0xff >> (7 - (_stop&0x7)); \
    } \
}
typedef struct _element
{
    int flag;
    int low;
    int high;
    int step;
}ELEMENT;
typedef struct _date_rec
{
    ELEMENT minute;
    ELEMENT hour;
    ELEMENT day_of_mon;
    ELEMENT mon_of_year;
    ELEMENT day_of_week;
}DATE_REC;
typedef struct _entry
{
    int flags;
    unsigned char bit_decl(minute, MINUTE_COUNT);
    unsigned char bit_decl(hour, HOUR_COUNT);
    unsigned char bit_decl(day_of_mon, DAYOFMON_COUNT);
    unsigned char bit_decl(mon_of_year, MONTH_COUNT);
    unsigned char bit_decl(day_of_week, DAYOFWEEK_COUNT);
    char command[MAX_BUF];
    struct _entry *next;
}ENTRY;
#endif //_DBCRON_COMM_H_
 
/* cron.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <sys/time.h>
#include <time.h>
//#include <sqlite3.h>
#include "dbcron_comm.h"
static ENTRY *head = NULL;
static ENTRY *tail = NULL;
int set_bit(unsigned char *bits, int low, int high, int number)
{
    if (number < low || number > high)
        return -1;
    bit_set(bits, (number - low));
    return 0;
}
int set_element(unsigned char *bits, ELEMENT element, int low, int high)
{
    int i;
    int num1;
    int num2;
    int num3;
   
    bit_nclear(bits, 0, (high - low + 1));
    if (E_EVERY == element.flag)
    {
        num1 = low;
        num2 = high;
    }
    else if (E_NORMAL == element.flag)
    {
        num1 = element.low;
        set_bit(bits, low, high, num1);
        return 0;
    }
    else if (E_AREA == element.flag)
    {
        num1 = element.low;
        num2 = element.high;
    }

    num3 = element.step;
    if (num3 <= 0)
        return -1;
   
    for (i = num1; i <= num2; i += num3)
    {
        set_bit(bits, low, high, i);
    }
   
    return 0;
}
int load_entry(ENTRY *pEntry, DATE_REC *pDate)
{
    if (E_EVERY == pDate->minute.flag)
        pEntry->flags |= MINUTE_EVERY;
    set_element(pEntry->minute, pDate->minute, FIRST_MINUTE, LAST_MINUTE);
    if (E_EVERY == pDate->hour.flag)
        pEntry->flags |= HOUR_EVERY;
    set_element(pEntry->hour, pDate->hour, FIRST_HOUR, LAST_HOUR);
    if (E_EVERY == pDate->day_of_mon.flag)
        pEntry->flags |= DAY_MON_EVERY;
    set_element(pEntry->day_of_mon, pDate->day_of_mon, FIRST_DAYOFMON, LAST_DAYOFMON);
    if (E_EVERY == pDate->mon_of_year.flag)
        pEntry->flags |= MON_YEAR_EVERY;
    set_element(pEntry->mon_of_year, pDate->mon_of_year, FIRST_MONTH, LAST_MONTH);
    if (E_EVERY == pDate->day_of_week.flag)
        pEntry->flags |= DAY_WEEK_EVERY;
    set_element(pEntry->day_of_week, pDate->day_of_week, FIRST_DAYOFWEEK, LAST_DAYOFWEEK);
    if (bit_test(pEntry->day_of_week, 0) || bit_test(pEntry->day_of_week, 7))
    {
        bit_set(pEntry->day_of_week, 0);
        bit_set(pEntry->day_of_week, 7);
    }
    return 0;
   
}
ENTRY *add_entry(void)
{
    ENTRY *pEntry;
    pEntry = (ENTRY *)malloc(sizeof(ENTRY));
    if (NULL == pEntry)
    {
        return NULL;
    }
    memset(pEntry, 0, sizeof(ENTRY));
    pEntry->next = NULL;
    if (NULL == head)
        head = pEntry;
    else
        tail->next = pEntry;
    tail = pEntry;
    return pEntry;
   
}
void del_entry()
{
    ENTRY *pEntry;
    if (NULL == head)
        return;
    pEntry = head;
    while (head)
    {
        pEntry = head->next;
        free(head);
        head = pEntry;
    }
    head = NULL;
    tail = NULL;
}
void display_entry()
{
#if 1
    ENTRY *pe;
   
    int i;
    bool n;
    pe = head;
    while (pe)
    {
        printf("minute: ");
        for (i = 0; i <= LAST_MINUTE; i ++)
        {
            n = bit_test(pe->minute, i);
            printf("%d:%d ", i, n);
        }
        printf("\n");
   
        printf("hour: ");
        for (i = 0; i <= LAST_HOUR; i ++)
        {
            n = bit_test(pe->hour, i);
            printf("%d:%d ", i, n);
        }
        printf("\n");
   
        printf("day of mon: ");
        for (i = 0; i < LAST_DAYOFMON; i ++)
        {
            n = bit_test(pe->day_of_mon, i);
            printf("%d:%d ", i, n);
        }
        printf("\n");
   
        printf("mon of year: ");
        for (i = 0; i < LAST_MONTH; i ++)
        {
            n = bit_test(pe->mon_of_year, i);
            printf("%d:%d ", i, n);
        }
        printf("\n");
   
        printf("day of week: ");
        for (i = 0; i <= LAST_DAYOFWEEK; i ++)
        {
            n = bit_test(pe->day_of_week, i);
            printf("%d:%d ", i, n);
        }
        printf("\n");
        printf("\n");
        pe = pe->next;
    }
   
#endif
}
void do_job(time_t current_time)
{
    struct tm *p_tm;
    ENTRY *pe;
    int minute;
    int hour;
    int day_of_month;
    int month_of_year;
    int day_of_week;
   
    p_tm = localtime(&current_time);
    pe = head;
    minute = p_tm->tm_min - FIRST_MINUTE;
    hour = p_tm->tm_hour - FIRST_HOUR;
    day_of_month = p_tm->tm_mday - FIRST_DAYOFMON;
    month_of_year = p_tm->tm_mon + 1 - FIRST_MONTH;
    day_of_week = p_tm->tm_wday - FIRST_DAYOFWEEK;
#if 1
    printf("%d, %d, %d, %d, %d\n",
        minute,
        hour,
        day_of_month,
        month_of_year,
        day_of_week);
#endif
    while (pe)
    {
        if (bit_test(pe->minute, minute) &&
            bit_test(pe->hour, hour) &&
            bit_test(pe->day_of_mon, day_of_month) &&
            bit_test(pe->mon_of_year, month_of_year) &&
            bit_test(pe->day_of_week, day_of_week))
        {                  
            printf("%s\n", pe->command);
        }
       
        pe = pe->next;
    }
   
}
void cron_sleep(int target)
{
    int seconds_to_wait;
    seconds_to_wait = (int)(target - time((time_t *)NULL)) + 1;
   
    if (seconds_to_wait > 0 && seconds_to_wait < 65)
        sleep((unsigned int) seconds_to_wait);
}
int main(int argc, char *argv[])
{
    /* 测试例程 */
    ENTRY *pEntry;
    DATE_REC date;
    int running_time;
    date.minute.flag = E_EVERY;
    date.minute.low = FIRST_MINUTE;
    date.minute.high = LAST_MINUTE;
    date.minute.step = 2;
    date.hour.flag = E_NORMAL;
    date.hour.low = 9;
    date.hour.high = LAST_HOUR;
    date.hour.step = 1;
    date.day_of_mon.flag = E_EVERY;
    date.day_of_mon.low = FIRST_DAYOFMON;
    date.day_of_mon.high = LAST_DAYOFMON;
    date.day_of_mon.step = 1;
    date.mon_of_year.flag = E_EVERY;
    date.mon_of_year.low = FIRST_MONTH;
    date.mon_of_year.high = LAST_MONTH;
    date.mon_of_year.step = 1;
    date.day_of_week.flag = E_EVERY;
    date.day_of_week.low = FIRST_DAYOFWEEK;
    date.day_of_week.high = LAST_DAYOFWEEK;
    date.day_of_week.step = 1;
    pEntry = add_entry();
    load_entry(pEntry, &date);
    strcpy(pEntry->command, "Hello");
    date.minute.flag = E_EVERY;
    date.minute.low = FIRST_MINUTE;
    date.minute.high = LAST_MINUTE;
    date.minute.step = 3;
    date.hour.flag = E_NORMAL;
    date.hour.low = 9;
    date.hour.high = LAST_HOUR;
    date.hour.step = 1;
    date.day_of_mon.flag = E_EVERY;
    date.day_of_mon.low = FIRST_DAYOFMON;
    date.day_of_mon.high = LAST_DAYOFMON;
    date.day_of_mon.step = 1;
    date.mon_of_year.flag = E_EVERY;
    date.mon_of_year.low = FIRST_MONTH;
    date.mon_of_year.high = LAST_MONTH;
    date.mon_of_year.step = 1;
    date.day_of_week.flag = E_EVERY;
    date.day_of_week.low = FIRST_DAYOFWEEK;
    date.day_of_week.high = LAST_DAYOFWEEK;
    date.day_of_week.step = 1;
    pEntry = add_entry();
    load_entry(pEntry, &date);
    strcpy(pEntry->command, "Welcome");
    display_entry();
    running_time = time(NULL);
    while (1)
    {
        do_job(running_time);
        running_time += 60;
        cron_sleep(running_time);
    }
    return 0;
}