1*3b6c3722Schristos /** strptime workaround (for oa macos leopard)
2*3b6c3722Schristos * This strptime follows the man strptime (2001-11-12)
3*3b6c3722Schristos * conforming to SUSv2, POSIX.1-2001
4*3b6c3722Schristos *
5*3b6c3722Schristos * This very simple version of strptime has no:
6*3b6c3722Schristos * - E alternatives
7*3b6c3722Schristos * - O alternatives
8*3b6c3722Schristos * - Glibc additions
9*3b6c3722Schristos * - Does not process week numbers
10*3b6c3722Schristos * - Does not properly processes year day
11*3b6c3722Schristos *
12*3b6c3722Schristos * LICENSE
13*3b6c3722Schristos * Copyright (c) 2008, NLnet Labs, Matthijs Mekking
14*3b6c3722Schristos * All rights reserved.
15*3b6c3722Schristos *
16*3b6c3722Schristos * Redistribution and use in source and binary forms, with or without
17*3b6c3722Schristos * modification, are permitted provided that the following conditions are met:
18*3b6c3722Schristos * * Redistributions of source code must retain the above copyright notice,
19*3b6c3722Schristos * this list of conditions and the following disclaimer.
20*3b6c3722Schristos * * Redistributions in binary form must reproduce the above copyright
21*3b6c3722Schristos * notice, this list of conditions and the following disclaimer in the
22*3b6c3722Schristos * documentation and/or other materials provided with the distribution.
23*3b6c3722Schristos * * Neither the name of NLnetLabs nor the names of its
24*3b6c3722Schristos * contributors may be used to endorse or promote products derived from this
25*3b6c3722Schristos * software without specific prior written permission.
26*3b6c3722Schristos *
27*3b6c3722Schristos * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28*3b6c3722Schristos * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29*3b6c3722Schristos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30*3b6c3722Schristos * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
31*3b6c3722Schristos * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32*3b6c3722Schristos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33*3b6c3722Schristos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34*3b6c3722Schristos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35*3b6c3722Schristos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36*3b6c3722Schristos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37*3b6c3722Schristos * POSSIBILITY OF SUCH DAMAGE.
38*3b6c3722Schristos **/
39*3b6c3722Schristos
40*3b6c3722Schristos #include "config.h"
41*3b6c3722Schristos
42*3b6c3722Schristos #ifndef HAVE_CONFIG_H
43*3b6c3722Schristos #include <time.h>
44*3b6c3722Schristos #endif
45*3b6c3722Schristos
46*3b6c3722Schristos #ifndef STRPTIME_WORKS
47*3b6c3722Schristos
48*3b6c3722Schristos #define TM_YEAR_BASE 1900
49*3b6c3722Schristos
50*3b6c3722Schristos #include <ctype.h>
51*3b6c3722Schristos #include <string.h>
52*3b6c3722Schristos
53*3b6c3722Schristos static const char *abb_weekdays[] = {
54*3b6c3722Schristos "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
55*3b6c3722Schristos };
56*3b6c3722Schristos static const char *full_weekdays[] = {
57*3b6c3722Schristos "Sunday", "Monday", "Tuesday", "Wednesday",
58*3b6c3722Schristos "Thursday", "Friday", "Saturday", NULL
59*3b6c3722Schristos };
60*3b6c3722Schristos static const char *abb_months[] = {
61*3b6c3722Schristos "Jan", "Feb", "Mar", "Apr", "May", "Jun",
62*3b6c3722Schristos "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
63*3b6c3722Schristos };
64*3b6c3722Schristos static const char *full_months[] = {
65*3b6c3722Schristos "January", "February", "March", "April", "May", "June",
66*3b6c3722Schristos "July", "August", "September", "October", "November", "December", NULL
67*3b6c3722Schristos };
68*3b6c3722Schristos static const char *ampm[] = {
69*3b6c3722Schristos "am", "pm", NULL
70*3b6c3722Schristos };
71*3b6c3722Schristos
72*3b6c3722Schristos static int
match_string(const char ** buf,const char ** strs)73*3b6c3722Schristos match_string(const char **buf, const char **strs)
74*3b6c3722Schristos {
75*3b6c3722Schristos int i = 0;
76*3b6c3722Schristos
77*3b6c3722Schristos for (i = 0; strs[i] != NULL; i++) {
78*3b6c3722Schristos int len = strlen(strs[i]);
79*3b6c3722Schristos if (strncasecmp (*buf, strs[i], len) == 0) {
80*3b6c3722Schristos *buf += len;
81*3b6c3722Schristos return i;
82*3b6c3722Schristos }
83*3b6c3722Schristos }
84*3b6c3722Schristos return -1;
85*3b6c3722Schristos }
86*3b6c3722Schristos
87*3b6c3722Schristos static int
str2int(const char ** buf,int max)88*3b6c3722Schristos str2int(const char **buf, int max)
89*3b6c3722Schristos {
90*3b6c3722Schristos int ret=0, count=0;
91*3b6c3722Schristos
92*3b6c3722Schristos while (*buf[0] != '\0' && isdigit((unsigned char)*buf[0]) && count<max) {
93*3b6c3722Schristos ret = ret*10 + (*buf[0] - '0');
94*3b6c3722Schristos (*buf)++;
95*3b6c3722Schristos count++;
96*3b6c3722Schristos }
97*3b6c3722Schristos
98*3b6c3722Schristos if (!count)
99*3b6c3722Schristos return -1;
100*3b6c3722Schristos return ret;
101*3b6c3722Schristos }
102*3b6c3722Schristos
103*3b6c3722Schristos /** Converts the character string s to values which are stored in tm
104*3b6c3722Schristos * using the format specified by format
105*3b6c3722Schristos **/
106*3b6c3722Schristos char *
unbound_strptime(const char * s,const char * format,struct tm * tm)107*3b6c3722Schristos unbound_strptime(const char *s, const char *format, struct tm *tm)
108*3b6c3722Schristos {
109*3b6c3722Schristos int c, ret;
110*3b6c3722Schristos int split_year = 0;
111*3b6c3722Schristos
112*3b6c3722Schristos while ((c = *format) != '\0') {
113*3b6c3722Schristos /* whitespace, literal or format */
114*3b6c3722Schristos if (isspace((unsigned char)c)) { /* whitespace */
115*3b6c3722Schristos /** whitespace matches zero or more whitespace characters in the
116*3b6c3722Schristos * input string.
117*3b6c3722Schristos **/
118*3b6c3722Schristos while (isspace((unsigned char)*s))
119*3b6c3722Schristos s++;
120*3b6c3722Schristos }
121*3b6c3722Schristos else if (c == '%') { /* format */
122*3b6c3722Schristos format++;
123*3b6c3722Schristos c = *format;
124*3b6c3722Schristos switch (c) {
125*3b6c3722Schristos case '%': /* %% is converted to % */
126*3b6c3722Schristos if (*s != c) {
127*3b6c3722Schristos return NULL;
128*3b6c3722Schristos }
129*3b6c3722Schristos s++;
130*3b6c3722Schristos break;
131*3b6c3722Schristos case 'a': /* weekday name, abbreviated or full */
132*3b6c3722Schristos case 'A':
133*3b6c3722Schristos ret = match_string(&s, full_weekdays);
134*3b6c3722Schristos if (ret < 0)
135*3b6c3722Schristos ret = match_string(&s, abb_weekdays);
136*3b6c3722Schristos if (ret < 0) {
137*3b6c3722Schristos return NULL;
138*3b6c3722Schristos }
139*3b6c3722Schristos tm->tm_wday = ret;
140*3b6c3722Schristos break;
141*3b6c3722Schristos case 'b': /* month name, abbreviated or full */
142*3b6c3722Schristos case 'B':
143*3b6c3722Schristos case 'h':
144*3b6c3722Schristos ret = match_string(&s, full_months);
145*3b6c3722Schristos if (ret < 0)
146*3b6c3722Schristos ret = match_string(&s, abb_months);
147*3b6c3722Schristos if (ret < 0) {
148*3b6c3722Schristos return NULL;
149*3b6c3722Schristos }
150*3b6c3722Schristos tm->tm_mon = ret;
151*3b6c3722Schristos break;
152*3b6c3722Schristos case 'c': /* date and time representation */
153*3b6c3722Schristos if (!(s = unbound_strptime(s, "%x %X", tm))) {
154*3b6c3722Schristos return NULL;
155*3b6c3722Schristos }
156*3b6c3722Schristos break;
157*3b6c3722Schristos case 'C': /* century number */
158*3b6c3722Schristos ret = str2int(&s, 2);
159*3b6c3722Schristos if (ret < 0 || ret > 99) { /* must be in [00,99] */
160*3b6c3722Schristos return NULL;
161*3b6c3722Schristos }
162*3b6c3722Schristos
163*3b6c3722Schristos if (split_year) {
164*3b6c3722Schristos tm->tm_year = ret*100 + (tm->tm_year%100);
165*3b6c3722Schristos }
166*3b6c3722Schristos else {
167*3b6c3722Schristos tm->tm_year = ret*100 - TM_YEAR_BASE;
168*3b6c3722Schristos split_year = 1;
169*3b6c3722Schristos }
170*3b6c3722Schristos break;
171*3b6c3722Schristos case 'd': /* day of month */
172*3b6c3722Schristos case 'e':
173*3b6c3722Schristos ret = str2int(&s, 2);
174*3b6c3722Schristos if (ret < 1 || ret > 31) { /* must be in [01,31] */
175*3b6c3722Schristos return NULL;
176*3b6c3722Schristos }
177*3b6c3722Schristos tm->tm_mday = ret;
178*3b6c3722Schristos break;
179*3b6c3722Schristos case 'D': /* equivalent to %m/%d/%y */
180*3b6c3722Schristos if (!(s = unbound_strptime(s, "%m/%d/%y", tm))) {
181*3b6c3722Schristos return NULL;
182*3b6c3722Schristos }
183*3b6c3722Schristos break;
184*3b6c3722Schristos case 'H': /* hour */
185*3b6c3722Schristos ret = str2int(&s, 2);
186*3b6c3722Schristos if (ret < 0 || ret > 23) { /* must be in [00,23] */
187*3b6c3722Schristos return NULL;
188*3b6c3722Schristos }
189*3b6c3722Schristos tm->tm_hour = ret;
190*3b6c3722Schristos break;
191*3b6c3722Schristos case 'I': /* 12hr clock hour */
192*3b6c3722Schristos ret = str2int(&s, 2);
193*3b6c3722Schristos if (ret < 1 || ret > 12) { /* must be in [01,12] */
194*3b6c3722Schristos return NULL;
195*3b6c3722Schristos }
196*3b6c3722Schristos if (ret == 12) /* actually [0,11] */
197*3b6c3722Schristos ret = 0;
198*3b6c3722Schristos tm->tm_hour = ret;
199*3b6c3722Schristos break;
200*3b6c3722Schristos case 'j': /* day of year */
201*3b6c3722Schristos ret = str2int(&s, 2);
202*3b6c3722Schristos if (ret < 1 || ret > 366) { /* must be in [001,366] */
203*3b6c3722Schristos return NULL;
204*3b6c3722Schristos }
205*3b6c3722Schristos tm->tm_yday = ret;
206*3b6c3722Schristos break;
207*3b6c3722Schristos case 'm': /* month */
208*3b6c3722Schristos ret = str2int(&s, 2);
209*3b6c3722Schristos if (ret < 1 || ret > 12) { /* must be in [01,12] */
210*3b6c3722Schristos return NULL;
211*3b6c3722Schristos }
212*3b6c3722Schristos /* months go from 0-11 */
213*3b6c3722Schristos tm->tm_mon = (ret-1);
214*3b6c3722Schristos break;
215*3b6c3722Schristos case 'M': /* minute */
216*3b6c3722Schristos ret = str2int(&s, 2);
217*3b6c3722Schristos if (ret < 0 || ret > 59) { /* must be in [00,59] */
218*3b6c3722Schristos return NULL;
219*3b6c3722Schristos }
220*3b6c3722Schristos tm->tm_min = ret;
221*3b6c3722Schristos break;
222*3b6c3722Schristos case 'n': /* arbitrary whitespace */
223*3b6c3722Schristos case 't':
224*3b6c3722Schristos while (isspace((unsigned char)*s))
225*3b6c3722Schristos s++;
226*3b6c3722Schristos break;
227*3b6c3722Schristos case 'p': /* am pm */
228*3b6c3722Schristos ret = match_string(&s, ampm);
229*3b6c3722Schristos if (ret < 0) {
230*3b6c3722Schristos return NULL;
231*3b6c3722Schristos }
232*3b6c3722Schristos if (tm->tm_hour < 0 || tm->tm_hour > 11) { /* %I */
233*3b6c3722Schristos return NULL;
234*3b6c3722Schristos }
235*3b6c3722Schristos
236*3b6c3722Schristos if (ret == 1) /* pm */
237*3b6c3722Schristos tm->tm_hour += 12;
238*3b6c3722Schristos break;
239*3b6c3722Schristos case 'r': /* equivalent of %I:%M:%S %p */
240*3b6c3722Schristos if (!(s = unbound_strptime(s, "%I:%M:%S %p", tm))) {
241*3b6c3722Schristos return NULL;
242*3b6c3722Schristos }
243*3b6c3722Schristos break;
244*3b6c3722Schristos case 'R': /* equivalent of %H:%M */
245*3b6c3722Schristos if (!(s = unbound_strptime(s, "%H:%M", tm))) {
246*3b6c3722Schristos return NULL;
247*3b6c3722Schristos }
248*3b6c3722Schristos break;
249*3b6c3722Schristos case 'S': /* seconds */
250*3b6c3722Schristos ret = str2int(&s, 2);
251*3b6c3722Schristos /* 60 may occur for leap seconds */
252*3b6c3722Schristos /* earlier 61 was also allowed */
253*3b6c3722Schristos if (ret < 0 || ret > 60) { /* must be in [00,60] */
254*3b6c3722Schristos return NULL;
255*3b6c3722Schristos }
256*3b6c3722Schristos tm->tm_sec = ret;
257*3b6c3722Schristos break;
258*3b6c3722Schristos case 'T': /* equivalent of %H:%M:%S */
259*3b6c3722Schristos if (!(s = unbound_strptime(s, "%H:%M:%S", tm))) {
260*3b6c3722Schristos return NULL;
261*3b6c3722Schristos }
262*3b6c3722Schristos break;
263*3b6c3722Schristos case 'U': /* week number, with the first Sun of Jan being w1 */
264*3b6c3722Schristos ret = str2int(&s, 2);
265*3b6c3722Schristos if (ret < 0 || ret > 53) { /* must be in [00,53] */
266*3b6c3722Schristos return NULL;
267*3b6c3722Schristos }
268*3b6c3722Schristos /** it is hard (and not necessary for nsd) to determine time
269*3b6c3722Schristos * data from week number.
270*3b6c3722Schristos **/
271*3b6c3722Schristos break;
272*3b6c3722Schristos case 'w': /* day of week */
273*3b6c3722Schristos ret = str2int(&s, 1);
274*3b6c3722Schristos if (ret < 0 || ret > 6) { /* must be in [0,6] */
275*3b6c3722Schristos return NULL;
276*3b6c3722Schristos }
277*3b6c3722Schristos tm->tm_wday = ret;
278*3b6c3722Schristos break;
279*3b6c3722Schristos case 'W': /* week number, with the first Mon of Jan being w1 */
280*3b6c3722Schristos ret = str2int(&s, 2);
281*3b6c3722Schristos if (ret < 0 || ret > 53) { /* must be in [00,53] */
282*3b6c3722Schristos return NULL;
283*3b6c3722Schristos }
284*3b6c3722Schristos /** it is hard (and not necessary for nsd) to determine time
285*3b6c3722Schristos * data from week number.
286*3b6c3722Schristos **/
287*3b6c3722Schristos break;
288*3b6c3722Schristos case 'x': /* date format */
289*3b6c3722Schristos if (!(s = unbound_strptime(s, "%m/%d/%y", tm))) {
290*3b6c3722Schristos return NULL;
291*3b6c3722Schristos }
292*3b6c3722Schristos break;
293*3b6c3722Schristos case 'X': /* time format */
294*3b6c3722Schristos if (!(s = unbound_strptime(s, "%H:%M:%S", tm))) {
295*3b6c3722Schristos return NULL;
296*3b6c3722Schristos }
297*3b6c3722Schristos break;
298*3b6c3722Schristos case 'y': /* last two digits of a year */
299*3b6c3722Schristos ret = str2int(&s, 2);
300*3b6c3722Schristos if (ret < 0 || ret > 99) { /* must be in [00,99] */
301*3b6c3722Schristos return NULL;
302*3b6c3722Schristos }
303*3b6c3722Schristos if (split_year) {
304*3b6c3722Schristos tm->tm_year = ((tm->tm_year/100) * 100) + ret;
305*3b6c3722Schristos }
306*3b6c3722Schristos else {
307*3b6c3722Schristos split_year = 1;
308*3b6c3722Schristos
309*3b6c3722Schristos /** currently:
310*3b6c3722Schristos * if in [0,68] we are in 21th century,
311*3b6c3722Schristos * if in [69,99] we are in 20th century.
312*3b6c3722Schristos **/
313*3b6c3722Schristos if (ret < 69) /* 2000 */
314*3b6c3722Schristos ret += 100;
315*3b6c3722Schristos tm->tm_year = ret;
316*3b6c3722Schristos }
317*3b6c3722Schristos break;
318*3b6c3722Schristos case 'Y': /* year */
319*3b6c3722Schristos ret = str2int(&s, 4);
320*3b6c3722Schristos if (ret < 0 || ret > 9999) {
321*3b6c3722Schristos return NULL;
322*3b6c3722Schristos }
323*3b6c3722Schristos tm->tm_year = ret - TM_YEAR_BASE;
324*3b6c3722Schristos break;
325*3b6c3722Schristos case '\0':
326*3b6c3722Schristos default: /* unsupported, cannot match format */
327*3b6c3722Schristos return NULL;
328*3b6c3722Schristos break;
329*3b6c3722Schristos }
330*3b6c3722Schristos }
331*3b6c3722Schristos else { /* literal */
332*3b6c3722Schristos /* if input cannot match format, return NULL */
333*3b6c3722Schristos if (*s != c)
334*3b6c3722Schristos return NULL;
335*3b6c3722Schristos s++;
336*3b6c3722Schristos }
337*3b6c3722Schristos
338*3b6c3722Schristos format++;
339*3b6c3722Schristos }
340*3b6c3722Schristos
341*3b6c3722Schristos /* return pointer to remainder of s */
342*3b6c3722Schristos return (char*) s;
343*3b6c3722Schristos }
344*3b6c3722Schristos
345*3b6c3722Schristos #endif /* STRPTIME_WORKS */
346