1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2011 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
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 				/* FALLTHROUGH */
384 			case 'z':
385 				if ((zp = tmzone(s, &u, u, &m)))
386 				{
387 					s = u;
388 					set.zone = zp->west + m;
389 					tm_info.date = zp;
390 				}
391 				continue;
392 			case '|':
393 				s = b;
394 				goto again;
395 			case '&':
396 				x = gen(tm, &set);
397 				x = tmxdate(s, e, t);
398 				if (s == (const char*)*e)
399 					goto next;
400 				t = x;
401 				s = (const char*)*e;
402 				if (!*format || *format == '%' && *(format + 1) == '|')
403 					goto done;
404 				goto again;
405 			default:
406 				goto next;
407 			}
408 			if (sp >= &stack[elementsof(stack)])
409 				goto next;
410 			*sp++ = (char*)format;
411 			format = (const char*)p;
412 		}
413 		else if (isspace(d))
414 			while (isspace(*s))
415 				s++;
416 		else if (*s != d)
417 			break;
418 		else
419 			s++;
420 	}
421  next:
422 	if (sp > &stack[0])
423 		format = (const char*)stack[0];
424 	if (*format)
425 	{
426 		p = (char*)format;
427 		if (!*s && *p == '%' && *(p + 1) == '|')
428 			format += strlen(format);
429 		else
430 			while (*p)
431 				if (*p++ == '%' && *p && *p++ == '|' && *p)
432 				{
433 					format = (const char*)p;
434 					s = b;
435 					goto again;
436 				}
437 	}
438 	t = gen(tm, &set);
439  done:
440 	if (e)
441 	{
442 		while (isspace(*s))
443 			s++;
444 		*e = (char*)s;
445 	}
446 	if (f)
447 	{
448 		while (isspace(*format))
449 			format++;
450 		*f = (char*)format;
451 	}
452 	return t;
453 }
454 
455 /*
456  *  format==0	DATEMSK
457  * *format==0	DATEMSK and tmxdate()
458  * *format!=0	format
459  */
460 
461 Time_t
tmxscan(const char * s,char ** e,const char * format,char ** f,Time_t t,long flags)462 tmxscan(const char* s, char** e, const char* format, char** f, Time_t t, long flags)
463 {
464 	register char*	v;
465 	register char**	p;
466 	char*		q;
467 	char*		r;
468 	Time_t		x;
469 
470 	static int	initialized;
471 	static char**	datemask;
472 
473 	tmlocale();
474 	if (!format || !*format)
475 	{
476 		if (!initialized)
477 		{
478 			register Sfio_t*	sp;
479 			register int		n;
480 			off_t			m;
481 
482 			initialized = 1;
483 			if ((v = getenv("DATEMSK")) && *v && (sp = sfopen(NiL, v, "r")))
484 			{
485 				for (n = 1; sfgetr(sp, '\n', 0); n++);
486 				m = sfseek(sp, 0L, SEEK_CUR);
487 				if (p = newof(0, char*, n, m))
488 				{
489 					sfseek(sp, 0L, SEEK_SET);
490 					v = (char*)(p + n);
491 					if (sfread(sp, v, m) != m)
492 					{
493 						free(p);
494 						p = 0;
495 					}
496 					else
497 					{
498 						datemask = p;
499 						v[m] = 0;
500 						while (*v)
501 						{
502 							*p++ = v;
503 							if (!(v = strchr(v, '\n')))
504 								break;
505 							*v++ = 0;
506 						}
507 						*p = 0;
508 					}
509 				}
510 			}
511 		}
512 		if (p = datemask)
513 			while (v = *p++)
514 			{
515 				x = scan(s, &q, v, &r, t, flags);
516 				if (!*q && !*r)
517 				{
518 					if (e)
519 						*e = q;
520 					if (f)
521 						*f = r;
522 					return x;
523 				}
524 			}
525 		if (f)
526 			*f = (char*)format;
527 		if (format)
528 			return tmxdate(s, e, t);
529 		if (e)
530 			*e = (char*)s;
531 		return 0;
532 	}
533 	return scan(s, e, format, f, t, flags);
534 }
535