#include "prjlibs-include/standards.h" #include #include #include #include #include "skalibs/include/stddjb.h" #include "prjlibs-include/constants.h" #include "runwhen.h" char const* PROG="caldelay"; static char const var_delay[]="$DELAY"; static char const var_weekday[]="$WEEKDAY"; static char const var_year[]="$YEAR"; static char const var_month[]="$MONTH"; static char const var_monthday[]="$MONTHDAY"; static char const var_hour[]="$HOUR"; static char const var_minute[]="$MINUTE"; static char const var_second[]="$SECOND"; typedef enum { eq, eqn, div } rw_match_type; typedef struct constraint constraint; struct constraint { constraint const* next; rw_match_type type; unsigned long spec; }; typedef struct { char const* const env; constraint const* constraints; unsigned long value; unsigned long (* const limit)(void); } unit; static unsigned long limit_weekday (void) { return 6; } static unsigned long limit_year (void) { return -1; } static unsigned long limit_month (void) { return 11; } static unsigned long limit_monthday(void); static unsigned long limit_hour (void) { return 23; } static unsigned long limit_minute (void) { return 59; } static unsigned long limit_second (void) { return 59; } static unit weekday_units[]={ { var_weekday, null, -1, limit_weekday }, { var_hour, null, -1, limit_hour }, { var_minute, null, -1, limit_minute }, { var_second, null, -1, limit_second } }; static unit monthday_units[]={ { var_year, null, -1, limit_year }, { var_month, null, -1, limit_month }, { var_monthday, null, -1, limit_monthday }, { var_hour, null, -1, limit_hour }, { var_minute, null, -1, limit_minute }, { var_second, null, -1, limit_second } }; static unsigned long limit_monthday(void) { static unsigned long const limits[]={ 30, 27, 30, 29, 30, 29, 30, 30, 29, 30, 29, 30 }; unsigned long leap; { unsigned long const month=monthday_units[1].value; if (month!=1) return limits[month]; } leap=monthday_units[0].value; if (leap%4!=0) leap=0; else { leap/=4; if (leap%25!=0) leap=1; else { leap/=25; if (leap%4!=0) leap=0; else leap=1; } } return 27+leap; } static int increased=0; static int need_increase=0; static void do_unit(unit* const u) { constraint const* c; unsigned long const value=(increased? 0: u->value); unsigned long least; int found_any=0; for (c=u->constraints; c!=null; c=c->next) { unsigned long const spec=c->spec; unsigned long newval; if (c->type==div) { newval=value+(spec-1); newval-=newval%spec; if (need_increase && newval==value) newval+=spec; if (newval>u->limit()) continue; } else { if (c->type==eq) newval=spec; else newval=u->limit()-spec; if (newval=least) continue; found_any=1; least=newval; if (least==0) break; /* can't get any lower */ } if (!found_any) { need_increase=1; return; } if (least>value) { increased=1; need_increase=0; } u->value=least; return; } int main(int argc, char** argv) { struct tai now; struct caltime ct; int weekday; char const* x; unit* units; size_t n_units; size_t i; if (argc<2) strerr_die3x(111, usage, PROG, " program [arg ...]"); x=env_get(var_weekday+1); if (x!=null) { units=weekday_units; n_units=sizeof weekday_units/sizeof (unit); } else { units=monthday_units; n_units=sizeof monthday_units/sizeof (unit); } { int seen_any=0; for (i=0; i!=n_units; ++i) { constraint const** clink; constraint* c; x=env_get(units[i].env+1); clink=&units[i].constraints; c=(void*)alloc(sizeof *c); if (c==null) strerr_die4sys(111, PROG, err_unable, err_alloc, ": "); if (x!=null && *x!='\0') { seen_any=1; for (;;) { unsigned int len; switch (*x) { case '=': c->type=eq; break; case '-': c->type=eqn; break; case '/': c->type=div; break; default: strerr_die4x(111, PROG, ": ", units[i].env, err_malformed); } ++x; len=scan_ulong(x, &c->spec); if (len==0) strerr_die4x(111, PROG, ": ", units[i].env, err_malformed); if (c->spec==0) c->type=eq; else if (c->type==eqn) c->spec--; x+=len; c->next=null; *clink=c; clink=&c->next; if (*x=='\0') break; c=(void*)alloc(sizeof *c); if (c==null) strerr_die4sys(111, PROG, err_unable, err_alloc, ": "); } } else { if (seen_any) { c->type=eq; c->spec=0; } else { c->type=div; c->spec=1; } units[i].constraints=c; } } } x=env_get(var_delay+1); if (x!=null) { { struct tai d; { unsigned char taibuf[TAI_PACK]={ 0, 0, 0, 0, 0, 0, 0, 0 }; { unsigned char* bufp; unsigned long delay; { unsigned int const len=scan_ulong(x, &delay); if (len==0 || x[len]!='\0') strerr_die4x(111, PROG, ": ", var_delay, err_malformed); } bufp=taibuf+sizeof taibuf; while (delay!=0) { if (bufp==taibuf) strerr_die5x(100, PROG, ": ", var_delay, err_oflow, "struct tai"); --bufp; *bufp=delay&(unsigned long)0xff; delay>>=8; } } tai_unpack((char*)taibuf, &d); } tai_now(&now); tai_add(&now, &now, &d); caltime_utc(&ct, &now, &weekday, null); tai_sub(&now, &now, &d); } if (units==weekday_units) { weekday_units[0].value=weekday; weekday_units[1].value=ct.hour; weekday_units[2].value=ct.minute; weekday_units[3].value=ct.second; } else { monthday_units[0].value=ct.date.year; monthday_units[1].value=ct.date.month-1; monthday_units[2].value=ct.date.day-1; monthday_units[3].value=ct.hour; monthday_units[4].value=ct.minute; monthday_units[5].value=ct.second; } for (i=0; i!=n_units;) { do_unit(&units[i]); while (need_increase) { if (i==0) { if (units==monthday_units) strerr_die2x(99, PROG, ": all run times are in the past"); units[0].value=0; ct.date.day+=7; increased=1; need_increase=0; continue; } --i; do_unit(&units[i]); } ++i; } if (units==weekday_units) { ct.date.day+=weekday_units[0].value; ct.date.day-=weekday; ct.hour =weekday_units[1].value; ct.minute =weekday_units[2].value; ct.second =weekday_units[3].value; } else { ct.date.year =monthday_units[0].value; ct.date.month=monthday_units[1].value+1; ct.date.day =monthday_units[2].value+1; ct.hour =monthday_units[3].value; ct.minute =monthday_units[4].value; ct.second =monthday_units[5].value; } { char delaybuf[(sizeof (unsigned long)*CHAR_BIT+2)/3+1]; { unsigned long delay; { unsigned char taibuf[TAI_PACK]; { struct tai then; caltime_tai(&ct, &then); tai_sub(&then, &then, &now); tai_pack((char*)taibuf, &then); } delay=0; for (i=0; i>8!=delay) strerr_die5x(100, PROG, ": ", var_delay, err_oflow, "long"); delay=d+taibuf[i]; if (delay