1 /*        $NetBSD: strftime.c,v 1.2 2017/01/28 21:31:50 christos Exp $          */
2 
3 /*
4  * Copyright (c) 1999 - 2002 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of KTH nor the names of its contributors may be
20  *    used to endorse or promote products derived from this software without
21  *    specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
24  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
30  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
32  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
34 
35 #include <config.h>
36 #include <krb5/roken.h>
37 #ifdef TEST_STRPFTIME
38 #include "strpftime-test.h"
39 #endif
40 
41 static const char *abb_weekdays[] = {
42     "Sun",
43     "Mon",
44     "Tue",
45     "Wed",
46     "Thu",
47     "Fri",
48     "Sat",
49 };
50 
51 static const char *full_weekdays[] = {
52     "Sunday",
53     "Monday",
54     "Tuesday",
55     "Wednesday",
56     "Thursday",
57     "Friday",
58     "Saturday",
59 };
60 
61 static const char *abb_month[] = {
62     "Jan",
63     "Feb",
64     "Mar",
65     "Apr",
66     "May",
67     "Jun",
68     "Jul",
69     "Aug",
70     "Sep",
71     "Oct",
72     "Nov",
73     "Dec"
74 };
75 
76 static const char *full_month[] = {
77     "January",
78     "February",
79     "Mars",
80     "April",
81     "May",
82     "June",
83     "July",
84     "August",
85     "September",
86     "October",
87     "November",
88     "December"
89 };
90 
91 static const char *ampm[] = {
92     "AM",
93     "PM"
94 };
95 
96 /*
97  * Convert hour in [0, 24] to [12 1 - 11 12 1 - 11 12]
98  */
99 
100 static int
hour_24to12(int hour)101 hour_24to12 (int hour)
102 {
103     int ret = hour % 12;
104 
105     if (ret == 0)
106           ret = 12;
107     return ret;
108 }
109 
110 /*
111  * Return AM or PM for `hour'
112  */
113 
114 static const char *
hour_to_ampm(int hour)115 hour_to_ampm (int hour)
116 {
117     return ampm[hour / 12];
118 }
119 
120 /*
121  * Return the week number of `tm' (Sunday being the first day of the week)
122  * as [0, 53]
123  */
124 
125 static int
week_number_sun(const struct tm * tm)126 week_number_sun (const struct tm *tm)
127 {
128     return (tm->tm_yday + 7 - (tm->tm_yday % 7 - tm->tm_wday + 7) % 7) / 7;
129 }
130 
131 /*
132  * Return the week number of `tm' (Monday being the first day of the week)
133  * as [0, 53]
134  */
135 
136 static int
week_number_mon(const struct tm * tm)137 week_number_mon (const struct tm *tm)
138 {
139     int wday = (tm->tm_wday + 6) % 7;
140 
141     return (tm->tm_yday + 7 - (tm->tm_yday % 7 - wday + 7) % 7) / 7;
142 }
143 
144 /*
145  * Return the week number of `tm' (Monday being the first day of the
146  * week) as [01, 53].  Week number one is the one that has four or more
147  * days in that year.
148  */
149 
150 static int
week_number_mon4(const struct tm * tm)151 week_number_mon4 (const struct tm *tm)
152 {
153     int wday  = (tm->tm_wday + 6) % 7;
154     int w1day = (wday - tm->tm_yday % 7 + 7) % 7;
155     int ret;
156 
157     ret = (tm->tm_yday + w1day) / 7;
158     if (w1day >= 4)
159           --ret;
160     if (ret == -1)
161           ret = 53;
162     else
163           ++ret;
164     return ret;
165 }
166 
167 /*
168  *
169  */
170 
171 ROKEN_LIB_FUNCTION size_t ROKEN_LIB_CALL
strftime(char * buf,size_t maxsize,const char * format,const struct tm * tm)172 strftime (char *buf, size_t maxsize, const char *format,
173             const struct tm *tm)
174 {
175     size_t n = 0;
176     int ret;
177 
178     while (*format != '\0' && n < maxsize) {
179           if (*format == '%') {
180               ++format;
181               if(*format == 'E' || *format == 'O')
182                     ++format;
183               switch (*format) {
184               case 'a' :
185                     ret = snprintf (buf, maxsize - n,
186                                         "%s", abb_weekdays[tm->tm_wday]);
187                     break;
188               case 'A' :
189                     ret = snprintf (buf, maxsize - n,
190                                         "%s", full_weekdays[tm->tm_wday]);
191                     break;
192               case 'h' :
193               case 'b' :
194                     ret = snprintf (buf, maxsize - n,
195                                         "%s", abb_month[tm->tm_mon]);
196                     break;
197               case 'B' :
198                     ret = snprintf (buf, maxsize - n,
199                                         "%s", full_month[tm->tm_mon]);
200                     break;
201               case 'c' :
202                     ret = snprintf (buf, maxsize - n,
203                                         "%d:%02d:%02d %02d:%02d:%02d",
204                                         tm->tm_year,
205                                         tm->tm_mon + 1,
206                                         tm->tm_mday,
207                                         tm->tm_hour,
208                                         tm->tm_min,
209                                         tm->tm_sec);
210                     break;
211               case 'C' :
212                     ret = snprintf (buf, maxsize - n,
213                                         "%02d", (tm->tm_year + 1900) / 100);
214                     break;
215               case 'd' :
216                     ret = snprintf (buf, maxsize - n,
217                                         "%02d", tm->tm_mday);
218                     break;
219               case 'D' :
220                     ret = snprintf (buf, maxsize - n,
221                                         "%02d/%02d/%02d",
222                                         tm->tm_mon + 1,
223                                         tm->tm_mday,
224                                         (tm->tm_year + 1900) % 100);
225                     break;
226               case 'e' :
227                     ret = snprintf (buf, maxsize - n,
228                                         "%2d", tm->tm_mday);
229                     break;
230               case 'F':
231                     ret = snprintf (buf, maxsize - n,
232                                         "%04d-%02d-%02d", tm->tm_year + 1900,
233                                         tm->tm_mon + 1, tm->tm_mday);
234                     break;
235               case 'g':
236                     /* last two digits of week-based year */
237                     abort();
238               case 'G':
239                     /* week-based year */
240                     abort();
241               case 'H' :
242                     ret = snprintf (buf, maxsize - n,
243                                         "%02d", tm->tm_hour);
244                     break;
245               case 'I' :
246                     ret = snprintf (buf, maxsize - n,
247                                         "%02d",
248                                         hour_24to12 (tm->tm_hour));
249                     break;
250               case 'j' :
251                     ret = snprintf (buf, maxsize - n,
252                                         "%03d", tm->tm_yday + 1);
253                     break;
254               case 'k' :
255                     ret = snprintf (buf, maxsize - n,
256                                         "%2d", tm->tm_hour);
257                     break;
258               case 'l' :
259                     ret = snprintf (buf, maxsize - n,
260                                         "%2d",
261                                         hour_24to12 (tm->tm_hour));
262                     break;
263               case 'm' :
264                     ret = snprintf (buf, maxsize - n,
265                                         "%02d", tm->tm_mon + 1);
266                     break;
267               case 'M' :
268                     ret = snprintf (buf, maxsize - n,
269                                         "%02d", tm->tm_min);
270                     break;
271               case 'n' :
272                     ret = snprintf (buf, maxsize - n, "\n");
273                     break;
274               case 'p' :
275                     ret = snprintf (buf, maxsize - n, "%s",
276                                         hour_to_ampm (tm->tm_hour));
277                     break;
278               case 'r' :
279                     ret = snprintf (buf, maxsize - n,
280                                         "%02d:%02d:%02d %s",
281                                         hour_24to12 (tm->tm_hour),
282                                         tm->tm_min,
283                                         tm->tm_sec,
284                                         hour_to_ampm (tm->tm_hour));
285                     break;
286               case 'R' :
287                     ret = snprintf (buf, maxsize - n,
288                                         "%02d:%02d",
289                                         tm->tm_hour,
290                                         tm->tm_min);
291                     break;
292               case 's' :
293                     ret = snprintf (buf, maxsize - n,
294                                         "%d", (int)mktime(rk_UNCONST(tm)));
295                     break;
296               case 'S' :
297                     ret = snprintf (buf, maxsize - n,
298                                         "%02d", tm->tm_sec);
299                     break;
300               case 't' :
301                     ret = snprintf (buf, maxsize - n, "\t");
302                     break;
303               case 'T' :
304               case 'X' :
305                     ret = snprintf (buf, maxsize - n,
306                                         "%02d:%02d:%02d",
307                                         tm->tm_hour,
308                                         tm->tm_min,
309                                         tm->tm_sec);
310                     break;
311               case 'u' :
312                     ret = snprintf (buf, maxsize - n,
313                                         "%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday);
314                     break;
315               case 'U' :
316                     ret = snprintf (buf, maxsize - n,
317                                         "%02d", week_number_sun (tm));
318                     break;
319               case 'V' :
320                     ret = snprintf (buf, maxsize - n,
321                                         "%02d", week_number_mon4 (tm));
322                     break;
323               case 'w' :
324                     ret = snprintf (buf, maxsize - n,
325                                         "%d", tm->tm_wday);
326                     break;
327               case 'W' :
328                     ret = snprintf (buf, maxsize - n,
329                                         "%02d", week_number_mon (tm));
330                     break;
331               case 'x' :
332                     ret = snprintf (buf, maxsize - n,
333                                         "%d:%02d:%02d",
334                                         tm->tm_year,
335                                         tm->tm_mon + 1,
336                                         tm->tm_mday);
337                     break;
338               case 'y' :
339                     ret = snprintf (buf, maxsize - n,
340                                         "%02d", (tm->tm_year + 1900) % 100);
341                     break;
342               case 'Y' :
343                     ret = snprintf (buf, maxsize - n,
344                                         "%d", tm->tm_year + 1900);
345                     break;
346               case 'z':
347                     ret = snprintf (buf, maxsize - n,
348                                         "%ld",
349 #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
350                                         (long)tm->tm_gmtoff
351 #elif defined(HAVE_TIMEZONE)
352 #ifdef HAVE_ALTZONE
353                                         tm->tm_isdst ?
354                                         (long)altzone :
355 #endif
356                                         (long)timezone
357 #else
358 #error Where in timezone chaos are you?
359 #endif
360                                         );
361                     break;
362               case 'Z' :
363                     ret = snprintf (buf, maxsize - n,
364                                         "%s",
365 
366 #if defined(HAVE_STRUCT_TM_TM_ZONE)
367                                         tm->tm_zone
368 #elif defined(HAVE_TIMEZONE)
369                                         tzname[tm->tm_isdst]
370 #else
371 #error what?
372 #endif
373                         );
374                     break;
375               case '\0' :
376                     --format;
377                     /* FALLTHROUGH */
378               case '%' :
379                     ret = snprintf (buf, maxsize - n,
380                                         "%%");
381                     break;
382               default :
383                     ret = snprintf (buf, maxsize - n,
384                                         "%%%c", *format);
385                     break;
386               }
387               if (ret < 0 || ret >= (int)(maxsize - n))
388                     return 0;
389               n   += ret;
390               buf += ret;
391               ++format;
392           } else {
393               *buf++ = *format++;
394               ++n;
395           }
396     }
397     *buf = '\0';
398     return n;
399 }
400