xref: /titanic_51/usr/src/lib/libast/common/tm/tmxfmt.c (revision 3e14f97f673e8a630f076077de35afdd43dc1587)
1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin *                                                                      *
3da2e3ebdSchin *               This software is part of the ast package               *
4*3e14f97fSRoger A. Faulkner *          Copyright (c) 1985-2010 AT&T Intellectual Property          *
5da2e3ebdSchin *                      and is licensed under the                       *
6da2e3ebdSchin *                  Common Public License, Version 1.0                  *
77c2fbfb3SApril Chin *                    by AT&T Intellectual Property                     *
8da2e3ebdSchin *                                                                      *
9da2e3ebdSchin *                A copy of the License is available at                 *
10da2e3ebdSchin *            http://www.opensource.org/licenses/cpl1.0.txt             *
11da2e3ebdSchin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
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)
58da2e3ebdSchin 			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':       /* OBSOLETE no pad day of month */
27234f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tm->tm_mday, 0, width, pad);
273da2e3ebdSchin 			continue;
274da2e3ebdSchin 		case 'e':       /* blank padded day of month */
27534f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tm->tm_mday, -2, width, pad);
276da2e3ebdSchin 			continue;
277da2e3ebdSchin 		case 'f':	/* TM_DEFAULT override */
278da2e3ebdSchin 			p = tm_info.deformat;
279da2e3ebdSchin 			goto push;
2807c2fbfb3SApril Chin 		case 'F':	/* ISO 8601:2000 standard date format */
2817c2fbfb3SApril Chin 			p = "%Y-%m-%d";
282da2e3ebdSchin 			goto push;
283da2e3ebdSchin 		case 'g':	/* %V 2 digit year */
284da2e3ebdSchin 		case 'G':	/* %V 4 digit year */
28534f9b3eeSRoland Mainz 			n = tm->tm_year + 1900;
28634f9b3eeSRoland Mainz 			if (tm->tm_yday < 7)
287da2e3ebdSchin 			{
28834f9b3eeSRoland Mainz 				if (tmweek(tm, 2, -1, -1) >= 52)
289da2e3ebdSchin 					n--;
290da2e3ebdSchin 			}
29134f9b3eeSRoland Mainz 			else if (tm->tm_yday > 358)
292da2e3ebdSchin 			{
29334f9b3eeSRoland Mainz 				if (tmweek(tm, 2, -1, -1) <= 1)
294da2e3ebdSchin 					n++;
295da2e3ebdSchin 			}
296da2e3ebdSchin 			if (c == 'g')
297da2e3ebdSchin 			{
298da2e3ebdSchin 				n %= 100;
299da2e3ebdSchin 				c = 2;
300da2e3ebdSchin 			}
301da2e3ebdSchin 			else
302da2e3ebdSchin 				c = 4;
303da2e3ebdSchin 			cp = number(cp, ep, (long)n, c, width, pad);
304da2e3ebdSchin 			continue;
305da2e3ebdSchin 		case 'H':	/* hour (0 - 23) */
30634f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tm->tm_hour, 2, width, pad);
307da2e3ebdSchin 			continue;
308da2e3ebdSchin 		case 'i':	/* international `date(1)' date */
309da2e3ebdSchin 			p = tm_info.format[TM_INTERNATIONAL];
310da2e3ebdSchin 			goto push;
311da2e3ebdSchin 		case 'I':	/* hour (0 - 12) */
31234f9b3eeSRoland Mainz 			if ((n = tm->tm_hour) > 12) n -= 12;
313da2e3ebdSchin 			else if (n == 0) n = 12;
314da2e3ebdSchin 			cp = number(cp, ep, (long)n, 2, width, pad);
315da2e3ebdSchin 			continue;
316da2e3ebdSchin 		case 'J':	/* Julian date (0 offset) */
31734f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tm->tm_yday, 3, width, pad);
318da2e3ebdSchin 			continue;
319da2e3ebdSchin 		case 'j':	/* Julian date (1 offset) */
32034f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)(tm->tm_yday + 1), 3, width, pad);
321da2e3ebdSchin 			continue;
322da2e3ebdSchin 		case 'k':	/* `date(1)' date */
323da2e3ebdSchin 			p = tm_info.format[TM_DATE_1];
324da2e3ebdSchin 			goto push;
325da2e3ebdSchin 		case 'K':
32634f9b3eeSRoland Mainz 			switch (alt)
32734f9b3eeSRoland Mainz 			{
32834f9b3eeSRoland Mainz 			case 'E':
32934f9b3eeSRoland Mainz 				p = (pad == '_') ? "%Y-%m-%d %H:%M:%S.%N %z" : "%Y-%m-%d+%H:%M:%S.%N%z";
33034f9b3eeSRoland Mainz 				break;
33134f9b3eeSRoland Mainz 			case 'O':
33234f9b3eeSRoland Mainz 				p = (pad == '_') ? "%Y-%m-%d %H:%M:%S.%N" : "%Y-%m-%d+%H:%M:%S.%N";
33334f9b3eeSRoland Mainz 				break;
33434f9b3eeSRoland Mainz 			default:
33534f9b3eeSRoland Mainz 				p = (pad == '_') ? "%Y-%m-%d %H:%M:%S" : "%Y-%m-%d+%H:%M:%S";
33634f9b3eeSRoland Mainz 				break;
33734f9b3eeSRoland Mainz 			}
338da2e3ebdSchin 			goto push;
339da2e3ebdSchin 		case 'l':	/* `ls -l' date */
340da2e3ebdSchin 			if (t)
341da2e3ebdSchin 			{
342da2e3ebdSchin 				now = tmxgettime();
343da2e3ebdSchin 				if (warped(t, now))
344da2e3ebdSchin 				{
345da2e3ebdSchin 					p = tm_info.format[TM_DISTANT];
346da2e3ebdSchin 					goto push;
347da2e3ebdSchin 				}
348da2e3ebdSchin 			}
349da2e3ebdSchin 			p = tm_info.format[TM_RECENT];
350da2e3ebdSchin 			goto push;
3517c2fbfb3SApril Chin 		case 'L':	/* TM_DEFAULT */
3527c2fbfb3SApril Chin 		case 'O':	/* OBSOLETE */
3537c2fbfb3SApril Chin 			p = tm_info.format[TM_DEFAULT];
3547c2fbfb3SApril Chin 			goto push;
355da2e3ebdSchin 		case 'm':	/* month number */
35634f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)(tm->tm_mon + 1), 2, width, pad);
357da2e3ebdSchin 			continue;
358da2e3ebdSchin 		case 'M':	/* minutes */
35934f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tm->tm_min, 2, width, pad);
360da2e3ebdSchin 			continue;
361da2e3ebdSchin 		case 'n':
362da2e3ebdSchin 			if (cp < ep)
363da2e3ebdSchin 				*cp++ = '\n';
364da2e3ebdSchin 			continue;
365da2e3ebdSchin 		case 'N':	/* nanosecond part */
36634f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tm->tm_nsec, 9, width, pad);
367da2e3ebdSchin 			continue;
36834f9b3eeSRoland Mainz 		case 'o':	/* set options */
36934f9b3eeSRoland Mainz 			if (arg)
37034f9b3eeSRoland Mainz 				goto options;
37134f9b3eeSRoland Mainz 			/*OBSOLETE*/
37234f9b3eeSRoland Mainz 			p = tm_info.deformat;
37334f9b3eeSRoland Mainz 			goto push;
374da2e3ebdSchin 		case 'p':	/* meridian */
37534f9b3eeSRoland Mainz 			n = TM_MERIDIAN + (tm->tm_hour >= 12);
376da2e3ebdSchin 			goto index;
377da2e3ebdSchin 		case 'q':	/* time zone type (nation code) */
378da2e3ebdSchin 			if (!(flags & TM_UTC))
379da2e3ebdSchin 			{
38034f9b3eeSRoland Mainz 				if ((zp = tm->tm_zone) != tm_info.local)
381da2e3ebdSchin 					for (; zp >= tm_data.zone; zp--)
382da2e3ebdSchin 						if (p = zp->type)
383da2e3ebdSchin 							goto string;
384da2e3ebdSchin 				else if (p = zp->type)
385da2e3ebdSchin 					goto string;
386da2e3ebdSchin 			}
387da2e3ebdSchin 			continue;
388da2e3ebdSchin 		case 'Q':	/* %Q<alpha> or %Q<delim>recent<delim>distant<delim> */
389da2e3ebdSchin 			if (c = *format)
390da2e3ebdSchin 			{
391da2e3ebdSchin 				format++;
392da2e3ebdSchin 				if (isalpha(c))
393da2e3ebdSchin 				{
394da2e3ebdSchin 					switch (c)
395da2e3ebdSchin 					{
396da2e3ebdSchin 					case 'd':	/* `ls -l' distant date */
397da2e3ebdSchin 						p = tm_info.format[TM_DISTANT];
398da2e3ebdSchin 						goto push;
399da2e3ebdSchin 					case 'r':	/* `ls -l' recent date */
400da2e3ebdSchin 						p = tm_info.format[TM_RECENT];
401da2e3ebdSchin 						goto push;
402da2e3ebdSchin 					default:
403da2e3ebdSchin 						format--;
404da2e3ebdSchin 						break;
405da2e3ebdSchin 					}
406da2e3ebdSchin 				}
407da2e3ebdSchin 				else
408da2e3ebdSchin 				{
409da2e3ebdSchin 					if (t)
410da2e3ebdSchin 					{
411da2e3ebdSchin 						now = tmxgettime();
412da2e3ebdSchin 						p = warped(t, now) ? (char*)0 : (char*)format;
413da2e3ebdSchin 					}
414da2e3ebdSchin 					else
415da2e3ebdSchin 						p = (char*)format;
416da2e3ebdSchin 					i = 0;
417da2e3ebdSchin 					while (n = *format)
418da2e3ebdSchin 					{
419da2e3ebdSchin 						format++;
420da2e3ebdSchin 						if (n == c)
421da2e3ebdSchin 						{
422da2e3ebdSchin 							if (!p)
423da2e3ebdSchin 								p = (char*)format;
424da2e3ebdSchin 							if (++i == 2)
425da2e3ebdSchin 								goto push_delimiter;
426da2e3ebdSchin 						}
427da2e3ebdSchin 					}
428da2e3ebdSchin 				}
429da2e3ebdSchin 			}
430da2e3ebdSchin 			continue;
431da2e3ebdSchin 		case 'r':
432da2e3ebdSchin 			p = tm_info.format[TM_MERIDIAN_TIME];
433da2e3ebdSchin 			goto push;
434da2e3ebdSchin 		case 'R':
435da2e3ebdSchin 			p = "%H:%M";
436da2e3ebdSchin 			goto push;
437da2e3ebdSchin 		case 's':	/* seconds[.nanoseconds] since the epoch */
438da2e3ebdSchin 		case '#':
439da2e3ebdSchin 			now = t;
440da2e3ebdSchin 			f = fmt;
441da2e3ebdSchin 			*f++ = '%';
442da2e3ebdSchin 			if (pad == '0')
443da2e3ebdSchin 				*f++ = pad;
444da2e3ebdSchin 			if (width)
445da2e3ebdSchin 				f += sfsprintf(f, &fmt[sizeof(fmt)] - f, "%d", width);
446da2e3ebdSchin 			f += sfsprintf(f, &fmt[sizeof(fmt)] - f, "I%du", sizeof(Tmxsec_t));
447da2e3ebdSchin 			cp += sfsprintf(cp, ep - cp, fmt, tmxsec(now));
448da2e3ebdSchin 			if (parts > 1)
449da2e3ebdSchin 			{
450da2e3ebdSchin 				n = sfsprintf(cp, ep - cp, ".%09I*u", sizeof(Tmxnsec_t), tmxnsec(now));
451da2e3ebdSchin 				if (prec && n >= prec)
452da2e3ebdSchin 					n = prec + 1;
453da2e3ebdSchin 				cp += n;
454da2e3ebdSchin 			}
455da2e3ebdSchin 			continue;
456da2e3ebdSchin 		case 'S':	/* seconds */
45734f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tm->tm_sec, 2, width, pad);
458da2e3ebdSchin 			if ((flags & TM_SUBSECOND) && (format - 2) != oformat)
459da2e3ebdSchin 			{
460da2e3ebdSchin 				p = ".%N";
461da2e3ebdSchin 				goto push;
462da2e3ebdSchin 			}
463da2e3ebdSchin 			continue;
464da2e3ebdSchin 		case 't':
465da2e3ebdSchin 			if (cp < ep)
466da2e3ebdSchin 				*cp++ = '\t';
467da2e3ebdSchin 			continue;
468da2e3ebdSchin 		case 'T':
469da2e3ebdSchin 			p = tm_info.format[TM_TIME];
470da2e3ebdSchin 			goto push;
471da2e3ebdSchin 		case 'u':	/* weekday number [1(Monday)-7] */
47234f9b3eeSRoland Mainz 			if (!(i = tm->tm_wday))
473da2e3ebdSchin 				i = 7;
474da2e3ebdSchin 			cp = number(cp, ep, (long)i, 0, width, pad);
475da2e3ebdSchin 			continue;
476da2e3ebdSchin 		case 'U':	/* week number, Sunday as first day */
47734f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tmweek(tm, 0, -1, -1), 2, width, pad);
478da2e3ebdSchin 			continue;
479da2e3ebdSchin 		case 'V':	/* ISO week number */
48034f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tmweek(tm, 2, -1, -1), 2, width, pad);
481da2e3ebdSchin 			continue;
482da2e3ebdSchin 		case 'W':	/* week number, Monday as first day */
48334f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tmweek(tm, 1, -1, -1), 2, width, pad);
484da2e3ebdSchin 			continue;
485da2e3ebdSchin 		case 'w':	/* weekday number [0(Sunday)-6] */
48634f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)tm->tm_wday, 0, width, pad);
487da2e3ebdSchin 			continue;
488da2e3ebdSchin 		case 'x':
489da2e3ebdSchin 			p = tm_info.format[TM_DATE];
490da2e3ebdSchin 			goto push;
491da2e3ebdSchin 		case 'X':
492da2e3ebdSchin 			p = tm_info.format[TM_TIME];
493da2e3ebdSchin 			goto push;
494da2e3ebdSchin 		case 'y':	/* year in the form yy */
49534f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)(tm->tm_year % 100), 2, width, pad);
496da2e3ebdSchin 			continue;
497da2e3ebdSchin 		case 'Y':	/* year in the form ccyy */
49834f9b3eeSRoland Mainz 			cp = number(cp, ep, (long)(1900 + tm->tm_year), 4, width, pad);
499da2e3ebdSchin 			continue;
500da2e3ebdSchin 		case 'z':	/* time zone west offset */
50134f9b3eeSRoland Mainz 			if (arg)
50234f9b3eeSRoland Mainz 			{
50334f9b3eeSRoland Mainz 				if ((zp = tmzone(arg, &f, 0, 0)) && !*f && tm->tm_zone != zp)
50434f9b3eeSRoland Mainz 					tm = tmxtm(tm, tmxtime(tm, tm->tm_zone->west + (tm->tm_isdst ? tm->tm_zone->dst : 0)), zp);
50534f9b3eeSRoland Mainz 				continue;
50634f9b3eeSRoland Mainz 			}
507da2e3ebdSchin 			if ((ep - cp) >= 16)
50834f9b3eeSRoland Mainz 				cp = tmpoff(cp, ep - cp, "", (flags & TM_UTC) ? 0 : tm->tm_zone->west + (tm->tm_isdst ? tm->tm_zone->dst : 0), 24 * 60);
509da2e3ebdSchin 			continue;
510da2e3ebdSchin 		case 'Z':	/* time zone */
51134f9b3eeSRoland Mainz 			if (arg)
51234f9b3eeSRoland Mainz 			{
51334f9b3eeSRoland Mainz 				if ((zp = tmzone(arg, &f, 0, 0)) && !*f && tm->tm_zone != zp)
51434f9b3eeSRoland Mainz 					tm = tmxtm(tm, tmxtime(tm, tm->tm_zone->west + (tm->tm_isdst ? tm->tm_zone->dst : 0)), zp);
51534f9b3eeSRoland Mainz 				continue;
51634f9b3eeSRoland Mainz 			}
51734f9b3eeSRoland 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;
518da2e3ebdSchin 			goto string;
519da2e3ebdSchin 		case '+':	/* old %+flag */
520da2e3ebdSchin 		case '!':	/* old %!flag */
521da2e3ebdSchin 			format--;
522da2e3ebdSchin 			/*FALLTHROUGH*/
52334f9b3eeSRoland Mainz 		case '=':	/* old %=[=][+-]flag */
52434f9b3eeSRoland Mainz 			for (arg = argbuf; *format == '=' || *format == '-' || *format == '+' || *format == '!'; format++)
52534f9b3eeSRoland Mainz 				if (arg < &argbuf[sizeof(argbuf) - 2])
52634f9b3eeSRoland Mainz 					*arg++ = *format;
52734f9b3eeSRoland Mainz 			if (*arg++ = *format)
528da2e3ebdSchin 				format++;
52934f9b3eeSRoland Mainz 			*arg = 0;
53034f9b3eeSRoland Mainz 			arg = argbuf;
53134f9b3eeSRoland Mainz 			goto options;
532da2e3ebdSchin 		default:
533da2e3ebdSchin 			if (cp < ep)
534da2e3ebdSchin 				*cp++ = '%';
535da2e3ebdSchin 			if (cp < ep)
536da2e3ebdSchin 				*cp++ = c;
537da2e3ebdSchin 			continue;
538da2e3ebdSchin 		}
539da2e3ebdSchin 	index:
540da2e3ebdSchin 		p = tm_info.format[n];
541da2e3ebdSchin 	string:
542da2e3ebdSchin 		while (cp < ep && (*cp = *p++))
543da2e3ebdSchin 			cp++;
544da2e3ebdSchin 		continue;
54534f9b3eeSRoland Mainz 	options:
54634f9b3eeSRoland Mainz 		c = '+';
54734f9b3eeSRoland Mainz 		i = 0;
54834f9b3eeSRoland Mainz 		for (;;)
54934f9b3eeSRoland Mainz 		{
55034f9b3eeSRoland Mainz 			switch (*arg++)
55134f9b3eeSRoland Mainz 			{
55234f9b3eeSRoland Mainz 			case 0:
55334f9b3eeSRoland Mainz 				n = 0;
55434f9b3eeSRoland Mainz 				break;
55534f9b3eeSRoland Mainz 			case '=':
55634f9b3eeSRoland Mainz 				i = !i;
55734f9b3eeSRoland Mainz 				continue;
55834f9b3eeSRoland Mainz 			case '+':
55934f9b3eeSRoland Mainz 			case '-':
56034f9b3eeSRoland Mainz 			case '!':
56134f9b3eeSRoland Mainz 				c = *(arg - 1);
56234f9b3eeSRoland Mainz 				continue;
56334f9b3eeSRoland Mainz 			case 'l':
56434f9b3eeSRoland Mainz 				n = TM_LEAP;
56534f9b3eeSRoland Mainz 				break;
56634f9b3eeSRoland Mainz 			case 'n':
56734f9b3eeSRoland Mainz 			case 's':
56834f9b3eeSRoland Mainz 				n = TM_SUBSECOND;
56934f9b3eeSRoland Mainz 				break;
57034f9b3eeSRoland Mainz 			case 'u':
57134f9b3eeSRoland Mainz 				n = TM_UTC;
57234f9b3eeSRoland Mainz 				break;
57334f9b3eeSRoland Mainz 			default:
57434f9b3eeSRoland Mainz 				continue;
57534f9b3eeSRoland Mainz 			}
57634f9b3eeSRoland Mainz 			if (!n)
57734f9b3eeSRoland Mainz 				break;
57834f9b3eeSRoland Mainz 
57934f9b3eeSRoland Mainz 			/*
58034f9b3eeSRoland Mainz 			 * right, the global state stinks
58134f9b3eeSRoland Mainz 			 * but we respect its locale-like status
58234f9b3eeSRoland Mainz 			 */
58334f9b3eeSRoland Mainz 
58434f9b3eeSRoland Mainz 			if (c == '+')
58534f9b3eeSRoland Mainz 			{
58634f9b3eeSRoland Mainz 				if (!(flags & n))
58734f9b3eeSRoland Mainz 				{
58834f9b3eeSRoland Mainz 					flags |= n;
58934f9b3eeSRoland Mainz 					tm_info.flags |= n;
59034f9b3eeSRoland Mainz 					tm = tmxtm(tm, t, (flags & TM_UTC) ? &tm_data.zone[2] : tm->tm_zone);
59134f9b3eeSRoland Mainz 					if (!i)
59234f9b3eeSRoland Mainz 						tm_info.flags &= ~n;
59334f9b3eeSRoland Mainz 				}
59434f9b3eeSRoland Mainz 			}
59534f9b3eeSRoland Mainz 			else if (flags & n)
59634f9b3eeSRoland Mainz 			{
59734f9b3eeSRoland Mainz 				flags &= ~n;
59834f9b3eeSRoland Mainz 				tm_info.flags &= ~n;
59934f9b3eeSRoland Mainz 				tm = tmxtm(tm, t, (flags & TM_UTC) ? &tm_data.zone[2] : tm->tm_zone);
60034f9b3eeSRoland Mainz 				if (!i)
60134f9b3eeSRoland Mainz 					tm_info.flags |= n;
60234f9b3eeSRoland Mainz 			}
60334f9b3eeSRoland Mainz 		}
60434f9b3eeSRoland Mainz 		continue;
605da2e3ebdSchin 	push:
606da2e3ebdSchin 		c = 0;
607da2e3ebdSchin 	push_delimiter:
608da2e3ebdSchin 		if (sp < &stack[elementsof(stack)])
609da2e3ebdSchin 		{
610da2e3ebdSchin 			sp->format = (char*)format;
611da2e3ebdSchin 			format = p;
612da2e3ebdSchin 			sp->delimiter = delimiter;
613da2e3ebdSchin 			delimiter = c;
614da2e3ebdSchin 			sp++;
615da2e3ebdSchin 		}
616da2e3ebdSchin 		continue;
617da2e3ebdSchin 	}
618da2e3ebdSchin 	tm_info.flags = flags;
619da2e3ebdSchin 	if (cp >= ep)
620da2e3ebdSchin 		cp = ep - 1;
621da2e3ebdSchin 	*cp = 0;
622da2e3ebdSchin 	return cp;
623da2e3ebdSchin }
624