1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * Phong Vo <kpv@research.att.com> *
20 * *
21 ***********************************************************************/
22 #pragma prototyped
23 /*
24 * Glenn Fowler
25 * AT&T Research
26 *
27 * Time_t conversion support
28 *
29 * scan date expression in s using format
30 * if non-null, e points to the first invalid sequence in s
31 * if non-null, f points to the first unused format char
32 * t provides default values
33 */
34
35 #include <tmx.h>
36 #include <ctype.h>
37
38 typedef struct
39 {
40 int32_t nsec;
41 int year;
42 int mon;
43 int week;
44 int weektype;
45 int yday;
46 int mday;
47 int wday;
48 int hour;
49 int min;
50 int sec;
51 int meridian;
52 int zone;
53 } Set_t;
54
55 #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)
56
57 #define INDEX(m,x) (((n)>=((x)-(m)))?((n)-=((x)-(m))):(n))
58
59 #define NUMBER(d,m,x) do \
60 { \
61 n = 0; \
62 u = (char*)s; \
63 while (s < (const char*)(u + d) && *s >= '0' && *s <= '9') \
64 n = n * 10 + *s++ - '0'; \
65 if (u == (char*)s || n < m || n > x) \
66 goto next; \
67 } while (0)
68
69 /*
70 * generate a Time_t from tm + set
71 */
72
73 static Time_t
gen(register Tm_t * tm,register Set_t * set)74 gen(register Tm_t* tm, register Set_t* set)
75 {
76 register int n;
77 int z;
78 Time_t t;
79
80 if (set->year >= 0)
81 tm->tm_year = set->year;
82 if (set->mon >= 0)
83 {
84 if (set->year < 0 && set->mon < tm->tm_mon)
85 tm->tm_year++;
86 tm->tm_mon = set->mon;
87 if (set->yday < 0 && set->mday < 0)
88 tm->tm_mday = set->mday = 1;
89 }
90 if (set->week >= 0)
91 {
92 if (set->mon < 0)
93 {
94 tmweek(tm, set->weektype, set->week, set->wday);
95 set->wday = -1;
96 }
97 }
98 else if (set->yday >= 0)
99 {
100 if (set->mon < 0)
101 {
102 tm->tm_mon = 0;
103 tm->tm_mday = set->yday + 1;
104 }
105 }
106 else if (set->mday >= 0)
107 tm->tm_mday = set->mday;
108 if (set->hour >= 0)
109 {
110 if (set->hour < tm->tm_hour && set->yday < 0 && set->mday < 0 && set->wday < 0)
111 tm->tm_mday++;
112 tm->tm_hour = set->hour;
113 tm->tm_min = (set->min >= 0) ? set->min : 0;
114 tm->tm_sec = (set->sec >= 0) ? set->sec : 0;
115 }
116 else if (set->min >= 0)
117 {
118 tm->tm_min = set->min;
119 tm->tm_sec = (set->sec >= 0) ? set->sec : 0;
120 }
121 else if (set->sec >= 0)
122 tm->tm_sec = set->sec;
123 if (set->nsec < 1000000000L)
124 tm->tm_nsec = set->nsec;
125 if (set->meridian > 0)
126 {
127 if (tm->tm_hour < 12)
128 tm->tm_hour += 12;
129 }
130 else if (set->meridian == 0)
131 {
132 if (tm->tm_hour >= 12)
133 tm->tm_hour -= 12;
134 }
135 t = tmxtime(tm, set->zone);
136 if (set->yday >= 0)
137 {
138 z = 1;
139 tm = tmxtm(tm, t, tm->tm_zone);
140 tm->tm_mday += set->yday - tm->tm_yday;
141 }
142 else if (set->wday >= 0)
143 {
144 z = 1;
145 tm = tmxtm(tm, t, tm->tm_zone);
146 if ((n = set->wday - tm->tm_wday) < 0)
147 n += 7;
148 tm->tm_mday += n;
149 }
150 else
151 z = 0;
152 if (set->nsec < 1000000000L)
153 {
154 if (!z)
155 {
156 z = 1;
157 tm = tmxtm(tm, t, tm->tm_zone);
158 }
159 tm->tm_nsec = set->nsec;
160 }
161 return z ? tmxtime(tm, set->zone) : t;
162 }
163
164 /*
165 * the format scan workhorse
166 */
167
168 static Time_t
scan(register const char * s,char ** e,const char * format,char ** f,Time_t t,long flags)169 scan(register const char* s, char** e, const char* format, char** f, Time_t t, long flags)
170 {
171 register int d;
172 register int n;
173 register char* p;
174 register Tm_t* tm;
175 const char* b;
176 char* u;
177 char* stack[4];
178 int m;
179 int hi;
180 int lo;
181 int pedantic;
182 Time_t x;
183 Set_t set;
184 Tm_zone_t* zp;
185 Tm_t ts;
186
187 char** sp = &stack[0];
188
189 while (isspace(*s))
190 s++;
191 b = s;
192 again:
193 CLEAR(set);
194 tm = tmxtm(&ts, t, NiL);
195 pedantic = (flags & TM_PEDANTIC) != 0;
196 for (;;)
197 {
198 if (!(d = *format++))
199 {
200 if (sp <= &stack[0])
201 {
202 format--;
203 break;
204 }
205 format = (const char*)*--sp;
206 }
207 else if (!*s)
208 {
209 format--;
210 break;
211 }
212 else if (d == '%' && (d = *format) && format++ && d != '%')
213 {
214 more:
215 switch (d)
216 {
217 case 'a':
218 lo = TM_DAY_ABBREV;
219 hi = pedantic ? TM_DAY : TM_TIME;
220 goto get_wday;
221 case 'A':
222 lo = pedantic ? TM_DAY : TM_DAY_ABBREV;
223 hi = TM_TIME;
224 get_wday:
225 if ((n = tmlex(s, &u, tm_info.format + lo, hi - lo, NiL, 0)) < 0)
226 goto next;
227 s = u;
228 INDEX(TM_DAY_ABBREV, TM_DAY);
229 set.wday = n;
230 continue;
231 case 'b':
232 case 'h':
233 lo = TM_MONTH_ABBREV;
234 hi = pedantic ? TM_MONTH : TM_DAY_ABBREV;
235 goto get_mon;
236 case 'B':
237 lo = pedantic ? TM_MONTH : TM_MONTH_ABBREV;
238 hi = TM_DAY_ABBREV;
239 get_mon:
240 if ((n = tmlex(s, &u, tm_info.format + lo, hi - lo, NiL, 0)) < 0)
241 goto next;
242 s = u;
243 INDEX(TM_MONTH_ABBREV, TM_MONTH);
244 set.mon = n;
245 continue;
246 case 'c':
247 p = "%a %b %e %T %Y";
248 break;
249 case 'C':
250 NUMBER(2, 19, 99);
251 set.year = (n - 19) * 100 + tm->tm_year % 100;
252 continue;
253 case 'd':
254 if (pedantic && !isdigit(*s))
255 goto next;
256 /*FALLTHROUGH*/
257 case 'e':
258 NUMBER(2, 1, 31);
259 set.mday = n;
260 continue;
261 case 'D':
262 p = "%m/%d/%y";
263 break;
264 case 'E':
265 case 'O':
266 if (*format)
267 {
268 d = *format++;
269 goto more;
270 }
271 continue;
272 case 'F':
273 p = "%Y-%m-%d";
274 break;
275 case 'H':
276 case 'k':
277 NUMBER(2, 0, 23);
278 set.hour = n;
279 continue;
280 case 'I':
281 case 'l':
282 NUMBER(2, 1, 12);
283 set.hour = n;
284 continue;
285 case 'j':
286 NUMBER(3, 1, 366);
287 set.yday = n - 1;
288 continue;
289 case 'm':
290 NUMBER(2, 1, 12);
291 set.mon = n - 1;
292 continue;
293 case 'M':
294 NUMBER(2, 0, 59);
295 set.min = n;
296 continue;
297 case 'n':
298 if (pedantic)
299 while (*s == '\n')
300 s++;
301 else
302 while (isspace(*s))
303 s++;
304 continue;
305 case 'N':
306 NUMBER(9, 0, 999999999L);
307 set.nsec = n;
308 continue;
309 case 'p':
310 if ((n = tmlex(s, &u, tm_info.format + TM_MERIDIAN, TM_UT - TM_MERIDIAN, NiL, 0)) < 0)
311 goto next;
312 set.meridian = n;
313 s = u;
314 continue;
315 case 'r':
316 p = "%I:%M:%S %p";
317 break;
318 case 'R':
319 p = "%H:%M:%S";
320 break;
321 case 's':
322 x = strtoul(s, &u, 0);
323 if (s == u)
324 goto next;
325 tm = tmxtm(tm, tmxsns(x, 0), tm->tm_zone);
326 s = u;
327 CLEAR(set);
328 continue;
329 case 'S':
330 NUMBER(2, 0, 61);
331 set.sec = n;
332 continue;
333 case 'u':
334 NUMBER(2, 1, 7);
335 set.wday = n % 7;
336 continue;
337 case 'U':
338 NUMBER(2, 0, 52);
339 set.week = n;
340 set.weektype = 0;
341 continue;
342 case 'V':
343 NUMBER(2, 1, 53);
344 set.week = n;
345 set.weektype = 2;
346 continue;
347 case 'w':
348 NUMBER(2, 0, 6);
349 set.wday = n;
350 continue;
351 case 'W':
352 NUMBER(2, 0, 52);
353 set.week = n;
354 set.weektype = 1;
355 continue;
356 case 'x':
357 p = tm_info.format[TM_DATE];
358 break;
359 case 'X':
360 p = tm_info.format[TM_TIME];
361 break;
362 case 'y':
363 NUMBER(2, 0, 99);
364 if (n < TM_WINDOW)
365 n += 100;
366 set.year = n;
367 continue;
368 case 'Y':
369 NUMBER(4, 1969, 2100);
370 set.year = n - 1900;
371 continue;
372 case 'Z':
373 case 'q':
374 if (zp = tmtype(s, &u))
375 {
376 s = u;
377 u = zp->type;
378 }
379 else
380 u = 0;
381 if (d == 'q')
382 continue;
383 case 'z':
384 if ((zp = tmzone(s, &u, u, &m)))
385 {
386 s = u;
387 set.zone = zp->west + m;
388 tm_info.date = zp;
389 }
390 continue;
391 case '|':
392 s = b;
393 goto again;
394 case '&':
395 x = gen(tm, &set);
396 x = tmxdate(s, e, t);
397 if (s == (const char*)*e)
398 goto next;
399 t = x;
400 s = (const char*)*e;
401 if (!*format || *format == '%' && *(format + 1) == '|')
402 goto done;
403 goto again;
404 default:
405 goto next;
406 }
407 if (sp >= &stack[elementsof(stack)])
408 goto next;
409 *sp++ = (char*)format;
410 format = (const char*)p;
411 }
412 else if (isspace(d))
413 while (isspace(*s))
414 s++;
415 else if (*s != d)
416 break;
417 else
418 s++;
419 }
420 next:
421 if (sp > &stack[0])
422 format = (const char*)stack[0];
423 if (*format)
424 {
425 p = (char*)format;
426 if (!*s && *p == '%' && *(p + 1) == '|')
427 format += strlen(format);
428 else
429 while (*p)
430 if (*p++ == '%' && *p && *p++ == '|' && *p)
431 {
432 format = (const char*)p;
433 s = b;
434 goto again;
435 }
436 }
437 t = gen(tm, &set);
438 done:
439 if (e)
440 {
441 while (isspace(*s))
442 s++;
443 *e = (char*)s;
444 }
445 if (f)
446 {
447 while (isspace(*format))
448 format++;
449 *f = (char*)format;
450 }
451 return t;
452 }
453
454 /*
455 * format==0 DATEMSK
456 * *format==0 DATEMSK and tmxdate()
457 * *format!=0 format
458 */
459
460 Time_t
tmxscan(const char * s,char ** e,const char * format,char ** f,Time_t t,long flags)461 tmxscan(const char* s, char** e, const char* format, char** f, Time_t t, long flags)
462 {
463 register char* v;
464 register char** p;
465 char* q;
466 char* r;
467 Time_t x;
468
469 static int initialized;
470 static char** datemask;
471
472 tmlocale();
473 if (!format || !*format)
474 {
475 if (!initialized)
476 {
477 register Sfio_t* sp;
478 register int n;
479 off_t m;
480
481 initialized = 1;
482 if ((v = getenv("DATEMSK")) && *v && (sp = sfopen(NiL, v, "r")))
483 {
484 for (n = 1; sfgetr(sp, '\n', 0); n++);
485 m = sfseek(sp, 0L, SEEK_CUR);
486 if (p = newof(0, char*, n, m))
487 {
488 sfseek(sp, 0L, SEEK_SET);
489 v = (char*)(p + n);
490 if (sfread(sp, v, m) != m)
491 {
492 free(p);
493 p = 0;
494 }
495 else
496 {
497 datemask = p;
498 v[m] = 0;
499 while (*v)
500 {
501 *p++ = v;
502 if (!(v = strchr(v, '\n')))
503 break;
504 *v++ = 0;
505 }
506 *p = 0;
507 }
508 }
509 }
510 }
511 if (p = datemask)
512 while (v = *p++)
513 {
514 x = scan(s, &q, v, &r, t, flags);
515 if (!*q && !*r)
516 {
517 if (e)
518 *e = q;
519 if (f)
520 *f = r;
521 return x;
522 }
523 }
524 if (f)
525 *f = (char*)format;
526 if (format)
527 return tmxdate(s, e, t);
528 if (e)
529 *e = (char*)s;
530 return 0;
531 }
532 return scan(s, e, format, f, t, flags);
533 }
534