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