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