Initial import from FreeBSD RELENG_4:
[games.git] / lib / libcr / stdtime / strptime.c
1 /*
2  * Powerdog Industries kindly requests feedback from anyone modifying
3  * this function:
4  *
5  * Date: Thu, 05 Jun 1997 23:17:17 -0400  
6  * From: Kevin Ruddy <kevin.ruddy@powerdog.com>
7  * To: James FitzGibbon <james@nexis.net>
8  * Subject: Re: Use of your strptime(3) code (fwd)
9  * 
10  * The reason for the "no mod" clause was so that modifications would
11  * come back and we could integrate them and reissue so that a wider 
12  * audience could use it (thereby spreading the wealth).  This has   
13  * made it possible to get strptime to work on many operating systems.
14  * I'm not sure why that's "plain unacceptable" to the FreeBSD team.
15  * 
16  * Anyway, you can change it to "with or without modification" as
17  * you see fit.  Enjoy.                                          
18  * 
19  * Kevin Ruddy
20  * Powerdog Industries, Inc.
21  */
22 /*
23  * Copyright (c) 1994 Powerdog Industries.  All rights reserved.
24  *
25  * Redistribution and use in source and binary forms, with or without
26  * modification, are permitted provided that the following conditions
27  * are met:
28  * 1. Redistributions of source code must retain the above copyright
29  *    notice, this list of conditions and the following disclaimer.
30  * 2. Redistributions in binary form must reproduce the above copyright
31  *    notice, this list of conditions and the following disclaimer
32  *    in the documentation and/or other materials provided with the
33  *    distribution.
34  * 3. All advertising materials mentioning features or use of this
35  *    software must display the following acknowledgement:
36  *      This product includes software developed by Powerdog Industries.
37  * 4. The name of Powerdog Industries may not be used to endorse or
38  *    promote products derived from this software without specific prior
39  *    written permission.
40  *
41  * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
42  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
44  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
45  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
46  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
47  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
48  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
49  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
50  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
51  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52  */
53
54 #ifdef LIBC_RCS
55 static const char rcsid[] =
56   "$FreeBSD: src/lib/libc/stdtime/strptime.c,v 1.17.2.3 2002/03/12 17:24:54 phantom Exp $";
57 #endif
58
59 #ifndef lint
60 #ifndef NOID
61 static char copyright[] =
62 "@(#) Copyright (c) 1994 Powerdog Industries.  All rights reserved.";
63 static char sccsid[] = "@(#)strptime.c  0.1 (Powerdog) 94/03/27";
64 #endif /* !defined NOID */
65 #endif /* not lint */
66
67 #include <time.h>
68 #include <ctype.h>
69 #include <limits.h>
70 #include <stdlib.h>
71 #include <string.h>
72 #ifdef  _THREAD_SAFE
73 #include <pthread.h>
74 #include "pthread_private.h"
75 #endif
76 #include "timelocal.h"
77
78 static char * _strptime(const char *, const char *, struct tm *);
79
80 #ifdef  _THREAD_SAFE
81 static struct pthread_mutex     _gotgmt_mutexd = PTHREAD_MUTEX_STATIC_INITIALIZER;
82 static pthread_mutex_t          gotgmt_mutex   = &_gotgmt_mutexd;
83 #endif
84 static int got_GMT;
85
86 #define asizeof(a)      (sizeof (a) / sizeof ((a)[0]))
87
88 static char *
89 _strptime(const char *buf, const char *fmt, struct tm *tm)
90 {
91         char    c;
92         const char *ptr;
93         int     i,
94                 len;
95         int Ealternative, Oalternative;
96         struct lc_time_T *tptr = __get_current_time_locale();
97
98         ptr = fmt;
99         while (*ptr != 0) {
100                 if (*buf == 0)
101                         break;
102
103                 c = *ptr++;
104
105                 if (c != '%') {
106                         if (isspace((unsigned char)c))
107                                 while (*buf != 0 && isspace((unsigned char)*buf))
108                                         buf++;
109                         else if (c != *buf++)
110                                 return 0;
111                         continue;
112                 }
113
114                 Ealternative = 0;
115                 Oalternative = 0;
116 label:
117                 c = *ptr++;
118                 switch (c) {
119                 case 0:
120                 case '%':
121                         if (*buf++ != '%')
122                                 return 0;
123                         break;
124
125                 case '+':
126                         buf = _strptime(buf, tptr->date_fmt, tm);
127                         if (buf == 0)
128                                 return 0;
129                         break;
130
131                 case 'C':
132                         if (!isdigit((unsigned char)*buf))
133                                 return 0;
134
135                         /* XXX This will break for 3-digit centuries. */
136                         len = 2;
137                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
138                                 i *= 10;
139                                 i += *buf - '0';
140                                 len--;
141                         }
142                         if (i < 19)
143                                 return 0;
144
145                         tm->tm_year = i * 100 - 1900;
146                         break;
147
148                 case 'c':
149                         buf = _strptime(buf, tptr->c_fmt, tm);
150                         if (buf == 0)
151                                 return 0;
152                         break;
153
154                 case 'D':
155                         buf = _strptime(buf, "%m/%d/%y", tm);
156                         if (buf == 0)
157                                 return 0;
158                         break;
159
160                 case 'E':
161                         if (Ealternative || Oalternative)
162                                 break;
163                         Ealternative++;
164                         goto label;
165
166                 case 'O':
167                         if (Ealternative || Oalternative)
168                                 break;
169                         Oalternative++;
170                         goto label;
171
172                 case 'F':
173                         buf = _strptime(buf, "%Y-%m-%d", tm);
174                         if (buf == 0)
175                                 return 0;
176                         break;
177
178                 case 'R':
179                         buf = _strptime(buf, "%H:%M", tm);
180                         if (buf == 0)
181                                 return 0;
182                         break;
183
184                 case 'r':
185                         buf = _strptime(buf, tptr->ampm_fmt, tm);
186                         if (buf == 0)
187                                 return 0;
188                         break;
189
190                 case 'T':
191                         buf = _strptime(buf, "%H:%M:%S", tm);
192                         if (buf == 0)
193                                 return 0;
194                         break;
195
196                 case 'X':
197                         buf = _strptime(buf, tptr->X_fmt, tm);
198                         if (buf == 0)
199                                 return 0;
200                         break;
201
202                 case 'x':
203                         buf = _strptime(buf, tptr->x_fmt, tm);
204                         if (buf == 0)
205                                 return 0;
206                         break;
207
208                 case 'j':
209                         if (!isdigit((unsigned char)*buf))
210                                 return 0;
211
212                         len = 3;
213                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
214                                 i *= 10;
215                                 i += *buf - '0';
216                                 len--;
217                         }
218                         if (i < 1 || i > 366)
219                                 return 0;
220
221                         tm->tm_yday = i - 1;
222                         break;
223
224                 case 'M':
225                 case 'S':
226                         if (*buf == 0 || isspace((unsigned char)*buf))
227                                 break;
228
229                         if (!isdigit((unsigned char)*buf))
230                                 return 0;
231
232                         len = 2;
233                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
234                                 i *= 10;
235                                 i += *buf - '0';
236                                 len--;
237                         }
238
239                         if (c == 'M') {
240                                 if (i > 59)
241                                         return 0;
242                                 tm->tm_min = i;
243                         } else {
244                                 if (i > 60)
245                                         return 0;
246                                 tm->tm_sec = i;
247                         }
248
249                         if (*buf != 0 && isspace((unsigned char)*buf))
250                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
251                                         ptr++;
252                         break;
253
254                 case 'H':
255                 case 'I':
256                 case 'k':
257                 case 'l':
258                         /*
259                          * Of these, %l is the only specifier explicitly
260                          * documented as not being zero-padded.  However,
261                          * there is no harm in allowing zero-padding.
262                          *
263                          * XXX The %l specifier may gobble one too many
264                          * digits if used incorrectly.
265                          */
266                         if (!isdigit((unsigned char)*buf))
267                                 return 0;
268
269                         len = 2;
270                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
271                                 i *= 10;
272                                 i += *buf - '0';
273                                 len--;
274                         }
275                         if (c == 'H' || c == 'k') {
276                                 if (i > 23)
277                                         return 0;
278                         } else if (i > 12)
279                                 return 0;
280
281                         tm->tm_hour = i;
282
283                         if (*buf != 0 && isspace((unsigned char)*buf))
284                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
285                                         ptr++;
286                         break;
287
288                 case 'p':
289                         /*
290                          * XXX This is bogus if parsed before hour-related
291                          * specifiers.
292                          */
293                         len = strlen(tptr->am);
294                         if (strncasecmp(buf, tptr->am, len) == 0) {
295                                 if (tm->tm_hour > 12)
296                                         return 0;
297                                 if (tm->tm_hour == 12)
298                                         tm->tm_hour = 0;
299                                 buf += len;
300                                 break;
301                         }
302
303                         len = strlen(tptr->pm);
304                         if (strncasecmp(buf, tptr->pm, len) == 0) {
305                                 if (tm->tm_hour > 12)
306                                         return 0;
307                                 if (tm->tm_hour != 12)
308                                         tm->tm_hour += 12;
309                                 buf += len;
310                                 break;
311                         }
312
313                         return 0;
314
315                 case 'A':
316                 case 'a':
317                         for (i = 0; i < asizeof(tptr->weekday); i++) {
318                                 len = strlen(tptr->weekday[i]);
319                                 if (strncasecmp(buf, tptr->weekday[i],
320                                                 len) == 0)
321                                         break;
322                                 len = strlen(tptr->wday[i]);
323                                 if (strncasecmp(buf, tptr->wday[i],
324                                                 len) == 0)
325                                         break;
326                         }
327                         if (i == asizeof(tptr->weekday))
328                                 return 0;
329
330                         tm->tm_wday = i;
331                         buf += len;
332                         break;
333
334                 case 'U':
335                 case 'W':
336                         /*
337                          * XXX This is bogus, as we can not assume any valid
338                          * information present in the tm structure at this
339                          * point to calculate a real value, so just check the
340                          * range for now.
341                          */
342                         if (!isdigit((unsigned char)*buf))
343                                 return 0;
344
345                         len = 2;
346                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
347                                 i *= 10;
348                                 i += *buf - '0';
349                                 len--;
350                         }
351                         if (i > 53)
352                                 return 0;
353
354                         if (*buf != 0 && isspace((unsigned char)*buf))
355                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
356                                         ptr++;
357                         break;
358
359                 case 'w':
360                         if (!isdigit((unsigned char)*buf))
361                                 return 0;
362
363                         i = *buf - '0';
364                         if (i > 6)
365                                 return 0;
366
367                         tm->tm_wday = i;
368
369                         if (*buf != 0 && isspace((unsigned char)*buf))
370                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
371                                         ptr++;
372                         break;
373
374                 case 'd':
375                 case 'e':
376                         /*
377                          * The %e specifier is explicitly documented as not
378                          * being zero-padded but there is no harm in allowing
379                          * such padding.
380                          *
381                          * XXX The %e specifier may gobble one too many
382                          * digits if used incorrectly.
383                          */
384                         if (!isdigit((unsigned char)*buf))
385                                 return 0;
386
387                         len = 2;
388                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
389                                 i *= 10;
390                                 i += *buf - '0';
391                                 len--;
392                         }
393                         if (i > 31)
394                                 return 0;
395
396                         tm->tm_mday = i;
397
398                         if (*buf != 0 && isspace((unsigned char)*buf))
399                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
400                                         ptr++;
401                         break;
402
403                 case 'B':
404                 case 'b':
405                 case 'h':
406                         for (i = 0; i < asizeof(tptr->month); i++) {
407                                 if (Oalternative) {
408                                         if (c == 'B') {
409                                                 len = strlen(tptr->alt_month[i]);
410                                                 if (strncasecmp(buf,
411                                                                 tptr->alt_month[i],
412                                                                 len) == 0)
413                                                         break;
414                                         }
415                                 } else {
416                                         len = strlen(tptr->month[i]);
417                                         if (strncasecmp(buf, tptr->month[i],
418                                                         len) == 0)
419                                                 break;
420                                         len = strlen(tptr->mon[i]);
421                                         if (strncasecmp(buf, tptr->mon[i],
422                                                         len) == 0)
423                                                 break;
424                                 }
425                         }
426                         if (i == asizeof(tptr->month))
427                                 return 0;
428
429                         tm->tm_mon = i;
430                         buf += len;
431                         break;
432
433                 case 'm':
434                         if (!isdigit((unsigned char)*buf))
435                                 return 0;
436
437                         len = 2;
438                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
439                                 i *= 10;
440                                 i += *buf - '0';
441                                 len--;
442                         }
443                         if (i < 1 || i > 12)
444                                 return 0;
445
446                         tm->tm_mon = i - 1;
447
448                         if (*buf != 0 && isspace((unsigned char)*buf))
449                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
450                                         ptr++;
451                         break;
452
453                 case 's':
454                         {
455                         char *cp;
456                         time_t t;
457
458                         t = strtol(buf, &cp, 10);
459                         if (t == LONG_MAX)
460                                 return 0;
461                         buf = cp;
462                         gmtime_r(&t, tm);
463                         got_GMT = 1;
464                         }
465                         break;
466
467                 case 'Y':
468                 case 'y':
469                         if (*buf == 0 || isspace((unsigned char)*buf))
470                                 break;
471
472                         if (!isdigit((unsigned char)*buf))
473                                 return 0;
474
475                         len = (c == 'Y') ? 4 : 2;
476                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
477                                 i *= 10;
478                                 i += *buf - '0';
479                                 len--;
480                         }
481                         if (c == 'Y')
482                                 i -= 1900;
483                         if (c == 'y' && i < 69)
484                                 i += 100;
485                         if (i < 0)
486                                 return 0;
487
488                         tm->tm_year = i;
489
490                         if (*buf != 0 && isspace((unsigned char)*buf))
491                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
492                                         ptr++;
493                         break;
494
495                 case 'Z':
496                         {
497                         const char *cp;
498                         char *zonestr;
499
500                         for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) {/*empty*/}
501                         if (cp - buf) {
502                                 zonestr = alloca(cp - buf + 1);
503                                 strncpy(zonestr, buf, cp - buf);
504                                 zonestr[cp - buf] = '\0';
505                                 tzset();
506                                 if (0 == strcmp(zonestr, "GMT")) {
507                                     got_GMT = 1;
508                                 } else if (0 == strcmp(zonestr, tzname[0])) {
509                                     tm->tm_isdst = 0;
510                                 } else if (0 == strcmp(zonestr, tzname[1])) {
511                                     tm->tm_isdst = 1;
512                                 } else {
513                                     return 0;
514                                 }
515                                 buf += cp - buf;
516                         }
517                         }
518                         break;
519                 }
520         }
521         return (char *)buf;
522 }
523
524
525 char *
526 strptime(const char *buf, const char *fmt, struct tm *tm)
527 {
528         char *ret;
529
530 #ifdef  _THREAD_SAFE
531         pthread_mutex_lock(&gotgmt_mutex);
532 #endif
533
534         got_GMT = 0;
535         ret = _strptime(buf, fmt, tm);
536         if (ret && got_GMT) {
537                 time_t t = timegm(tm);
538             localtime_r(&t, tm);
539                 got_GMT = 0;
540         }
541
542 #ifdef  _THREAD_SAFE
543         pthread_mutex_unlock(&gotgmt_mutex);
544 #endif
545
546         return ret;
547 }