14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*8462SApril.Chin@Sun.COM *          Copyright (c) 1985-2008 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
7*8462SApril.Chin@Sun.COM *                    by AT&T Intellectual Property                     *
84887Schin *                                                                      *
94887Schin *                A copy of the License is available at                 *
104887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
114887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
124887Schin *                                                                      *
134887Schin *              Information and Software Systems Research               *
144887Schin *                            AT&T Research                             *
154887Schin *                           Florham Park NJ                            *
164887Schin *                                                                      *
174887Schin *                 Glenn Fowler <gsf@research.att.com>                  *
184887Schin *                  David Korn <dgk@research.att.com>                   *
194887Schin *                   Phong Vo <kpv@research.att.com>                    *
204887Schin *                                                                      *
214887Schin ***********************************************************************/
224887Schin #pragma prototyped
234887Schin /*
244887Schin  * Glenn Fowler
254887Schin  * AT&T Research
264887Schin  *
274887Schin  * Time_t conversion support
284887Schin  *
294887Schin  * scan date expression in s using format
304887Schin  * if non-null, e points to the first invalid sequence in s
314887Schin  * if non-null, f points to the first unused format char
324887Schin  * t provides default values
334887Schin  */
344887Schin 
354887Schin #include <tmx.h>
364887Schin #include <ctype.h>
374887Schin 
384887Schin typedef struct
394887Schin {
404887Schin 	int32_t		nsec;
414887Schin 	int		year;
424887Schin 	int		mon;
434887Schin 	int		week;
444887Schin 	int		weektype;
454887Schin 	int		yday;
464887Schin 	int		mday;
474887Schin 	int		wday;
484887Schin 	int		hour;
494887Schin 	int		min;
504887Schin 	int		sec;
514887Schin 	int		meridian;
524887Schin 	int		zone;
534887Schin } Set_t;
544887Schin 
554887Schin #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)
564887Schin 
574887Schin #define INDEX(m,x)	(((n)>=((x)-(m)))?((n)-=((x)-(m))):(n))
584887Schin 
594887Schin #define NUMBER(d,m,x)	do \
604887Schin 			{ \
614887Schin 				n = 0; \
624887Schin 				u = (char*)s; \
634887Schin 				while (s < (const char*)(u + d) && *s >= '0' && *s <= '9') \
644887Schin 					n = n * 10 + *s++ - '0'; \
654887Schin 				if (u == (char*)s || n < m || n > x) \
664887Schin 					goto next; \
674887Schin 			} while (0)
684887Schin 
694887Schin /*
704887Schin  * generate a Time_t from tm + set
714887Schin  */
724887Schin 
734887Schin static Time_t
744887Schin gen(register Tm_t* tm, register Set_t* set)
754887Schin {
764887Schin 	register int	n;
774887Schin 	Time_t		t;
784887Schin 
794887Schin 	if (set->year >= 0)
804887Schin 		tm->tm_year = set->year;
814887Schin 	if (set->mon >= 0)
824887Schin 	{
834887Schin 		if (set->year < 0 && set->mon < tm->tm_mon)
844887Schin 			tm->tm_year++;
854887Schin 		tm->tm_mon = set->mon;
864887Schin 		if (set->yday < 0 && set->mday < 0)
874887Schin 			tm->tm_mday = set->mday = 1;
884887Schin 	}
894887Schin 	if (set->week >= 0)
904887Schin 	{
914887Schin 		if (set->mon < 0)
924887Schin 		{
934887Schin 			tmweek(tm, set->weektype, set->week, set->wday);
944887Schin 			set->wday = -1;
954887Schin 		}
964887Schin 	}
974887Schin 	else if (set->yday >= 0)
984887Schin 	{
994887Schin 		if (set->mon < 0)
1004887Schin 		{
1014887Schin 			tm->tm_mon = 0;
1024887Schin 			tm->tm_mday = set->yday + 1;
1034887Schin 		}
1044887Schin 	}
1054887Schin 	else if (set->mday >= 0)
1064887Schin 		tm->tm_mday = set->mday;
1074887Schin 	if (set->hour >= 0)
1084887Schin 	{
1094887Schin 		if (set->hour < tm->tm_hour && set->yday < 0 && set->mday < 0 && set->wday < 0)
1104887Schin 			tm->tm_mday++;
1114887Schin 		tm->tm_hour = set->hour;
1124887Schin 		tm->tm_min = (set->min >= 0) ? set->min : 0;
1134887Schin 		tm->tm_sec = (set->sec >= 0) ? set->sec : 0;
1144887Schin 	}
1154887Schin 	else if (set->min >= 0)
1164887Schin 	{
1174887Schin 		tm->tm_min = set->min;
1184887Schin 		tm->tm_sec = (set->sec >= 0) ? set->sec : 0;
1194887Schin 	}
1204887Schin 	else if (set->sec >= 0)
1214887Schin 		tm->tm_sec = set->sec;
1224887Schin 	if (set->nsec < 1000000000L)
1234887Schin 		tm->tm_nsec = set->nsec;
1244887Schin 	if (set->meridian > 0)
1254887Schin 	{
1264887Schin 		if (tm->tm_hour < 12)
1274887Schin 			tm->tm_hour += 12;
1284887Schin 	}
1294887Schin 	else if (set->meridian == 0)
1304887Schin 	{
1314887Schin 		if (tm->tm_hour >= 12)
1324887Schin 			tm->tm_hour -= 12;
1334887Schin 	}
1344887Schin 	t = tmxtime(tm, set->zone);
1354887Schin 	tm = 0;
1364887Schin 	if (set->yday >= 0)
1374887Schin 	{
1384887Schin 		tm = tmxmake(t);
1394887Schin 		tm->tm_mday += set->yday - tm->tm_yday;
1404887Schin 	}
1414887Schin 	else if (set->wday >= 0)
1424887Schin 	{
1434887Schin 		tm = tmxmake(t);
1444887Schin 		if ((n = set->wday - tm->tm_wday) < 0)
1454887Schin 			n += 7;
1464887Schin 		tm->tm_mday += n;
1474887Schin 	}
1484887Schin 	if (set->nsec < 1000000000L)
1494887Schin 	{
1504887Schin 		if (!tm)
1514887Schin 			tm = tmxmake(t);
1524887Schin 		tm->tm_nsec = set->nsec;
1534887Schin 	}
1544887Schin 	return tm ? tmxtime(tm, set->zone) : t;
1554887Schin }
1564887Schin 
1574887Schin /*
1584887Schin  * the format scan workhorse
1594887Schin  */
1604887Schin 
1614887Schin static Time_t
1624887Schin scan(register const char* s, char** e, const char* format, char** f, Time_t t, long flags)
1634887Schin {
1644887Schin 	register int	d;
1654887Schin 	register int	n;
1664887Schin 	register char*	p;
1674887Schin 	register Tm_t*	tm;
1684887Schin 	const char*	b;
1694887Schin 	char*		u;
1704887Schin 	char*		stack[4];
1714887Schin 	int		m;
1724887Schin 	int		hi;
1734887Schin 	int		lo;
1744887Schin 	int		pedantic;
1754887Schin 	Time_t		x;
1764887Schin 	Set_t		set;
1774887Schin 	Tm_zone_t*	zp;
1784887Schin 
1794887Schin 	char**		sp = &stack[0];
1804887Schin 
1814887Schin 	while (isspace(*s))
1824887Schin 		s++;
1834887Schin 	b = s;
1844887Schin  again:
1854887Schin 	CLEAR(set);
1864887Schin 	tm = tmxmake(t);
1874887Schin 	tm_info.date = tm_info.zone;
1884887Schin 	pedantic = (flags & TM_PEDANTIC) != 0;
1894887Schin 	for (;;)
1904887Schin 	{
1914887Schin 		if (!(d = *format++))
1924887Schin 		{
1934887Schin 			if (sp <= &stack[0])
1944887Schin 			{
1954887Schin 				format--;
1964887Schin 				break;
1974887Schin 			}
1984887Schin 			format = (const char*)*--sp;
1994887Schin 		}
2004887Schin 		else if (!*s)
2014887Schin 		{
2024887Schin 			format--;
2034887Schin 			break;
2044887Schin 		}
2054887Schin 		else if (d == '%' && (d = *format) && format++ && d != '%')
2064887Schin 		{
2074887Schin 		more:
2084887Schin 			switch (d)
2094887Schin 			{
2104887Schin 			case 'a':
2114887Schin 				lo = TM_DAY_ABBREV;
2124887Schin 				hi = pedantic ? TM_DAY : TM_TIME;
2134887Schin 				goto get_wday;
2144887Schin 			case 'A':
2154887Schin 				lo = pedantic ? TM_DAY : TM_DAY_ABBREV;
2164887Schin 				hi = TM_TIME;
2174887Schin 			get_wday:
2184887Schin 				if ((n = tmlex(s, &u, tm_info.format + lo, hi - lo, NiL, 0)) < 0)
2194887Schin 					goto next;
2204887Schin 				s = u;
2214887Schin 				INDEX(TM_DAY_ABBREV, TM_DAY);
2224887Schin 				set.wday = n;
2234887Schin 				continue;
2244887Schin 			case 'b':
2254887Schin 			case 'h':
2264887Schin 				lo = TM_MONTH_ABBREV;
2274887Schin 				hi = pedantic ? TM_MONTH : TM_DAY_ABBREV;
2284887Schin 				goto get_mon;
2294887Schin 			case 'B':
2304887Schin 				lo = pedantic ? TM_MONTH : TM_MONTH_ABBREV;
2314887Schin 				hi = TM_DAY_ABBREV;
2324887Schin 			get_mon:
2334887Schin 				if ((n = tmlex(s, &u, tm_info.format + lo, hi - lo, NiL, 0)) < 0)
2344887Schin 					goto next;
2354887Schin 				s = u;
2364887Schin 				INDEX(TM_MONTH_ABBREV, TM_MONTH);
2374887Schin 				set.mon = n;
2384887Schin 				continue;
2394887Schin 			case 'c':
2404887Schin 				p = "%a %b %e %T %Y";
2414887Schin 				break;
2424887Schin 			case 'C':
2434887Schin 				NUMBER(2, 19, 99);
2444887Schin 				set.year = (n - 19) * 100 + tm->tm_year % 100;
2454887Schin 				continue;
2464887Schin 			case 'd':
2474887Schin 				if (pedantic && !isdigit(*s))
2484887Schin 					goto next;
2494887Schin 				/*FALLTHROUGH*/
2504887Schin 			case 'e':
2514887Schin 				NUMBER(2, 1, 31);
2524887Schin 				set.mday = n;
2534887Schin 				continue;
2544887Schin 			case 'D':
2554887Schin 				p = "%m/%d/%y";
2564887Schin 				break;
2574887Schin 			case 'E':
2584887Schin 			case 'O':
2594887Schin 				if (*format)
2604887Schin 				{
2614887Schin 					d = *format++;
2624887Schin 					goto more;
2634887Schin 				}
2644887Schin 				continue;
265*8462SApril.Chin@Sun.COM 			case 'F':
266*8462SApril.Chin@Sun.COM 				p = "%Y-%m-%d";
267*8462SApril.Chin@Sun.COM 				break;
2684887Schin 			case 'H':
2694887Schin 			case 'k':
2704887Schin 				NUMBER(2, 0, 23);
2714887Schin 				set.hour = n;
2724887Schin 				continue;
2734887Schin 			case 'I':
2744887Schin 			case 'l':
2754887Schin 				NUMBER(2, 1, 12);
2764887Schin 				set.hour = n;
2774887Schin 				continue;
2784887Schin 			case 'j':
2794887Schin 				NUMBER(3, 1, 366);
2804887Schin 				set.yday = n - 1;
2814887Schin 				continue;
2824887Schin 			case 'm':
2834887Schin 				NUMBER(2, 1, 12);
2844887Schin 				set.mon = n - 1;
2854887Schin 				continue;
2864887Schin 			case 'M':
2874887Schin 				NUMBER(2, 0, 59);
2884887Schin 				set.min = n;
2894887Schin 				continue;
2904887Schin 			case 'n':
2914887Schin 				if (pedantic)
2924887Schin 					while (*s == '\n')
2934887Schin 						s++;
2944887Schin 				else
2954887Schin 					while (isspace(*s))
2964887Schin 						s++;
2974887Schin 				continue;
2984887Schin 			case 'N':
2994887Schin 				NUMBER(9, 0, 999999999L);
3004887Schin 				set.nsec = n;
3014887Schin 				continue;
3024887Schin 			case 'p':
3034887Schin 				if ((n = tmlex(s, &u, tm_info.format + TM_MERIDIAN, TM_UT - TM_MERIDIAN, NiL, 0)) < 0)
3044887Schin 					goto next;
3054887Schin 				set.meridian = n;
3064887Schin 				s = u;
3074887Schin 				continue;
3084887Schin 			case 'r':
3094887Schin 				p = "%I:%M:%S %p";
3104887Schin 				break;
3114887Schin 			case 'R':
3124887Schin 				p = "%H:%M:%S";
3134887Schin 				break;
3144887Schin 			case 's':
3154887Schin 				x = strtoul(s, &u, 0);
3164887Schin 				if (s == u)
3174887Schin 					goto next;
3184887Schin 				tm = tmxmake(tmxsns(x, 0));
3194887Schin 				s = u;
3204887Schin 				CLEAR(set);
3214887Schin 				continue;
3224887Schin 			case 'S':
3234887Schin 				NUMBER(2, 0, 61);
3244887Schin 				set.sec = n;
3254887Schin 				continue;
3264887Schin 			case 'u':
3274887Schin 				NUMBER(2, 1, 7);
3284887Schin 				set.wday = n % 7;
3294887Schin 				continue;
3304887Schin 			case 'U':
3314887Schin 				NUMBER(2, 0, 52);
3324887Schin 				set.week = n;
3334887Schin 				set.weektype = 0;
3344887Schin 				continue;
3354887Schin 			case 'V':
3364887Schin 				NUMBER(2, 1, 53);
3374887Schin 				set.week = n;
3384887Schin 				set.weektype = 2;
3394887Schin 				continue;
3404887Schin 			case 'w':
3414887Schin 				NUMBER(2, 0, 6);
3424887Schin 				set.wday = n;
3434887Schin 				continue;
3444887Schin 			case 'W':
3454887Schin 				NUMBER(2, 0, 52);
3464887Schin 				set.week = n;
3474887Schin 				set.weektype = 1;
3484887Schin 				continue;
3494887Schin 			case 'x':
3504887Schin 				p = tm_info.format[TM_DATE];
3514887Schin 				break;
3524887Schin 			case 'X':
3534887Schin 				p = tm_info.format[TM_TIME];
3544887Schin 				break;
3554887Schin 			case 'y':
3564887Schin 				NUMBER(2, 0, 99);
3574887Schin 				if (n < TM_WINDOW)
3584887Schin 					n += 100;
3594887Schin 				set.year = n;
3604887Schin 				continue;
3614887Schin 			case 'Y':
3624887Schin 				NUMBER(4, 1969, 2100);
3634887Schin 				set.year = n - 1900;
3644887Schin 				continue;
3654887Schin 			case 'Z':
3664887Schin 			case 'q':
3674887Schin 				if (zp = tmtype(s, &u))
3684887Schin 				{
3694887Schin 					s = u;
3704887Schin 					u = zp->type;
3714887Schin 				}
3724887Schin 				else
3734887Schin 					u = 0;
3744887Schin 				if (d == 'q')
3754887Schin 					continue;
3764887Schin 			case 'z':
3774887Schin 				if ((zp = tmzone(s, &u, u, &m)))
3784887Schin 				{
3794887Schin 					s = u;
3804887Schin 					set.zone = zp->west + m;
3814887Schin 					tm_info.date = zp;
3824887Schin 				}
3834887Schin 				continue;
3844887Schin 			case '|':
3854887Schin 				s = b;
3864887Schin 				goto again;
3874887Schin 			case '&':
3884887Schin 				x = gen(tm, &set);
3894887Schin 				x = tmxdate(s, e, t);
3904887Schin 				if (s == (const char*)*e)
3914887Schin 					goto next;
3924887Schin 				t = x;
3934887Schin 				s = (const char*)*e;
3944887Schin 				if (!*format || *format == '%' && *(format + 1) == '|')
3954887Schin 					goto done;
3964887Schin 				goto again;
3974887Schin 			default:
3984887Schin 				goto next;
3994887Schin 			}
4004887Schin 			if (sp >= &stack[elementsof(stack)])
4014887Schin 				goto next;
4024887Schin 			*sp++ = (char*)format;
4034887Schin 			format = (const char*)p;
4044887Schin 		}
4054887Schin 		else if (isspace(d))
4064887Schin 			while (isspace(*s))
4074887Schin 				s++;
4084887Schin 		else if (*s != d)
4094887Schin 			break;
4104887Schin 		else
4114887Schin 			s++;
4124887Schin 	}
4134887Schin  next:
4144887Schin 	if (sp > &stack[0])
4154887Schin 		format = (const char*)stack[0];
4164887Schin 	if (*format)
4174887Schin 	{
4184887Schin 		p = (char*)format;
4194887Schin 		if (!*s && *p == '%' && *(p + 1) == '|')
4204887Schin 			format += strlen(format);
4214887Schin 		else
4224887Schin 			while (*p)
4234887Schin 				if (*p++ == '%' && *p && *p++ == '|' && *p)
4244887Schin 				{
4254887Schin 					format = (const char*)p;
4264887Schin 					s = b;
4274887Schin 					goto again;
4284887Schin 				}
4294887Schin 	}
4304887Schin 	t = gen(tm, &set);
4314887Schin  done:
4324887Schin 	if (e)
4334887Schin 	{
4344887Schin 		while (isspace(*s))
4354887Schin 			s++;
4364887Schin 		*e = (char*)s;
4374887Schin 	}
4384887Schin 	if (f)
4394887Schin 	{
4404887Schin 		while (isspace(*format))
4414887Schin 			format++;
4424887Schin 		*f = (char*)format;
4434887Schin 	}
4444887Schin 	return t;
4454887Schin }
4464887Schin 
4474887Schin /*
4484887Schin  *  format==0	DATEMSK
4494887Schin  * *format==0	DATEMSK and tmxdate()
4504887Schin  * *format!=0	format
4514887Schin  */
4524887Schin 
4534887Schin Time_t
4544887Schin tmxscan(const char* s, char** e, const char* format, char** f, Time_t t, long flags)
4554887Schin {
4564887Schin 	register char*	v;
4574887Schin 	register char**	p;
4584887Schin 	char*		q;
4594887Schin 	char*		r;
4604887Schin 	Time_t		x;
4614887Schin 
4624887Schin 	static int	initialized;
4634887Schin 	static char**	datemask;
4644887Schin 
4654887Schin 	tmlocale();
4664887Schin 	if (!format || !*format)
4674887Schin 	{
4684887Schin 		if (!initialized)
4694887Schin 		{
4704887Schin 			register Sfio_t*	sp;
4714887Schin 			register int		n;
4724887Schin 			off_t			m;
4734887Schin 
4744887Schin 			initialized = 1;
4754887Schin 			if ((v = getenv("DATEMSK")) && *v && (sp = sfopen(NiL, v, "r")))
4764887Schin 			{
4774887Schin 				for (n = 1; sfgetr(sp, '\n', 0); n++);
4784887Schin 				m = sfseek(sp, 0L, SEEK_CUR);
4794887Schin 				if (p = newof(0, char*, n, m))
4804887Schin 				{
4814887Schin 					sfseek(sp, 0L, SEEK_SET);
4824887Schin 					v = (char*)(p + n);
4834887Schin 					if (sfread(sp, v, m) != m)
4844887Schin 					{
4854887Schin 						free(p);
4864887Schin 						p = 0;
4874887Schin 					}
4884887Schin 					else
4894887Schin 					{
4904887Schin 						datemask = p;
4914887Schin 						v[m] = 0;
4924887Schin 						while (*v)
4934887Schin 						{
4944887Schin 							*p++ = v;
4954887Schin 							if (!(v = strchr(v, '\n')))
4964887Schin 								break;
4974887Schin 							*v++ = 0;
4984887Schin 						}
4994887Schin 						*p = 0;
5004887Schin 					}
5014887Schin 				}
5024887Schin 			}
5034887Schin 		}
5044887Schin 		if (p = datemask)
5054887Schin 			while (v = *p++)
5064887Schin 			{
5074887Schin 				x = scan(s, &q, v, &r, t, flags);
5084887Schin 				if (!*q && !*r)
5094887Schin 				{
5104887Schin 					if (e)
5114887Schin 						*e = q;
5124887Schin 					if (f)
5134887Schin 						*f = r;
5144887Schin 					return x;
5154887Schin 				}
5164887Schin 			}
5174887Schin 		if (f)
5184887Schin 			*f = (char*)format;
5194887Schin 		if (format)
5204887Schin 			return tmxdate(s, e, t);
5214887Schin 		if (e)
5224887Schin 			*e = (char*)s;
5234887Schin 		return 0;
5244887Schin 	}
5254887Schin 	return scan(s, e, format, f, t, flags);
5264887Schin }
527