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