xref: /csrg-svn/lib/libc/string/strftime.c (revision 37103)
1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #if defined(LIBC_SCCS) && !defined(lint)
19 static char sccsid[] = "@(#)strftime.c	5.1 (Berkeley) 03/08/89";
20 #endif /* LIBC_SCCS and not lint */
21 
22 #include <sys/types.h>
23 #include <sys/time.h>
24 #include <tzfile.h>
25 
26 static char *afmt[] = {
27 	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
28 };
29 static char *Afmt[] = {
30 	"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
31 	"Saturday",
32 };
33 static char *bfmt[] = {
34 	"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
35 	"Oct", "Nov", "Dec",
36 };
37 static char *Bfmt[] = {
38 	"January", "February", "March", "April", "May", "June", "July",
39 	"August", "September", "October", "November", "December",
40 };
41 
42 static size_t gsize;
43 static char *pt;
44 
45 size_t
46 strftime(s, maxsize, format, t)
47 	char *s;
48 	char *format;
49 	size_t maxsize;
50 	struct tm *t;
51 {
52 	size_t _fmt();
53 
54 	pt = s;
55 	if ((gsize = maxsize) < 1)
56 		return(0);
57 	if (_fmt(format, t)) {
58 		*pt = '\0';
59 		return(maxsize - gsize);
60 	}
61 	return(0);
62 }
63 
64 static size_t
65 _fmt(format, t)
66 	register char *format;
67 	struct tm *t;
68 {
69 	char *timezone();
70 
71 	for (; *format; ++format) {
72 		if (*format == '%')
73 			switch(*++format) {
74 			case 'A':
75 				if (!_add(Afmt[t->tm_mon]))
76 					return(0);
77 				continue;
78 			case 'a':
79 				if (!_add(afmt[t->tm_mon]))
80 					return(0);
81 				continue;
82 			case 'B':
83 				if (!_add(Bfmt[t->tm_mon]))
84 					return(0);
85 				continue;
86 			case 'b':
87 				if (!_add(bfmt[t->tm_mon]))
88 					return(0);
89 				continue;
90 			case 'c':
91 				if (!_fmt("%x %X %Z %Y", t))
92 					return(0);
93 				continue;
94 			case 'd':
95 				if (!_conv(t->tm_mday, 2))
96 					return(0);
97 				continue;
98 			case 'H':
99 				if (!_conv(t->tm_hour, 2))
100 					return(0);
101 				continue;
102 			case 'I':
103 				if (!_conv((t->tm_hour - 1) % 12 + 1, 2))
104 					return(0);
105 				continue;
106 			case 'j':
107 				if (!_conv(t->tm_yday + 1, 3))
108 					return(0);
109 				continue;
110 			case 'M':
111 				if (!_conv(t->tm_min, 2))
112 					return(0);
113 				continue;
114 			case 'm':
115 				if (!_conv(t->tm_mon + 1, 2))
116 					return(0);
117 				continue;
118 			case 'p':
119 				if (!_add(t->tm_hour >= 12 ? "AM" : "PM"))
120 					return(0);
121 				continue;
122 			case 'S':
123 				if (!_conv(t->tm_sec, 2))
124 					return(0);
125 				continue;
126 			case 'U':
127 				if (!_conv((t->tm_yday + 7 - t->tm_wday) / 7,
128 				    2))
129 					return(0);
130 				continue;
131 			case 'W':
132 				if (!_conv((t->tm_yday + 7 -
133 				    (t->tm_wday ? t->tm_wday : 6)) / 7, 2))
134 					return(0);
135 				continue;
136 			case 'w':
137 				if (!_conv(t->tm_wday, 1))
138 					return(0);
139 				continue;
140 			case 'X':
141 				if (!_fmt("%H:%M:%S", t))
142 					return(0);
143 				continue;
144 			case 'x':
145 				if (!_fmt("%a %b %d", t))
146 					return(0);
147 				continue;
148 			case 'y':
149 				if (!_conv((t->tm_year + TM_YEAR_BASE)
150 				    % 100, 2))
151 					return(0);
152 				continue;
153 			case 'Y':
154 				if (!_conv(t->tm_year + TM_YEAR_BASE, 4))
155 					return(0);
156 				continue;
157 			case 'Z':
158 				if (!_add(t->tm_zone))
159 					return(0);
160 				continue;
161 			case '%':
162 			/*
163 			 * X311J/88-090 (4.12.3.5): if conversion char is
164 			 * undefined, behavior is undefined.  Print out the
165 			 * character itself as printf(3) also does.
166 			 */
167 			default:
168 				break;
169 		}
170 		if (!gsize--)
171 			return(0);
172 		*pt++ = *format;
173 	}
174 	return(gsize);
175 }
176 
177 static
178 _conv(n, digits)
179 	int n, digits;
180 {
181 	static char buf[10];
182 	register char *p;
183 
184 	for (p = buf + sizeof(buf) - 2; n > 0 && p > buf; n /= 10, --digits)
185 		*p-- = n % 10 + '0';
186 	while (p > buf && digits-- > 0)
187 		*p-- = '0';
188 	return(_add(++p));
189 }
190 
191 static
192 _add(str)
193 	register char *str;
194 {
195 	for (;; ++pt, --gsize) {
196 		if (!gsize)
197 			return(0);
198 		if (!(*pt = *str++))
199 			return(1);
200 	}
201 }
202