/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1985-2007 AT&T Knowledge Ventures * * and is licensed under the * * Common Public License, Version 1.0 * * by AT&T Knowledge Ventures * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * * * * Information and Software Systems Research * * AT&T Research * * Florham Park NJ * * * * Glenn Fowler * * David Korn * * Phong Vo * * * ***********************************************************************/ #pragma prototyped /* * Glenn Fowler * AT&T Research * * Time_t conversion support * * scan date expression in s using format * if non-null, e points to the first invalid sequence in s * if non-null, f points to the first unused format char * t provides default values */ #include #include typedef struct { int32_t nsec; int year; int mon; int week; int weektype; int yday; int mday; int wday; int hour; int min; int sec; int meridian; int zone; } Set_t; #define CLEAR(s) (s.year=s.mon=s.week=s.weektype=s.yday=s.mday=s.wday=s.hour=s.min=s.sec=s.meridian=(-1),s.nsec=1000000000L,s.zone=TM_LOCALZONE) #define INDEX(m,x) (((n)>=((x)-(m)))?((n)-=((x)-(m))):(n)) #define NUMBER(d,m,x) do \ { \ n = 0; \ u = (char*)s; \ while (s < (const char*)(u + d) && *s >= '0' && *s <= '9') \ n = n * 10 + *s++ - '0'; \ if (u == (char*)s || n < m || n > x) \ goto next; \ } while (0) /* * generate a Time_t from tm + set */ static Time_t gen(register Tm_t* tm, register Set_t* set) { register int n; Time_t t; if (set->year >= 0) tm->tm_year = set->year; if (set->mon >= 0) { if (set->year < 0 && set->mon < tm->tm_mon) tm->tm_year++; tm->tm_mon = set->mon; if (set->yday < 0 && set->mday < 0) tm->tm_mday = set->mday = 1; } if (set->week >= 0) { if (set->mon < 0) { tmweek(tm, set->weektype, set->week, set->wday); set->wday = -1; } } else if (set->yday >= 0) { if (set->mon < 0) { tm->tm_mon = 0; tm->tm_mday = set->yday + 1; } } else if (set->mday >= 0) tm->tm_mday = set->mday; if (set->hour >= 0) { if (set->hour < tm->tm_hour && set->yday < 0 && set->mday < 0 && set->wday < 0) tm->tm_mday++; tm->tm_hour = set->hour; tm->tm_min = (set->min >= 0) ? set->min : 0; tm->tm_sec = (set->sec >= 0) ? set->sec : 0; } else if (set->min >= 0) { tm->tm_min = set->min; tm->tm_sec = (set->sec >= 0) ? set->sec : 0; } else if (set->sec >= 0) tm->tm_sec = set->sec; if (set->nsec < 1000000000L) tm->tm_nsec = set->nsec; if (set->meridian > 0) { if (tm->tm_hour < 12) tm->tm_hour += 12; } else if (set->meridian == 0) { if (tm->tm_hour >= 12) tm->tm_hour -= 12; } t = tmxtime(tm, set->zone); tm = 0; if (set->yday >= 0) { tm = tmxmake(t); tm->tm_mday += set->yday - tm->tm_yday; } else if (set->wday >= 0) { tm = tmxmake(t); if ((n = set->wday - tm->tm_wday) < 0) n += 7; tm->tm_mday += n; } if (set->nsec < 1000000000L) { if (!tm) tm = tmxmake(t); tm->tm_nsec = set->nsec; } return tm ? tmxtime(tm, set->zone) : t; } /* * the format scan workhorse */ static Time_t scan(register const char* s, char** e, const char* format, char** f, Time_t t, long flags) { register int d; register int n; register char* p; register Tm_t* tm; const char* b; char* u; char* stack[4]; int m; int hi; int lo; int pedantic; Time_t x; Set_t set; Tm_zone_t* zp; char** sp = &stack[0]; while (isspace(*s)) s++; b = s; again: CLEAR(set); tm = tmxmake(t); tm_info.date = tm_info.zone; pedantic = (flags & TM_PEDANTIC) != 0; for (;;) { if (!(d = *format++)) { if (sp <= &stack[0]) { format--; break; } format = (const char*)*--sp; } else if (!*s) { format--; break; } else if (d == '%' && (d = *format) && format++ && d != '%') { more: switch (d) { case 'a': lo = TM_DAY_ABBREV; hi = pedantic ? TM_DAY : TM_TIME; goto get_wday; case 'A': lo = pedantic ? TM_DAY : TM_DAY_ABBREV; hi = TM_TIME; get_wday: if ((n = tmlex(s, &u, tm_info.format + lo, hi - lo, NiL, 0)) < 0) goto next; s = u; INDEX(TM_DAY_ABBREV, TM_DAY); set.wday = n; continue; case 'b': case 'h': lo = TM_MONTH_ABBREV; hi = pedantic ? TM_MONTH : TM_DAY_ABBREV; goto get_mon; case 'B': lo = pedantic ? TM_MONTH : TM_MONTH_ABBREV; hi = TM_DAY_ABBREV; get_mon: if ((n = tmlex(s, &u, tm_info.format + lo, hi - lo, NiL, 0)) < 0) goto next; s = u; INDEX(TM_MONTH_ABBREV, TM_MONTH); set.mon = n; continue; case 'c': p = "%a %b %e %T %Y"; break; case 'C': NUMBER(2, 19, 99); set.year = (n - 19) * 100 + tm->tm_year % 100; continue; case 'd': if (pedantic && !isdigit(*s)) goto next; /*FALLTHROUGH*/ case 'e': NUMBER(2, 1, 31); set.mday = n; continue; case 'D': p = "%m/%d/%y"; break; case 'E': case 'O': if (*format) { d = *format++; goto more; } continue; case 'H': case 'k': NUMBER(2, 0, 23); set.hour = n; continue; case 'I': case 'l': NUMBER(2, 1, 12); set.hour = n; continue; case 'j': NUMBER(3, 1, 366); set.yday = n - 1; continue; case 'm': NUMBER(2, 1, 12); set.mon = n - 1; continue; case 'M': NUMBER(2, 0, 59); set.min = n; continue; case 'n': if (pedantic) while (*s == '\n') s++; else while (isspace(*s)) s++; continue; case 'N': NUMBER(9, 0, 999999999L); set.nsec = n; continue; case 'p': if ((n = tmlex(s, &u, tm_info.format + TM_MERIDIAN, TM_UT - TM_MERIDIAN, NiL, 0)) < 0) goto next; set.meridian = n; s = u; continue; case 'r': p = "%I:%M:%S %p"; break; case 'R': p = "%H:%M:%S"; break; case 's': x = strtoul(s, &u, 0); if (s == u) goto next; tm = tmxmake(tmxsns(x, 0)); s = u; CLEAR(set); continue; case 'S': NUMBER(2, 0, 61); set.sec = n; continue; case 'u': NUMBER(2, 1, 7); set.wday = n % 7; continue; case 'U': NUMBER(2, 0, 52); set.week = n; set.weektype = 0; continue; case 'V': NUMBER(2, 1, 53); set.week = n; set.weektype = 2; continue; case 'w': NUMBER(2, 0, 6); set.wday = n; continue; case 'W': NUMBER(2, 0, 52); set.week = n; set.weektype = 1; continue; case 'x': p = tm_info.format[TM_DATE]; break; case 'X': p = tm_info.format[TM_TIME]; break; case 'y': NUMBER(2, 0, 99); if (n < TM_WINDOW) n += 100; set.year = n; continue; case 'Y': NUMBER(4, 1969, 2100); set.year = n - 1900; continue; case 'Z': case 'q': if (zp = tmtype(s, &u)) { s = u; u = zp->type; } else u = 0; if (d == 'q') continue; case 'z': if ((zp = tmzone(s, &u, u, &m))) { s = u; set.zone = zp->west + m; tm_info.date = zp; } continue; case '|': s = b; goto again; case '&': x = gen(tm, &set); x = tmxdate(s, e, t); if (s == (const char*)*e) goto next; t = x; s = (const char*)*e; if (!*format || *format == '%' && *(format + 1) == '|') goto done; goto again; default: goto next; } if (sp >= &stack[elementsof(stack)]) goto next; *sp++ = (char*)format; format = (const char*)p; } else if (isspace(d)) while (isspace(*s)) s++; else if (*s != d) break; else s++; } next: if (sp > &stack[0]) format = (const char*)stack[0]; if (*format) { p = (char*)format; if (!*s && *p == '%' && *(p + 1) == '|') format += strlen(format); else while (*p) if (*p++ == '%' && *p && *p++ == '|' && *p) { format = (const char*)p; s = b; goto again; } } t = gen(tm, &set); done: if (e) { while (isspace(*s)) s++; *e = (char*)s; } if (f) { while (isspace(*format)) format++; *f = (char*)format; } return t; } /* * format==0 DATEMSK * *format==0 DATEMSK and tmxdate() * *format!=0 format */ Time_t tmxscan(const char* s, char** e, const char* format, char** f, Time_t t, long flags) { register char* v; register char** p; char* q; char* r; Time_t x; static int initialized; static char** datemask; tmlocale(); if (!format || !*format) { if (!initialized) { register Sfio_t* sp; register int n; off_t m; initialized = 1; if ((v = getenv("DATEMSK")) && *v && (sp = sfopen(NiL, v, "r"))) { for (n = 1; sfgetr(sp, '\n', 0); n++); m = sfseek(sp, 0L, SEEK_CUR); if (p = newof(0, char*, n, m)) { sfseek(sp, 0L, SEEK_SET); v = (char*)(p + n); if (sfread(sp, v, m) != m) { free(p); p = 0; } else { datemask = p; v[m] = 0; while (*v) { *p++ = v; if (!(v = strchr(v, '\n'))) break; *v++ = 0; } *p = 0; } } } } if (p = datemask) while (v = *p++) { x = scan(s, &q, v, &r, t, flags); if (!*q && !*r) { if (e) *e = q; if (f) *f = r; return x; } } if (f) *f = (char*)format; if (format) return tmxdate(s, e, t); if (e) *e = (char*)s; return 0; } return scan(s, e, format, f, t, flags); }