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