1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin *                                                                      *
3da2e3ebdSchin *               This software is part of the ast package               *
4*b30d1939SAndy Fiddaman *          Copyright (c) 1985-2011 AT&T Intellectual Property          *
5da2e3ebdSchin *                      and is licensed under the                       *
6*b30d1939SAndy Fiddaman *                 Eclipse Public License, Version 1.0                  *
77c2fbfb3SApril Chin *                    by AT&T Intellectual Property                     *
8da2e3ebdSchin *                                                                      *
9da2e3ebdSchin *                A copy of the License is available at                 *
10*b30d1939SAndy Fiddaman *          http://www.eclipse.org/org/documents/epl-v10.html           *
11*b30d1939SAndy Fiddaman *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12da2e3ebdSchin *                                                                      *
13da2e3ebdSchin *              Information and Software Systems Research               *
14da2e3ebdSchin *                            AT&T Research                             *
15da2e3ebdSchin *                           Florham Park NJ                            *
16da2e3ebdSchin *                                                                      *
17da2e3ebdSchin *                 Glenn Fowler <gsf@research.att.com>                  *
18da2e3ebdSchin *                  David Korn <dgk@research.att.com>                   *
19da2e3ebdSchin *                   Phong Vo <kpv@research.att.com>                    *
20da2e3ebdSchin *                                                                      *
21da2e3ebdSchin ***********************************************************************/
22da2e3ebdSchin #pragma prototyped
23da2e3ebdSchin /*
24da2e3ebdSchin  * Glenn Fowler
25da2e3ebdSchin  * AT&T Research
26da2e3ebdSchin  *
27da2e3ebdSchin  * Time_t conversion support
28da2e3ebdSchin  */
29da2e3ebdSchin 
30da2e3ebdSchin #include <tmx.h>
31da2e3ebdSchin #include <ctype.h>
32da2e3ebdSchin 
33da2e3ebdSchin #define warped(t,n)	((t)<((n)-tmxsns(6L*30L*24L*60L*60L,0))||(t)>((n)+tmxsns(24L*60L*60L,0)))
34da2e3ebdSchin 
35da2e3ebdSchin /*
36da2e3ebdSchin  * format n with padding p into s
37da2e3ebdSchin  * return end of s
38da2e3ebdSchin  *
39da2e3ebdSchin  * p:	<0	blank padding
40da2e3ebdSchin  *	 0	no padding
41da2e3ebdSchin  *	>0	0 padding
42da2e3ebdSchin  */
43da2e3ebdSchin 
44da2e3ebdSchin static char*
number(register char * s,register char * e,register long n,register int p,int w,int pad)45da2e3ebdSchin number(register char* s, register char* e, register long n, register int p, int w, int pad)
46da2e3ebdSchin {
47da2e3ebdSchin 	char*	b;
48da2e3ebdSchin 
497c2fbfb3SApril Chin 	if (w)
507c2fbfb3SApril Chin 	{
517c2fbfb3SApril Chin 		if (p > 0 && (pad == 0 || pad == '0'))
527c2fbfb3SApril Chin 			while (w > p)
537c2fbfb3SApril Chin 			{
547c2fbfb3SApril Chin 				p++;
557c2fbfb3SApril Chin 				n *= 10;
567c2fbfb3SApril Chin 			}
577c2fbfb3SApril Chin 		else if (w > p)
587c2fbfb3SApril Chin 			p = w;
597c2fbfb3SApril Chin 	}
60da2e3ebdSchin 	switch (pad)
61da2e3ebdSchin 	{
62da2e3ebdSchin 	case '-':
63da2e3ebdSchin 		p = 0;
64da2e3ebdSchin 		break;
65da2e3ebdSchin 	case '_':
66da2e3ebdSchin 		if (p > 0)
67da2e3ebdSchin 			p = -p;
68da2e3ebdSchin 		break;
69da2e3ebdSchin 	case '0':
70da2e3ebdSchin 		if (p < 0)
71da2e3ebdSchin 			p = -p;
72da2e3ebdSchin 		break;
73da2e3ebdSchin 	}
74da2e3ebdSchin 	b = s;
75da2e3ebdSchin 	if (p > 0)
76da2e3ebdSchin 		s += sfsprintf(s, e - s, "%0*lu", p, n);
77da2e3ebdSchin 	else if (p < 0)
78da2e3ebdSchin 		s += sfsprintf(s, e - s, "%*lu", -p, n);
79da2e3ebdSchin 	else
80da2e3ebdSchin 		s += sfsprintf(s, e - s, "%lu", n);
81da2e3ebdSchin 	if (w && (s - b) > w)
82da2e3ebdSchin 		*(s = b + w) = 0;
83da2e3ebdSchin 	return s;
84da2e3ebdSchin }
85da2e3ebdSchin 
86da2e3ebdSchin typedef struct Stack_s
87da2e3ebdSchin {
88da2e3ebdSchin 	char*		format;
89da2e3ebdSchin 	int		delimiter;
90da2e3ebdSchin } Stack_t;
91da2e3ebdSchin 
92da2e3ebdSchin /*
93da2e3ebdSchin  * format t into buf of length len
94da2e3ebdSchin  * end of buf is returned
95da2e3ebdSchin  */
96da2e3ebdSchin 
97da2e3ebdSchin char*
tmxfmt(char * buf,size_t len,const char * format,Time_t t)98da2e3ebdSchin tmxfmt(char* buf, size_t len, const char* format, Time_t t)
99da2e3ebdSchin {
100da2e3ebdSchin 	register char*	cp;
101da2e3ebdSchin 	register char*	ep;
102da2e3ebdSchin 	register char*	p;
103da2e3ebdSchin 	register int	n;
104da2e3ebdSchin 	int		c;
105da2e3ebdSchin 	int		i;
106da2e3ebdSchin 	int		flags;
10734f9b3eeSRoland Mainz 	int		alt;
108da2e3ebdSchin 	int		pad;
109da2e3ebdSchin 	int		delimiter;
110da2e3ebdSchin 	int		width;
111da2e3ebdSchin 	int		prec;
112da2e3ebdSchin 	int		parts;
11334f9b3eeSRoland Mainz 	char*		arg;
114da2e3ebdSchin 	char*		f;
115da2e3ebdSchin 	const char*	oformat;
11634f9b3eeSRoland Mainz 	Tm_t*		tm;
117da2e3ebdSchin 	Tm_zone_t*	zp;
118da2e3ebdSchin 	Time_t		now;
119da2e3ebdSchin 	Stack_t*	sp;
120da2e3ebdSchin 	Stack_t		stack[8];
12134f9b3eeSRoland Mainz 	Tm_t		ts;
12234f9b3eeSRoland Mainz 	char		argbuf[256];
123da2e3ebdSchin 	char		fmt[32];
124da2e3ebdSchin 
125da2e3ebdSchin 	tmlocale();
12634f9b3eeSRoland Mainz 	tm = tmxtm(&ts, t, NiL);
127da2e3ebdSchin 	if (!format || !*format)
128da2e3ebdSchin 		format = tm_info.deformat;
129da2e3ebdSchin 	oformat = format;
130da2e3ebdSchin 	flags = tm_info.flags;
131da2e3ebdSchin 	sp = &stack[0];
132da2e3ebdSchin 	cp = buf;
133da2e3ebdSchin 	ep = buf + len;
134da2e3ebdSchin 	delimiter = 0;
135da2e3ebdSchin 	for (;;)
136da2e3ebdSchin 	{
137da2e3ebdSchin 		if ((c = *format++) == delimiter)
138da2e3ebdSchin 		{
139da2e3ebdSchin 			delimiter = 0;
140da2e3ebdSchin 			if (sp <= &stack[0])
141da2e3ebdSchin 				break;
142da2e3ebdSchin 			sp--;
143da2e3ebdSchin 			format = sp->format;
144da2e3ebdSchin 			delimiter = sp->delimiter;
145da2e3ebdSchin 			continue;
146da2e3ebdSchin 		}
147da2e3ebdSchin 		if (c != '%')
148da2e3ebdSchin 		{
149da2e3ebdSchin 			if (cp < ep)
150da2e3ebdSchin 				*cp++ = c;
151da2e3ebdSchin 			continue;
152da2e3ebdSchin 		}
15334f9b3eeSRoland Mainz 		alt = 0;
15434f9b3eeSRoland Mainz 		arg = 0;
155da2e3ebdSchin 		pad = 0;
156da2e3ebdSchin 		width = 0;
157da2e3ebdSchin 		prec = 0;
158da2e3ebdSchin 		parts = 0;
159da2e3ebdSchin 		for (;;)
160da2e3ebdSchin 		{
161da2e3ebdSchin 			switch (c = *format++)
162da2e3ebdSchin 			{
163da2e3ebdSchin 			case '_':
164da2e3ebdSchin 			case '-':
165da2e3ebdSchin 				pad = c;
166da2e3ebdSchin 				continue;
167da2e3ebdSchin 			case 'E':
168da2e3ebdSchin 			case 'O':
169da2e3ebdSchin 				if (!isalpha(*format))
170da2e3ebdSchin 					break;
17134f9b3eeSRoland Mainz 				alt = c;
172da2e3ebdSchin 				continue;
173da2e3ebdSchin 			case '0':
174da2e3ebdSchin 				if (!parts)
175da2e3ebdSchin 				{
176da2e3ebdSchin 					pad = c;
177da2e3ebdSchin 					continue;
178da2e3ebdSchin 				}
179da2e3ebdSchin 				/*FALLTHROUGH*/
180da2e3ebdSchin 			case '1':
181da2e3ebdSchin 			case '2':
182da2e3ebdSchin 			case '3':
183da2e3ebdSchin 			case '4':
184da2e3ebdSchin 			case '5':
185da2e3ebdSchin 			case '6':
186da2e3ebdSchin 			case '7':
187da2e3ebdSchin 			case '8':
188da2e3ebdSchin 			case '9':
189da2e3ebdSchin 				switch (parts)
190da2e3ebdSchin 				{
191da2e3ebdSchin 				case 0:
192da2e3ebdSchin 					parts++;
193da2e3ebdSchin 					/*FALLTHROUGH*/
194da2e3ebdSchin 				case 1:
195da2e3ebdSchin 					width = width * 10 + (c - '0');
196da2e3ebdSchin 					break;
197da2e3ebdSchin 				case 2:
198da2e3ebdSchin 					prec = prec * 10 + (c - '0');
199da2e3ebdSchin 					break;
200da2e3ebdSchin 				}
201da2e3ebdSchin 				continue;
202da2e3ebdSchin 			case '.':
203da2e3ebdSchin 				if (!parts++)
204da2e3ebdSchin 					parts++;
205da2e3ebdSchin 				continue;
20634f9b3eeSRoland Mainz 			case '(':
20734f9b3eeSRoland Mainz 				i = 1;
20834f9b3eeSRoland Mainz 				arg = argbuf;
20934f9b3eeSRoland Mainz 				for (;;)
21034f9b3eeSRoland Mainz 				{
21134f9b3eeSRoland Mainz 					if (!(c = *format++))
21234f9b3eeSRoland Mainz 					{
21334f9b3eeSRoland Mainz 						format--;
21434f9b3eeSRoland Mainz 						break;
21534f9b3eeSRoland Mainz 					}
21634f9b3eeSRoland Mainz 					else if (c == '(')
21734f9b3eeSRoland Mainz 						i++;
21834f9b3eeSRoland Mainz 					else if (c == ')' && !--i)
21934f9b3eeSRoland Mainz 						break;
22034f9b3eeSRoland Mainz 					else if (arg < &argbuf[sizeof(argbuf) - 1])
22134f9b3eeSRoland Mainz 						*arg++ = c;
22234f9b3eeSRoland Mainz 				}
22334f9b3eeSRoland Mainz 				*arg = 0;
22434f9b3eeSRoland Mainz 				arg = argbuf;
22534f9b3eeSRoland Mainz 				continue;
226da2e3ebdSchin 			default:
227da2e3ebdSchin 				break;
228da2e3ebdSchin 			}
229da2e3ebdSchin 			break;
230da2e3ebdSchin 		}
231da2e3ebdSchin 		switch (c)
232da2e3ebdSchin 		{
233da2e3ebdSchin 		case 0:
234da2e3ebdSchin 			format--;
235da2e3ebdSchin 			continue;
236da2e3ebdSchin 		case '%':
237da2e3ebdSchin 			if (cp < ep)
238da2e3ebdSchin 				*cp++ = '%';
239da2e3ebdSchin 			continue;
240da2e3ebdSchin 		case '?':
241da2e3ebdSchin 			if (tm_info.deformat != tm_info.format[TM_DEFAULT])
242da2e3ebdSchin 				format = tm_info.deformat;
243da2e3ebdSchin 			else if (!*format)
244da2e3ebdSchin 				format = tm_info.format[TM_DEFAULT];
245da2e3ebdSchin 			continue;
246da2e3ebdSchin 		case 'a':	/* abbreviated day of week name */
24734f9b3eeSRoland Mainz 			n = TM_DAY_ABBREV + tm->tm_wday;
248da2e3ebdSchin 			goto index;
249da2e3ebdSchin 		case 'A':	/* day of week name */
25034f9b3eeSRoland Mainz 			n = TM_DAY + tm->tm_wday;
251da2e3ebdSchin 			goto index;
252da2e3ebdSchin 		case 'b':	/* abbreviated month name */
253da2e3ebdSchin 		case 'h':
25434f9b3eeSRoland Mainz 			n = TM_MONTH_ABBREV + tm->tm_mon;
255da2e3ebdSchin 			goto index;
256da2e3ebdSchin 		case 'B':	/* month name */
25734f9b3eeSRoland Mainz 			n = TM_MONTH + tm->tm_mon;
258da2e3ebdSchin 			goto index;
259da2e3ebdSchin 		case 'c':	/* `ctime(3)' date sans newline */
260da2e3ebdSchin 			p = tm_info.format[TM_CTIME];
261da2e3ebdSchin 			goto push;
262da2e3ebdSchin 		case 'C':	/* 2 digit century */
26334f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)(1900 + tm->tm_year) / 100, 2, width, pad);
264da2e3ebdSchin 			continue;
265da2e3ebdSchin 		case 'd':	/* day of month */
26634f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tm->tm_mday, 2, width, pad);
267da2e3ebdSchin 			continue;
268da2e3ebdSchin 		case 'D':	/* date */
269da2e3ebdSchin 			p = tm_info.format[TM_DATE];
270da2e3ebdSchin 			goto push;
271da2e3ebdSchin 		case 'e':       /* blank padded day of month */
27234f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tm->tm_mday, -2, width, pad);
273da2e3ebdSchin 			continue;
274*b30d1939SAndy Fiddaman 		case 'f':	/* (AST) OBSOLETE use %Qf */
275*b30d1939SAndy Fiddaman 			p = "%Qf";
276da2e3ebdSchin 			goto push;
2777c2fbfb3SApril Chin 		case 'F':	/* ISO 8601:2000 standard date format */
2787c2fbfb3SApril Chin 			p = "%Y-%m-%d";
279da2e3ebdSchin 			goto push;
280da2e3ebdSchin 		case 'g':	/* %V 2 digit year */
281da2e3ebdSchin 		case 'G':	/* %V 4 digit year */
28234f9b3eeSRoland Mainz 			n = tm->tm_year + 1900;
28334f9b3eeSRoland Mainz 			if (tm->tm_yday < 7)
284da2e3ebdSchin 			{
28534f9b3eeSRoland Mainz 				if (tmweek(tm, 2, -1, -1) >= 52)
286da2e3ebdSchin 					n--;
287da2e3ebdSchin 			}
28834f9b3eeSRoland Mainz 			else if (tm->tm_yday > 358)
289da2e3ebdSchin 			{
29034f9b3eeSRoland Mainz 				if (tmweek(tm, 2, -1, -1) <= 1)
291da2e3ebdSchin 					n++;
292da2e3ebdSchin 			}
293da2e3ebdSchin 			if (c == 'g')
294da2e3ebdSchin 			{
295da2e3ebdSchin 				n %= 100;
296da2e3ebdSchin 				c = 2;
297da2e3ebdSchin 			}
298da2e3ebdSchin 			else
299da2e3ebdSchin 				c = 4;
300da2e3ebdSchin 			cp = number(cp, ep, (long)n, c, width, pad);
301da2e3ebdSchin 			continue;
302da2e3ebdSchin 		case 'H':	/* hour (0 - 23) */
30334f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tm->tm_hour, 2, width, pad);
304da2e3ebdSchin 			continue;
305*b30d1939SAndy Fiddaman 		case 'i':	/* (AST) OBSOLETE use %QI */
306*b30d1939SAndy Fiddaman 			p = "%QI";
307da2e3ebdSchin 			goto push;
308da2e3ebdSchin 		case 'I':	/* hour (0 - 12) */
30934f9b3eeSRoland Mainz 			if ((n = tm->tm_hour) > 12) n -= 12;
310da2e3ebdSchin 			else if (n == 0) n = 12;
311da2e3ebdSchin 			cp = number(cp, ep, (long)n, 2, width, pad);
312da2e3ebdSchin 			continue;
313da2e3ebdSchin 		case 'j':	/* Julian date (1 offset) */
31434f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)(tm->tm_yday + 1), 3, width, pad);
315da2e3ebdSchin 			continue;
316*b30d1939SAndy Fiddaman 		case 'J':	/* Julian date (0 offset) */
317*b30d1939SAndy Fiddaman 			cp = number(cp, ep, (long)tm->tm_yday, 3, width, pad);
318*b30d1939SAndy Fiddaman 			continue;
319*b30d1939SAndy Fiddaman 		case 'k':	/* (AST) OBSOLETE use %QD */
320*b30d1939SAndy Fiddaman 			p = "%QD";
321da2e3ebdSchin 			goto push;
322*b30d1939SAndy Fiddaman 		case 'K':	/* (AST) largest to smallest */
32334f9b3eeSRoland Mainz 			switch (alt)
32434f9b3eeSRoland Mainz 			{
32534f9b3eeSRoland Mainz 			case 'E':
32634f9b3eeSRoland Mainz 				p = (pad == '_') ? "%Y-%m-%d %H:%M:%S.%N %z" : "%Y-%m-%d+%H:%M:%S.%N%z";
32734f9b3eeSRoland Mainz 				break;
32834f9b3eeSRoland Mainz 			case 'O':
32934f9b3eeSRoland Mainz 				p = (pad == '_') ? "%Y-%m-%d %H:%M:%S.%N" : "%Y-%m-%d+%H:%M:%S.%N";
33034f9b3eeSRoland Mainz 				break;
33134f9b3eeSRoland Mainz 			default:
33234f9b3eeSRoland Mainz 				p = (pad == '_') ? "%Y-%m-%d %H:%M:%S" : "%Y-%m-%d+%H:%M:%S";
33334f9b3eeSRoland Mainz 				break;
33434f9b3eeSRoland Mainz 			}
335da2e3ebdSchin 			goto push;
336*b30d1939SAndy Fiddaman 		case 'l':	/* (AST) OBSOLETE use %QL */
337*b30d1939SAndy Fiddaman 			p = "%QL";
338da2e3ebdSchin 			goto push;
339*b30d1939SAndy Fiddaman 		case 'L':	/* (AST) OBSOLETE use %Ql */
340*b30d1939SAndy Fiddaman 			p = "%Ql";
3417c2fbfb3SApril Chin 			goto push;
342da2e3ebdSchin 		case 'm':	/* month number */
34334f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)(tm->tm_mon + 1), 2, width, pad);
344da2e3ebdSchin 			continue;
345da2e3ebdSchin 		case 'M':	/* minutes */
34634f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tm->tm_min, 2, width, pad);
347da2e3ebdSchin 			continue;
348da2e3ebdSchin 		case 'n':
349da2e3ebdSchin 			if (cp < ep)
350da2e3ebdSchin 				*cp++ = '\n';
351da2e3ebdSchin 			continue;
352*b30d1939SAndy Fiddaman 		case 'N':	/* (AST|GNU) nanosecond part */
35334f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tm->tm_nsec, 9, width, pad);
354da2e3ebdSchin 			continue;
355*b30d1939SAndy Fiddaman #if 0
356*b30d1939SAndy Fiddaman 		case 'o':	/* (UNUSED) */
357*b30d1939SAndy Fiddaman 			continue;
358*b30d1939SAndy Fiddaman #endif
359da2e3ebdSchin 		case 'p':	/* meridian */
36034f9b3eeSRoland Mainz 			n = TM_MERIDIAN + (tm->tm_hour >= 12);
361da2e3ebdSchin 			goto index;
362*b30d1939SAndy Fiddaman 		case 'P':	/* (AST|GNU) lower case meridian */
363*b30d1939SAndy Fiddaman 			p = tm_info.format[TM_MERIDIAN + (tm->tm_hour >= 12)];
364*b30d1939SAndy Fiddaman 			while (cp < ep && (n = *p++))
365*b30d1939SAndy Fiddaman 				*cp++ = isupper(n) ? tolower(n) : n;
366da2e3ebdSchin 			continue;
367*b30d1939SAndy Fiddaman 		case 'q':	/* (AST) OBSOLETE use %Qz */
368*b30d1939SAndy Fiddaman 			p = "%Qz";
369*b30d1939SAndy Fiddaman 			goto push;
370*b30d1939SAndy Fiddaman 		case 'Q':	/* (AST) %Q<alpha> or %Q<delim>recent<delim>distant<delim> */
371da2e3ebdSchin 			if (c = *format)
372da2e3ebdSchin 			{
373da2e3ebdSchin 				format++;
374da2e3ebdSchin 				if (isalpha(c))
375da2e3ebdSchin 				{
376da2e3ebdSchin 					switch (c)
377da2e3ebdSchin 					{
378da2e3ebdSchin 					case 'd':	/* `ls -l' distant date */
379da2e3ebdSchin 						p = tm_info.format[TM_DISTANT];
380da2e3ebdSchin 						goto push;
381*b30d1939SAndy Fiddaman 					case 'D':	/* `date(1)' date */
382*b30d1939SAndy Fiddaman 						p = tm_info.format[TM_DATE_1];
383*b30d1939SAndy Fiddaman 						goto push;
384*b30d1939SAndy Fiddaman 					case 'f':	/* TM_DEFAULT override */
385*b30d1939SAndy Fiddaman 						p = tm_info.deformat;
386*b30d1939SAndy Fiddaman 						goto push;
387*b30d1939SAndy Fiddaman 					case 'I':	/* international `date(1)' date */
388*b30d1939SAndy Fiddaman 						p = tm_info.format[TM_INTERNATIONAL];
389*b30d1939SAndy Fiddaman 						goto push;
390*b30d1939SAndy Fiddaman 					case 'l':	/* TM_DEFAULT */
391*b30d1939SAndy Fiddaman 						p = tm_info.format[TM_DEFAULT];
392*b30d1939SAndy Fiddaman 						goto push;
393*b30d1939SAndy Fiddaman 					case 'L':	/* `ls -l' date */
394*b30d1939SAndy Fiddaman 						if (t)
395*b30d1939SAndy Fiddaman 						{
396*b30d1939SAndy Fiddaman 							now = tmxgettime();
397*b30d1939SAndy Fiddaman 							if (warped(t, now))
398*b30d1939SAndy Fiddaman 							{
399*b30d1939SAndy Fiddaman 								p = tm_info.format[TM_DISTANT];
400*b30d1939SAndy Fiddaman 								goto push;
401*b30d1939SAndy Fiddaman 							}
402*b30d1939SAndy Fiddaman 						}
403*b30d1939SAndy Fiddaman 						p = tm_info.format[TM_RECENT];
404*b30d1939SAndy Fiddaman 						goto push;
405*b30d1939SAndy Fiddaman 					case 'o':	/* set options ( %([+-]flag...)o ) */
406*b30d1939SAndy Fiddaman 						if (arg)
407*b30d1939SAndy Fiddaman 						{
408*b30d1939SAndy Fiddaman 							c = '+';
409*b30d1939SAndy Fiddaman 							i = 0;
410*b30d1939SAndy Fiddaman 							for (;;)
411*b30d1939SAndy Fiddaman 							{
412*b30d1939SAndy Fiddaman 								switch (*arg++)
413*b30d1939SAndy Fiddaman 								{
414*b30d1939SAndy Fiddaman 								case 0:
415*b30d1939SAndy Fiddaman 									n = 0;
416*b30d1939SAndy Fiddaman 									break;
417*b30d1939SAndy Fiddaman 								case '=':
418*b30d1939SAndy Fiddaman 									i = !i;
419*b30d1939SAndy Fiddaman 									continue;
420*b30d1939SAndy Fiddaman 								case '+':
421*b30d1939SAndy Fiddaman 								case '-':
422*b30d1939SAndy Fiddaman 								case '!':
423*b30d1939SAndy Fiddaman 									c = *(arg - 1);
424*b30d1939SAndy Fiddaman 									continue;
425*b30d1939SAndy Fiddaman 								case 'l':
426*b30d1939SAndy Fiddaman 									n = TM_LEAP;
427*b30d1939SAndy Fiddaman 									break;
428*b30d1939SAndy Fiddaman 								case 'n':
429*b30d1939SAndy Fiddaman 								case 's':
430*b30d1939SAndy Fiddaman 									n = TM_SUBSECOND;
431*b30d1939SAndy Fiddaman 									break;
432*b30d1939SAndy Fiddaman 								case 'u':
433*b30d1939SAndy Fiddaman 									n = TM_UTC;
434*b30d1939SAndy Fiddaman 									break;
435*b30d1939SAndy Fiddaman 								default:
436*b30d1939SAndy Fiddaman 									continue;
437*b30d1939SAndy Fiddaman 								}
438*b30d1939SAndy Fiddaman 								if (!n)
439*b30d1939SAndy Fiddaman 									break;
440*b30d1939SAndy Fiddaman 
441*b30d1939SAndy Fiddaman 								/*
442*b30d1939SAndy Fiddaman 								 * right, the global state stinks
443*b30d1939SAndy Fiddaman 								 * but we respect its locale-like status
444*b30d1939SAndy Fiddaman 								 */
445*b30d1939SAndy Fiddaman 
446*b30d1939SAndy Fiddaman 								if (c == '+')
447*b30d1939SAndy Fiddaman 								{
448*b30d1939SAndy Fiddaman 									if (!(flags & n))
449*b30d1939SAndy Fiddaman 									{
450*b30d1939SAndy Fiddaman 										flags |= n;
451*b30d1939SAndy Fiddaman 										tm_info.flags |= n;
452*b30d1939SAndy Fiddaman 										tm = tmxtm(tm, t, (flags & TM_UTC) ? &tm_data.zone[2] : tm->tm_zone);
453*b30d1939SAndy Fiddaman 										if (!i)
454*b30d1939SAndy Fiddaman 											tm_info.flags &= ~n;
455*b30d1939SAndy Fiddaman 									}
456*b30d1939SAndy Fiddaman 								}
457*b30d1939SAndy Fiddaman 								else if (flags & n)
458*b30d1939SAndy Fiddaman 								{
459*b30d1939SAndy Fiddaman 									flags &= ~n;
460*b30d1939SAndy Fiddaman 									tm_info.flags &= ~n;
461*b30d1939SAndy Fiddaman 									tm = tmxtm(tm, t, (flags & TM_UTC) ? &tm_data.zone[2] : tm->tm_zone);
462*b30d1939SAndy Fiddaman 									if (!i)
463*b30d1939SAndy Fiddaman 										tm_info.flags |= n;
464*b30d1939SAndy Fiddaman 								}
465*b30d1939SAndy Fiddaman 							}
466*b30d1939SAndy Fiddaman 						}
467*b30d1939SAndy Fiddaman 						break;
468da2e3ebdSchin 					case 'r':	/* `ls -l' recent date */
469da2e3ebdSchin 						p = tm_info.format[TM_RECENT];
470da2e3ebdSchin 						goto push;
471*b30d1939SAndy Fiddaman 					case 'z':	/* time zone nation code */
472*b30d1939SAndy Fiddaman 						if (!(flags & TM_UTC))
473*b30d1939SAndy Fiddaman 						{
474*b30d1939SAndy Fiddaman 							if ((zp = tm->tm_zone) != tm_info.local)
475*b30d1939SAndy Fiddaman 								for (; zp >= tm_data.zone; zp--)
476*b30d1939SAndy Fiddaman 									if (p = zp->type)
477*b30d1939SAndy Fiddaman 										goto string;
478*b30d1939SAndy Fiddaman 							else if (p = zp->type)
479*b30d1939SAndy Fiddaman 								goto string;
480*b30d1939SAndy Fiddaman 						}
481*b30d1939SAndy Fiddaman 						break;
482da2e3ebdSchin 					default:
483da2e3ebdSchin 						format--;
484da2e3ebdSchin 						break;
485da2e3ebdSchin 					}
486da2e3ebdSchin 				}
487da2e3ebdSchin 				else
488da2e3ebdSchin 				{
489da2e3ebdSchin 					if (t)
490da2e3ebdSchin 					{
491da2e3ebdSchin 						now = tmxgettime();
492da2e3ebdSchin 						p = warped(t, now) ? (char*)0 : (char*)format;
493da2e3ebdSchin 					}
494da2e3ebdSchin 					else
495da2e3ebdSchin 						p = (char*)format;
496da2e3ebdSchin 					i = 0;
497da2e3ebdSchin 					while (n = *format)
498da2e3ebdSchin 					{
499da2e3ebdSchin 						format++;
500da2e3ebdSchin 						if (n == c)
501da2e3ebdSchin 						{
502da2e3ebdSchin 							if (!p)
503da2e3ebdSchin 								p = (char*)format;
504da2e3ebdSchin 							if (++i == 2)
505da2e3ebdSchin 								goto push_delimiter;
506da2e3ebdSchin 						}
507da2e3ebdSchin 					}
508da2e3ebdSchin 				}
509da2e3ebdSchin 			}
510da2e3ebdSchin 			continue;
511da2e3ebdSchin 		case 'r':
512da2e3ebdSchin 			p = tm_info.format[TM_MERIDIAN_TIME];
513da2e3ebdSchin 			goto push;
514da2e3ebdSchin 		case 'R':
515da2e3ebdSchin 			p = "%H:%M";
516da2e3ebdSchin 			goto push;
517*b30d1939SAndy Fiddaman 		case 's':	/* (DEFACTO) seconds[.nanoseconds] since the epoch */
518da2e3ebdSchin 		case '#':
51934f9b3eeSRoland Mainz 			now = t;
520da2e3ebdSchin 			f = fmt;
521da2e3ebdSchin 			*f++ = '%';
522da2e3ebdSchin 			if (pad == '0')
523da2e3ebdSchin 				*f++ = pad;
524da2e3ebdSchin 			if (width)
525da2e3ebdSchin 				f += sfsprintf(f, &fmt[sizeof(fmt)] - f, "%d", width);
526da2e3ebdSchin 			f += sfsprintf(f, &fmt[sizeof(fmt)] - f, "I%du", sizeof(Tmxsec_t));
527da2e3ebdSchin 			cp += sfsprintf(cp, ep - cp, fmt, tmxsec(now));
528da2e3ebdSchin 			if (parts > 1)
529da2e3ebdSchin 			{
530da2e3ebdSchin 				n = sfsprintf(cp, ep - cp, ".%09I*u", sizeof(Tmxnsec_t), tmxnsec(now));
531da2e3ebdSchin 				if (prec && n >= prec)
532da2e3ebdSchin 					n = prec + 1;
533da2e3ebdSchin 				cp += n;
534da2e3ebdSchin 			}
535da2e3ebdSchin 			continue;
536da2e3ebdSchin 		case 'S':	/* seconds */
53734f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tm->tm_sec, 2, width, pad);
538da2e3ebdSchin 			if ((flags & TM_SUBSECOND) && (format - 2) != oformat)
539da2e3ebdSchin 			{
540da2e3ebdSchin 				p = ".%N";
541da2e3ebdSchin 				goto push;
542da2e3ebdSchin 			}
543da2e3ebdSchin 			continue;
544da2e3ebdSchin 		case 't':
545da2e3ebdSchin 			if (cp < ep)
546da2e3ebdSchin 				*cp++ = '\t';
547da2e3ebdSchin 			continue;
548da2e3ebdSchin 		case 'T':
549da2e3ebdSchin 			p = tm_info.format[TM_TIME];
550da2e3ebdSchin 			goto push;
551da2e3ebdSchin 		case 'u':	/* weekday number [1(Monday)-7] */
55234f9b3eeSRoland Mainz 			if (!(i = tm->tm_wday))
553da2e3ebdSchin 				i = 7;
554da2e3ebdSchin 			cp = number(cp, ep, (long)i, 0, width, pad);
555da2e3ebdSchin 			continue;
556da2e3ebdSchin 		case 'U':	/* week number, Sunday as first day */
55734f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tmweek(tm, 0, -1, -1), 2, width, pad);
558da2e3ebdSchin 			continue;
559*b30d1939SAndy Fiddaman #if 0
560*b30d1939SAndy Fiddaman 		case 'v':	/* (UNUSED) */
561*b30d1939SAndy Fiddaman 			continue;
562*b30d1939SAndy Fiddaman #endif
563da2e3ebdSchin 		case 'V':	/* ISO week number */
56434f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tmweek(tm, 2, -1, -1), 2, width, pad);
565da2e3ebdSchin 			continue;
566da2e3ebdSchin 		case 'W':	/* week number, Monday as first day */
56734f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tmweek(tm, 1, -1, -1), 2, width, pad);
568da2e3ebdSchin 			continue;
569da2e3ebdSchin 		case 'w':	/* weekday number [0(Sunday)-6] */
57034f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tm->tm_wday, 0, width, pad);
571da2e3ebdSchin 			continue;
572da2e3ebdSchin 		case 'x':
573da2e3ebdSchin 			p = tm_info.format[TM_DATE];
574da2e3ebdSchin 			goto push;
575da2e3ebdSchin 		case 'X':
576da2e3ebdSchin 			p = tm_info.format[TM_TIME];
577da2e3ebdSchin 			goto push;
578da2e3ebdSchin 		case 'y':	/* year in the form yy */
57934f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)(tm->tm_year % 100), 2, width, pad);
580da2e3ebdSchin 			continue;
581da2e3ebdSchin 		case 'Y':	/* year in the form ccyy */
58234f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)(1900 + tm->tm_year), 4, width, pad);
583da2e3ebdSchin 			continue;
584da2e3ebdSchin 		case 'z':	/* time zone west offset */
58534f9b3eeSRoland Mainz 			if (arg)
58634f9b3eeSRoland Mainz 			{
58734f9b3eeSRoland Mainz 				if ((zp = tmzone(arg, &f, 0, 0)) && !*f && tm->tm_zone != zp)
58834f9b3eeSRoland Mainz 					tm = tmxtm(tm, tmxtime(tm, tm->tm_zone->west + (tm->tm_isdst ? tm->tm_zone->dst : 0)), zp);
58934f9b3eeSRoland Mainz 				continue;
59034f9b3eeSRoland Mainz 			}
591da2e3ebdSchin 			if ((ep - cp) >= 16)
592*b30d1939SAndy Fiddaman 				cp = tmpoff(cp, ep - cp, "", (flags & TM_UTC) ? 0 : tm->tm_zone->west + (tm->tm_isdst ? tm->tm_zone->dst : 0), pad == '_' ? -24 * 60 : 24 * 60);
593da2e3ebdSchin 			continue;
594da2e3ebdSchin 		case 'Z':	/* time zone */
59534f9b3eeSRoland Mainz 			if (arg)
59634f9b3eeSRoland Mainz 			{
59734f9b3eeSRoland Mainz 				if ((zp = tmzone(arg, &f, 0, 0)) && !*f && tm->tm_zone != zp)
59834f9b3eeSRoland Mainz 					tm = tmxtm(tm, tmxtime(tm, tm->tm_zone->west + (tm->tm_isdst ? tm->tm_zone->dst : 0)), zp);
59934f9b3eeSRoland Mainz 				continue;
60034f9b3eeSRoland Mainz 			}
60134f9b3eeSRoland Mainz 			p = (flags & TM_UTC) ? tm_info.format[TM_UT] : tm->tm_isdst && tm->tm_zone->daylight ? tm->tm_zone->daylight : tm->tm_zone->standard;
602da2e3ebdSchin 			goto string;
603*b30d1939SAndy Fiddaman 		case '=':	/* (AST) OBSOLETE use %([+-]flag...)Qo (old %=[=][+-]flag) */
60434f9b3eeSRoland Mainz 			for (arg = argbuf; *format == '=' || *format == '-' || *format == '+' || *format == '!'; format++)
60534f9b3eeSRoland Mainz 				if (arg < &argbuf[sizeof(argbuf) - 2])
60634f9b3eeSRoland Mainz 					*arg++ = *format;
60734f9b3eeSRoland Mainz 			if (*arg++ = *format)
608da2e3ebdSchin 				format++;
60934f9b3eeSRoland Mainz 			*arg = 0;
61034f9b3eeSRoland Mainz 			arg = argbuf;
61134f9b3eeSRoland Mainz 			goto options;
61234f9b3eeSRoland Mainz 		default:
61334f9b3eeSRoland Mainz 			if (cp < ep)
61434f9b3eeSRoland Mainz 				*cp++ = '%';
61534f9b3eeSRoland Mainz 			if (cp < ep)
61634f9b3eeSRoland Mainz 				*cp++ = c;
61734f9b3eeSRoland Mainz 			continue;
61834f9b3eeSRoland Mainz 		}
61934f9b3eeSRoland Mainz 	index:
62034f9b3eeSRoland Mainz 		p = tm_info.format[n];
62134f9b3eeSRoland Mainz 	string:
62234f9b3eeSRoland Mainz 		while (cp < ep && (*cp = *p++))
62334f9b3eeSRoland Mainz 			cp++;
62434f9b3eeSRoland Mainz 		continue;
62534f9b3eeSRoland Mainz 	options:
62634f9b3eeSRoland Mainz 		c = '+';
62734f9b3eeSRoland Mainz 		i = 0;
62834f9b3eeSRoland Mainz 		for (;;)
62934f9b3eeSRoland Mainz 		{
63034f9b3eeSRoland Mainz 			switch (*arg++)
631da2e3ebdSchin 			{
632da2e3ebdSchin 			case 0:
63334f9b3eeSRoland Mainz 				n = 0;
63434f9b3eeSRoland Mainz 				break;
63534f9b3eeSRoland Mainz 			case '=':
63634f9b3eeSRoland Mainz 				i = !i;
63734f9b3eeSRoland Mainz 				continue;
63834f9b3eeSRoland Mainz 			case '+':
63934f9b3eeSRoland Mainz 			case '-':
64034f9b3eeSRoland Mainz 			case '!':
64134f9b3eeSRoland Mainz 				c = *(arg - 1);
642da2e3ebdSchin 				continue;
643da2e3ebdSchin 			case 'l':
644da2e3ebdSchin 				n = TM_LEAP;
645da2e3ebdSchin 				break;
646da2e3ebdSchin 			case 'n':
647da2e3ebdSchin 			case 's':
648da2e3ebdSchin 				n = TM_SUBSECOND;
649da2e3ebdSchin 				break;
650da2e3ebdSchin 			case 'u':
651da2e3ebdSchin 				n = TM_UTC;
652da2e3ebdSchin 				break;
65334f9b3eeSRoland Mainz 			default:
65434f9b3eeSRoland Mainz 				continue;
655da2e3ebdSchin 			}
65634f9b3eeSRoland Mainz 			if (!n)
65734f9b3eeSRoland Mainz 				break;
658da2e3ebdSchin 
65934f9b3eeSRoland Mainz 			/*
66034f9b3eeSRoland Mainz 			 * right, the global state stinks
66134f9b3eeSRoland Mainz 			 * but we respect its locale-like status
66234f9b3eeSRoland Mainz 			 */
66334f9b3eeSRoland Mainz 
66434f9b3eeSRoland Mainz 			if (c == '+')
66534f9b3eeSRoland Mainz 			{
66634f9b3eeSRoland Mainz 				if (!(flags & n))
667da2e3ebdSchin 				{
66834f9b3eeSRoland Mainz 					flags |= n;
66934f9b3eeSRoland Mainz 					tm_info.flags |= n;
67034f9b3eeSRoland Mainz 					tm = tmxtm(tm, t, (flags & TM_UTC) ? &tm_data.zone[2] : tm->tm_zone);
671da2e3ebdSchin 					if (!i)
67234f9b3eeSRoland Mainz 						tm_info.flags &= ~n;
673da2e3ebdSchin 				}
674da2e3ebdSchin 			}
67534f9b3eeSRoland Mainz 			else if (flags & n)
67634f9b3eeSRoland Mainz 			{
67734f9b3eeSRoland Mainz 				flags &= ~n;
67834f9b3eeSRoland Mainz 				tm_info.flags &= ~n;
67934f9b3eeSRoland Mainz 				tm = tmxtm(tm, t, (flags & TM_UTC) ? &tm_data.zone[2] : tm->tm_zone);
68034f9b3eeSRoland Mainz 				if (!i)
68134f9b3eeSRoland Mainz 					tm_info.flags |= n;
68234f9b3eeSRoland Mainz 			}
683da2e3ebdSchin 		}
684da2e3ebdSchin 		continue;
685da2e3ebdSchin 	push:
686da2e3ebdSchin 		c = 0;
687da2e3ebdSchin 	push_delimiter:
688da2e3ebdSchin 		if (sp < &stack[elementsof(stack)])
689da2e3ebdSchin 		{
690da2e3ebdSchin 			sp->format = (char*)format;
691da2e3ebdSchin 			format = p;
692da2e3ebdSchin 			sp->delimiter = delimiter;
693da2e3ebdSchin 			delimiter = c;
694da2e3ebdSchin 			sp++;
695da2e3ebdSchin 		}
696da2e3ebdSchin 		continue;
697da2e3ebdSchin 	}
698da2e3ebdSchin 	tm_info.flags = flags;
699da2e3ebdSchin 	if (cp >= ep)
700da2e3ebdSchin 		cp = ep - 1;
701da2e3ebdSchin 	*cp = 0;
702da2e3ebdSchin 	return cp;
703da2e3ebdSchin }
704