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