xref: /onnv-gate/usr/src/lib/libast/common/tm/tmxdate.c (revision 4887:feebf9260c2e)
1*4887Schin /***********************************************************************
2*4887Schin *                                                                      *
3*4887Schin *               This software is part of the ast package               *
4*4887Schin *           Copyright (c) 1985-2007 AT&T Knowledge Ventures            *
5*4887Schin *                      and is licensed under the                       *
6*4887Schin *                  Common Public License, Version 1.0                  *
7*4887Schin *                      by AT&T Knowledge Ventures                      *
8*4887Schin *                                                                      *
9*4887Schin *                A copy of the License is available at                 *
10*4887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
11*4887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*4887Schin *                                                                      *
13*4887Schin *              Information and Software Systems Research               *
14*4887Schin *                            AT&T Research                             *
15*4887Schin *                           Florham Park NJ                            *
16*4887Schin *                                                                      *
17*4887Schin *                 Glenn Fowler <gsf@research.att.com>                  *
18*4887Schin *                  David Korn <dgk@research.att.com>                   *
19*4887Schin *                   Phong Vo <kpv@research.att.com>                    *
20*4887Schin *                                                                      *
21*4887Schin ***********************************************************************/
22*4887Schin #pragma prototyped
23*4887Schin /*
24*4887Schin  * Glenn Fowler
25*4887Schin  * AT&T Research
26*4887Schin  *
27*4887Schin  * Time_t conversion support
28*4887Schin  *
29*4887Schin  * relative times inspired by Steve Bellovin's netnews getdate(3)
30*4887Schin  */
31*4887Schin 
32*4887Schin #include <tmx.h>
33*4887Schin #include <ctype.h>
34*4887Schin 
35*4887Schin #define dig1(s,n)	((n)=((*(s)++)-'0'))
36*4887Schin #define dig2(s,n)	((n)=((*(s)++)-'0')*10,(n)+=(*(s)++)-'0')
37*4887Schin #define dig3(s,n)	((n)=((*(s)++)-'0')*100,(n)+=((*(s)++)-'0')*10,(n)+=(*(s)++)-'0')
38*4887Schin #define dig4(s,n)	((n)=((*(s)++)-'0')*1000,(n)+=((*(s)++)-'0')*100,(n)+=((*(s)++)-'0')*10,(n)+=(*(s)++)-'0')
39*4887Schin 
40*4887Schin #define BREAK		(1<<0)
41*4887Schin #define CCYYMMDDHHMMSS	(1<<1)
42*4887Schin #define CRON		(1<<2)
43*4887Schin #define DAY		(1<<3)
44*4887Schin #define EXACT		(1<<4)
45*4887Schin #define FINAL		(1<<5)
46*4887Schin #define HOLD		(1<<6)
47*4887Schin #define HOUR		(1<<7)
48*4887Schin #define LAST		(1<<8)
49*4887Schin #define MDAY		(1<<9)
50*4887Schin #define MINUTE		(1<<10)
51*4887Schin #define MONTH		(1<<11)
52*4887Schin #define NEXT		(1<<12)
53*4887Schin #define NSEC		(1<<13)
54*4887Schin #define SECOND		(1<<14)
55*4887Schin #define THIS		(1L<<15)
56*4887Schin #define WDAY		(1L<<16)
57*4887Schin #define YEAR		(1L<<17)
58*4887Schin #define ZONE		(1L<<18)
59*4887Schin 
60*4887Schin /*
61*4887Schin  * parse cron range into set
62*4887Schin  * return: -1:error 0:* 1:some
63*4887Schin  */
64*4887Schin 
65*4887Schin static int
66*4887Schin range(register char* s, char** e, char* set, int lo, int hi)
67*4887Schin {
68*4887Schin 	int	n;
69*4887Schin 	int	m;
70*4887Schin 	int	i;
71*4887Schin 	char*	t;
72*4887Schin 
73*4887Schin 	while (isspace(*s) || *s == '_')
74*4887Schin 		s++;
75*4887Schin 	if (*s == '*')
76*4887Schin 	{
77*4887Schin 		*e = s + 1;
78*4887Schin 		return 0;
79*4887Schin 	}
80*4887Schin 	memset(set, 0, hi + 1);
81*4887Schin 	for (;;)
82*4887Schin 	{
83*4887Schin 		n = strtol(s, &t, 10);
84*4887Schin 		if (s == t || n < lo || n > hi)
85*4887Schin 			return -1;
86*4887Schin 		i = 1;
87*4887Schin 		if (*(s = t) == '-')
88*4887Schin 		{
89*4887Schin 			m = strtol(++s, &t, 10);
90*4887Schin 			if (s == t || m < n || m > hi)
91*4887Schin 				return -1;
92*4887Schin 			if (*(s = t) == '/')
93*4887Schin 			{
94*4887Schin 				i = strtol(++s, &t, 10);
95*4887Schin 				if (s == t || i < 1)
96*4887Schin 					return -1;
97*4887Schin 				s = t;
98*4887Schin 			}
99*4887Schin 		}
100*4887Schin 		else
101*4887Schin 			m = n;
102*4887Schin 		for (; n <= m; n += i)
103*4887Schin 			set[n] = 1;
104*4887Schin 		if (*s != ',')
105*4887Schin 			break;
106*4887Schin 		s++;
107*4887Schin 	}
108*4887Schin 	*e = s;
109*4887Schin 	return 1;
110*4887Schin }
111*4887Schin 
112*4887Schin /*
113*4887Schin  * parse date expression in s and return Time_t value
114*4887Schin  *
115*4887Schin  * if non-null, e points to the first invalid sequence in s
116*4887Schin  * now provides default values
117*4887Schin  */
118*4887Schin 
119*4887Schin Time_t
120*4887Schin tmxdate(register const char* s, char** e, Time_t now)
121*4887Schin {
122*4887Schin 	register Tm_t*	tm;
123*4887Schin 	register long	n;
124*4887Schin 	register int	w;
125*4887Schin 	unsigned long	set;
126*4887Schin 	unsigned long	state;
127*4887Schin 	unsigned long	flags;
128*4887Schin 	Time_t		fix;
129*4887Schin 	char*		t;
130*4887Schin 	char*		u;
131*4887Schin 	const char*	x;
132*4887Schin 	char*		last;
133*4887Schin 	char*		type;
134*4887Schin 	int		day;
135*4887Schin 	int		dir;
136*4887Schin 	int		dst;
137*4887Schin 	int		zone;
138*4887Schin 	int		c;
139*4887Schin 	int		f;
140*4887Schin 	int		i;
141*4887Schin 	int		j;
142*4887Schin 	int		k;
143*4887Schin 	int		l;
144*4887Schin 	long		m;
145*4887Schin 	long		p;
146*4887Schin 	long		q;
147*4887Schin 	Tm_zone_t*	zp;
148*4887Schin 	char		skip[UCHAR_MAX + 1];
149*4887Schin 
150*4887Schin 	/*
151*4887Schin 	 * check DATEMSK first
152*4887Schin 	 */
153*4887Schin 
154*4887Schin 	fix = tmxscan(s, &last, NiL, &t, now, 0);
155*4887Schin 	if (t && !*last)
156*4887Schin 	{
157*4887Schin 		if (e)
158*4887Schin 			*e = last;
159*4887Schin 		return fix;
160*4887Schin 	}
161*4887Schin 
162*4887Schin  reset:
163*4887Schin 
164*4887Schin 	/*
165*4887Schin 	 * use now for defaults
166*4887Schin 	 */
167*4887Schin 
168*4887Schin 	tm = tmxmake(now);
169*4887Schin 	tm_info.date = tm_info.zone;
170*4887Schin 	day = -1;
171*4887Schin 	dst = TM_DST;
172*4887Schin 	set = state = 0;
173*4887Schin 	type = 0;
174*4887Schin 	zone = TM_LOCALZONE;
175*4887Schin 	skip[0] = 0;
176*4887Schin 	for (n = 1; n <= UCHAR_MAX; n++)
177*4887Schin 		skip[n] = isspace(n) || strchr("_,;@=|!^()[]{}", n);
178*4887Schin 
179*4887Schin 	/*
180*4887Schin 	 * get <weekday year month day hour minutes seconds ?[ds]t [ap]m>
181*4887Schin 	 */
182*4887Schin 
183*4887Schin 	for (;;)
184*4887Schin 	{
185*4887Schin 		state &= (state & HOLD) ? ~(HOLD) : ~(EXACT|LAST|NEXT|THIS);
186*4887Schin 		if ((set|state) & (YEAR|MONTH|DAY))
187*4887Schin 			skip['/'] = 1;
188*4887Schin 		for (;;)
189*4887Schin 		{
190*4887Schin 			if (*s == '.' || *s == '-' || *s == '+')
191*4887Schin 			{
192*4887Schin 				if (((set|state) & (YEAR|MONTH|HOUR|MINUTE|ZONE)) == (YEAR|MONTH|HOUR|MINUTE) && (i = tmgoff(s, &t, TM_LOCALZONE)) != TM_LOCALZONE)
193*4887Schin 				{
194*4887Schin 					zone = i;
195*4887Schin 					state |= ZONE;
196*4887Schin 					if (!*(s = t))
197*4887Schin 						break;
198*4887Schin 				}
199*4887Schin 				else if (*s == '+')
200*4887Schin 					break;
201*4887Schin 			}
202*4887Schin 			else if (!skip[*s])
203*4887Schin 				break;
204*4887Schin 			s++;
205*4887Schin 		}
206*4887Schin 		if (!*(last = (char*)s))
207*4887Schin 			break;
208*4887Schin 		if (*s == '#')
209*4887Schin 		{
210*4887Schin 			if (isdigit(*++s))
211*4887Schin 			{
212*4887Schin 				now = strtoull(s, &t, 0);
213*4887Schin 			sns:
214*4887Schin 				if (*(s = t) == '.')
215*4887Schin 				{
216*4887Schin 					fix = 0;
217*4887Schin 					m = 1000000000;
218*4887Schin 					while (isdigit(*++s))
219*4887Schin 						fix += (*s - '0') * (m /= 10);
220*4887Schin 					now = tmxsns(now, fix);
221*4887Schin 				}
222*4887Schin 				else if (now <= 0x7fffffff)
223*4887Schin 					now = tmxsns(now, 0);
224*4887Schin 				goto reset;
225*4887Schin 			}
226*4887Schin 			else if (*s++ == '#')
227*4887Schin 			{
228*4887Schin 				now = tmxtime(tm, zone);
229*4887Schin 				goto reset;
230*4887Schin 			}
231*4887Schin 			break;
232*4887Schin 		}
233*4887Schin 		f = -1;
234*4887Schin 		if (*s == '+')
235*4887Schin 		{
236*4887Schin 			while (isspace(*++s) || *s == '_');
237*4887Schin 			n = strtol(s, &t, 0);
238*4887Schin 			if (w = t - s)
239*4887Schin 			{
240*4887Schin 				for (s = t; skip[*s]; s++);
241*4887Schin 				state |= (f = n) ? NEXT : THIS;
242*4887Schin 				set &= ~(EXACT|LAST|NEXT|THIS);
243*4887Schin 				set |= state & (EXACT|LAST|NEXT|THIS);
244*4887Schin 			}
245*4887Schin 			else
246*4887Schin 				s = last;
247*4887Schin 		}
248*4887Schin 		if (!(state & CRON))
249*4887Schin 		{
250*4887Schin 			/*
251*4887Schin 			 * check for cron date
252*4887Schin 			 *
253*4887Schin 			 *	min hour day-of-month month day-of-week
254*4887Schin 			 *
255*4887Schin 			 * if it's cron then determine the next time
256*4887Schin 			 * that satisfies the specification
257*4887Schin 			 *
258*4887Schin 			 * NOTE: the only spacing is ' '||'_'||';'
259*4887Schin 			 */
260*4887Schin 
261*4887Schin 			i = 0;
262*4887Schin 			n = *(t = (char*)s);
263*4887Schin 			for (;;)
264*4887Schin 			{
265*4887Schin 				if (n == '*')
266*4887Schin 					n = *++s;
267*4887Schin 				else if (!isdigit(n))
268*4887Schin 					break;
269*4887Schin 				else
270*4887Schin 					while ((n = *++s) == ',' || n == '-' || n == '/' || isdigit(n));
271*4887Schin 				if (n != ' ' && n != '_' && n != ';')
272*4887Schin 				{
273*4887Schin 					if (!n)
274*4887Schin 						i++;
275*4887Schin 					break;
276*4887Schin 				}
277*4887Schin 				i++;
278*4887Schin 				while ((n = *++s) == ' ' || n == '_');
279*4887Schin 			}
280*4887Schin 			if (i == 5)
281*4887Schin 			{
282*4887Schin 				Time_t	tt;
283*4887Schin 				char	hit[60];
284*4887Schin 				char	mon[12];
285*4887Schin 				char	day[7];
286*4887Schin 
287*4887Schin 				state |= CRON;
288*4887Schin 				flags = 0;
289*4887Schin 				tm->tm_sec = 0;
290*4887Schin 				tm->tm_min++;
291*4887Schin 				tmfix(tm);
292*4887Schin 
293*4887Schin 				/*
294*4887Schin 				 * minute
295*4887Schin 				 */
296*4887Schin 
297*4887Schin 				if ((k = range(t, &t, hit, 0, 59)) < 0)
298*4887Schin 					break;
299*4887Schin 				if (k && !hit[i = tm->tm_min])
300*4887Schin 				{
301*4887Schin 					hit[i] = 1;
302*4887Schin 					do if (++i > 59)
303*4887Schin 					{
304*4887Schin 						i = 0;
305*4887Schin 						if (++tm->tm_hour > 59)
306*4887Schin 						{
307*4887Schin 							tm->tm_min = i;
308*4887Schin 							tmfix(tm);
309*4887Schin 						}
310*4887Schin 					} while (!hit[i]);
311*4887Schin 					tm->tm_min = i;
312*4887Schin 				}
313*4887Schin 
314*4887Schin 				/*
315*4887Schin 				 * hour
316*4887Schin 				 */
317*4887Schin 
318*4887Schin 				if ((k = range(t, &t, hit, 0, 23)) < 0)
319*4887Schin 					break;
320*4887Schin 				if (k && !hit[i = tm->tm_hour])
321*4887Schin 				{
322*4887Schin 					hit[i] = 1;
323*4887Schin 					do if (++i > 23)
324*4887Schin 					{
325*4887Schin 						i = 0;
326*4887Schin 						if (++tm->tm_mday > 28)
327*4887Schin 						{
328*4887Schin 							tm->tm_hour = i;
329*4887Schin 							tmfix(tm);
330*4887Schin 						}
331*4887Schin 					} while (!hit[i]);
332*4887Schin 					tm->tm_hour = i;
333*4887Schin 				}
334*4887Schin 
335*4887Schin 				/*
336*4887Schin 				 * day of month
337*4887Schin 				 */
338*4887Schin 
339*4887Schin 				if ((k = range(t, &t, hit, 1, 31)) < 0)
340*4887Schin 					break;
341*4887Schin 				if (k)
342*4887Schin 					flags |= DAY|MDAY;
343*4887Schin 
344*4887Schin 				/*
345*4887Schin 				 * month
346*4887Schin 				 */
347*4887Schin 
348*4887Schin 				if ((k = range(t, &t, mon, 1, 12)) < 0)
349*4887Schin 					break;
350*4887Schin 				if (k)
351*4887Schin 					flags |= MONTH;
352*4887Schin 				else
353*4887Schin 					for (i = 1; i <= 12; i++)
354*4887Schin 						mon[i] = 1;
355*4887Schin 
356*4887Schin 				/*
357*4887Schin 				 * day of week
358*4887Schin 				 */
359*4887Schin 
360*4887Schin 				if ((k = range(t, &t, day, 0, 6)) < 0)
361*4887Schin 					break;
362*4887Schin 				if (k)
363*4887Schin 					flags |= WDAY;
364*4887Schin 				s = t;
365*4887Schin 				if (flags & (MONTH|MDAY|WDAY))
366*4887Schin 				{
367*4887Schin 					fix = tmxtime(tm, zone);
368*4887Schin 					tm = tmxmake(fix);
369*4887Schin 					i = tm->tm_mon + 1;
370*4887Schin 					j = tm->tm_mday;
371*4887Schin 					k = tm->tm_wday;
372*4887Schin 					for (;;)
373*4887Schin 					{
374*4887Schin 						if (!mon[i])
375*4887Schin 						{
376*4887Schin 							if (++i > 12)
377*4887Schin 							{
378*4887Schin 								i = 1;
379*4887Schin 								tm->tm_year++;
380*4887Schin 							}
381*4887Schin 							tm->tm_mon = i - 1;
382*4887Schin 							tm->tm_mday = 1;
383*4887Schin 							tt = tmxtime(tm, zone);
384*4887Schin 							if (tt < fix)
385*4887Schin 								goto done;
386*4887Schin 							tm = tmxmake(tt);
387*4887Schin 							i = tm->tm_mon + 1;
388*4887Schin 							j = tm->tm_mday;
389*4887Schin 							k = tm->tm_wday;
390*4887Schin 							continue;
391*4887Schin 						}
392*4887Schin 						if (flags & (MDAY|WDAY))
393*4887Schin 						{
394*4887Schin 							if ((flags & (MDAY|WDAY)) == (MDAY|WDAY))
395*4887Schin 							{
396*4887Schin 								if (hit[j] && day[k])
397*4887Schin 									break;
398*4887Schin 							}
399*4887Schin 							else if ((flags & MDAY) && hit[j])
400*4887Schin 								break;
401*4887Schin 							else if ((flags & WDAY) && day[k])
402*4887Schin 								break;
403*4887Schin 							if (++j > 28)
404*4887Schin 							{
405*4887Schin 								tm->tm_mon = i - 1;
406*4887Schin 								tm->tm_mday = j;
407*4887Schin 								tm = tmxmake(tmxtime(tm, zone));
408*4887Schin 								i = tm->tm_mon + 1;
409*4887Schin 								j = tm->tm_mday;
410*4887Schin 								k = tm->tm_wday;
411*4887Schin 							}
412*4887Schin 							else if ((flags & WDAY) && ++k > 6)
413*4887Schin 								k = 0;
414*4887Schin 						}
415*4887Schin 						else if (flags & MONTH)
416*4887Schin 							break;
417*4887Schin 					}
418*4887Schin 					tm->tm_mon = i - 1;
419*4887Schin 					tm->tm_mday = j;
420*4887Schin 					tm->tm_wday = k;
421*4887Schin 				}
422*4887Schin 				continue;
423*4887Schin 			}
424*4887Schin 			s = t;
425*4887Schin 		}
426*4887Schin 		n = -1;
427*4887Schin 		if (isdigit(*s))
428*4887Schin 		{
429*4887Schin 			n = strtol(s, &t, 10);
430*4887Schin 			if ((w = t - s) && *t == '.' && isdigit(*(t + 1)) && isdigit(*(t + 2)) && isdigit(*(t + 3)))
431*4887Schin 			{
432*4887Schin 				now = n;
433*4887Schin 				goto sns;
434*4887Schin 			}
435*4887Schin 			u = t + (*t == '-');
436*4887Schin 			if ((w == 2 || w == 4) && (*u == 'W' || *u == 'w') && isdigit(*(u + 1)))
437*4887Schin 			{
438*4887Schin 				t = u;
439*4887Schin 				if (w == 4)
440*4887Schin 				{
441*4887Schin 					if ((n -= 1900) < TM_WINDOW)
442*4887Schin 						break;
443*4887Schin 				}
444*4887Schin 				else if (n < TM_WINDOW)
445*4887Schin 					n += 100;
446*4887Schin 				m = n;
447*4887Schin 				n = strtol(s = t + 1, &t, 0);
448*4887Schin 				if ((i = (t - s)) < 2 || i > 3)
449*4887Schin 					break;
450*4887Schin 				if (dig2(s, j) < 0 || j > 53)
451*4887Schin 					break;
452*4887Schin 				if (!(t - s) && *t == '-')
453*4887Schin 					n = strtol(s = t + 1, &t, 0);
454*4887Schin 				if (!(i = (t - s)))
455*4887Schin 					k = 1;
456*4887Schin 				else if (i != 1 || dig1(s, k) < 1 || k > 7)
457*4887Schin 					break;
458*4887Schin 				else if (k == 7)
459*4887Schin 					k = 0;
460*4887Schin 				tm->tm_year = m;
461*4887Schin 				tmweek(tm, 2, j, k);
462*4887Schin 				set |= YEAR|MONTH|DAY;
463*4887Schin 				continue;
464*4887Schin 			}
465*4887Schin 			else if ((w == 6 || w == 8) && (*u == 'T' || *u == 't') && isdigit(*(u + 1)))
466*4887Schin 			{
467*4887Schin 				t = u;
468*4887Schin 				flags = 0;
469*4887Schin 				if (w == 8)
470*4887Schin 				{
471*4887Schin 					dig4(s, m);
472*4887Schin 					if ((m -= 1900) < TM_WINDOW)
473*4887Schin 						break;
474*4887Schin 				}
475*4887Schin 				else
476*4887Schin 				{
477*4887Schin 					dig2(s, m);
478*4887Schin 					if (m < TM_WINDOW)
479*4887Schin 						m += 100;
480*4887Schin 				}
481*4887Schin 				flags |= YEAR;
482*4887Schin 				if (dig2(s, l) <= 0 || l > 12)
483*4887Schin 					break;
484*4887Schin 				flags |= MONTH;
485*4887Schin 				if (dig2(s, k) < 1 || k > 31)
486*4887Schin 					break;
487*4887Schin 				n = strtol(s = t + 1, &t, 0);
488*4887Schin 				if ((t - s) < 2)
489*4887Schin 					break;
490*4887Schin 				if (dig2(s, j) > 24)
491*4887Schin 					break;
492*4887Schin 				if ((t - s) < 2)
493*4887Schin 				{
494*4887Schin 					if ((t - s) == 1 || *t++ != '-')
495*4887Schin 						break;
496*4887Schin 					n = strtol(s = t, &t, 0);
497*4887Schin 					if ((t - s) < 2)
498*4887Schin 						break;
499*4887Schin 				}
500*4887Schin 				if (dig2(s, i) > 59)
501*4887Schin 					break;
502*4887Schin 				flags |= HOUR|MINUTE;
503*4887Schin 				if ((t - s) == 2)
504*4887Schin 				{
505*4887Schin 					if (dig2(s, n) > (59 + TM_MAXLEAP))
506*4887Schin 						break;
507*4887Schin 					flags |= SECOND;
508*4887Schin 				}
509*4887Schin 				else if (t - s)
510*4887Schin 					break;
511*4887Schin 				else
512*4887Schin 					n = 0;
513*4887Schin 				p = 0;
514*4887Schin 				if (*t == '.')
515*4887Schin 				{
516*4887Schin 					q = 1000000000;
517*4887Schin 					while (isdigit(*++t))
518*4887Schin 						p += (*t - '0') * (q /= 10);
519*4887Schin 					set |= NSEC;
520*4887Schin 				}
521*4887Schin 				if (n > (59 + TM_MAXLEAP))
522*4887Schin 					break;
523*4887Schin 				goto save;
524*4887Schin 			}
525*4887Schin 			else if (f == -1 && isalpha(*t) && tmlex(t, &t, tm_info.format + TM_ORDINAL, TM_ORDINALS - TM_ORDINAL, NiL, 0) >= 0)
526*4887Schin 			{
527*4887Schin  ordinal:
528*4887Schin 				state |= (f = n) ? NEXT : THIS;
529*4887Schin 				set &= ~(EXACT|LAST|NEXT|THIS);
530*4887Schin 				set |= state & (EXACT|LAST|NEXT|THIS);
531*4887Schin 				for (s = t; skip[*s]; s++);
532*4887Schin 				if (isdigit(*s))
533*4887Schin 				{
534*4887Schin 					n = strtol(s, &t, 10);
535*4887Schin 					s = t;
536*4887Schin 					if (*s == '_')
537*4887Schin 						s++;
538*4887Schin 				}
539*4887Schin 				else
540*4887Schin 					n = -1;
541*4887Schin 			}
542*4887Schin 			else
543*4887Schin 			{
544*4887Schin 				if (!(state & (LAST|NEXT|THIS)) && ((i = t - s) == 4 && (*t == '.' && isdigit(*(t + 1)) && isdigit(*(t + 2)) && *(t + 3) != '.' || (!*t || isspace(*t) || *t == '_' || isalnum(*t)) && n >= 0 && (n % 100) < 60 && ((m = (n / 100)) < 20 || m < 24 && !((set|state) & (YEAR|MONTH|HOUR|MINUTE)))) || i > 4 && i <= 12))
545*4887Schin 				{
546*4887Schin 					/*
547*4887Schin 					 * various { date(1) touch(1) } formats
548*4887Schin 					 *
549*4887Schin 					 *	[[cc]yy[mm]]ddhhmm[.ss[.nn...]]
550*4887Schin 					 *	[cc]yyjjj
551*4887Schin 					 *	hhmm[.ss[.nn...]]
552*4887Schin 					 */
553*4887Schin 
554*4887Schin 					flags = 0;
555*4887Schin 					if (state & CCYYMMDDHHMMSS)
556*4887Schin 						break;
557*4887Schin 					state |= CCYYMMDDHHMMSS;
558*4887Schin 					p = 0;
559*4887Schin 					if ((i == 7 || i == 5) && !*t)
560*4887Schin 					{
561*4887Schin 						if (i == 7)
562*4887Schin 						{
563*4887Schin 							dig4(s, m);
564*4887Schin 							if ((m -= 1900) < TM_WINDOW)
565*4887Schin 								break;
566*4887Schin 						}
567*4887Schin 						else if (dig2(s, m) < TM_WINDOW)
568*4887Schin 							m += 100;
569*4887Schin 						dig3(s, k);
570*4887Schin 						l = 1;
571*4887Schin 						j = 0;
572*4887Schin 						i = 0;
573*4887Schin 						n = 0;
574*4887Schin 						flags |= MONTH;
575*4887Schin 					}
576*4887Schin 					else if (i & 1)
577*4887Schin 						break;
578*4887Schin 					else
579*4887Schin 					{
580*4887Schin 						u = t;
581*4887Schin 						if (i == 12)
582*4887Schin 						{
583*4887Schin 							x = s;
584*4887Schin 							dig2(x, m);
585*4887Schin 							if (m <= 12)
586*4887Schin 							{
587*4887Schin 								u -= 4;
588*4887Schin 								i -= 4;
589*4887Schin 								x = s + 8;
590*4887Schin 								dig4(x, m);
591*4887Schin 							}
592*4887Schin 							else
593*4887Schin 								dig4(s, m);
594*4887Schin 							m -= 1900;
595*4887Schin 						}
596*4887Schin 						else if (i == 10)
597*4887Schin 						{
598*4887Schin 							x = s;
599*4887Schin 							if (!dig2(x, m) || m > 12 || !dig2(x, m) || m > 31 || dig2(x, m) > 24 || dig2(x, m) > 60 || dig2(x, m) <= 60 && !(tm_info.flags & TM_DATESTYLE))
600*4887Schin 								dig2(s, m);
601*4887Schin 							else
602*4887Schin 							{
603*4887Schin 								u -= 2;
604*4887Schin 								i -= 2;
605*4887Schin 								x = s + 8;
606*4887Schin 								dig2(x, m);
607*4887Schin 							}
608*4887Schin 							if (m < TM_WINDOW)
609*4887Schin 								m += 100;
610*4887Schin 						}
611*4887Schin 						else
612*4887Schin 							m = tm->tm_year;
613*4887Schin 						if ((u - s) < 8)
614*4887Schin 							l = tm->tm_mon + 1;
615*4887Schin 						else if (dig2(s, l) <= 0 || l > 12)
616*4887Schin 							break;
617*4887Schin 						else
618*4887Schin 							flags |= MONTH;
619*4887Schin 						if ((u - s) < 6)
620*4887Schin 							k = tm->tm_mday;
621*4887Schin 						else if (dig2(s, k) < 1 || k > 31)
622*4887Schin 							break;
623*4887Schin 						else
624*4887Schin 							flags |= DAY;
625*4887Schin 						if ((u - s) < 4)
626*4887Schin 							break;
627*4887Schin 						if (dig2(s, j) > 24)
628*4887Schin 							break;
629*4887Schin 						if (dig2(s, i) > 59)
630*4887Schin 							break;
631*4887Schin 						flags |= HOUR|MINUTE;
632*4887Schin 						if ((u - s) == 2)
633*4887Schin 						{
634*4887Schin 							dig2(s, n);
635*4887Schin 							flags |= SECOND;
636*4887Schin 						}
637*4887Schin 						else if (u - s)
638*4887Schin 							break;
639*4887Schin 						else if (*t != '.')
640*4887Schin 							n = 0;
641*4887Schin 						else
642*4887Schin 						{
643*4887Schin 							n = strtol(t + 1, &t, 10);
644*4887Schin 							flags |= SECOND;
645*4887Schin 							if (*t == '.')
646*4887Schin 							{
647*4887Schin 								q = 1000000000;
648*4887Schin 								while (isdigit(*++t))
649*4887Schin 									p += (*t - '0') * (q /= 10);
650*4887Schin 								set |= NSEC;
651*4887Schin 							}
652*4887Schin 						}
653*4887Schin 						if (n > (59 + TM_MAXLEAP))
654*4887Schin 							break;
655*4887Schin 					}
656*4887Schin 				save:
657*4887Schin 					tm->tm_year = m;
658*4887Schin 					tm->tm_mon = l - 1;
659*4887Schin 					tm->tm_mday = k;
660*4887Schin 					tm->tm_hour = j;
661*4887Schin 					tm->tm_min = i;
662*4887Schin 					tm->tm_sec = n;
663*4887Schin 					tm->tm_nsec = p;
664*4887Schin 					s = t;
665*4887Schin 					set |= flags;
666*4887Schin 					continue;
667*4887Schin 				}
668*4887Schin 				for (s = t; skip[*s]; s++);
669*4887Schin 				if (*s == ':' || *s == '.' && ((set|state) & (YEAR|MONTH|DAY|HOUR)) == (YEAR|MONTH|DAY))
670*4887Schin 				{
671*4887Schin 					c = *s;
672*4887Schin 					if ((state & HOUR) || n > 24)
673*4887Schin 						break;
674*4887Schin 					while (isspace(*++s) || *s == '_');
675*4887Schin 					if (!isdigit(*s))
676*4887Schin 						break;
677*4887Schin 					i = n;
678*4887Schin 					n = strtol(s, &t, 10);
679*4887Schin 					for (s = t; isspace(*s) || *s == '_'; s++);
680*4887Schin 					if (n > 59)
681*4887Schin 						break;
682*4887Schin 					j = n;
683*4887Schin 					m = 0;
684*4887Schin 					if (*s == c)
685*4887Schin 					{
686*4887Schin 						while (isspace(*++s) || *s == '_');
687*4887Schin 						if (!isdigit(*s))
688*4887Schin 							break;
689*4887Schin 						n = strtol(s, &t, 10);
690*4887Schin 						s = t;
691*4887Schin 						if (n > (59 + TM_MAXLEAP))
692*4887Schin 							break;
693*4887Schin 						set |= SECOND;
694*4887Schin 						while (isspace(*s))
695*4887Schin 							s++;
696*4887Schin 						if (*s == '.')
697*4887Schin 						{
698*4887Schin 							q = 1000000000;
699*4887Schin 							while (isdigit(*++s))
700*4887Schin 								m += (*s - '0') * (q /= 10);
701*4887Schin 							set |= NSEC;
702*4887Schin 						}
703*4887Schin 					}
704*4887Schin 					else
705*4887Schin 						n = 0;
706*4887Schin 					set |= HOUR|MINUTE;
707*4887Schin 					skip[':'] = 1;
708*4887Schin 					k = tm->tm_hour;
709*4887Schin 					tm->tm_hour = i;
710*4887Schin 					l = tm->tm_min;
711*4887Schin 					tm->tm_min = j;
712*4887Schin 					tm->tm_sec = n;
713*4887Schin 					tm->tm_nsec = m;
714*4887Schin 					while (isspace(*s))
715*4887Schin 						s++;
716*4887Schin 					switch (tmlex(s, &t, tm_info.format, TM_NFORM, tm_info.format + TM_MERIDIAN, 2))
717*4887Schin 					{
718*4887Schin 					case TM_MERIDIAN:
719*4887Schin 						s = t;
720*4887Schin 						if (i == 12)
721*4887Schin 							tm->tm_hour = i = 0;
722*4887Schin 						break;
723*4887Schin 					case TM_MERIDIAN+1:
724*4887Schin 						if (i < 12)
725*4887Schin 							tm->tm_hour = i += 12;
726*4887Schin 						break;
727*4887Schin 					}
728*4887Schin 					if (f >= 0 || (state & (LAST|NEXT)))
729*4887Schin 					{
730*4887Schin 						state &= ~HOLD;
731*4887Schin 						if (f < 0)
732*4887Schin 						{
733*4887Schin 							if (state & LAST)
734*4887Schin 								f = -1;
735*4887Schin 							else if (state & NEXT)
736*4887Schin 								f = 1;
737*4887Schin 							else
738*4887Schin 								f = 0;
739*4887Schin 						}
740*4887Schin 						if (f > 0)
741*4887Schin 						{
742*4887Schin 							if (i > k || i == k && j > l)
743*4887Schin 								f--;
744*4887Schin 						}
745*4887Schin 						else if (i < k || i == k && j < l)
746*4887Schin 							f++;
747*4887Schin 						if (f > 0)
748*4887Schin 						{
749*4887Schin 							tm->tm_hour += f * 24;
750*4887Schin 							while (tm->tm_hour >= 24)
751*4887Schin 							{
752*4887Schin 								tm->tm_hour -= 24;
753*4887Schin 								tm->tm_mday++;
754*4887Schin 							}
755*4887Schin 						}
756*4887Schin 						else if (f < 0)
757*4887Schin 						{
758*4887Schin 							tm->tm_hour += f * 24;
759*4887Schin 							while (tm->tm_hour < 24)
760*4887Schin 							{
761*4887Schin 								tm->tm_hour += 24;
762*4887Schin 								tm->tm_mday--;
763*4887Schin 							}
764*4887Schin 						}
765*4887Schin 					}
766*4887Schin 					continue;
767*4887Schin 				}
768*4887Schin 			}
769*4887Schin 		}
770*4887Schin 		for (;;)
771*4887Schin 		{
772*4887Schin 			if (*s == '-' || *s == '+')
773*4887Schin 			{
774*4887Schin 				if (((set|state) & (MONTH|DAY|HOUR|MINUTE)) == (MONTH|DAY|HOUR|MINUTE) || *s == '+' && (!isdigit(s[1]) || !isdigit(s[2]) || s[3] != ':' && (s[3] != '.' || ((set|state) & (YEAR|MONTH)) != (YEAR|MONTH))))
775*4887Schin 					break;
776*4887Schin 				s++;
777*4887Schin 			}
778*4887Schin 			else if (skip[*s])
779*4887Schin 				s++;
780*4887Schin 			else
781*4887Schin 				break;
782*4887Schin 		}
783*4887Schin 		if (isalpha(*s) && n < 1000)
784*4887Schin 		{
785*4887Schin 			if ((j = tmlex(s, &t, tm_info.format, TM_NFORM, tm_info.format + TM_SUFFIXES, TM_PARTS - TM_SUFFIXES)) >= 0)
786*4887Schin 			{
787*4887Schin 				s = t;
788*4887Schin 				switch (tm_data.lex[j])
789*4887Schin 				{
790*4887Schin 				case TM_EXACT:
791*4887Schin 					state |= HOLD|EXACT;
792*4887Schin 					set &= ~(EXACT|LAST|NEXT|THIS);
793*4887Schin 					set |= state & (EXACT|LAST|NEXT|THIS);
794*4887Schin 					continue;
795*4887Schin 				case TM_LAST:
796*4887Schin 					state |= HOLD|LAST;
797*4887Schin 					set &= ~(EXACT|LAST|NEXT|THIS);
798*4887Schin 					set |= state & (EXACT|LAST|NEXT|THIS);
799*4887Schin 					continue;
800*4887Schin 				case TM_THIS:
801*4887Schin 					state |= HOLD|THIS;
802*4887Schin 					set &= ~(EXACT|LAST|NEXT|THIS);
803*4887Schin 					set |= state & (EXACT|LAST|NEXT|THIS);
804*4887Schin 					n = 0;
805*4887Schin 					continue;
806*4887Schin 				case TM_NEXT:
807*4887Schin 					/*
808*4887Schin 					 * disambiguate english "last ... in"
809*4887Schin 					 */
810*4887Schin 
811*4887Schin 					if (!((state|set) & LAST))
812*4887Schin 					{
813*4887Schin 						state |= HOLD|NEXT;
814*4887Schin 						set &= ~(EXACT|LAST|NEXT|THIS);
815*4887Schin 						set |= state & (EXACT|LAST|NEXT|THIS);
816*4887Schin 						continue;
817*4887Schin 					}
818*4887Schin 					/*FALLTHROUGH*/
819*4887Schin 				case TM_FINAL:
820*4887Schin 					state |= HOLD|THIS|FINAL;
821*4887Schin 					set &= ~(EXACT|LAST|NEXT|THIS);
822*4887Schin 					set |= state & (EXACT|LAST|NEXT|THIS|FINAL);
823*4887Schin 					continue;
824*4887Schin 				case TM_ORDINAL:
825*4887Schin 					j += TM_ORDINALS - TM_ORDINAL;
826*4887Schin 					/*FALLTHROUGH*/
827*4887Schin 				case TM_ORDINALS:
828*4887Schin 					n = j - TM_ORDINALS + 1;
829*4887Schin 					goto ordinal;
830*4887Schin 				case TM_MERIDIAN:
831*4887Schin 					if (f >= 0)
832*4887Schin 						f++;
833*4887Schin 					else if (state & LAST)
834*4887Schin 						f = -1;
835*4887Schin 					else if (state & THIS)
836*4887Schin 						f = 1;
837*4887Schin 					else if (state & NEXT)
838*4887Schin 						f = 2;
839*4887Schin 					else
840*4887Schin 						f = 0;
841*4887Schin 					if (n > 0)
842*4887Schin 					{
843*4887Schin 						if (n > 24)
844*4887Schin 							goto done;
845*4887Schin 						tm->tm_hour = n;
846*4887Schin 					}
847*4887Schin 					for (k = tm->tm_hour; k < 0; k += 24);
848*4887Schin 					k %= 24;
849*4887Schin 					if (j == TM_MERIDIAN)
850*4887Schin 					{
851*4887Schin 						if (k == 12)
852*4887Schin 							tm->tm_hour -= 12;
853*4887Schin 					}
854*4887Schin 					else if (k < 12)
855*4887Schin 						tm->tm_hour += 12;
856*4887Schin 					if (n > 0)
857*4887Schin 						goto clear_min;
858*4887Schin 					continue;
859*4887Schin 				case TM_DAY_ABBREV:
860*4887Schin 					j += TM_DAY - TM_DAY_ABBREV;
861*4887Schin 					/*FALLTHROUGH*/
862*4887Schin 				case TM_DAY:
863*4887Schin 				case TM_PARTS:
864*4887Schin 				case TM_HOURS:
865*4887Schin 					state |= set & (EXACT|LAST|NEXT|THIS);
866*4887Schin 					if (!(state & (LAST|NEXT|THIS)))
867*4887Schin 						for (;;)
868*4887Schin 						{
869*4887Schin 							while (skip[*s])
870*4887Schin 								s++;
871*4887Schin 							if ((k = tmlex(s, &t, tm_info.format + TM_LAST, TM_NOISE - TM_LAST, NiL, 0)) >= 0)
872*4887Schin 							{
873*4887Schin 								s = t;
874*4887Schin 								if (k <= 2)
875*4887Schin 									state |= LAST;
876*4887Schin 								else if (k <= 5)
877*4887Schin 									state |= THIS;
878*4887Schin 								else if (k <= 8)
879*4887Schin 									state |= NEXT;
880*4887Schin 								else
881*4887Schin 									state |= EXACT;
882*4887Schin 							}
883*4887Schin 							else
884*4887Schin 							{
885*4887Schin 								state |= (n > 0) ? NEXT : THIS;
886*4887Schin 								break;
887*4887Schin 							}
888*4887Schin 							set &= ~(EXACT|LAST|NEXT|THIS);
889*4887Schin 							set |= state & (EXACT|LAST|NEXT|THIS);
890*4887Schin 						}
891*4887Schin 					/*FALLTHROUGH*/
892*4887Schin 				case TM_DAYS:
893*4887Schin 					if (n == -1)
894*4887Schin 					{
895*4887Schin 						/*
896*4887Schin 						 * disambiguate english "second"
897*4887Schin 						 */
898*4887Schin 
899*4887Schin 						if (j == TM_PARTS && f == -1)
900*4887Schin 						{
901*4887Schin 							n = 2;
902*4887Schin 							goto ordinal;
903*4887Schin 						}
904*4887Schin 						n = 1;
905*4887Schin 					}
906*4887Schin 					if (state & LAST)
907*4887Schin 						n = -n;
908*4887Schin 					else if (!(state & NEXT))
909*4887Schin 						n--;
910*4887Schin 					m = (f > 0) ? f * n : n;
911*4887Schin 					switch (j)
912*4887Schin 					{
913*4887Schin 					case TM_DAYS+0:
914*4887Schin 						tm->tm_mday--;
915*4887Schin 						set |= DAY;
916*4887Schin 						goto clear_hour;
917*4887Schin 					case TM_DAYS+1:
918*4887Schin 						set |= DAY;
919*4887Schin 						goto clear_hour;
920*4887Schin 					case TM_DAYS+2:
921*4887Schin 						tm->tm_mday++;
922*4887Schin 						set |= DAY;
923*4887Schin 						goto clear_hour;
924*4887Schin 					case TM_PARTS+0:
925*4887Schin 						set |= SECOND;
926*4887Schin 						if ((m < 0 ? -m : m) > (365L*24L*60L*60L))
927*4887Schin 						{
928*4887Schin 							now = tmxtime(tm, zone) + tmxsns(m, 0);
929*4887Schin 							goto reset;
930*4887Schin 						}
931*4887Schin 						tm->tm_sec += m;
932*4887Schin 						goto clear_nsec;
933*4887Schin 					case TM_PARTS+1:
934*4887Schin 						tm->tm_min += m;
935*4887Schin 						set |= MINUTE;
936*4887Schin 						goto clear_sec;
937*4887Schin 					case TM_PARTS+2:
938*4887Schin 						tm->tm_hour += m;
939*4887Schin 						set |= MINUTE;
940*4887Schin 						goto clear_min;
941*4887Schin 					case TM_PARTS+3:
942*4887Schin 						tm->tm_mday += m;
943*4887Schin 						if (!(set & FINAL))
944*4887Schin 							set |= HOUR;
945*4887Schin 						goto clear_hour;
946*4887Schin 					case TM_PARTS+4:
947*4887Schin 						tm = tmxmake(tmxtime(tm, zone));
948*4887Schin 						tm->tm_mday += 7 * m - tm->tm_wday + 1;
949*4887Schin 						set |= DAY;
950*4887Schin 						goto clear_hour;
951*4887Schin 					case TM_PARTS+5:
952*4887Schin 						tm->tm_mon += m;
953*4887Schin 						set |= MONTH;
954*4887Schin 						goto clear_mday;
955*4887Schin 					case TM_PARTS+6:
956*4887Schin 						tm->tm_year += m;
957*4887Schin 						goto clear_mon;
958*4887Schin 					case TM_HOURS+0:
959*4887Schin 						tm->tm_mday += m;
960*4887Schin 						set |= DAY;
961*4887Schin 						goto clear_hour;
962*4887Schin 					case TM_HOURS+1:
963*4887Schin 						tm->tm_mday += m;
964*4887Schin 						tm->tm_hour = 6;
965*4887Schin 						set |= HOUR;
966*4887Schin 						goto clear_min;
967*4887Schin 					case TM_HOURS+2:
968*4887Schin 						tm->tm_mday += m;
969*4887Schin 						tm->tm_hour = 12;
970*4887Schin 						set |= HOUR;
971*4887Schin 						goto clear_min;
972*4887Schin 					case TM_HOURS+3:
973*4887Schin 						tm->tm_mday += m;
974*4887Schin 						tm->tm_hour = 18;
975*4887Schin 						set |= HOUR;
976*4887Schin 						goto clear_min;
977*4887Schin 					}
978*4887Schin 					tm = tmxmake(tmxtime(tm, zone));
979*4887Schin 					day = j -= TM_DAY;
980*4887Schin 					dir = m;
981*4887Schin 					j -= tm->tm_wday;
982*4887Schin 					if (state & (LAST|NEXT|THIS))
983*4887Schin 					{
984*4887Schin 						if (j < 0)
985*4887Schin 							j += 7;
986*4887Schin 					}
987*4887Schin 					else if (j > 0)
988*4887Schin 						j -= 7;
989*4887Schin 					tm->tm_mday += j + m * 7;
990*4887Schin 					set |= DAY;
991*4887Schin 					if (state & (LAST|NEXT|THIS))
992*4887Schin 						goto clear_hour;
993*4887Schin 					continue;
994*4887Schin 				case TM_MONTH_ABBREV:
995*4887Schin 					j += TM_MONTH - TM_MONTH_ABBREV;
996*4887Schin 					/*FALLTHROUGH*/
997*4887Schin 				case TM_MONTH:
998*4887Schin 					if (state & MONTH)
999*4887Schin 						goto done;
1000*4887Schin 					state |= MONTH;
1001*4887Schin 					i = tm->tm_mon;
1002*4887Schin 					tm->tm_mon = j - TM_MONTH;
1003*4887Schin 					if (n < 0)
1004*4887Schin 					{
1005*4887Schin 						while (skip[*s])
1006*4887Schin 							s++;
1007*4887Schin 						if (isdigit(*s))
1008*4887Schin 						{
1009*4887Schin 							n = strtol(s, &t, 10);
1010*4887Schin 							if (n <= 31 && *t != ':')
1011*4887Schin 								s = t;
1012*4887Schin 							else
1013*4887Schin 								n = -1;
1014*4887Schin 						}
1015*4887Schin 					}
1016*4887Schin 					if (n >= 0)
1017*4887Schin 					{
1018*4887Schin 						if (n > 31)
1019*4887Schin 							goto done;
1020*4887Schin 						state |= DAY|MDAY;
1021*4887Schin 						tm->tm_mday = n;
1022*4887Schin 						if (f > 0)
1023*4887Schin 							tm->tm_year += f;
1024*4887Schin 					}
1025*4887Schin 					if (state & (LAST|NEXT|THIS))
1026*4887Schin 					{
1027*4887Schin 						n = i;
1028*4887Schin 						goto rel_month;
1029*4887Schin 					}
1030*4887Schin 					continue;
1031*4887Schin 				case TM_UT:
1032*4887Schin 					if (state & ZONE)
1033*4887Schin 						goto done;
1034*4887Schin 					state |= ZONE;
1035*4887Schin 					zone = tmgoff(s, &t, 0);
1036*4887Schin 					s = t;
1037*4887Schin 					continue;
1038*4887Schin 				case TM_DT:
1039*4887Schin 					if (!dst)
1040*4887Schin 						goto done;
1041*4887Schin 					if (!(state & ZONE))
1042*4887Schin 					{
1043*4887Schin 						dst = tm_info.zone->dst;
1044*4887Schin 						zone = tm_info.zone->west;
1045*4887Schin 					}
1046*4887Schin 					zone += tmgoff(s, &t, dst);
1047*4887Schin 					s = t;
1048*4887Schin 					dst = 0;
1049*4887Schin 					state |= ZONE;
1050*4887Schin 					continue;
1051*4887Schin 				case TM_NOISE:
1052*4887Schin 					continue;
1053*4887Schin 				}
1054*4887Schin 			}
1055*4887Schin 			if (!(state & ZONE) && (zp = tmzone(s, &t, type, &dst)))
1056*4887Schin 			{
1057*4887Schin 				s = t;
1058*4887Schin 				zone = zp->west + dst;
1059*4887Schin 				tm_info.date = zp;
1060*4887Schin 				state |= ZONE;
1061*4887Schin 				continue;
1062*4887Schin 			}
1063*4887Schin 			if (!type && (zp = tmtype(s, &t)))
1064*4887Schin 			{
1065*4887Schin 				s = t;
1066*4887Schin 				type = zp->type;
1067*4887Schin 				continue;
1068*4887Schin 			}
1069*4887Schin 			state |= BREAK;
1070*4887Schin 		}
1071*4887Schin 		else if (*s == '/')
1072*4887Schin 		{
1073*4887Schin 			if (!(state & (YEAR|MONTH)) && n >= 1900 && n < 3000 && (i = strtol(s + 1, &t, 10)) > 0 && i <= 12)
1074*4887Schin 			{
1075*4887Schin 				state |= YEAR;
1076*4887Schin 				tm->tm_year = n - 1900;
1077*4887Schin 				s = t;
1078*4887Schin 				i--;
1079*4887Schin 			}
1080*4887Schin 			else
1081*4887Schin 			{
1082*4887Schin 				if ((state & MONTH) || n <= 0 || n > 31)
1083*4887Schin 					break;
1084*4887Schin 				if (isalpha(*++s))
1085*4887Schin 				{
1086*4887Schin 					if ((i = tmlex(s, &t, tm_info.format, TM_DAY_ABBREV, NiL, 0)) < 0)
1087*4887Schin 						break;
1088*4887Schin 					if (i >= TM_MONTH)
1089*4887Schin 						i -= TM_MONTH;
1090*4887Schin 					s = t;
1091*4887Schin 				}
1092*4887Schin 				else
1093*4887Schin 				{
1094*4887Schin 					i = n - 1;
1095*4887Schin 					n = strtol(s, &t, 10);
1096*4887Schin 					s = t;
1097*4887Schin 					if (n <= 0 || n > 31)
1098*4887Schin 						break;
1099*4887Schin 					if (*s == '/' && !isdigit(*(s + 1)))
1100*4887Schin 						break;
1101*4887Schin 				}
1102*4887Schin 				state |= DAY;
1103*4887Schin 				tm->tm_mday = n;
1104*4887Schin 			}
1105*4887Schin 			state |= MONTH;
1106*4887Schin 			n = tm->tm_mon;
1107*4887Schin 			tm->tm_mon = i;
1108*4887Schin 			if (*s == '/')
1109*4887Schin 			{
1110*4887Schin 				n = strtol(++s, &t, 10);
1111*4887Schin 				w = t - s;
1112*4887Schin 				s = t;
1113*4887Schin 				if (*s == '/' || *s == ':' || *s == '-' || *s == '_')
1114*4887Schin 					s++;
1115*4887Schin 			}
1116*4887Schin 			else
1117*4887Schin 			{
1118*4887Schin 				if (state & (LAST|NEXT|THIS))
1119*4887Schin 				{
1120*4887Schin 				rel_month:
1121*4887Schin 					if (state & LAST)
1122*4887Schin 						tm->tm_year -= (tm->tm_mon < n) ? 0 : 1;
1123*4887Schin 					else
1124*4887Schin 						tm->tm_year += ((state & NEXT) ? 1 : 0) + ((tm->tm_mon < n) ? 1 : 0);
1125*4887Schin 					if (state & MDAY)
1126*4887Schin 						goto clear_hour;
1127*4887Schin 					goto clear_mday;
1128*4887Schin 				}
1129*4887Schin 				continue;
1130*4887Schin 			}
1131*4887Schin 		}
1132*4887Schin 		if (n < 0 || w > 4)
1133*4887Schin 			break;
1134*4887Schin 		if (w == 4)
1135*4887Schin 		{
1136*4887Schin 			if ((state & YEAR) || n < 1900 || n >= 3000)
1137*4887Schin 				break;
1138*4887Schin 			state |= YEAR;
1139*4887Schin 			tm->tm_year = n - 1900;
1140*4887Schin 		}
1141*4887Schin 		else if (w == 3)
1142*4887Schin 		{
1143*4887Schin 			if (state & (MONTH|MDAY|WDAY))
1144*4887Schin 				break;
1145*4887Schin 			state |= MONTH|DAY|MDAY;
1146*4887Schin 			tm->tm_mon = 0;
1147*4887Schin 			tm->tm_mday = n;
1148*4887Schin 		}
1149*4887Schin 		else if (w == 2 && !(state & YEAR))
1150*4887Schin 		{
1151*4887Schin 			state |= YEAR;
1152*4887Schin 			if (n < TM_WINDOW)
1153*4887Schin 				n += 100;
1154*4887Schin 			tm->tm_year = n;
1155*4887Schin 		}
1156*4887Schin 		else if (!(state & MONTH) && n >= 1 && n <= 12)
1157*4887Schin 		{
1158*4887Schin 			state |= MONTH;
1159*4887Schin 			tm->tm_mon = n - 1;
1160*4887Schin 		}
1161*4887Schin 		else if (!(state & (MDAY|WDAY)) && n >= 1 && n <= 31)
1162*4887Schin 		{
1163*4887Schin 			state |= DAY|MDAY|WDAY;
1164*4887Schin 			tm->tm_mday = n;
1165*4887Schin 		}
1166*4887Schin 		else
1167*4887Schin 			break;
1168*4887Schin 		if (state & BREAK)
1169*4887Schin 		{
1170*4887Schin 			last = t;
1171*4887Schin 			break;
1172*4887Schin 		}
1173*4887Schin 		continue;
1174*4887Schin 	clear_mon:
1175*4887Schin 		if ((set|state) & (EXACT|MONTH))
1176*4887Schin 			continue;
1177*4887Schin 		tm->tm_mon = 0;
1178*4887Schin 	clear_mday:
1179*4887Schin 		set |= MONTH;
1180*4887Schin 		if ((set|state) & (EXACT|DAY|HOUR))
1181*4887Schin 			continue;
1182*4887Schin 		tm->tm_mday = 1;
1183*4887Schin 	clear_hour:
1184*4887Schin 		set |= DAY;
1185*4887Schin 		if ((set|state) & (EXACT|HOUR))
1186*4887Schin 			continue;
1187*4887Schin 		tm->tm_hour = 0;
1188*4887Schin 	clear_min:
1189*4887Schin 		set |= HOUR;
1190*4887Schin 		if ((set|state) & (EXACT|MINUTE))
1191*4887Schin 			continue;
1192*4887Schin 		tm->tm_min = 0;
1193*4887Schin 	clear_sec:
1194*4887Schin 		set |= MINUTE;
1195*4887Schin 		if ((set|state) & (EXACT|SECOND))
1196*4887Schin 			continue;
1197*4887Schin 		tm->tm_sec = 0;
1198*4887Schin 	clear_nsec:
1199*4887Schin 		set |= SECOND;
1200*4887Schin 		if ((set|state) & (EXACT|NSEC))
1201*4887Schin 			continue;
1202*4887Schin 		tm->tm_nsec = 0;
1203*4887Schin 	}
1204*4887Schin  done:
1205*4887Schin 	if (day >= 0 && !(state & (MDAY|WDAY)))
1206*4887Schin 	{
1207*4887Schin 		if ((m = dir) > 0)
1208*4887Schin 			m--;
1209*4887Schin 		if (state & MONTH)
1210*4887Schin 			tm->tm_mday = 1;
1211*4887Schin 		else if (m < 0)
1212*4887Schin 			m++;
1213*4887Schin 		tm = tmxmake(tmxtime(tm, zone));
1214*4887Schin 		j = day - tm->tm_wday;
1215*4887Schin 		if (j < 0)
1216*4887Schin 			j += 7;
1217*4887Schin 		tm->tm_mday += j + m * 7;
1218*4887Schin 		if (state & FINAL)
1219*4887Schin 			for (n = tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year)); (tm->tm_mday + 7) <= n; tm->tm_mday += 7);
1220*4887Schin 	}
1221*4887Schin 	else if (day < 0 && (state & FINAL) && (set & DAY))
1222*4887Schin 		tm->tm_mday = tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year));
1223*4887Schin 	if (e)
1224*4887Schin 		*e = last;
1225*4887Schin 	return tmxtime(tm, zone);
1226*4887Schin }
1227