xref: /onnv-gate/usr/src/lib/libast/common/tm/tmxscan.c (revision 12068:08a39a083754)
14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*12068SRoger.Faulkner@Oracle.COM *          Copyright (c) 1985-2010 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
78462SApril.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
gen(register Tm_t * tm,register Set_t * set)744887Schin gen(register Tm_t* tm, register Set_t* set)
754887Schin {
764887Schin 	register int	n;
7710898Sroland.mainz@nrubsig.org 	int		z;
784887Schin 	Time_t		t;
794887Schin 
804887Schin 	if (set->year >= 0)
814887Schin 		tm->tm_year = set->year;
824887Schin 	if (set->mon >= 0)
834887Schin 	{
844887Schin 		if (set->year < 0 && set->mon < tm->tm_mon)
854887Schin 			tm->tm_year++;
864887Schin 		tm->tm_mon = set->mon;
874887Schin 		if (set->yday < 0 && set->mday < 0)
884887Schin 			tm->tm_mday = set->mday = 1;
894887Schin 	}
904887Schin 	if (set->week >= 0)
914887Schin 	{
924887Schin 		if (set->mon < 0)
934887Schin 		{
944887Schin 			tmweek(tm, set->weektype, set->week, set->wday);
954887Schin 			set->wday = -1;
964887Schin 		}
974887Schin 	}
984887Schin 	else if (set->yday >= 0)
994887Schin 	{
1004887Schin 		if (set->mon < 0)
1014887Schin 		{
1024887Schin 			tm->tm_mon = 0;
1034887Schin 			tm->tm_mday = set->yday + 1;
1044887Schin 		}
1054887Schin 	}
1064887Schin 	else if (set->mday >= 0)
1074887Schin 		tm->tm_mday = set->mday;
1084887Schin 	if (set->hour >= 0)
1094887Schin 	{
1104887Schin 		if (set->hour < tm->tm_hour && set->yday < 0 && set->mday < 0 && set->wday < 0)
1114887Schin 			tm->tm_mday++;
1124887Schin 		tm->tm_hour = set->hour;
1134887Schin 		tm->tm_min = (set->min >= 0) ? set->min : 0;
1144887Schin 		tm->tm_sec = (set->sec >= 0) ? set->sec : 0;
1154887Schin 	}
1164887Schin 	else if (set->min >= 0)
1174887Schin 	{
1184887Schin 		tm->tm_min = set->min;
1194887Schin 		tm->tm_sec = (set->sec >= 0) ? set->sec : 0;
1204887Schin 	}
1214887Schin 	else if (set->sec >= 0)
1224887Schin 		tm->tm_sec = set->sec;
1234887Schin 	if (set->nsec < 1000000000L)
1244887Schin 		tm->tm_nsec = set->nsec;
1254887Schin 	if (set->meridian > 0)
1264887Schin 	{
1274887Schin 		if (tm->tm_hour < 12)
1284887Schin 			tm->tm_hour += 12;
1294887Schin 	}
1304887Schin 	else if (set->meridian == 0)
1314887Schin 	{
1324887Schin 		if (tm->tm_hour >= 12)
1334887Schin 			tm->tm_hour -= 12;
1344887Schin 	}
1354887Schin 	t = tmxtime(tm, set->zone);
1364887Schin 	if (set->yday >= 0)
1374887Schin 	{
13810898Sroland.mainz@nrubsig.org 		z = 1;
13910898Sroland.mainz@nrubsig.org 		tm = tmxtm(tm, t, tm->tm_zone);
1404887Schin 		tm->tm_mday += set->yday - tm->tm_yday;
1414887Schin 	}
1424887Schin 	else if (set->wday >= 0)
1434887Schin 	{
14410898Sroland.mainz@nrubsig.org 		z = 1;
14510898Sroland.mainz@nrubsig.org 		tm = tmxtm(tm, t, tm->tm_zone);
1464887Schin 		if ((n = set->wday - tm->tm_wday) < 0)
1474887Schin 			n += 7;
1484887Schin 		tm->tm_mday += n;
1494887Schin 	}
15010898Sroland.mainz@nrubsig.org 	else
15110898Sroland.mainz@nrubsig.org 		z = 0;
1524887Schin 	if (set->nsec < 1000000000L)
1534887Schin 	{
15410898Sroland.mainz@nrubsig.org 		if (!z)
15510898Sroland.mainz@nrubsig.org 		{
15610898Sroland.mainz@nrubsig.org 			z = 1;
15710898Sroland.mainz@nrubsig.org 			tm = tmxtm(tm, t, tm->tm_zone);
15810898Sroland.mainz@nrubsig.org 		}
1594887Schin 		tm->tm_nsec = set->nsec;
1604887Schin 	}
16110898Sroland.mainz@nrubsig.org 	return z ? tmxtime(tm, set->zone) : t;
1624887Schin }
1634887Schin 
1644887Schin /*
1654887Schin  * the format scan workhorse
1664887Schin  */
1674887Schin 
1684887Schin static Time_t
scan(register const char * s,char ** e,const char * format,char ** f,Time_t t,long flags)1694887Schin scan(register const char* s, char** e, const char* format, char** f, Time_t t, long flags)
1704887Schin {
1714887Schin 	register int	d;
1724887Schin 	register int	n;
1734887Schin 	register char*	p;
1744887Schin 	register Tm_t*	tm;
1754887Schin 	const char*	b;
1764887Schin 	char*		u;
1774887Schin 	char*		stack[4];
1784887Schin 	int		m;
1794887Schin 	int		hi;
1804887Schin 	int		lo;
1814887Schin 	int		pedantic;
1824887Schin 	Time_t		x;
1834887Schin 	Set_t		set;
1844887Schin 	Tm_zone_t*	zp;
18510898Sroland.mainz@nrubsig.org 	Tm_t		ts;
1864887Schin 
1874887Schin 	char**		sp = &stack[0];
1884887Schin 
1894887Schin 	while (isspace(*s))
1904887Schin 		s++;
1914887Schin 	b = s;
1924887Schin  again:
1934887Schin 	CLEAR(set);
19410898Sroland.mainz@nrubsig.org 	tm = tmxtm(&ts, t, NiL);
1954887Schin 	pedantic = (flags & TM_PEDANTIC) != 0;
1964887Schin 	for (;;)
1974887Schin 	{
1984887Schin 		if (!(d = *format++))
1994887Schin 		{
2004887Schin 			if (sp <= &stack[0])
2014887Schin 			{
2024887Schin 				format--;
2034887Schin 				break;
2044887Schin 			}
2054887Schin 			format = (const char*)*--sp;
2064887Schin 		}
2074887Schin 		else if (!*s)
2084887Schin 		{
2094887Schin 			format--;
2104887Schin 			break;
2114887Schin 		}
2124887Schin 		else if (d == '%' && (d = *format) && format++ && d != '%')
2134887Schin 		{
2144887Schin 		more:
2154887Schin 			switch (d)
2164887Schin 			{
2174887Schin 			case 'a':
2184887Schin 				lo = TM_DAY_ABBREV;
2194887Schin 				hi = pedantic ? TM_DAY : TM_TIME;
2204887Schin 				goto get_wday;
2214887Schin 			case 'A':
2224887Schin 				lo = pedantic ? TM_DAY : TM_DAY_ABBREV;
2234887Schin 				hi = TM_TIME;
2244887Schin 			get_wday:
2254887Schin 				if ((n = tmlex(s, &u, tm_info.format + lo, hi - lo, NiL, 0)) < 0)
2264887Schin 					goto next;
2274887Schin 				s = u;
2284887Schin 				INDEX(TM_DAY_ABBREV, TM_DAY);
2294887Schin 				set.wday = n;
2304887Schin 				continue;
2314887Schin 			case 'b':
2324887Schin 			case 'h':
2334887Schin 				lo = TM_MONTH_ABBREV;
2344887Schin 				hi = pedantic ? TM_MONTH : TM_DAY_ABBREV;
2354887Schin 				goto get_mon;
2364887Schin 			case 'B':
2374887Schin 				lo = pedantic ? TM_MONTH : TM_MONTH_ABBREV;
2384887Schin 				hi = TM_DAY_ABBREV;
2394887Schin 			get_mon:
2404887Schin 				if ((n = tmlex(s, &u, tm_info.format + lo, hi - lo, NiL, 0)) < 0)
2414887Schin 					goto next;
2424887Schin 				s = u;
2434887Schin 				INDEX(TM_MONTH_ABBREV, TM_MONTH);
2444887Schin 				set.mon = n;
2454887Schin 				continue;
2464887Schin 			case 'c':
2474887Schin 				p = "%a %b %e %T %Y";
2484887Schin 				break;
2494887Schin 			case 'C':
2504887Schin 				NUMBER(2, 19, 99);
2514887Schin 				set.year = (n - 19) * 100 + tm->tm_year % 100;
2524887Schin 				continue;
2534887Schin 			case 'd':
2544887Schin 				if (pedantic && !isdigit(*s))
2554887Schin 					goto next;
2564887Schin 				/*FALLTHROUGH*/
2574887Schin 			case 'e':
2584887Schin 				NUMBER(2, 1, 31);
2594887Schin 				set.mday = n;
2604887Schin 				continue;
2614887Schin 			case 'D':
2624887Schin 				p = "%m/%d/%y";
2634887Schin 				break;
2644887Schin 			case 'E':
2654887Schin 			case 'O':
2664887Schin 				if (*format)
2674887Schin 				{
2684887Schin 					d = *format++;
2694887Schin 					goto more;
2704887Schin 				}
2714887Schin 				continue;
2728462SApril.Chin@Sun.COM 			case 'F':
2738462SApril.Chin@Sun.COM 				p = "%Y-%m-%d";
2748462SApril.Chin@Sun.COM 				break;
2754887Schin 			case 'H':
2764887Schin 			case 'k':
2774887Schin 				NUMBER(2, 0, 23);
2784887Schin 				set.hour = n;
2794887Schin 				continue;
2804887Schin 			case 'I':
2814887Schin 			case 'l':
2824887Schin 				NUMBER(2, 1, 12);
2834887Schin 				set.hour = n;
2844887Schin 				continue;
2854887Schin 			case 'j':
2864887Schin 				NUMBER(3, 1, 366);
2874887Schin 				set.yday = n - 1;
2884887Schin 				continue;
2894887Schin 			case 'm':
2904887Schin 				NUMBER(2, 1, 12);
2914887Schin 				set.mon = n - 1;
2924887Schin 				continue;
2934887Schin 			case 'M':
2944887Schin 				NUMBER(2, 0, 59);
2954887Schin 				set.min = n;
2964887Schin 				continue;
2974887Schin 			case 'n':
2984887Schin 				if (pedantic)
2994887Schin 					while (*s == '\n')
3004887Schin 						s++;
3014887Schin 				else
3024887Schin 					while (isspace(*s))
3034887Schin 						s++;
3044887Schin 				continue;
3054887Schin 			case 'N':
3064887Schin 				NUMBER(9, 0, 999999999L);
3074887Schin 				set.nsec = n;
3084887Schin 				continue;
3094887Schin 			case 'p':
3104887Schin 				if ((n = tmlex(s, &u, tm_info.format + TM_MERIDIAN, TM_UT - TM_MERIDIAN, NiL, 0)) < 0)
3114887Schin 					goto next;
3124887Schin 				set.meridian = n;
3134887Schin 				s = u;
3144887Schin 				continue;
3154887Schin 			case 'r':
3164887Schin 				p = "%I:%M:%S %p";
3174887Schin 				break;
3184887Schin 			case 'R':
3194887Schin 				p = "%H:%M:%S";
3204887Schin 				break;
3214887Schin 			case 's':
3224887Schin 				x = strtoul(s, &u, 0);
3234887Schin 				if (s == u)
3244887Schin 					goto next;
32510898Sroland.mainz@nrubsig.org 				tm = tmxtm(tm, tmxsns(x, 0), tm->tm_zone);
3264887Schin 				s = u;
3274887Schin 				CLEAR(set);
3284887Schin 				continue;
3294887Schin 			case 'S':
3304887Schin 				NUMBER(2, 0, 61);
3314887Schin 				set.sec = n;
3324887Schin 				continue;
3334887Schin 			case 'u':
3344887Schin 				NUMBER(2, 1, 7);
3354887Schin 				set.wday = n % 7;
3364887Schin 				continue;
3374887Schin 			case 'U':
3384887Schin 				NUMBER(2, 0, 52);
3394887Schin 				set.week = n;
3404887Schin 				set.weektype = 0;
3414887Schin 				continue;
3424887Schin 			case 'V':
3434887Schin 				NUMBER(2, 1, 53);
3444887Schin 				set.week = n;
3454887Schin 				set.weektype = 2;
3464887Schin 				continue;
3474887Schin 			case 'w':
3484887Schin 				NUMBER(2, 0, 6);
3494887Schin 				set.wday = n;
3504887Schin 				continue;
3514887Schin 			case 'W':
3524887Schin 				NUMBER(2, 0, 52);
3534887Schin 				set.week = n;
3544887Schin 				set.weektype = 1;
3554887Schin 				continue;
3564887Schin 			case 'x':
3574887Schin 				p = tm_info.format[TM_DATE];
3584887Schin 				break;
3594887Schin 			case 'X':
3604887Schin 				p = tm_info.format[TM_TIME];
3614887Schin 				break;
3624887Schin 			case 'y':
3634887Schin 				NUMBER(2, 0, 99);
3644887Schin 				if (n < TM_WINDOW)
3654887Schin 					n += 100;
3664887Schin 				set.year = n;
3674887Schin 				continue;
3684887Schin 			case 'Y':
3694887Schin 				NUMBER(4, 1969, 2100);
3704887Schin 				set.year = n - 1900;
3714887Schin 				continue;
3724887Schin 			case 'Z':
3734887Schin 			case 'q':
3744887Schin 				if (zp = tmtype(s, &u))
3754887Schin 				{
3764887Schin 					s = u;
3774887Schin 					u = zp->type;
3784887Schin 				}
3794887Schin 				else
3804887Schin 					u = 0;
3814887Schin 				if (d == 'q')
3824887Schin 					continue;
3834887Schin 			case 'z':
3844887Schin 				if ((zp = tmzone(s, &u, u, &m)))
3854887Schin 				{
3864887Schin 					s = u;
3874887Schin 					set.zone = zp->west + m;
3884887Schin 					tm_info.date = zp;
3894887Schin 				}
3904887Schin 				continue;
3914887Schin 			case '|':
3924887Schin 				s = b;
3934887Schin 				goto again;
3944887Schin 			case '&':
3954887Schin 				x = gen(tm, &set);
3964887Schin 				x = tmxdate(s, e, t);
3974887Schin 				if (s == (const char*)*e)
3984887Schin 					goto next;
3994887Schin 				t = x;
4004887Schin 				s = (const char*)*e;
4014887Schin 				if (!*format || *format == '%' && *(format + 1) == '|')
4024887Schin 					goto done;
4034887Schin 				goto again;
4044887Schin 			default:
4054887Schin 				goto next;
4064887Schin 			}
4074887Schin 			if (sp >= &stack[elementsof(stack)])
4084887Schin 				goto next;
4094887Schin 			*sp++ = (char*)format;
4104887Schin 			format = (const char*)p;
4114887Schin 		}
4124887Schin 		else if (isspace(d))
4134887Schin 			while (isspace(*s))
4144887Schin 				s++;
4154887Schin 		else if (*s != d)
4164887Schin 			break;
4174887Schin 		else
4184887Schin 			s++;
4194887Schin 	}
4204887Schin  next:
4214887Schin 	if (sp > &stack[0])
4224887Schin 		format = (const char*)stack[0];
4234887Schin 	if (*format)
4244887Schin 	{
4254887Schin 		p = (char*)format;
4264887Schin 		if (!*s && *p == '%' && *(p + 1) == '|')
4274887Schin 			format += strlen(format);
4284887Schin 		else
4294887Schin 			while (*p)
4304887Schin 				if (*p++ == '%' && *p && *p++ == '|' && *p)
4314887Schin 				{
4324887Schin 					format = (const char*)p;
4334887Schin 					s = b;
4344887Schin 					goto again;
4354887Schin 				}
4364887Schin 	}
4374887Schin 	t = gen(tm, &set);
4384887Schin  done:
4394887Schin 	if (e)
4404887Schin 	{
4414887Schin 		while (isspace(*s))
4424887Schin 			s++;
4434887Schin 		*e = (char*)s;
4444887Schin 	}
4454887Schin 	if (f)
4464887Schin 	{
4474887Schin 		while (isspace(*format))
4484887Schin 			format++;
4494887Schin 		*f = (char*)format;
4504887Schin 	}
4514887Schin 	return t;
4524887Schin }
4534887Schin 
4544887Schin /*
4554887Schin  *  format==0	DATEMSK
4564887Schin  * *format==0	DATEMSK and tmxdate()
4574887Schin  * *format!=0	format
4584887Schin  */
4594887Schin 
4604887Schin Time_t
tmxscan(const char * s,char ** e,const char * format,char ** f,Time_t t,long flags)4614887Schin tmxscan(const char* s, char** e, const char* format, char** f, Time_t t, long flags)
4624887Schin {
4634887Schin 	register char*	v;
4644887Schin 	register char**	p;
4654887Schin 	char*		q;
4664887Schin 	char*		r;
4674887Schin 	Time_t		x;
4684887Schin 
4694887Schin 	static int	initialized;
4704887Schin 	static char**	datemask;
4714887Schin 
4724887Schin 	tmlocale();
4734887Schin 	if (!format || !*format)
4744887Schin 	{
4754887Schin 		if (!initialized)
4764887Schin 		{
4774887Schin 			register Sfio_t*	sp;
4784887Schin 			register int		n;
4794887Schin 			off_t			m;
4804887Schin 
4814887Schin 			initialized = 1;
4824887Schin 			if ((v = getenv("DATEMSK")) && *v && (sp = sfopen(NiL, v, "r")))
4834887Schin 			{
4844887Schin 				for (n = 1; sfgetr(sp, '\n', 0); n++);
4854887Schin 				m = sfseek(sp, 0L, SEEK_CUR);
4864887Schin 				if (p = newof(0, char*, n, m))
4874887Schin 				{
4884887Schin 					sfseek(sp, 0L, SEEK_SET);
4894887Schin 					v = (char*)(p + n);
4904887Schin 					if (sfread(sp, v, m) != m)
4914887Schin 					{
4924887Schin 						free(p);
4934887Schin 						p = 0;
4944887Schin 					}
4954887Schin 					else
4964887Schin 					{
4974887Schin 						datemask = p;
4984887Schin 						v[m] = 0;
4994887Schin 						while (*v)
5004887Schin 						{
5014887Schin 							*p++ = v;
5024887Schin 							if (!(v = strchr(v, '\n')))
5034887Schin 								break;
5044887Schin 							*v++ = 0;
5054887Schin 						}
5064887Schin 						*p = 0;
5074887Schin 					}
5084887Schin 				}
5094887Schin 			}
5104887Schin 		}
5114887Schin 		if (p = datemask)
5124887Schin 			while (v = *p++)
5134887Schin 			{
5144887Schin 				x = scan(s, &q, v, &r, t, flags);
5154887Schin 				if (!*q && !*r)
5164887Schin 				{
5174887Schin 					if (e)
5184887Schin 						*e = q;
5194887Schin 					if (f)
5204887Schin 						*f = r;
5214887Schin 					return x;
5224887Schin 				}
5234887Schin 			}
5244887Schin 		if (f)
5254887Schin 			*f = (char*)format;
5264887Schin 		if (format)
5274887Schin 			return tmxdate(s, e, t);
5284887Schin 		if (e)
5294887Schin 			*e = (char*)s;
5304887Schin 		return 0;
5314887Schin 	}
5324887Schin 	return scan(s, e, format, f, t, flags);
5334887Schin }
534