Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libast/tm/tmxfmt.c
1810 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1985-2011 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* Glenn Fowler <[email protected]> *
18
* David Korn <[email protected]> *
19
* Phong Vo <[email protected]> *
20
* *
21
***********************************************************************/
22
#pragma prototyped
23
/*
24
* Glenn Fowler
25
* AT&T Research
26
*
27
* Time_t conversion support
28
*/
29
30
#include <tmx.h>
31
#include <ctype.h>
32
33
#define warped(t,n) ((t)<((n)-tmxsns(6L*30L*24L*60L*60L,0))||(t)>((n)+tmxsns(24L*60L*60L,0)))
34
35
/*
36
* format n with padding p into s
37
* return end of s
38
*
39
* p: <0 blank padding
40
* 0 no padding
41
* >0 0 padding
42
*/
43
44
static char*
45
number(register char* s, register char* e, register long n, register int p, int w, int pad)
46
{
47
char* b;
48
49
if (w)
50
{
51
if (p > 0 && (pad == 0 || pad == '0'))
52
while (w > p)
53
{
54
p++;
55
n *= 10;
56
}
57
else if (w > p)
58
p = w;
59
}
60
switch (pad)
61
{
62
case '-':
63
p = 0;
64
break;
65
case '_':
66
if (p > 0)
67
p = -p;
68
break;
69
case '0':
70
if (p < 0)
71
p = -p;
72
break;
73
}
74
b = s;
75
if (p > 0)
76
s += sfsprintf(s, e - s, "%0*lu", p, n);
77
else if (p < 0)
78
s += sfsprintf(s, e - s, "%*lu", -p, n);
79
else
80
s += sfsprintf(s, e - s, "%lu", n);
81
if (w && (s - b) > w)
82
*(s = b + w) = 0;
83
return s;
84
}
85
86
typedef struct Stack_s
87
{
88
char* format;
89
int delimiter;
90
} Stack_t;
91
92
/*
93
* format t into buf of length len
94
* end of buf is returned
95
*/
96
97
char*
98
tmxfmt(char* buf, size_t len, const char* format, Time_t t)
99
{
100
register char* cp;
101
register char* ep;
102
register char* p;
103
register int n;
104
int c;
105
int i;
106
int flags;
107
int alt;
108
int pad;
109
int delimiter;
110
int width;
111
int prec;
112
int parts;
113
char* arg;
114
char* f;
115
const char* oformat;
116
Tm_t* tm;
117
Tm_zone_t* zp;
118
Time_t now;
119
Stack_t* sp;
120
Stack_t stack[8];
121
Tm_t ts;
122
char argbuf[256];
123
char fmt[32];
124
125
tmlocale();
126
tm = tmxtm(&ts, t, NiL);
127
if (!format || !*format)
128
format = tm_info.deformat;
129
oformat = format;
130
flags = tm_info.flags;
131
sp = &stack[0];
132
cp = buf;
133
ep = buf + len;
134
delimiter = 0;
135
for (;;)
136
{
137
if ((c = *format++) == delimiter)
138
{
139
delimiter = 0;
140
if (sp <= &stack[0])
141
break;
142
sp--;
143
format = sp->format;
144
delimiter = sp->delimiter;
145
continue;
146
}
147
if (c != '%')
148
{
149
if (cp < ep)
150
*cp++ = c;
151
continue;
152
}
153
alt = 0;
154
arg = 0;
155
pad = 0;
156
width = 0;
157
prec = 0;
158
parts = 0;
159
for (;;)
160
{
161
switch (c = *format++)
162
{
163
case '_':
164
case '-':
165
pad = c;
166
continue;
167
case 'E':
168
case 'O':
169
if (!isalpha(*format))
170
break;
171
alt = c;
172
continue;
173
case '0':
174
if (!parts)
175
{
176
pad = c;
177
continue;
178
}
179
/*FALLTHROUGH*/
180
case '1':
181
case '2':
182
case '3':
183
case '4':
184
case '5':
185
case '6':
186
case '7':
187
case '8':
188
case '9':
189
switch (parts)
190
{
191
case 0:
192
parts++;
193
/*FALLTHROUGH*/
194
case 1:
195
width = width * 10 + (c - '0');
196
break;
197
case 2:
198
prec = prec * 10 + (c - '0');
199
break;
200
}
201
continue;
202
case '.':
203
if (!parts++)
204
parts++;
205
continue;
206
case '(':
207
i = 1;
208
arg = argbuf;
209
for (;;)
210
{
211
if (!(c = *format++))
212
{
213
format--;
214
break;
215
}
216
else if (c == '(')
217
i++;
218
else if (c == ')' && !--i)
219
break;
220
else if (arg < &argbuf[sizeof(argbuf) - 1])
221
*arg++ = c;
222
}
223
*arg = 0;
224
arg = argbuf;
225
continue;
226
default:
227
break;
228
}
229
break;
230
}
231
switch (c)
232
{
233
case 0:
234
format--;
235
continue;
236
case '%':
237
if (cp < ep)
238
*cp++ = '%';
239
continue;
240
case '?':
241
if (tm_info.deformat != tm_info.format[TM_DEFAULT])
242
format = tm_info.deformat;
243
else if (!*format)
244
format = tm_info.format[TM_DEFAULT];
245
continue;
246
case 'a': /* abbreviated day of week name */
247
n = TM_DAY_ABBREV + tm->tm_wday;
248
goto index;
249
case 'A': /* day of week name */
250
n = TM_DAY + tm->tm_wday;
251
goto index;
252
case 'b': /* abbreviated month name */
253
case 'h':
254
n = TM_MONTH_ABBREV + tm->tm_mon;
255
goto index;
256
case 'B': /* month name */
257
n = TM_MONTH + tm->tm_mon;
258
goto index;
259
case 'c': /* `ctime(3)' date sans newline */
260
p = tm_info.format[TM_CTIME];
261
goto push;
262
case 'C': /* 2 digit century */
263
cp = number(cp, ep, (long)(1900 + tm->tm_year) / 100, 2, width, pad);
264
continue;
265
case 'd': /* day of month */
266
cp = number(cp, ep, (long)tm->tm_mday, 2, width, pad);
267
continue;
268
case 'D': /* date */
269
p = tm_info.format[TM_DATE];
270
goto push;
271
case 'e': /* blank padded day of month */
272
cp = number(cp, ep, (long)tm->tm_mday, -2, width, pad);
273
continue;
274
case 'f': /* (AST) OBSOLETE use %Qf */
275
p = "%Qf";
276
goto push;
277
case 'F': /* ISO 8601:2000 standard date format */
278
p = "%Y-%m-%d";
279
goto push;
280
case 'g': /* %V 2 digit year */
281
case 'G': /* %V 4 digit year */
282
n = tm->tm_year + 1900;
283
if (tm->tm_yday < 7)
284
{
285
if (tmweek(tm, 2, -1, -1) >= 52)
286
n--;
287
}
288
else if (tm->tm_yday > 358)
289
{
290
if (tmweek(tm, 2, -1, -1) <= 1)
291
n++;
292
}
293
if (c == 'g')
294
{
295
n %= 100;
296
c = 2;
297
}
298
else
299
c = 4;
300
cp = number(cp, ep, (long)n, c, width, pad);
301
continue;
302
case 'H': /* hour (0 - 23) */
303
cp = number(cp, ep, (long)tm->tm_hour, 2, width, pad);
304
continue;
305
case 'i': /* (AST) OBSOLETE use %QI */
306
p = "%QI";
307
goto push;
308
case 'I': /* hour (0 - 12) */
309
if ((n = tm->tm_hour) > 12) n -= 12;
310
else if (n == 0) n = 12;
311
cp = number(cp, ep, (long)n, 2, width, pad);
312
continue;
313
case 'j': /* Julian date (1 offset) */
314
cp = number(cp, ep, (long)(tm->tm_yday + 1), 3, width, pad);
315
continue;
316
case 'J': /* Julian date (0 offset) */
317
cp = number(cp, ep, (long)tm->tm_yday, 3, width, pad);
318
continue;
319
case 'k': /* (AST) OBSOLETE use %QD */
320
p = "%QD";
321
goto push;
322
case 'K': /* (AST) largest to smallest */
323
switch (alt)
324
{
325
case 'E':
326
p = (pad == '_') ? "%Y-%m-%d %H:%M:%S.%N %z" : "%Y-%m-%d+%H:%M:%S.%N%z";
327
break;
328
case 'O':
329
p = (pad == '_') ? "%Y-%m-%d %H:%M:%S.%N" : "%Y-%m-%d+%H:%M:%S.%N";
330
break;
331
default:
332
p = (pad == '_') ? "%Y-%m-%d %H:%M:%S" : "%Y-%m-%d+%H:%M:%S";
333
break;
334
}
335
goto push;
336
case 'l': /* (AST) OBSOLETE use %QL */
337
p = "%QL";
338
goto push;
339
case 'L': /* (AST) OBSOLETE use %Ql */
340
p = "%Ql";
341
goto push;
342
case 'm': /* month number */
343
cp = number(cp, ep, (long)(tm->tm_mon + 1), 2, width, pad);
344
continue;
345
case 'M': /* minutes */
346
cp = number(cp, ep, (long)tm->tm_min, 2, width, pad);
347
continue;
348
case 'n':
349
if (cp < ep)
350
*cp++ = '\n';
351
continue;
352
case 'N': /* (AST|GNU) nanosecond part */
353
cp = number(cp, ep, (long)tm->tm_nsec, 9, width, pad);
354
continue;
355
#if 0
356
case 'o': /* (UNUSED) */
357
continue;
358
#endif
359
case 'p': /* meridian */
360
n = TM_MERIDIAN + (tm->tm_hour >= 12);
361
goto index;
362
case 'P': /* (AST|GNU) lower case meridian */
363
p = tm_info.format[TM_MERIDIAN + (tm->tm_hour >= 12)];
364
while (cp < ep && (n = *p++))
365
*cp++ = isupper(n) ? tolower(n) : n;
366
continue;
367
case 'q': /* (AST) OBSOLETE use %Qz */
368
p = "%Qz";
369
goto push;
370
case 'Q': /* (AST) %Q<alpha> or %Q<delim>recent<delim>distant<delim> */
371
if (c = *format)
372
{
373
format++;
374
if (isalpha(c))
375
{
376
switch (c)
377
{
378
case 'd': /* `ls -l' distant date */
379
p = tm_info.format[TM_DISTANT];
380
goto push;
381
case 'D': /* `date(1)' date */
382
p = tm_info.format[TM_DATE_1];
383
goto push;
384
case 'f': /* TM_DEFAULT override */
385
p = tm_info.deformat;
386
goto push;
387
case 'I': /* international `date(1)' date */
388
p = tm_info.format[TM_INTERNATIONAL];
389
goto push;
390
case 'l': /* TM_DEFAULT */
391
p = tm_info.format[TM_DEFAULT];
392
goto push;
393
case 'L': /* `ls -l' date */
394
if (t)
395
{
396
now = tmxgettime();
397
if (warped(t, now))
398
{
399
p = tm_info.format[TM_DISTANT];
400
goto push;
401
}
402
}
403
p = tm_info.format[TM_RECENT];
404
goto push;
405
case 'o': /* set options ( %([+-]flag...)o ) */
406
if (arg)
407
{
408
c = '+';
409
i = 0;
410
for (;;)
411
{
412
switch (*arg++)
413
{
414
case 0:
415
n = 0;
416
break;
417
case '=':
418
i = !i;
419
continue;
420
case '+':
421
case '-':
422
case '!':
423
c = *(arg - 1);
424
continue;
425
case 'l':
426
n = TM_LEAP;
427
break;
428
case 'n':
429
case 's':
430
n = TM_SUBSECOND;
431
break;
432
case 'u':
433
n = TM_UTC;
434
break;
435
default:
436
continue;
437
}
438
if (!n)
439
break;
440
441
/*
442
* right, the global state stinks
443
* but we respect its locale-like status
444
*/
445
446
if (c == '+')
447
{
448
if (!(flags & n))
449
{
450
flags |= n;
451
tm_info.flags |= n;
452
tm = tmxtm(tm, t, (flags & TM_UTC) ? &tm_data.zone[2] : tm->tm_zone);
453
if (!i)
454
tm_info.flags &= ~n;
455
}
456
}
457
else if (flags & n)
458
{
459
flags &= ~n;
460
tm_info.flags &= ~n;
461
tm = tmxtm(tm, t, (flags & TM_UTC) ? &tm_data.zone[2] : tm->tm_zone);
462
if (!i)
463
tm_info.flags |= n;
464
}
465
}
466
}
467
break;
468
case 'r': /* `ls -l' recent date */
469
p = tm_info.format[TM_RECENT];
470
goto push;
471
case 'z': /* time zone nation code */
472
if (!(flags & TM_UTC))
473
{
474
if ((zp = tm->tm_zone) != tm_info.local)
475
for (; zp >= tm_data.zone; zp--)
476
if (p = zp->type)
477
goto string;
478
else if (p = zp->type)
479
goto string;
480
}
481
break;
482
default:
483
format--;
484
break;
485
}
486
}
487
else
488
{
489
if (t)
490
{
491
now = tmxgettime();
492
p = warped(t, now) ? (char*)0 : (char*)format;
493
}
494
else
495
p = (char*)format;
496
i = 0;
497
while (n = *format)
498
{
499
format++;
500
if (n == c)
501
{
502
if (!p)
503
p = (char*)format;
504
if (++i == 2)
505
goto push_delimiter;
506
}
507
}
508
}
509
}
510
continue;
511
case 'r':
512
p = tm_info.format[TM_MERIDIAN_TIME];
513
goto push;
514
case 'R':
515
p = "%H:%M";
516
goto push;
517
case 's': /* (DEFACTO) seconds[.nanoseconds] since the epoch */
518
case '#':
519
now = t;
520
f = fmt;
521
*f++ = '%';
522
if (pad == '0')
523
*f++ = pad;
524
if (width)
525
f += sfsprintf(f, &fmt[sizeof(fmt)] - f, "%d", width);
526
f += sfsprintf(f, &fmt[sizeof(fmt)] - f, "I%du", sizeof(Tmxsec_t));
527
cp += sfsprintf(cp, ep - cp, fmt, tmxsec(now));
528
if (parts > 1)
529
{
530
n = sfsprintf(cp, ep - cp, ".%09I*u", sizeof(Tmxnsec_t), tmxnsec(now));
531
if (prec && n >= prec)
532
n = prec + 1;
533
cp += n;
534
}
535
continue;
536
case 'S': /* seconds */
537
cp = number(cp, ep, (long)tm->tm_sec, 2, width, pad);
538
if ((flags & TM_SUBSECOND) && (format - 2) != oformat)
539
{
540
p = ".%N";
541
goto push;
542
}
543
continue;
544
case 't':
545
if (cp < ep)
546
*cp++ = '\t';
547
continue;
548
case 'T':
549
p = tm_info.format[TM_TIME];
550
goto push;
551
case 'u': /* weekday number [1(Monday)-7] */
552
if (!(i = tm->tm_wday))
553
i = 7;
554
cp = number(cp, ep, (long)i, 0, width, pad);
555
continue;
556
case 'U': /* week number, Sunday as first day */
557
cp = number(cp, ep, (long)tmweek(tm, 0, -1, -1), 2, width, pad);
558
continue;
559
#if 0
560
case 'v': /* (UNUSED) */
561
continue;
562
#endif
563
case 'V': /* ISO week number */
564
cp = number(cp, ep, (long)tmweek(tm, 2, -1, -1), 2, width, pad);
565
continue;
566
case 'W': /* week number, Monday as first day */
567
cp = number(cp, ep, (long)tmweek(tm, 1, -1, -1), 2, width, pad);
568
continue;
569
case 'w': /* weekday number [0(Sunday)-6] */
570
cp = number(cp, ep, (long)tm->tm_wday, 0, width, pad);
571
continue;
572
case 'x':
573
p = tm_info.format[TM_DATE];
574
goto push;
575
case 'X':
576
p = tm_info.format[TM_TIME];
577
goto push;
578
case 'y': /* year in the form yy */
579
cp = number(cp, ep, (long)(tm->tm_year % 100), 2, width, pad);
580
continue;
581
case 'Y': /* year in the form ccyy */
582
cp = number(cp, ep, (long)(1900 + tm->tm_year), 4, width, pad);
583
continue;
584
case 'z': /* time zone west offset */
585
if (arg)
586
{
587
if ((zp = tmzone(arg, &f, 0, 0)) && !*f && tm->tm_zone != zp)
588
tm = tmxtm(tm, tmxtime(tm, tm->tm_zone->west + (tm->tm_isdst ? tm->tm_zone->dst : 0)), zp);
589
continue;
590
}
591
if ((ep - cp) >= 16)
592
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);
593
continue;
594
case 'Z': /* time zone */
595
if (arg)
596
{
597
if ((zp = tmzone(arg, &f, 0, 0)) && !*f && tm->tm_zone != zp)
598
tm = tmxtm(tm, tmxtime(tm, tm->tm_zone->west + (tm->tm_isdst ? tm->tm_zone->dst : 0)), zp);
599
continue;
600
}
601
p = (flags & TM_UTC) ? tm_info.format[TM_UT] : tm->tm_isdst && tm->tm_zone->daylight ? tm->tm_zone->daylight : tm->tm_zone->standard;
602
goto string;
603
case '=': /* (AST) OBSOLETE use %([+-]flag...)Qo (old %=[=][+-]flag) */
604
for (arg = argbuf; *format == '=' || *format == '-' || *format == '+' || *format == '!'; format++)
605
if (arg < &argbuf[sizeof(argbuf) - 2])
606
*arg++ = *format;
607
if (*arg++ = *format)
608
format++;
609
*arg = 0;
610
arg = argbuf;
611
goto options;
612
default:
613
if (cp < ep)
614
*cp++ = '%';
615
if (cp < ep)
616
*cp++ = c;
617
continue;
618
}
619
index:
620
p = tm_info.format[n];
621
string:
622
while (cp < ep && (*cp = *p++))
623
cp++;
624
continue;
625
options:
626
c = '+';
627
i = 0;
628
for (;;)
629
{
630
switch (*arg++)
631
{
632
case 0:
633
n = 0;
634
break;
635
case '=':
636
i = !i;
637
continue;
638
case '+':
639
case '-':
640
case '!':
641
c = *(arg - 1);
642
continue;
643
case 'l':
644
n = TM_LEAP;
645
break;
646
case 'n':
647
case 's':
648
n = TM_SUBSECOND;
649
break;
650
case 'u':
651
n = TM_UTC;
652
break;
653
default:
654
continue;
655
}
656
if (!n)
657
break;
658
659
/*
660
* right, the global state stinks
661
* but we respect its locale-like status
662
*/
663
664
if (c == '+')
665
{
666
if (!(flags & n))
667
{
668
flags |= n;
669
tm_info.flags |= n;
670
tm = tmxtm(tm, t, (flags & TM_UTC) ? &tm_data.zone[2] : tm->tm_zone);
671
if (!i)
672
tm_info.flags &= ~n;
673
}
674
}
675
else if (flags & n)
676
{
677
flags &= ~n;
678
tm_info.flags &= ~n;
679
tm = tmxtm(tm, t, (flags & TM_UTC) ? &tm_data.zone[2] : tm->tm_zone);
680
if (!i)
681
tm_info.flags |= n;
682
}
683
}
684
continue;
685
push:
686
c = 0;
687
push_delimiter:
688
if (sp < &stack[elementsof(stack)])
689
{
690
sp->format = (char*)format;
691
format = p;
692
sp->delimiter = delimiter;
693
delimiter = c;
694
sp++;
695
}
696
continue;
697
}
698
tm_info.flags = flags;
699
if (cp >= ep)
700
cp = ep - 1;
701
*cp = 0;
702
return cp;
703
}
704
705