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