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*                  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
38typedef 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
73static Time_t
74gen(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
168static Time_t
169scan(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
460Time_t
461tmxscan(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