xref: /original-bsd/lib/libc/string/strftime.c (revision feb5f8e2)
1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #if defined(LIBC_SCCS) && !defined(lint)
9 static char sccsid[] = "@(#)strftime.c	5.11 (Berkeley) 02/24/91";
10 #endif /* LIBC_SCCS and not lint */
11 
12 #include <sys/types.h>
13 #include <sys/time.h>
14 #include <tzfile.h>
15 #include <string.h>
16 
17 static char *afmt[] = {
18 	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
19 };
20 static char *Afmt[] = {
21 	"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
22 	"Saturday",
23 };
24 static char *bfmt[] = {
25 	"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
26 	"Oct", "Nov", "Dec",
27 };
28 static char *Bfmt[] = {
29 	"January", "February", "March", "April", "May", "June", "July",
30 	"August", "September", "October", "November", "December",
31 };
32 
33 static size_t gsize;
34 static char *pt;
35 static int _add(), _conv(), _secs();
36 
37 size_t
38 strftime(s, maxsize, format, t)
39 	char *s;
40 	size_t maxsize;
41 	const char *format;
42 	const struct tm *t;
43 {
44 	static size_t _fmt();
45 
46 	pt = s;
47 	if ((gsize = maxsize) < 1)
48 		return(0);
49 	if (_fmt(format, t)) {
50 		*pt = '\0';
51 		return(maxsize - gsize);
52 	}
53 	return(0);
54 }
55 
56 static size_t
57 _fmt(format, t)
58 	register char *format;
59 	struct tm *t;
60 {
61 	for (; *format; ++format) {
62 		if (*format == '%')
63 			switch(*++format) {
64 			case '\0':
65 				--format;
66 				break;
67 			case 'A':
68 				if (t->tm_wday < 0 || t->tm_wday > 6)
69 					return(0);
70 				if (!_add(Afmt[t->tm_wday]))
71 					return(0);
72 				continue;
73 			case 'a':
74 				if (t->tm_wday < 0 || t->tm_wday > 6)
75 					return(0);
76 				if (!_add(afmt[t->tm_wday]))
77 					return(0);
78 				continue;
79 			case 'B':
80 				if (t->tm_mon < 0 || t->tm_mon > 11)
81 					return(0);
82 				if (!_add(Bfmt[t->tm_mon]))
83 					return(0);
84 				continue;
85 			case 'b':
86 			case 'h':
87 				if (t->tm_mon < 0 || t->tm_mon > 11)
88 					return(0);
89 				if (!_add(bfmt[t->tm_mon]))
90 					return(0);
91 				continue;
92 			case 'C':
93 				if (!_fmt("%a %b %e %H:%M:%S %Y", t))
94 					return(0);
95 				continue;
96 			case 'c':
97 				if (!_fmt("%m/%d/%y %H:%M:%S", t))
98 					return(0);
99 				continue;
100 			case 'D':
101 				if (!_fmt("%m/%d/%y", t))
102 					return(0);
103 				continue;
104 			case 'd':
105 				if (!_conv(t->tm_mday, 2, '0'))
106 					return(0);
107 				continue;
108 			case 'e':
109 				if (!_conv(t->tm_mday, 2, ' '))
110 					return(0);
111 				continue;
112 			case 'H':
113 				if (!_conv(t->tm_hour, 2, '0'))
114 					return(0);
115 				continue;
116 			case 'I':
117 				if (!_conv(t->tm_hour % 12 ?
118 				    t->tm_hour % 12 : 12, 2, '0'))
119 					return(0);
120 				continue;
121 			case 'j':
122 				if (!_conv(t->tm_yday + 1, 3, '0'))
123 					return(0);
124 				continue;
125 			case 'k':
126 				if (!_conv(t->tm_hour, 2, ' '))
127 					return(0);
128 				continue;
129 			case 'l':
130 				if (!_conv(t->tm_hour % 12 ?
131 				    t->tm_hour % 12 : 12, 2, ' '))
132 					return(0);
133 				continue;
134 			case 'M':
135 				if (!_conv(t->tm_min, 2, '0'))
136 					return(0);
137 				continue;
138 			case 'm':
139 				if (!_conv(t->tm_mon + 1, 2, '0'))
140 					return(0);
141 				continue;
142 			case 'n':
143 				if (!_add("\n"))
144 					return(0);
145 				continue;
146 			case 'p':
147 				if (!_add(t->tm_hour >= 12 ? "PM" : "AM"))
148 					return(0);
149 				continue;
150 			case 'R':
151 				if (!_fmt("%H:%M", t))
152 					return(0);
153 				continue;
154 			case 'r':
155 				if (!_fmt("%I:%M:%S %p", t))
156 					return(0);
157 				continue;
158 			case 'S':
159 				if (!_conv(t->tm_sec, 2, '0'))
160 					return(0);
161 				continue;
162 			case 's':
163 				if (!_secs(t))
164 					return(0);
165 				continue;
166 			case 'T':
167 			case 'X':
168 				if (!_fmt("%H:%M:%S", t))
169 					return(0);
170 				continue;
171 			case 't':
172 				if (!_add("\t"))
173 					return(0);
174 				continue;
175 			case 'U':
176 				if (!_conv((t->tm_yday + 7 - t->tm_wday) / 7,
177 				    2, '0'))
178 					return(0);
179 				continue;
180 			case 'W':
181 				if (!_conv((t->tm_yday + 7 -
182 				    (t->tm_wday ? (t->tm_wday - 1) : 6))
183 				    / 7, 2, '0'))
184 					return(0);
185 				continue;
186 			case 'w':
187 				if (!_conv(t->tm_wday, 1, '0'))
188 					return(0);
189 				continue;
190 			case 'x':
191 				if (!_fmt("%m/%d/%y", t))
192 					return(0);
193 				continue;
194 			case 'y':
195 				if (!_conv((t->tm_year + TM_YEAR_BASE)
196 				    % 100, 2, '0'))
197 					return(0);
198 				continue;
199 			case 'Y':
200 				if (!_conv(t->tm_year + TM_YEAR_BASE, 4, '0'))
201 					return(0);
202 				continue;
203 			case 'Z':
204 				if (!t->tm_zone || !_add(t->tm_zone))
205 					return(0);
206 				continue;
207 			case '%':
208 			/*
209 			 * X311J/88-090 (4.12.3.5): if conversion char is
210 			 * undefined, behavior is undefined.  Print out the
211 			 * character itself as printf(3) does.
212 			 */
213 			default:
214 				break;
215 		}
216 		if (!gsize--)
217 			return(0);
218 		*pt++ = *format;
219 	}
220 	return(gsize);
221 }
222 
223 static
224 _secs(t)
225 	struct tm *t;
226 {
227 	static char buf[15];
228 	register time_t s;
229 	register char *p;
230 	struct tm tmp;
231 
232 	/* Make a copy, mktime(3) modifies the tm struct. */
233 	tmp = *t;
234 	s = mktime(&tmp);
235 	for (p = buf + sizeof(buf) - 2; s > 0 && p > buf; s /= 10)
236 		*p-- = s % 10 + '0';
237 	return(_add(++p));
238 }
239 
240 static
241 _conv(n, digits, pad)
242 	int n, digits;
243 	char pad;
244 {
245 	static char buf[10];
246 	register char *p;
247 
248 	for (p = buf + sizeof(buf) - 2; n > 0 && p > buf; n /= 10, --digits)
249 		*p-- = n % 10 + '0';
250 	while (p > buf && digits-- > 0)
251 		*p-- = pad;
252 	return(_add(++p));
253 }
254 
255 static
256 _add(str)
257 	register char *str;
258 {
259 	for (;; ++pt, --gsize) {
260 		if (!gsize)
261 			return(0);
262 		if (!(*pt = *str++))
263 			return(1);
264 	}
265 }
266