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