xref: /titanic_41/usr/src/lib/libast/common/tm/tmxscan.c (revision 9444c26f4faabda140242c3986089704c4073ced)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2009 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 	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
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
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