1*4887Schin /*********************************************************************** 2*4887Schin * * 3*4887Schin * This software is part of the ast package * 4*4887Schin * Copyright (c) 1985-2007 AT&T Knowledge Ventures * 5*4887Schin * and is licensed under the * 6*4887Schin * Common Public License, Version 1.0 * 7*4887Schin * by AT&T Knowledge Ventures * 8*4887Schin * * 9*4887Schin * A copy of the License is available at * 10*4887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 11*4887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12*4887Schin * * 13*4887Schin * Information and Software Systems Research * 14*4887Schin * AT&T Research * 15*4887Schin * Florham Park NJ * 16*4887Schin * * 17*4887Schin * Glenn Fowler <gsf@research.att.com> * 18*4887Schin * David Korn <dgk@research.att.com> * 19*4887Schin * Phong Vo <kpv@research.att.com> * 20*4887Schin * * 21*4887Schin ***********************************************************************/ 22*4887Schin #pragma prototyped 23*4887Schin /* 24*4887Schin * Glenn Fowler 25*4887Schin * AT&T Research 26*4887Schin * 27*4887Schin * Time_t conversion support 28*4887Schin */ 29*4887Schin 30*4887Schin #include <tmx.h> 31*4887Schin #include <ctype.h> 32*4887Schin 33*4887Schin #define warped(t,n) ((t)<((n)-tmxsns(6L*30L*24L*60L*60L,0))||(t)>((n)+tmxsns(24L*60L*60L,0))) 34*4887Schin 35*4887Schin /* 36*4887Schin * format n with padding p into s 37*4887Schin * return end of s 38*4887Schin * 39*4887Schin * p: <0 blank padding 40*4887Schin * 0 no padding 41*4887Schin * >0 0 padding 42*4887Schin */ 43*4887Schin 44*4887Schin static char* 45*4887Schin number(register char* s, register char* e, register long n, register int p, int w, int pad) 46*4887Schin { 47*4887Schin char* b; 48*4887Schin 49*4887Schin if (w && w > p) 50*4887Schin p = w; 51*4887Schin switch (pad) 52*4887Schin { 53*4887Schin case '-': 54*4887Schin p = 0; 55*4887Schin break; 56*4887Schin case '_': 57*4887Schin if (p > 0) 58*4887Schin p = -p; 59*4887Schin break; 60*4887Schin case '0': 61*4887Schin if (p < 0) 62*4887Schin p = -p; 63*4887Schin break; 64*4887Schin } 65*4887Schin b = s; 66*4887Schin if (p > 0) 67*4887Schin s += sfsprintf(s, e - s, "%0*lu", p, n); 68*4887Schin else if (p < 0) 69*4887Schin s += sfsprintf(s, e - s, "%*lu", -p, n); 70*4887Schin else 71*4887Schin s += sfsprintf(s, e - s, "%lu", n); 72*4887Schin if (w && (s - b) > w) 73*4887Schin *(s = b + w) = 0; 74*4887Schin return s; 75*4887Schin } 76*4887Schin 77*4887Schin typedef struct Stack_s 78*4887Schin { 79*4887Schin char* format; 80*4887Schin int delimiter; 81*4887Schin } Stack_t; 82*4887Schin 83*4887Schin /* 84*4887Schin * format t into buf of length len 85*4887Schin * end of buf is returned 86*4887Schin */ 87*4887Schin 88*4887Schin char* 89*4887Schin tmxfmt(char* buf, size_t len, const char* format, Time_t t) 90*4887Schin { 91*4887Schin register char* cp; 92*4887Schin register char* ep; 93*4887Schin register char* p; 94*4887Schin register int n; 95*4887Schin int c; 96*4887Schin int i; 97*4887Schin int flags; 98*4887Schin int pad; 99*4887Schin int delimiter; 100*4887Schin int width; 101*4887Schin int prec; 102*4887Schin int parts; 103*4887Schin char* f; 104*4887Schin const char* oformat; 105*4887Schin Tm_t* tp; 106*4887Schin Tm_zone_t* zp; 107*4887Schin Time_t now; 108*4887Schin Stack_t* sp; 109*4887Schin Stack_t stack[8]; 110*4887Schin char fmt[32]; 111*4887Schin 112*4887Schin tmlocale(); 113*4887Schin tp = tmxmake(t); 114*4887Schin if (!format || !*format) 115*4887Schin format = tm_info.deformat; 116*4887Schin oformat = format; 117*4887Schin flags = tm_info.flags; 118*4887Schin sp = &stack[0]; 119*4887Schin cp = buf; 120*4887Schin ep = buf + len; 121*4887Schin delimiter = 0; 122*4887Schin for (;;) 123*4887Schin { 124*4887Schin if ((c = *format++) == delimiter) 125*4887Schin { 126*4887Schin delimiter = 0; 127*4887Schin if (sp <= &stack[0]) 128*4887Schin break; 129*4887Schin sp--; 130*4887Schin format = sp->format; 131*4887Schin delimiter = sp->delimiter; 132*4887Schin continue; 133*4887Schin } 134*4887Schin if (c != '%') 135*4887Schin { 136*4887Schin if (cp < ep) 137*4887Schin *cp++ = c; 138*4887Schin continue; 139*4887Schin } 140*4887Schin pad = 0; 141*4887Schin width = 0; 142*4887Schin prec = 0; 143*4887Schin parts = 0; 144*4887Schin for (;;) 145*4887Schin { 146*4887Schin switch (c = *format++) 147*4887Schin { 148*4887Schin case '_': 149*4887Schin case '-': 150*4887Schin pad = c; 151*4887Schin continue; 152*4887Schin case 'E': 153*4887Schin case 'O': 154*4887Schin if (!isalpha(*format)) 155*4887Schin break; 156*4887Schin continue; 157*4887Schin case '0': 158*4887Schin if (!parts) 159*4887Schin { 160*4887Schin pad = c; 161*4887Schin continue; 162*4887Schin } 163*4887Schin /*FALLTHROUGH*/ 164*4887Schin case '1': 165*4887Schin case '2': 166*4887Schin case '3': 167*4887Schin case '4': 168*4887Schin case '5': 169*4887Schin case '6': 170*4887Schin case '7': 171*4887Schin case '8': 172*4887Schin case '9': 173*4887Schin switch (parts) 174*4887Schin { 175*4887Schin case 0: 176*4887Schin parts++; 177*4887Schin /*FALLTHROUGH*/ 178*4887Schin case 1: 179*4887Schin width = width * 10 + (c - '0'); 180*4887Schin break; 181*4887Schin case 2: 182*4887Schin prec = prec * 10 + (c - '0'); 183*4887Schin break; 184*4887Schin } 185*4887Schin continue; 186*4887Schin case '.': 187*4887Schin if (!parts++) 188*4887Schin parts++; 189*4887Schin continue; 190*4887Schin default: 191*4887Schin break; 192*4887Schin } 193*4887Schin break; 194*4887Schin } 195*4887Schin switch (c) 196*4887Schin { 197*4887Schin case 0: 198*4887Schin format--; 199*4887Schin continue; 200*4887Schin case '%': 201*4887Schin if (cp < ep) 202*4887Schin *cp++ = '%'; 203*4887Schin continue; 204*4887Schin case '?': 205*4887Schin if (tm_info.deformat != tm_info.format[TM_DEFAULT]) 206*4887Schin format = tm_info.deformat; 207*4887Schin else if (!*format) 208*4887Schin format = tm_info.format[TM_DEFAULT]; 209*4887Schin continue; 210*4887Schin case 'a': /* abbreviated day of week name */ 211*4887Schin n = TM_DAY_ABBREV + tp->tm_wday; 212*4887Schin goto index; 213*4887Schin case 'A': /* day of week name */ 214*4887Schin n = TM_DAY + tp->tm_wday; 215*4887Schin goto index; 216*4887Schin case 'b': /* abbreviated month name */ 217*4887Schin case 'h': 218*4887Schin n = TM_MONTH_ABBREV + tp->tm_mon; 219*4887Schin goto index; 220*4887Schin case 'B': /* month name */ 221*4887Schin n = TM_MONTH + tp->tm_mon; 222*4887Schin goto index; 223*4887Schin case 'c': /* `ctime(3)' date sans newline */ 224*4887Schin p = tm_info.format[TM_CTIME]; 225*4887Schin goto push; 226*4887Schin case 'C': /* 2 digit century */ 227*4887Schin cp = number(cp, ep, (long)(1900 + tp->tm_year) / 100, 2, width, pad); 228*4887Schin continue; 229*4887Schin case 'd': /* day of month */ 230*4887Schin cp = number(cp, ep, (long)tp->tm_mday, 2, width, pad); 231*4887Schin continue; 232*4887Schin case 'D': /* date */ 233*4887Schin p = tm_info.format[TM_DATE]; 234*4887Schin goto push; 235*4887Schin case 'E': /* OBSOLETE no pad day of month */ 236*4887Schin cp = number(cp, ep, (long)tp->tm_mday, 0, width, pad); 237*4887Schin continue; 238*4887Schin case 'e': /* blank padded day of month */ 239*4887Schin cp = number(cp, ep, (long)tp->tm_mday, -2, width, pad); 240*4887Schin continue; 241*4887Schin case 'f': /* TM_DEFAULT override */ 242*4887Schin case 'o': /* OBSOLETE */ 243*4887Schin p = tm_info.deformat; 244*4887Schin goto push; 245*4887Schin case 'F': /* TM_DEFAULT */ 246*4887Schin case 'O': /* OBSOLETE */ 247*4887Schin p = tm_info.format[TM_DEFAULT]; 248*4887Schin goto push; 249*4887Schin case 'g': /* %V 2 digit year */ 250*4887Schin case 'G': /* %V 4 digit year */ 251*4887Schin n = tp->tm_year + 1900; 252*4887Schin if (tp->tm_yday < 7) 253*4887Schin { 254*4887Schin if (tmweek(tp, 2, -1, -1) >= 52) 255*4887Schin n--; 256*4887Schin } 257*4887Schin else if (tp->tm_yday > 358) 258*4887Schin { 259*4887Schin if (tmweek(tp, 2, -1, -1) <= 1) 260*4887Schin n++; 261*4887Schin } 262*4887Schin if (c == 'g') 263*4887Schin { 264*4887Schin n %= 100; 265*4887Schin c = 2; 266*4887Schin } 267*4887Schin else 268*4887Schin c = 4; 269*4887Schin cp = number(cp, ep, (long)n, c, width, pad); 270*4887Schin continue; 271*4887Schin case 'H': /* hour (0 - 23) */ 272*4887Schin cp = number(cp, ep, (long)tp->tm_hour, 2, width, pad); 273*4887Schin continue; 274*4887Schin case 'i': /* international `date(1)' date */ 275*4887Schin p = tm_info.format[TM_INTERNATIONAL]; 276*4887Schin goto push; 277*4887Schin case 'I': /* hour (0 - 12) */ 278*4887Schin if ((n = tp->tm_hour) > 12) n -= 12; 279*4887Schin else if (n == 0) n = 12; 280*4887Schin cp = number(cp, ep, (long)n, 2, width, pad); 281*4887Schin continue; 282*4887Schin case 'J': /* Julian date (0 offset) */ 283*4887Schin cp = number(cp, ep, (long)tp->tm_yday, 3, width, pad); 284*4887Schin continue; 285*4887Schin case 'j': /* Julian date (1 offset) */ 286*4887Schin cp = number(cp, ep, (long)(tp->tm_yday + 1), 3, width, pad); 287*4887Schin continue; 288*4887Schin case 'k': /* `date(1)' date */ 289*4887Schin p = tm_info.format[TM_DATE_1]; 290*4887Schin goto push; 291*4887Schin case 'K': 292*4887Schin p = "%Y-%m-%d+%H:%M:%S"; 293*4887Schin goto push; 294*4887Schin case 'l': /* `ls -l' date */ 295*4887Schin if (t) 296*4887Schin { 297*4887Schin now = tmxgettime(); 298*4887Schin if (warped(t, now)) 299*4887Schin { 300*4887Schin p = tm_info.format[TM_DISTANT]; 301*4887Schin goto push; 302*4887Schin } 303*4887Schin } 304*4887Schin p = tm_info.format[TM_RECENT]; 305*4887Schin goto push; 306*4887Schin case 'm': /* month number */ 307*4887Schin cp = number(cp, ep, (long)(tp->tm_mon + 1), 2, width, pad); 308*4887Schin continue; 309*4887Schin case 'M': /* minutes */ 310*4887Schin cp = number(cp, ep, (long)tp->tm_min, 2, width, pad); 311*4887Schin continue; 312*4887Schin case 'n': 313*4887Schin if (cp < ep) 314*4887Schin *cp++ = '\n'; 315*4887Schin continue; 316*4887Schin case 'N': /* nanosecond part */ 317*4887Schin cp = number(cp, ep, (long)tp->tm_nsec, 9, width, pad); 318*4887Schin continue; 319*4887Schin case 'p': /* meridian */ 320*4887Schin n = TM_MERIDIAN + (tp->tm_hour >= 12); 321*4887Schin goto index; 322*4887Schin case 'q': /* time zone type (nation code) */ 323*4887Schin if (!(flags & TM_UTC)) 324*4887Schin { 325*4887Schin if ((zp = tm_info.zone) != tm_info.local) 326*4887Schin for (; zp >= tm_data.zone; zp--) 327*4887Schin if (p = zp->type) 328*4887Schin goto string; 329*4887Schin else if (p = zp->type) 330*4887Schin goto string; 331*4887Schin } 332*4887Schin continue; 333*4887Schin case 'Q': /* %Q<alpha> or %Q<delim>recent<delim>distant<delim> */ 334*4887Schin if (c = *format) 335*4887Schin { 336*4887Schin format++; 337*4887Schin if (isalpha(c)) 338*4887Schin { 339*4887Schin switch (c) 340*4887Schin { 341*4887Schin case 'd': /* `ls -l' distant date */ 342*4887Schin p = tm_info.format[TM_DISTANT]; 343*4887Schin goto push; 344*4887Schin case 'r': /* `ls -l' recent date */ 345*4887Schin p = tm_info.format[TM_RECENT]; 346*4887Schin goto push; 347*4887Schin default: 348*4887Schin format--; 349*4887Schin break; 350*4887Schin } 351*4887Schin } 352*4887Schin else 353*4887Schin { 354*4887Schin if (t) 355*4887Schin { 356*4887Schin now = tmxgettime(); 357*4887Schin p = warped(t, now) ? (char*)0 : (char*)format; 358*4887Schin } 359*4887Schin else 360*4887Schin p = (char*)format; 361*4887Schin i = 0; 362*4887Schin while (n = *format) 363*4887Schin { 364*4887Schin format++; 365*4887Schin if (n == c) 366*4887Schin { 367*4887Schin if (!p) 368*4887Schin p = (char*)format; 369*4887Schin if (++i == 2) 370*4887Schin goto push_delimiter; 371*4887Schin } 372*4887Schin } 373*4887Schin } 374*4887Schin } 375*4887Schin continue; 376*4887Schin case 'r': 377*4887Schin p = tm_info.format[TM_MERIDIAN_TIME]; 378*4887Schin goto push; 379*4887Schin case 'R': 380*4887Schin p = "%H:%M"; 381*4887Schin goto push; 382*4887Schin case 's': /* seconds[.nanoseconds] since the epoch */ 383*4887Schin case '#': 384*4887Schin if (t) 385*4887Schin now = t; 386*4887Schin else 387*4887Schin now = tmxgettime(); 388*4887Schin f = fmt; 389*4887Schin *f++ = '%'; 390*4887Schin if (pad == '0') 391*4887Schin *f++ = pad; 392*4887Schin if (width) 393*4887Schin f += sfsprintf(f, &fmt[sizeof(fmt)] - f, "%d", width); 394*4887Schin f += sfsprintf(f, &fmt[sizeof(fmt)] - f, "I%du", sizeof(Tmxsec_t)); 395*4887Schin cp += sfsprintf(cp, ep - cp, fmt, tmxsec(now)); 396*4887Schin if (parts > 1) 397*4887Schin { 398*4887Schin n = sfsprintf(cp, ep - cp, ".%09I*u", sizeof(Tmxnsec_t), tmxnsec(now)); 399*4887Schin if (prec && n >= prec) 400*4887Schin n = prec + 1; 401*4887Schin cp += n; 402*4887Schin } 403*4887Schin continue; 404*4887Schin case 'S': /* seconds */ 405*4887Schin cp = number(cp, ep, (long)tp->tm_sec, 2, width, pad); 406*4887Schin if ((flags & TM_SUBSECOND) && (format - 2) != oformat) 407*4887Schin { 408*4887Schin p = ".%N"; 409*4887Schin goto push; 410*4887Schin } 411*4887Schin continue; 412*4887Schin case 't': 413*4887Schin if (cp < ep) 414*4887Schin *cp++ = '\t'; 415*4887Schin continue; 416*4887Schin case 'T': 417*4887Schin p = tm_info.format[TM_TIME]; 418*4887Schin goto push; 419*4887Schin case 'u': /* weekday number [1(Monday)-7] */ 420*4887Schin if (!(i = tp->tm_wday)) 421*4887Schin i = 7; 422*4887Schin cp = number(cp, ep, (long)i, 0, width, pad); 423*4887Schin continue; 424*4887Schin case 'U': /* week number, Sunday as first day */ 425*4887Schin cp = number(cp, ep, (long)tmweek(tp, 0, -1, -1), 2, width, pad); 426*4887Schin continue; 427*4887Schin case 'V': /* ISO week number */ 428*4887Schin cp = number(cp, ep, (long)tmweek(tp, 2, -1, -1), 2, width, pad); 429*4887Schin continue; 430*4887Schin case 'W': /* week number, Monday as first day */ 431*4887Schin cp = number(cp, ep, (long)tmweek(tp, 1, -1, -1), 2, width, pad); 432*4887Schin continue; 433*4887Schin case 'w': /* weekday number [0(Sunday)-6] */ 434*4887Schin cp = number(cp, ep, (long)tp->tm_wday, 0, width, pad); 435*4887Schin continue; 436*4887Schin case 'x': 437*4887Schin p = tm_info.format[TM_DATE]; 438*4887Schin goto push; 439*4887Schin case 'X': 440*4887Schin p = tm_info.format[TM_TIME]; 441*4887Schin goto push; 442*4887Schin case 'y': /* year in the form yy */ 443*4887Schin cp = number(cp, ep, (long)(tp->tm_year % 100), 2, width, pad); 444*4887Schin continue; 445*4887Schin case 'Y': /* year in the form ccyy */ 446*4887Schin cp = number(cp, ep, (long)(1900 + tp->tm_year), 4, width, pad); 447*4887Schin continue; 448*4887Schin case 'z': /* time zone west offset */ 449*4887Schin if ((ep - cp) >= 16) 450*4887Schin cp = tmpoff(cp, ep - cp, "", (flags & TM_UTC) ? 0 : tm_info.zone->west, 24 * 60); 451*4887Schin continue; 452*4887Schin case 'Z': /* time zone */ 453*4887Schin p = (flags & TM_UTC) ? tm_info.format[TM_UT] : tp->tm_isdst && tm_info.zone->daylight ? tm_info.zone->daylight : tm_info.zone->standard; 454*4887Schin goto string; 455*4887Schin case '+': /* old %+flag */ 456*4887Schin case '!': /* old %!flag */ 457*4887Schin format--; 458*4887Schin /*FALLTHROUGH*/ 459*4887Schin case '=': /* %=[=][+-]flag */ 460*4887Schin if (i = *format == '=') 461*4887Schin format++; 462*4887Schin if (*format == '+' || *format == '-' || *format == '!') 463*4887Schin c = *format++; 464*4887Schin else 465*4887Schin c = '+'; 466*4887Schin switch (*format++) 467*4887Schin { 468*4887Schin case 0: 469*4887Schin format--; 470*4887Schin continue; 471*4887Schin case 'l': 472*4887Schin n = TM_LEAP; 473*4887Schin break; 474*4887Schin case 'n': 475*4887Schin case 's': 476*4887Schin n = TM_SUBSECOND; 477*4887Schin break; 478*4887Schin case 'u': 479*4887Schin n = TM_UTC; 480*4887Schin break; 481*4887Schin } 482*4887Schin if (n) 483*4887Schin { 484*4887Schin /* 485*4887Schin * right, the global state stinks 486*4887Schin * but we respect its locale-like status 487*4887Schin */ 488*4887Schin 489*4887Schin if (c == '+') 490*4887Schin { 491*4887Schin if (!(flags & n)) 492*4887Schin { 493*4887Schin flags |= n; 494*4887Schin tm_info.flags |= n; 495*4887Schin tp = tmxmake(t); 496*4887Schin if (!i) 497*4887Schin tm_info.flags &= ~n; 498*4887Schin } 499*4887Schin } 500*4887Schin else if (flags & n) 501*4887Schin { 502*4887Schin flags &= ~n; 503*4887Schin tm_info.flags &= ~n; 504*4887Schin tp = tmxmake(t); 505*4887Schin if (!i) 506*4887Schin tm_info.flags |= n; 507*4887Schin } 508*4887Schin } 509*4887Schin continue; 510*4887Schin default: 511*4887Schin if (cp < ep) 512*4887Schin *cp++ = '%'; 513*4887Schin if (cp < ep) 514*4887Schin *cp++ = c; 515*4887Schin continue; 516*4887Schin } 517*4887Schin index: 518*4887Schin p = tm_info.format[n]; 519*4887Schin string: 520*4887Schin while (cp < ep && (*cp = *p++)) 521*4887Schin cp++; 522*4887Schin continue; 523*4887Schin push: 524*4887Schin c = 0; 525*4887Schin push_delimiter: 526*4887Schin if (sp < &stack[elementsof(stack)]) 527*4887Schin { 528*4887Schin sp->format = (char*)format; 529*4887Schin format = p; 530*4887Schin sp->delimiter = delimiter; 531*4887Schin delimiter = c; 532*4887Schin sp++; 533*4887Schin } 534*4887Schin continue; 535*4887Schin } 536*4887Schin tm_info.flags = flags; 537*4887Schin if (cp >= ep) 538*4887Schin cp = ep - 1; 539*4887Schin *cp = 0; 540*4887Schin return cp; 541*4887Schin } 542