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