#include "prjlibs-include/standards.h"
#include <unistd.h>
#include <limits.h>
#include <stddef.h>
#include <errno.h>
#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<value) continue;
if (need_increase && newval==value) continue;
}
if (found_any && 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<TAI_PACK; ++i) {
unsigned long const d=delay<<8;
if (d>>8!=delay)
strerr_die5x(100, PROG, ": ", var_delay, err_oflow, "long");
delay=d+taibuf[i];
if (delay<d)
strerr_die5x(100, PROG, ": ", var_delay, err_oflow, "long");
}
}
delaybuf[fmt_ulong(delaybuf, delay)]='\0';
}
if (pathexec_env(var_delay+1, delaybuf)==0)
strerr_die5sys(111, PROG, err_unable, err_setvar, var_delay, ": ");
}
}
++argv;
pathexec((char const**)argv);
strerr_die5sys(errstat, PROG, err_unable, err_exec, argv[0], ": ");
}
syntax highlighted by Code2HTML, v. 0.9.1