#include "prjlibs-include/standards.h"
#include <unistd.h>
#include <limits.h>
#include <stddef.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include "skalibs/include/stddjb.h"
#include "prjlibs-include/constants.h"
#include "runwhen.h"
char const* PROG="rw-match";
static void die_malformed(char const*, char const*) gccattr_noreturn;
static void die_malformed(char const* str, char const* exp) {
strerr_die5x(100, PROG, ": malformed constraints (expected ", exp, "): ",
str);
}
static void die_range(void) gccattr_noreturn;
static void die_range(void) {
strerr_die2x(100, PROG, ": timestamp out of range");
}
static void die_unit_range(char const*) gccattr_noreturn;
static void die_unit_range(char const* x) {
strerr_die3x(100, PROG, ": value out of range for unit: ", x);
}
enum { unset, eq, eqn, div };
typedef struct {
unsigned int type;
unsigned int spec;
unsigned int value;
unsigned int const min;
unsigned int (* const max)(void);
unsigned int const maxmax;
char id;
} unit;
static struct taia const zero=TAIA_ZERO;
static unsigned int max_weekday (void) { return 6; }
static unsigned int max_year (void) { return -1; }
static unsigned int max_month (void) { return 12; }
static unsigned int max_monthday(void);
static unsigned int max_hour (void) { return 23; }
static unsigned int max_minute (void) { return 59; }
static unsigned int max_second (void) { return 59; }
static unit units_weekday[]={
{ unset, 0, -1, 0, max_weekday, 6, 'w' },
{ unset, 0, -1, 0, max_hour , 23, 'H' },
{ unset, 0, -1, 0, max_minute , 59, 'M' },
{ unset, 0, -1, 0, max_second , 59, 'S' },
{ unset, 0, -1, 0, null , 0, 0 }
};
static unit units_monthday[]={
{ unset, 0, -1, 0, max_year , -1, 'y' },
{ unset, 0, -1, 1, max_month , 12, 'm' },
{ unset, 0, -1, 1, max_monthday, 31, 'd' },
{ unset, 0, -1, 0, max_hour , 23, 'H' },
{ unset, 0, -1, 0, max_minute , 59, 'M' },
{ unset, 0, -1, 0, max_second , 59, 'S' },
{ unset, 0, -1, 0, null , 0, 0 }
};
static unsigned int max_monthday(void) {
static unsigned int const limits[]=
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
unsigned int const year =units_monthday[0].value;
unsigned int const month=units_monthday[1].value;
int leapday;
if (month!=2) leapday=0;
else if (year%400==0) leapday=1;
else if (year%100==0) leapday=0;
else if (year% 4==0) leapday=1;
else leapday=0;
return limits[month-1]+leapday;
}
int main(int argc, char** argv) {
char const* constraint_str;
unit* units;
unsigned int i;
int flag_increased=0;
int flag_need_increase=0;
struct tm tm;
RW_ARG_CHECK(2, " stamp constraints");
constraint_str=argv[2];
if (constraint_str[str_chr(constraint_str, 'w')]=='w')
units=units_weekday;
else units=units_monthday;
while (*constraint_str!='\0') {
char const* const constraint=constraint_str;
unsigned int len;
if (*constraint_str!=',') die_malformed(constraint_str, ",");
++constraint_str;
for (i=0;; ++i) {
if (units[i].max==null)
die_malformed(constraint_str,
"\"y\", \"m\", \"d\", \"w\", \"H\", \"M\", or \"S\"");
if (*constraint_str==units[i].id) break;
}
if (units[i].type!=unset)
strerr_die3x(100, PROG, ": duplicate constraint for unit: ", constraint);
++constraint_str;
switch (*constraint_str) {
case '=': units[i].type=eq; break;
case '-': units[i].type=eqn; break;
case '/': units[i].type=div; break;
default: die_malformed(constraint_str, "\"=\", \"-\", \"/\", or \",\"");
}
++constraint_str;
len=scan_uint(constraint_str, &units[i].spec);
if (len==0) die_malformed(constraint_str, "number");
if (units[i].spec==0) units[i].type=eq;
else if (units[i].type==eqn) units[i].spec--;
if (units[i].spec>units[i].maxmax) {
if (units[i].type!=div || units[i].min>0)
die_unit_range(constraint);
units[i].type=eq;
units[i].spec=0;
} else if (units[i].spec<units[i].min && units[i].type==eq)
die_unit_range(constraint);
constraint_str+=len;
}
{
struct taia stamp;
struct tm* ptm;
struct timeval tv;
time_t t;
rw_scan(&stamp, argv[1]);
if (timeval_from_taia(&tv, &stamp)==0) die_range();
t=tv.tv_sec;
ptm=localtime(&t);
if (ptm==null) die_range();
tm=*ptm;
if (units==units_weekday) {
units_weekday[0].value=tm.tm_wday;
units_weekday[1].value=tm.tm_hour;
units_weekday[2].value=tm.tm_min;
units_weekday[3].value=tm.tm_sec;
} else {
units_monthday[0].value=tm.tm_year+1900;
units_monthday[1].value=tm.tm_mon+1;
units_monthday[2].value=tm.tm_mday;
units_monthday[3].value=tm.tm_hour;
units_monthday[4].value=tm.tm_min;
units_monthday[5].value=tm.tm_sec;
}
}
for (i=0; units[i].max!=null && units[i].type==unset; ++i)
units[i].type=div, units[i].spec=1;
for (; units[i].max!=null; ++i)
if (units[i].type==unset)
units[i].type=eq, units[i].spec=units[i].min;
for (i=0; units[i].max!=null;) {
unsigned int const value=(flag_increased? units[i].min: units[i].value);
unsigned int const spec=units[i].spec;
unsigned int newval;
if (units[i].type==div) {
newval=value+(spec-1);
newval-=newval%spec;
if (flag_need_increase && newval==value) newval+=spec;
} else {
if (units[i].type==eq) newval=spec;
else newval=units[i].max()-spec;
if (newval<value) goto need_increase;
if (flag_need_increase && newval==value) goto need_increase;
}
if (newval>units[i].max()) goto need_increase;
if (newval>value) {
flag_increased=1;
flag_need_increase=0;
} else if (flag_need_increase) goto need_increase;
units[i].value=newval;
if (!flag_need_increase) { ++i; continue; }
need_increase:
if (i!=0) { flag_need_increase=1; flag_increased=0; --i; continue; }
if (units!=units_weekday)
strerr_die2x(99, PROG, ": no future times match constraints");
tm.tm_mday+=7-units[0].value;
units[0].value=tm.tm_wday=0;
flag_increased=1;
flag_need_increase=0;
}
{
struct taia stamp;
struct timeval tv;
time_t t;
if (units==units_weekday) {
tm.tm_mday+=units_weekday[0].value-tm.tm_wday;
tm.tm_hour =units_weekday[1].value;
tm.tm_min =units_weekday[2].value;
tm.tm_sec =units_weekday[3].value;
} else {
tm.tm_year=units_monthday[0].value-1900;
tm.tm_mon =units_monthday[1].value-1;
tm.tm_mday=units_monthday[2].value;
tm.tm_hour=units_monthday[3].value;
tm.tm_min =units_monthday[4].value;
tm.tm_sec =units_monthday[5].value;
}
tm.tm_isdst=-1;
t=mktime(&tm);
if (t==(time_t)-1) die_range();
tv.tv_sec=t;
tv.tv_usec=0;
taia_from_timeval(&stamp, &tv);
rw_pass(&stamp, argv+3);
}
}
syntax highlighted by Code2HTML, v. 0.9.1