strftime.c revision 1.4 1 /* $NetBSD: strftime.c,v 1.4 1997/07/21 14:09:22 jtc Exp $ */
2
3 /*
4 * Copyright (c) 1989 The Regents of the University of California.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #if defined(LIBC_SCCS) && !defined(lint)
38 #if 0
39 static char *sccsid = "@(#)strftime.c 5.11 (Berkeley) 2/24/91";
40 #else
41 __RCSID("$NetBSD: strftime.c,v 1.4 1997/07/21 14:09:22 jtc Exp $");
42 #endif
43 #endif /* LIBC_SCCS and not lint */
44
45 #include "namespace.h"
46 #include <sys/localedef.h>
47 #include <locale.h>
48 #include <string.h>
49 #include <tzfile.h>
50 #include <time.h>
51
52 static size_t gsize;
53 static char *pt;
54
55 static int _add __P((const char *));
56 static int _conv __P((int, int, char));
57 static int _secs __P((const struct tm *));
58 static size_t _fmt __P((const char *, const struct tm *));
59
60 size_t
61 strftime(s, maxsize, format, t)
62 char *s;
63 size_t maxsize;
64 const char *format;
65 const struct tm *t;
66 {
67 tzset();
68
69 pt = s;
70 if ((gsize = maxsize) < 1)
71 return (0);
72 if (_fmt(format, t)) {
73 *pt = '\0';
74 return (maxsize - gsize);
75 }
76 return (0);
77 }
78
79 #define SUN_WEEK(t) (((t)->tm_yday + 7 - \
80 ((t)->tm_wday)) / 7)
81 #define MON_WEEK(t) (((t)->tm_yday + 7 - \
82 ((t)->tm_wday ? (t)->tm_wday - 1 : 6)) / 7)
83 static size_t
84 _fmt(format, t)
85 register const char *format;
86 const struct tm *t;
87 {
88 for (; *format; ++format) {
89 if (*format == '%') {
90 ++format;
91 if (*format == 'E') {
92 /* Alternate Era */
93 ++format;
94 } else if (*format == 'O') {
95 /* Alternate numeric symbols */
96 ++format;
97 }
98 switch (*format) {
99 case '\0':
100 --format;
101 break;
102 case 'A':
103 if (t->tm_wday < 0 || t->tm_wday > 6)
104 return (0);
105 if (!_add(_CurrentTimeLocale->day[t->tm_wday]))
106 return (0);
107 continue;
108 case 'a':
109 if (t->tm_wday < 0 || t->tm_wday > 6)
110 return (0);
111 if (!_add(_CurrentTimeLocale->abday[t->tm_wday]))
112 return (0);
113 continue;
114 case 'B':
115 if (t->tm_mon < 0 || t->tm_mon > 11)
116 return (0);
117 if (!_add(_CurrentTimeLocale->mon[t->tm_mon]))
118 return (0);
119 continue;
120 case 'b':
121 case 'h':
122 if (t->tm_mon < 0 || t->tm_mon > 11)
123 return (0);
124 if (!_add(_CurrentTimeLocale->abmon[t->tm_mon]))
125 return (0);
126 continue;
127 case 'C':
128 if (!_conv((t->tm_year + TM_YEAR_BASE) / 100,
129 2, '0'))
130 return (0);
131 continue;
132 case 'c':
133 if (!_fmt(_CurrentTimeLocale->d_t_fmt, t))
134 return (0);
135 continue;
136 case 'D':
137 if (!_fmt("%m/%d/%y", t))
138 return (0);
139 continue;
140 case 'd':
141 if (!_conv(t->tm_mday, 2, '0'))
142 return (0);
143 continue;
144 case 'e':
145 if (!_conv(t->tm_mday, 2, ' '))
146 return (0);
147 continue;
148 case 'H':
149 if (!_conv(t->tm_hour, 2, '0'))
150 return (0);
151 continue;
152 case 'I':
153 if (!_conv(t->tm_hour % 12 ?
154 t->tm_hour % 12 : 12, 2, '0'))
155 return (0);
156 continue;
157 case 'j':
158 if (!_conv(t->tm_yday + 1, 3, '0'))
159 return (0);
160 continue;
161 case 'k':
162 if (!_conv(t->tm_hour, 2, ' '))
163 return (0);
164 continue;
165 case 'l':
166 if (!_conv(t->tm_hour % 12 ?
167 t->tm_hour % 12: 12, 2, ' '))
168 return (0);
169 continue;
170 case 'M':
171 if (!_conv(t->tm_min, 2, '0'))
172 return (0);
173 continue;
174 case 'm':
175 if (!_conv(t->tm_mon + 1, 2, '0'))
176 return (0);
177 continue;
178 case 'n':
179 if (!_add("\n"))
180 return (0);
181 continue;
182 case 'p':
183 if (!_add(_CurrentTimeLocale->am_pm[t->tm_hour >= 12]))
184 return (0);
185 continue;
186 case 'R':
187 if (!_fmt("%H:%M", t))
188 return (0);
189 continue;
190 case 'r':
191 if (!_fmt(_CurrentTimeLocale->t_fmt_ampm, t))
192 return (0);
193 continue;
194 case 'S':
195 if (!_conv(t->tm_sec, 2, '0'))
196 return (0);
197 continue;
198 case 's':
199 if (!_secs(t))
200 return (0);
201 continue;
202 case 'T':
203 if (!_fmt("%H:%M:%S", t))
204 return (0);
205 continue;
206 case 't':
207 if (!_add("\t"))
208 return (0);
209 continue;
210 case 'U':
211 if (!_conv(SUN_WEEK(t), 2, '0'))
212 return (0);
213 continue;
214 case 'u':
215 if (!_conv(t->tm_wday ? t->tm_wday : 7, 1, '0'))
216 return (0);
217 continue;
218 case 'V':
219 {
220 /* ISO 8601 Week Of Year:
221 * If the week (Monday - Sunday) containing
222 * January 1 has four or more days in the new
223 * year, then it is week 1; otherwise it is
224 * week 53 of the previous year and the next
225 * week is week one.
226 */
227
228 int week = MON_WEEK(t);
229
230 int days = (((t)->tm_yday + 7 -
231 ((t)->tm_wday ? (t)->tm_wday - 1 : 6)) % 7);
232
233
234 if (days >= 4) {
235 week++;
236 } else if (week == 0) {
237 week = 53;
238 }
239
240 if (!_conv(week, 2, '0'))
241 return (0);
242 continue;
243 }
244 case 'W':
245 if (!_conv(MON_WEEK(t), 2, '0'))
246 return (0);
247 continue;
248 case 'w':
249 if (!_conv(t->tm_wday, 1, '0'))
250 return (0);
251 continue;
252 case 'x':
253 if (!_fmt(_CurrentTimeLocale->d_fmt, t))
254 return (0);
255 continue;
256 case 'X':
257 if (!_fmt(_CurrentTimeLocale->t_fmt, t))
258 return (0);
259 continue;
260 case 'y':
261 if (!_conv((t->tm_year + TM_YEAR_BASE) % 100,
262 2, '0'))
263 return (0);
264 continue;
265 case 'Y':
266 if (!_conv((t->tm_year + TM_YEAR_BASE), 4, '0'))
267 return (0);
268 continue;
269 case 'Z':
270 if (tzname[t->tm_isdst ? 1 : 0] &&
271 !_add(tzname[t->tm_isdst ? 1 : 0]))
272 return (0);
273 continue;
274 case '%':
275 /*
276 * X311J/88-090 (4.12.3.5): if conversion char is
277 * undefined, behavior is undefined. Print out the
278 * character itself as printf(3) does.
279 */
280 default:
281 break;
282 }
283 }
284 if (!gsize--)
285 return (0);
286 *pt++ = *format;
287 }
288 return (gsize);
289 }
290
291 static int
292 _secs(t)
293 const struct tm *t;
294 {
295 static char buf[15];
296 register time_t s;
297 register char *p;
298 struct tm tmp;
299
300 /* Make a copy, mktime(3) modifies the tm struct. */
301 tmp = *t;
302 s = mktime(&tmp);
303 for (p = buf + sizeof(buf) - 2; s > 0 && p > buf; s /= 10)
304 *p-- = s % 10 + '0';
305 return (_add(++p));
306 }
307
308 static int
309 _conv(n, digits, pad)
310 int n, digits;
311 char pad;
312 {
313 static char buf[10];
314 register char *p;
315
316 for (p = buf + sizeof(buf) - 2; n > 0 && p > buf; n /= 10, --digits)
317 *p-- = n % 10 + '0';
318 while (p > buf && digits-- > 0)
319 *p-- = pad;
320 return (_add(++p));
321 }
322
323 static int
324 _add(str)
325 register const char *str;
326 {
327 for (;; ++pt, --gsize) {
328 if (!gsize)
329 return (0);
330 if (!(*pt = *str++))
331 return (1);
332 }
333 }
334