xref: /onnv-gate/usr/src/lib/libast/common/tm/tmxfmt.c (revision 8462:6e341f5569ba)
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