From b2739c367a8a99d6f8a7f44ae80cf951ee17eb1b Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Tue, 10 Oct 2006 13:25:45 +0100 Subject: [PATCH] WIP on the at client --- src/mini-at/TopSource.c | 4 + src/mini-at/build.monolithic | 3 +- src/mini-at/main.c | 31 ++- src/mini-at/time.c | 438 +++++++++++++++++++++++++++++++++++ 4 files changed, 469 insertions(+), 7 deletions(-) create mode 100644 src/mini-at/time.c diff --git a/src/mini-at/TopSource.c b/src/mini-at/TopSource.c index 50502d4..349f39f 100644 --- a/src/mini-at/TopSource.c +++ b/src/mini-at/TopSource.c @@ -8,8 +8,12 @@ // Below are all the includes used throughout the application. +#include #include +#include #include +#include +#include diff --git a/src/mini-at/build.monolithic b/src/mini-at/build.monolithic index c2f3f5e..83ccae7 100644 --- a/src/mini-at/build.monolithic +++ b/src/mini-at/build.monolithic @@ -7,7 +7,8 @@ MONOLITHIC_TESTS="src/mini-at/build.app src/mini-at/build.monolithic" if [ -z "${mini_at_MONOLITHIC}" ] then - MONOLITHIC_SOURCE="$(echo src/mini-at/TopSource.c)" + MONOLITHIC_SOURCE="src/common/mini-at.h + $(echo src/mini-at/{TopSource,time,main}.c)" make_monolithic ${SRC} C || return 1 mini_at_MONOLITHIC=1 diff --git a/src/mini-at/main.c b/src/mini-at/main.c index 712a685..432dad8 100644 --- a/src/mini-at/main.c +++ b/src/mini-at/main.c @@ -7,6 +7,19 @@ +void msg_add(char* buf, int* pos, const char* src, int amt) +{ + if((*pos + amt) > AT_MSG_SIZE) { + fprintf(stderr, "Message size too long: try reducing number of commandline arguments.\n"); + exit(1); + } + + memcpy(buf + *pos, src, amt); + *pos += amt; +} + + + void version(void) { printf("mini-at " VERSION "\n" @@ -22,7 +35,7 @@ void usage(void) "Options:\n" "-h, --help Display this screen.\n" "-V, --version Display version.\n" - "-f, --file Specify filename to read stdin from, - to copy stdin.\n", + "-f, --file Specify filename to read stdin from, - to copy stdin.\n" "-0, --argv0 Change argv[0] from `cmd' to `x'.\n" "\n" "'time' is an ISO8601 time. If you specify an incomplete time, e.g. '2010', we simply\n" @@ -39,7 +52,7 @@ struct option options[] = { { "file", required_argument, 0, 'f' }, { "argv0", required_argument, 0, '0' }, { 0, 0, 0, 0 } -} +}; @@ -49,6 +62,7 @@ int main(int argc, char* argv[]) struct at_msg a; char msg[AT_MSG_SIZE]; int size, i, x; + time_t t; while(1) { switch(getopt_long(argc, argv, "hVf:0:", options, 0)) { @@ -91,7 +105,12 @@ done: // parse time t = parse_time(argv[0]); - if(t == (time_t)-1) return 1; + if(t == (time_t)-1) { + fprintf(stderr, "Not a valid ISO8601 date: ``%s''.\n" + "See e.g. http://en.wikipedia.org/wiki/Iso8601 .\n", + argv[0]); + return 1; + } ++argv; --argc; @@ -109,18 +128,18 @@ done: // set up message size = 0; - msg_add(msg, &size, &a, sizeof(a)); + msg_add(msg, &size, (char*)&a, sizeof(a)); msg_add(msg, &size, argv[0], a.cmd_len); if(stdin_file) msg_add(msg, &size, stdin_file, a.in_fn_len); if(!argv0) argv0 = argv[0]; x = strlen(argv0); - msg_add(msg, &size, &x, sizeof(x)); + msg_add(msg, &size, (char*)&x, sizeof(x)); msg_add(msg, &size, argv0, x); for(i = 1; i < argc; ++i) { x = strlen(argv[i]); - msg_add(msg, &size, &x, sizeof(x)); + msg_add(msg, &size, (char*)&x, sizeof(x)); msg_add(msg, &size, argv[i], x); } diff --git a/src/mini-at/time.c b/src/mini-at/time.c new file mode 100644 index 0000000..3578abf --- /dev/null +++ b/src/mini-at/time.c @@ -0,0 +1,438 @@ +/* mini-at/src/mini-at/time.c + * + * (c)2006, Laurence Withers, . + * Released under the GNU GPLv2. See file COPYING or + * http://www.gnu.org/copyleft/gpl.html for details. +*/ + + + +int parse_time_digs(const char* str, int numdigs) +{ + int i = 0; + char ch; + + while(numdigs--) { + switch( (ch = *str++) ) { + case '0' ... '9': + i *= 10; + i += ch - '0'; + break; + + default: + return -1; + } + } + + return i; +} + + + +int parse_time_date_daysinyear(int year) +{ + if(!(year % 400)) return 366; + if(!(year % 100)) return 365; + if(!(year % 4)) return 366; + return 365; +} + + + +struct parse_time_date_dayselapsed_t { + int ndays, elapsed; +}; + +struct parse_time_date_dayselapsed_t parse_time_date_dayselapsed_cal[] = { + { 31, 0 }, + { 28, 31 }, + { 31, 59 }, + { 30, 90 }, + { 31, 120 }, + { 30, 151 }, + { 31, 181 }, + { 31, 212 }, + { 30, 243 }, + { 31, 273 }, + { 30, 304 }, + { 31, 334 } +}; + +struct parse_time_date_dayselapsed_t parse_time_date_dayselapsed_leapcal[] = { + { 31, 0 }, + { 29, 31 }, + { 31, 60 }, + { 30, 91 }, + { 31, 121 }, + { 30, 152 }, + { 31, 182 }, + { 31, 213 }, + { 30, 244 }, + { 31, 274 }, + { 30, 305 }, + { 31, 335 } +}; + +int parse_time_date_dayselapsed(int year, int mon, int day) +{ + struct parse_time_date_dayselapsed_t* t; + + --mon; + if(mon < 0 || mon > 11) return -1; + + if(parse_time_date_daysinyear(year) == 366) t = parse_time_date_dayselapsed_leapcal; + else t = parse_time_date_dayselapsed_cal; + + t += mon; + --day; + if(day < 0 || day > t->ndays) return -1; + + return day + t->elapsed; +} + + + +// number of seconds elapsed between 1970 and start of `year' +time_t parse_time_date_mkyear(int year) +{ + long nd; + + // we have an inverse y2k problem! + // basically, the algorithm needs to operate on a 400-year boundary for leap days + // since this is `at', we don't deal with dates in the past + // time_t can't deal with dates earlier than 1970 + // so 2000 seems a logical place to start + year -= 2000; + if(year < 0) return -1; + + /* Every 4th year is leap + * unless it's a century (not leap) + * unless it's a 4*century (leap) + * + * So there are 97 leap days in 400 years, giving 365 * 400 + 397 = 146097 days + * For each 100 years, there are 24 leaps, giving 365 * 100 + 24 = 36524 days + * For each 4 years, there is 1 leap, giving 365 * 4 + 1 = 1461 days + */ + nd = 146097l * (year / 400); + year %= 400; + if(year) { + nd += 36524 * (year / 100); + year %= 100; + nd += 1461 * (year / 4); + if(year) { + year %= 4; + nd += (year ? 1 : 0) + 365 * year; + } else { + nd++; + } + } + + /* Now add number of days between 1970 and 2000 (7 leap years) + * 30 * 365 + 7 = 10957 */ + nd += 10957; + + // ignoring leap seconds, as time_t does, there are 86400 seconds in a day + nd *= 86400; + + // done! + return (time_t)nd; +} + + + +time_t parse_time_date_mkwk(int year, int wk, int wday) +{ +// time_t t; + + fprintf(stderr, "ISO8601 week dates not supported yet\n"); + return -1; + +/* + t = parse_time_date_mkyear(year); + if(t == (time_t)-1) return -1; +*/ +} + + + +time_t parse_time_date_mkord(int year, int oday) +{ + time_t t; + + if(oday < 1 || oday > parse_time_date_daysinyear(year)) return -1; + t = parse_time_date_mkyear(year); + if(t == (time_t)-1) return -1; + + t += (oday - 1) * 86400; + return t; +} + + + +time_t parse_time_date_mkcal(int year, int mon, int day) +{ + time_t t; + + t = parse_time_date_mkyear(year); + if(t == (time_t)-1) return -1; + + t += parse_time_date_dayselapsed(year, mon, day) * 86400; + return t; +} + + + +time_t parse_time_date(const char* str, const char* end) +{ + int year, mon, day; + const char* pos; + + // we don't deal with negative years -- this is at! + if(*str < '0' || *str > '9') return -1; + + if( (pos = strchr(str, 'W')) ) { + if(strchr(pos, '-')) { + /* Either of: + * yyyy-Www + * yyyy-Www-d + */ + switch(end - pos) { + case 3: + year = parse_time_digs(str, pos - str - 1); + mon = parse_time_digs(pos + 1, 2); + return parse_time_date_mkwk(year, mon, 1); + + case 5: + year = parse_time_digs(str, pos - str - 1); + mon = parse_time_digs(pos + 1, 2); + day = parse_time_digs(pos + 4, 1); + return parse_time_date_mkwk(year, mon, day); + + default: + return -1; + } + + } else { + /* Either of: + * yyyyWww + * yyyyWwwd + */ + switch(end - pos) { + case 3: + year = parse_time_digs(str, pos - str); + mon = parse_time_digs(pos + 1, 2); + return parse_time_date_mkwk(year, mon, 1); + + case 4: + year = parse_time_digs(str, pos - str); + mon = parse_time_digs(pos + 1, 2); + day = parse_time_digs(pos + 3, 2); + return parse_time_date_mkwk(year, mon, day); + + default: + return -1; + } + } + + } else if( (pos = strchr(str, '-')) ) { + /* Valid extended formats are: + * yyyy-mm (year + len 3) + * yyyy-ooo (year + len 4) + * yyyy-mm-dd (year + len 6) + */ + switch(end - pos) { + case 3: + year = parse_time_digs(str, pos - str); + mon = parse_time_digs(pos + 1, 2); + return parse_time_date_mkcal(year, mon, 1); + + case 4: + year = parse_time_digs(str, pos - str); + day = parse_time_digs(pos + 1, 3); + return parse_time_date_mkord(year, day); + + case 6: + year = parse_time_digs(str, pos - str); + mon = parse_time_digs(pos + 1, 2); + day = parse_time_digs(pos + 4, 2); + return parse_time_date_mkcal(year, mon, day); + + default: + return -1; + } + } + + /* Valid basic formats, excluding week formats, are: + * yyyy (len 4) + * yyyymm (len 6) + * yyyyooo (len 7) + * yyyymmdd (len 8) + */ + + switch(end - str) { + case 4: + year = parse_time_digs(str, 4); + return parse_time_date_mkord(year, 1); + + case 6: + year = parse_time_digs(str, 4); + mon = parse_time_digs(str + 4, 2); + return parse_time_date_mkcal(year, mon, 1); + + case 7: + year = parse_time_digs(str, 4); + day = parse_time_digs(str + 4, 3); + return parse_time_date_mkord(year, day); + + case 8: + year = parse_time_digs(str, 4); + mon = parse_time_digs(str + 4, 2); + day = parse_time_digs(str + 6, 2); + return parse_time_date_mkcal(year, mon, day); + + default: + return -1; + } +} + + + +int parse_time_time(const char* str, const char* end) +{ + const char* pos; + double d = 0; + char* endp; + int h = 0, m = 0, s = 0, ret; + + pos = strchr(str, '.'); + if(pos) { + errno = 0; + endp = 0; + d = strtod(pos, &endp); + if(errno || !endp) return -1; + switch(*endp) { + case 'Z': + case '+': + case '-': + case 0: + break; + + default: + return -1; + } + } else { + pos = end; + } + + /* Valid formats, excluding fraction, are: + * + * hh (len 2) + * hhmm (len 4) + * hh:mm (len 5) + * hhmmss (len 6) + * hh:mm:ss (len 8) + */ + switch(pos - str) { + case 2: + h = parse_time_digs(str, 2); + ret = ((h + d) * 3600 + 0.5); + break; + + case 4: + h = parse_time_digs(str, 2); + m = parse_time_digs(str + 2, 2); + ret = h * 3600 + ((m + d) * 60 + 0.5); + break; + + case 5: + h = parse_time_digs(str, 2); + m = parse_time_digs(str + 3, 2); + ret = h * 3600 + ((m + d) * 60 + 0.5); + break; + + case 6: + h = parse_time_digs(str, 2); + m = parse_time_digs(str + 2, 2); + s = parse_time_digs(str + 4, 2); + ret = h * 3600 + m * 60 + s + d + 0.5; + break; + + case 8: + h = parse_time_digs(str, 2); + m = parse_time_digs(str + 3, 2); + s = parse_time_digs(str + 5, 2); + ret = h * 3600 + m * 60 + s + d + 0.5; + break; + + default: + return -1; + } + + // special cases + if(h == 24 && m == 0 && s == 0) return ret; // midnight at end of day + if(h == 23 && m == 59 && s == 60) return ret; // leap second + + if(h < 0 || h > 23) return -1; + if(m < 0 || m > 59) return -1; + if(s < 0 || s > 59) return -1; + + return ret; +} + + + +/* parse_time() + * + * Parses an ISO-8601 date/time. We support any of the ISO-8601 formats. + */ +time_t parse_time(const char* str) +{ + const char* p, * tz; + time_t t; + int s; + + // parse the date portion + p = strchr(str, 'T'); + t = p ? parse_time_date(str, p) : parse_time_date(str, str + strlen(str)); + if(t == (time_t)-1) return -1; + + // parse the time portion + if(p) { + ++p; + tz = strchr(p, 'Z'); + if(tz) { + if(tz[1]) return -1; + } else { + tz = strchr(p, '+'); + if(!tz) tz = strchr(p, '-'); + if(!tz) tz = p + strlen(p); + } + s = parse_time_time(p, tz); + if(s == -1) return -1; + t += s; + + p = tz + 1; + switch(*tz) { + case '+': + s = parse_time_time(p, p + strlen(p)); + if(s == -1) return -1; + t -= s; + break; + + case '-': + s = parse_time_time(p, p + strlen(p)); + if(s == -1) return -1; + t += s; + break; + } + } + + return t; +} + + + +/* options for text editors +kate: replace-trailing-space-save true; space-indent true; tab-width 4; +vim: expandtab:ts=4:sw=4 +*/