14887Schin /*********************************************************************** 24887Schin * * 34887Schin * This software is part of the ast package * 4*8462SApril.Chin@Sun.COM * Copyright (c) 1985-2008 AT&T Intellectual Property * 54887Schin * and is licensed under the * 64887Schin * Common Public License, Version 1.0 * 7*8462SApril.Chin@Sun.COM * by AT&T Intellectual Property * 84887Schin * * 94887Schin * A copy of the License is available at * 104887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 124887Schin * * 134887Schin * Information and Software Systems Research * 144887Schin * AT&T Research * 154887Schin * Florham Park NJ * 164887Schin * * 174887Schin * Glenn Fowler <gsf@research.att.com> * 184887Schin * David Korn <dgk@research.att.com> * 194887Schin * Phong Vo <kpv@research.att.com> * 204887Schin * * 214887Schin ***********************************************************************/ 224887Schin #pragma prototyped 234887Schin /* 244887Schin * Glenn Fowler 254887Schin * AT&T Research 264887Schin * 274887Schin * Time_t conversion support 284887Schin */ 294887Schin 304887Schin #include <tmx.h> 314887Schin #include <ctype.h> 324887Schin 334887Schin #define warped(t,n) ((t)<((n)-tmxsns(6L*30L*24L*60L*60L,0))||(t)>((n)+tmxsns(24L*60L*60L,0))) 344887Schin 354887Schin /* 364887Schin * format n with padding p into s 374887Schin * return end of s 384887Schin * 394887Schin * p: <0 blank padding 404887Schin * 0 no padding 414887Schin * >0 0 padding 424887Schin */ 434887Schin 444887Schin static char* 454887Schin number(register char* s, register char* e, register long n, register int p, int w, int pad) 464887Schin { 474887Schin char* b; 484887Schin 49*8462SApril.Chin@Sun.COM if (w) 50*8462SApril.Chin@Sun.COM { 51*8462SApril.Chin@Sun.COM if (p > 0 && (pad == 0 || pad == '0')) 52*8462SApril.Chin@Sun.COM while (w > p) 53*8462SApril.Chin@Sun.COM { 54*8462SApril.Chin@Sun.COM p++; 55*8462SApril.Chin@Sun.COM n *= 10; 56*8462SApril.Chin@Sun.COM } 57*8462SApril.Chin@Sun.COM else if (w > p) 58*8462SApril.Chin@Sun.COM p = w; 59*8462SApril.Chin@Sun.COM } 604887Schin switch (pad) 614887Schin { 624887Schin case '-': 634887Schin p = 0; 644887Schin break; 654887Schin case '_': 664887Schin if (p > 0) 674887Schin p = -p; 684887Schin break; 694887Schin case '0': 704887Schin if (p < 0) 714887Schin p = -p; 724887Schin break; 734887Schin } 744887Schin b = s; 754887Schin if (p > 0) 764887Schin s += sfsprintf(s, e - s, "%0*lu", p, n); 774887Schin else if (p < 0) 784887Schin s += sfsprintf(s, e - s, "%*lu", -p, n); 794887Schin else 804887Schin s += sfsprintf(s, e - s, "%lu", n); 814887Schin if (w && (s - b) > w) 824887Schin *(s = b + w) = 0; 834887Schin return s; 844887Schin } 854887Schin 864887Schin typedef struct Stack_s 874887Schin { 884887Schin char* format; 894887Schin int delimiter; 904887Schin } Stack_t; 914887Schin 924887Schin /* 934887Schin * format t into buf of length len 944887Schin * end of buf is returned 954887Schin */ 964887Schin 974887Schin char* 984887Schin tmxfmt(char* buf, size_t len, const char* format, Time_t t) 994887Schin { 1004887Schin register char* cp; 1014887Schin register char* ep; 1024887Schin register char* p; 1034887Schin register int n; 1044887Schin int c; 1054887Schin int i; 1064887Schin int flags; 1074887Schin int pad; 1084887Schin int delimiter; 1094887Schin int width; 1104887Schin int prec; 1114887Schin int parts; 1124887Schin char* f; 1134887Schin const char* oformat; 1144887Schin Tm_t* tp; 1154887Schin Tm_zone_t* zp; 1164887Schin Time_t now; 1174887Schin Stack_t* sp; 1184887Schin Stack_t stack[8]; 1194887Schin char fmt[32]; 1204887Schin 1214887Schin tmlocale(); 1224887Schin tp = tmxmake(t); 1234887Schin if (!format || !*format) 1244887Schin format = tm_info.deformat; 1254887Schin oformat = format; 1264887Schin flags = tm_info.flags; 1274887Schin sp = &stack[0]; 1284887Schin cp = buf; 1294887Schin ep = buf + len; 1304887Schin delimiter = 0; 1314887Schin for (;;) 1324887Schin { 1334887Schin if ((c = *format++) == delimiter) 1344887Schin { 1354887Schin delimiter = 0; 1364887Schin if (sp <= &stack[0]) 1374887Schin break; 1384887Schin sp--; 1394887Schin format = sp->format; 1404887Schin delimiter = sp->delimiter; 1414887Schin continue; 1424887Schin } 1434887Schin if (c != '%') 1444887Schin { 1454887Schin if (cp < ep) 1464887Schin *cp++ = c; 1474887Schin continue; 1484887Schin } 1494887Schin pad = 0; 1504887Schin width = 0; 1514887Schin prec = 0; 1524887Schin parts = 0; 1534887Schin for (;;) 1544887Schin { 1554887Schin switch (c = *format++) 1564887Schin { 1574887Schin case '_': 1584887Schin case '-': 1594887Schin pad = c; 1604887Schin continue; 1614887Schin case 'E': 1624887Schin case 'O': 1634887Schin if (!isalpha(*format)) 1644887Schin break; 1654887Schin continue; 1664887Schin case '0': 1674887Schin if (!parts) 1684887Schin { 1694887Schin pad = c; 1704887Schin continue; 1714887Schin } 1724887Schin /*FALLTHROUGH*/ 1734887Schin case '1': 1744887Schin case '2': 1754887Schin case '3': 1764887Schin case '4': 1774887Schin case '5': 1784887Schin case '6': 1794887Schin case '7': 1804887Schin case '8': 1814887Schin case '9': 1824887Schin switch (parts) 1834887Schin { 1844887Schin case 0: 1854887Schin parts++; 1864887Schin /*FALLTHROUGH*/ 1874887Schin case 1: 1884887Schin width = width * 10 + (c - '0'); 1894887Schin break; 1904887Schin case 2: 1914887Schin prec = prec * 10 + (c - '0'); 1924887Schin break; 1934887Schin } 1944887Schin continue; 1954887Schin case '.': 1964887Schin if (!parts++) 1974887Schin parts++; 1984887Schin continue; 1994887Schin default: 2004887Schin break; 2014887Schin } 2024887Schin break; 2034887Schin } 2044887Schin switch (c) 2054887Schin { 2064887Schin case 0: 2074887Schin format--; 2084887Schin continue; 2094887Schin case '%': 2104887Schin if (cp < ep) 2114887Schin *cp++ = '%'; 2124887Schin continue; 2134887Schin case '?': 2144887Schin if (tm_info.deformat != tm_info.format[TM_DEFAULT]) 2154887Schin format = tm_info.deformat; 2164887Schin else if (!*format) 2174887Schin format = tm_info.format[TM_DEFAULT]; 2184887Schin continue; 2194887Schin case 'a': /* abbreviated day of week name */ 2204887Schin n = TM_DAY_ABBREV + tp->tm_wday; 2214887Schin goto index; 2224887Schin case 'A': /* day of week name */ 2234887Schin n = TM_DAY + tp->tm_wday; 2244887Schin goto index; 2254887Schin case 'b': /* abbreviated month name */ 2264887Schin case 'h': 2274887Schin n = TM_MONTH_ABBREV + tp->tm_mon; 2284887Schin goto index; 2294887Schin case 'B': /* month name */ 2304887Schin n = TM_MONTH + tp->tm_mon; 2314887Schin goto index; 2324887Schin case 'c': /* `ctime(3)' date sans newline */ 2334887Schin p = tm_info.format[TM_CTIME]; 2344887Schin goto push; 2354887Schin case 'C': /* 2 digit century */ 2364887Schin cp = number(cp, ep, (long)(1900 + tp->tm_year) / 100, 2, width, pad); 2374887Schin continue; 2384887Schin case 'd': /* day of month */ 2394887Schin cp = number(cp, ep, (long)tp->tm_mday, 2, width, pad); 2404887Schin continue; 2414887Schin case 'D': /* date */ 2424887Schin p = tm_info.format[TM_DATE]; 2434887Schin goto push; 2444887Schin case 'E': /* OBSOLETE no pad day of month */ 2454887Schin cp = number(cp, ep, (long)tp->tm_mday, 0, width, pad); 2464887Schin continue; 2474887Schin case 'e': /* blank padded day of month */ 2484887Schin cp = number(cp, ep, (long)tp->tm_mday, -2, width, pad); 2494887Schin continue; 2504887Schin case 'f': /* TM_DEFAULT override */ 2514887Schin case 'o': /* OBSOLETE */ 2524887Schin p = tm_info.deformat; 2534887Schin goto push; 254*8462SApril.Chin@Sun.COM case 'F': /* ISO 8601:2000 standard date format */ 255*8462SApril.Chin@Sun.COM p = "%Y-%m-%d"; 2564887Schin goto push; 2574887Schin case 'g': /* %V 2 digit year */ 2584887Schin case 'G': /* %V 4 digit year */ 2594887Schin n = tp->tm_year + 1900; 2604887Schin if (tp->tm_yday < 7) 2614887Schin { 2624887Schin if (tmweek(tp, 2, -1, -1) >= 52) 2634887Schin n--; 2644887Schin } 2654887Schin else if (tp->tm_yday > 358) 2664887Schin { 2674887Schin if (tmweek(tp, 2, -1, -1) <= 1) 2684887Schin n++; 2694887Schin } 2704887Schin if (c == 'g') 2714887Schin { 2724887Schin n %= 100; 2734887Schin c = 2; 2744887Schin } 2754887Schin else 2764887Schin c = 4; 2774887Schin cp = number(cp, ep, (long)n, c, width, pad); 2784887Schin continue; 2794887Schin case 'H': /* hour (0 - 23) */ 2804887Schin cp = number(cp, ep, (long)tp->tm_hour, 2, width, pad); 2814887Schin continue; 2824887Schin case 'i': /* international `date(1)' date */ 2834887Schin p = tm_info.format[TM_INTERNATIONAL]; 2844887Schin goto push; 2854887Schin case 'I': /* hour (0 - 12) */ 2864887Schin if ((n = tp->tm_hour) > 12) n -= 12; 2874887Schin else if (n == 0) n = 12; 2884887Schin cp = number(cp, ep, (long)n, 2, width, pad); 2894887Schin continue; 2904887Schin case 'J': /* Julian date (0 offset) */ 2914887Schin cp = number(cp, ep, (long)tp->tm_yday, 3, width, pad); 2924887Schin continue; 2934887Schin case 'j': /* Julian date (1 offset) */ 2944887Schin cp = number(cp, ep, (long)(tp->tm_yday + 1), 3, width, pad); 2954887Schin continue; 2964887Schin case 'k': /* `date(1)' date */ 2974887Schin p = tm_info.format[TM_DATE_1]; 2984887Schin goto push; 2994887Schin case 'K': 3004887Schin p = "%Y-%m-%d+%H:%M:%S"; 3014887Schin goto push; 3024887Schin case 'l': /* `ls -l' date */ 3034887Schin if (t) 3044887Schin { 3054887Schin now = tmxgettime(); 3064887Schin if (warped(t, now)) 3074887Schin { 3084887Schin p = tm_info.format[TM_DISTANT]; 3094887Schin goto push; 3104887Schin } 3114887Schin } 3124887Schin p = tm_info.format[TM_RECENT]; 3134887Schin goto push; 314*8462SApril.Chin@Sun.COM case 'L': /* TM_DEFAULT */ 315*8462SApril.Chin@Sun.COM case 'O': /* OBSOLETE */ 316*8462SApril.Chin@Sun.COM p = tm_info.format[TM_DEFAULT]; 317*8462SApril.Chin@Sun.COM goto push; 3184887Schin case 'm': /* month number */ 3194887Schin cp = number(cp, ep, (long)(tp->tm_mon + 1), 2, width, pad); 3204887Schin continue; 3214887Schin case 'M': /* minutes */ 3224887Schin cp = number(cp, ep, (long)tp->tm_min, 2, width, pad); 3234887Schin continue; 3244887Schin case 'n': 3254887Schin if (cp < ep) 3264887Schin *cp++ = '\n'; 3274887Schin continue; 3284887Schin case 'N': /* nanosecond part */ 3294887Schin cp = number(cp, ep, (long)tp->tm_nsec, 9, width, pad); 3304887Schin continue; 3314887Schin case 'p': /* meridian */ 3324887Schin n = TM_MERIDIAN + (tp->tm_hour >= 12); 3334887Schin goto index; 3344887Schin case 'q': /* time zone type (nation code) */ 3354887Schin if (!(flags & TM_UTC)) 3364887Schin { 3374887Schin if ((zp = tm_info.zone) != tm_info.local) 3384887Schin for (; zp >= tm_data.zone; zp--) 3394887Schin if (p = zp->type) 3404887Schin goto string; 3414887Schin else if (p = zp->type) 3424887Schin goto string; 3434887Schin } 3444887Schin continue; 3454887Schin case 'Q': /* %Q<alpha> or %Q<delim>recent<delim>distant<delim> */ 3464887Schin if (c = *format) 3474887Schin { 3484887Schin format++; 3494887Schin if (isalpha(c)) 3504887Schin { 3514887Schin switch (c) 3524887Schin { 3534887Schin case 'd': /* `ls -l' distant date */ 3544887Schin p = tm_info.format[TM_DISTANT]; 3554887Schin goto push; 3564887Schin case 'r': /* `ls -l' recent date */ 3574887Schin p = tm_info.format[TM_RECENT]; 3584887Schin goto push; 3594887Schin default: 3604887Schin format--; 3614887Schin break; 3624887Schin } 3634887Schin } 3644887Schin else 3654887Schin { 3664887Schin if (t) 3674887Schin { 3684887Schin now = tmxgettime(); 3694887Schin p = warped(t, now) ? (char*)0 : (char*)format; 3704887Schin } 3714887Schin else 3724887Schin p = (char*)format; 3734887Schin i = 0; 3744887Schin while (n = *format) 3754887Schin { 3764887Schin format++; 3774887Schin if (n == c) 3784887Schin { 3794887Schin if (!p) 3804887Schin p = (char*)format; 3814887Schin if (++i == 2) 3824887Schin goto push_delimiter; 3834887Schin } 3844887Schin } 3854887Schin } 3864887Schin } 3874887Schin continue; 3884887Schin case 'r': 3894887Schin p = tm_info.format[TM_MERIDIAN_TIME]; 3904887Schin goto push; 3914887Schin case 'R': 3924887Schin p = "%H:%M"; 3934887Schin goto push; 3944887Schin case 's': /* seconds[.nanoseconds] since the epoch */ 3954887Schin case '#': 3964887Schin if (t) 3974887Schin now = t; 3984887Schin else 3994887Schin now = tmxgettime(); 4004887Schin f = fmt; 4014887Schin *f++ = '%'; 4024887Schin if (pad == '0') 4034887Schin *f++ = pad; 4044887Schin if (width) 4054887Schin f += sfsprintf(f, &fmt[sizeof(fmt)] - f, "%d", width); 4064887Schin f += sfsprintf(f, &fmt[sizeof(fmt)] - f, "I%du", sizeof(Tmxsec_t)); 4074887Schin cp += sfsprintf(cp, ep - cp, fmt, tmxsec(now)); 4084887Schin if (parts > 1) 4094887Schin { 4104887Schin n = sfsprintf(cp, ep - cp, ".%09I*u", sizeof(Tmxnsec_t), tmxnsec(now)); 4114887Schin if (prec && n >= prec) 4124887Schin n = prec + 1; 4134887Schin cp += n; 4144887Schin } 4154887Schin continue; 4164887Schin case 'S': /* seconds */ 4174887Schin cp = number(cp, ep, (long)tp->tm_sec, 2, width, pad); 4184887Schin if ((flags & TM_SUBSECOND) && (format - 2) != oformat) 4194887Schin { 4204887Schin p = ".%N"; 4214887Schin goto push; 4224887Schin } 4234887Schin continue; 4244887Schin case 't': 4254887Schin if (cp < ep) 4264887Schin *cp++ = '\t'; 4274887Schin continue; 4284887Schin case 'T': 4294887Schin p = tm_info.format[TM_TIME]; 4304887Schin goto push; 4314887Schin case 'u': /* weekday number [1(Monday)-7] */ 4324887Schin if (!(i = tp->tm_wday)) 4334887Schin i = 7; 4344887Schin cp = number(cp, ep, (long)i, 0, width, pad); 4354887Schin continue; 4364887Schin case 'U': /* week number, Sunday as first day */ 4374887Schin cp = number(cp, ep, (long)tmweek(tp, 0, -1, -1), 2, width, pad); 4384887Schin continue; 4394887Schin case 'V': /* ISO week number */ 4404887Schin cp = number(cp, ep, (long)tmweek(tp, 2, -1, -1), 2, width, pad); 4414887Schin continue; 4424887Schin case 'W': /* week number, Monday as first day */ 4434887Schin cp = number(cp, ep, (long)tmweek(tp, 1, -1, -1), 2, width, pad); 4444887Schin continue; 4454887Schin case 'w': /* weekday number [0(Sunday)-6] */ 4464887Schin cp = number(cp, ep, (long)tp->tm_wday, 0, width, pad); 4474887Schin continue; 4484887Schin case 'x': 4494887Schin p = tm_info.format[TM_DATE]; 4504887Schin goto push; 4514887Schin case 'X': 4524887Schin p = tm_info.format[TM_TIME]; 4534887Schin goto push; 4544887Schin case 'y': /* year in the form yy */ 4554887Schin cp = number(cp, ep, (long)(tp->tm_year % 100), 2, width, pad); 4564887Schin continue; 4574887Schin case 'Y': /* year in the form ccyy */ 4584887Schin cp = number(cp, ep, (long)(1900 + tp->tm_year), 4, width, pad); 4594887Schin continue; 4604887Schin case 'z': /* time zone west offset */ 4614887Schin if ((ep - cp) >= 16) 462*8462SApril.Chin@Sun.COM cp = tmpoff(cp, ep - cp, "", (flags & TM_UTC) ? 0 : tm_info.zone->west - (tp->tm_isdst ? 60 : 0), 24 * 60); 4634887Schin continue; 4644887Schin case 'Z': /* time zone */ 4654887Schin p = (flags & TM_UTC) ? tm_info.format[TM_UT] : tp->tm_isdst && tm_info.zone->daylight ? tm_info.zone->daylight : tm_info.zone->standard; 4664887Schin goto string; 4674887Schin case '+': /* old %+flag */ 4684887Schin case '!': /* old %!flag */ 4694887Schin format--; 4704887Schin /*FALLTHROUGH*/ 4714887Schin case '=': /* %=[=][+-]flag */ 4724887Schin if (i = *format == '=') 4734887Schin format++; 4744887Schin if (*format == '+' || *format == '-' || *format == '!') 4754887Schin c = *format++; 4764887Schin else 4774887Schin c = '+'; 4784887Schin switch (*format++) 4794887Schin { 4804887Schin case 0: 4814887Schin format--; 4824887Schin continue; 4834887Schin case 'l': 4844887Schin n = TM_LEAP; 4854887Schin break; 4864887Schin case 'n': 4874887Schin case 's': 4884887Schin n = TM_SUBSECOND; 4894887Schin break; 4904887Schin case 'u': 4914887Schin n = TM_UTC; 4924887Schin break; 4934887Schin } 4944887Schin if (n) 4954887Schin { 4964887Schin /* 4974887Schin * right, the global state stinks 4984887Schin * but we respect its locale-like status 4994887Schin */ 5004887Schin 5014887Schin if (c == '+') 5024887Schin { 5034887Schin if (!(flags & n)) 5044887Schin { 5054887Schin flags |= n; 5064887Schin tm_info.flags |= n; 5074887Schin tp = tmxmake(t); 5084887Schin if (!i) 5094887Schin tm_info.flags &= ~n; 5104887Schin } 5114887Schin } 5124887Schin else if (flags & n) 5134887Schin { 5144887Schin flags &= ~n; 5154887Schin tm_info.flags &= ~n; 5164887Schin tp = tmxmake(t); 5174887Schin if (!i) 5184887Schin tm_info.flags |= n; 5194887Schin } 5204887Schin } 5214887Schin continue; 5224887Schin default: 5234887Schin if (cp < ep) 5244887Schin *cp++ = '%'; 5254887Schin if (cp < ep) 5264887Schin *cp++ = c; 5274887Schin continue; 5284887Schin } 5294887Schin index: 5304887Schin p = tm_info.format[n]; 5314887Schin string: 5324887Schin while (cp < ep && (*cp = *p++)) 5334887Schin cp++; 5344887Schin continue; 5354887Schin push: 5364887Schin c = 0; 5374887Schin push_delimiter: 5384887Schin if (sp < &stack[elementsof(stack)]) 5394887Schin { 5404887Schin sp->format = (char*)format; 5414887Schin format = p; 5424887Schin sp->delimiter = delimiter; 5434887Schin delimiter = c; 5444887Schin sp++; 5454887Schin } 5464887Schin continue; 5474887Schin } 5484887Schin tm_info.flags = flags; 5494887Schin if (cp >= ep) 5504887Schin cp = ep - 1; 5514887Schin *cp = 0; 5524887Schin return cp; 5534887Schin } 554