* Use id(1) instead of grep(1) to detect the presence of the smmsp
[dragonfly.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  * @(#) 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/libcr/stdtime/Attic/strptime.c,v 1.2 2003/06/17 04:26:46 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 <time.h>
60 #include <ctype.h>
61 #include <limits.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #ifdef  _THREAD_SAFE
65 #include <pthread.h>
66 #include "pthread_private.h"
67 #endif
68 #include "timelocal.h"
69
70 static char * _strptime(const char *, const char *, struct tm *);
71
72 #ifdef  _THREAD_SAFE
73 static struct pthread_mutex     _gotgmt_mutexd = PTHREAD_MUTEX_STATIC_INITIALIZER;
74 static pthread_mutex_t          gotgmt_mutex   = &_gotgmt_mutexd;
75 #endif
76 static int got_GMT;
77
78 #define asizeof(a)      (sizeof (a) / sizeof ((a)[0]))
79
80 static char *
81 _strptime(const char *buf, const char *fmt, struct tm *tm)
82 {
83         char    c;
84         const char *ptr;
85         int     i,
86                 len;
87         int Ealternative, Oalternative;
88         struct lc_time_T *tptr = __get_current_time_locale();
89
90         ptr = fmt;
91         while (*ptr != 0) {
92                 if (*buf == 0)
93                         break;
94
95                 c = *ptr++;
96
97                 if (c != '%') {
98                         if (isspace((unsigned char)c))
99                                 while (*buf != 0 && isspace((unsigned char)*buf))
100                                         buf++;
101                         else if (c != *buf++)
102                                 return 0;
103                         continue;
104                 }
105
106                 Ealternative = 0;
107                 Oalternative = 0;
108 label:
109                 c = *ptr++;
110                 switch (c) {
111                 case 0:
112                 case '%':
113                         if (*buf++ != '%')
114                                 return 0;
115                         break;
116
117                 case '+':
118                         buf = _strptime(buf, tptr->date_fmt, tm);
119                         if (buf == 0)
120                                 return 0;
121                         break;
122
123                 case 'C':
124                         if (!isdigit((unsigned char)*buf))
125                                 return 0;
126
127                         /* XXX This will break for 3-digit centuries. */
128                         len = 2;
129                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
130                                 i *= 10;
131                                 i += *buf - '0';
132                                 len--;
133                         }
134                         if (i < 19)
135                                 return 0;
136
137                         tm->tm_year = i * 100 - 1900;
138                         break;
139
140                 case 'c':
141                         buf = _strptime(buf, tptr->c_fmt, tm);
142                         if (buf == 0)
143                                 return 0;
144                         break;
145
146                 case 'D':
147                         buf = _strptime(buf, "%m/%d/%y", tm);
148                         if (buf == 0)
149                                 return 0;
150                         break;
151
152                 case 'E':
153                         if (Ealternative || Oalternative)
154                                 break;
155                         Ealternative++;
156                         goto label;
157
158                 case 'O':
159                         if (Ealternative || Oalternative)
160                                 break;
161                         Oalternative++;
162                         goto label;
163
164                 case 'F':
165                         buf = _strptime(buf, "%Y-%m-%d", tm);
166                         if (buf == 0)
167                                 return 0;
168                         break;
169
170                 case 'R':
171                         buf = _strptime(buf, "%H:%M", tm);
172                         if (buf == 0)
173                                 return 0;
174                         break;
175
176                 case 'r':
177                         buf = _strptime(buf, tptr->ampm_fmt, tm);
178                         if (buf == 0)
179                                 return 0;
180                         break;
181
182                 case 'T':
183                         buf = _strptime(buf, "%H:%M:%S", tm);
184                         if (buf == 0)
185                                 return 0;
186                         break;
187
188                 case 'X':
189                         buf = _strptime(buf, tptr->X_fmt, tm);
190                         if (buf == 0)
191                                 return 0;
192                         break;
193
194                 case 'x':
195                         buf = _strptime(buf, tptr->x_fmt, tm);
196                         if (buf == 0)
197                                 return 0;
198                         break;
199
200                 case 'j':
201                         if (!isdigit((unsigned char)*buf))
202                                 return 0;
203
204                         len = 3;
205                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
206                                 i *= 10;
207                                 i += *buf - '0';
208                                 len--;
209                         }
210                         if (i < 1 || i > 366)
211                                 return 0;
212
213                         tm->tm_yday = i - 1;
214                         break;
215
216                 case 'M':
217                 case 'S':
218                         if (*buf == 0 || isspace((unsigned char)*buf))
219                                 break;
220
221                         if (!isdigit((unsigned char)*buf))
222                                 return 0;
223
224                         len = 2;
225                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
226                                 i *= 10;
227                                 i += *buf - '0';
228                                 len--;
229                         }
230
231                         if (c == 'M') {
232                                 if (i > 59)
233                                         return 0;
234                                 tm->tm_min = i;
235                         } else {
236                                 if (i > 60)
237                                         return 0;
238                                 tm->tm_sec = i;
239                         }
240
241                         if (*buf != 0 && isspace((unsigned char)*buf))
242                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
243                                         ptr++;
244                         break;
245
246                 case 'H':
247                 case 'I':
248                 case 'k':
249                 case 'l':
250                         /*
251                          * Of these, %l is the only specifier explicitly
252                          * documented as not being zero-padded.  However,
253                          * there is no harm in allowing zero-padding.
254                          *
255                          * XXX The %l specifier may gobble one too many
256                          * digits if used incorrectly.
257                          */
258                         if (!isdigit((unsigned char)*buf))
259                                 return 0;
260
261                         len = 2;
262                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
263                                 i *= 10;
264                                 i += *buf - '0';
265                                 len--;
266                         }
267                         if (c == 'H' || c == 'k') {
268                                 if (i > 23)
269                                         return 0;
270                         } else if (i > 12)
271                                 return 0;
272
273                         tm->tm_hour = i;
274
275                         if (*buf != 0 && isspace((unsigned char)*buf))
276                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
277                                         ptr++;
278                         break;
279
280                 case 'p':
281                         /*
282                          * XXX This is bogus if parsed before hour-related
283                          * specifiers.
284                          */
285                         len = strlen(tptr->am);
286                         if (strncasecmp(buf, tptr->am, len) == 0) {
287                                 if (tm->tm_hour > 12)
288                                         return 0;
289                                 if (tm->tm_hour == 12)
290                                         tm->tm_hour = 0;
291                                 buf += len;
292                                 break;
293                         }
294
295                         len = strlen(tptr->pm);
296                         if (strncasecmp(buf, tptr->pm, len) == 0) {
297                                 if (tm->tm_hour > 12)
298                                         return 0;
299                                 if (tm->tm_hour != 12)
300                                         tm->tm_hour += 12;
301                                 buf += len;
302                                 break;
303                         }
304
305                         return 0;
306
307                 case 'A':
308                 case 'a':
309                         for (i = 0; i < asizeof(tptr->weekday); i++) {
310                                 len = strlen(tptr->weekday[i]);
311                                 if (strncasecmp(buf, tptr->weekday[i],
312                                                 len) == 0)
313                                         break;
314                                 len = strlen(tptr->wday[i]);
315                                 if (strncasecmp(buf, tptr->wday[i],
316                                                 len) == 0)
317                                         break;
318                         }
319                         if (i == asizeof(tptr->weekday))
320                                 return 0;
321
322                         tm->tm_wday = i;
323                         buf += len;
324                         break;
325
326                 case 'U':
327                 case 'W':
328                         /*
329                          * XXX This is bogus, as we can not assume any valid
330                          * information present in the tm structure at this
331                          * point to calculate a real value, so just check the
332                          * range for now.
333                          */
334                         if (!isdigit((unsigned char)*buf))
335                                 return 0;
336
337                         len = 2;
338                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
339                                 i *= 10;
340                                 i += *buf - '0';
341                                 len--;
342                         }
343                         if (i > 53)
344                                 return 0;
345
346                         if (*buf != 0 && isspace((unsigned char)*buf))
347                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
348                                         ptr++;
349                         break;
350
351                 case 'w':
352                         if (!isdigit((unsigned char)*buf))
353                                 return 0;
354
355                         i = *buf - '0';
356                         if (i > 6)
357                                 return 0;
358
359                         tm->tm_wday = i;
360
361                         if (*buf != 0 && isspace((unsigned char)*buf))
362                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
363                                         ptr++;
364                         break;
365
366                 case 'd':
367                 case 'e':
368                         /*
369                          * The %e specifier is explicitly documented as not
370                          * being zero-padded but there is no harm in allowing
371                          * such padding.
372                          *
373                          * XXX The %e specifier may gobble one too many
374                          * digits if used incorrectly.
375                          */
376                         if (!isdigit((unsigned char)*buf))
377                                 return 0;
378
379                         len = 2;
380                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
381                                 i *= 10;
382                                 i += *buf - '0';
383                                 len--;
384                         }
385                         if (i > 31)
386                                 return 0;
387
388                         tm->tm_mday = i;
389
390                         if (*buf != 0 && isspace((unsigned char)*buf))
391                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
392                                         ptr++;
393                         break;
394
395                 case 'B':
396                 case 'b':
397                 case 'h':
398                         for (i = 0; i < asizeof(tptr->month); i++) {
399                                 if (Oalternative) {
400                                         if (c == 'B') {
401                                                 len = strlen(tptr->alt_month[i]);
402                                                 if (strncasecmp(buf,
403                                                                 tptr->alt_month[i],
404                                                                 len) == 0)
405                                                         break;
406                                         }
407                                 } else {
408                                         len = strlen(tptr->month[i]);
409                                         if (strncasecmp(buf, tptr->month[i],
410                                                         len) == 0)
411                                                 break;
412                                         len = strlen(tptr->mon[i]);
413                                         if (strncasecmp(buf, tptr->mon[i],
414                                                         len) == 0)
415                                                 break;
416                                 }
417                         }
418                         if (i == asizeof(tptr->month))
419                                 return 0;
420
421                         tm->tm_mon = i;
422                         buf += len;
423                         break;
424
425                 case 'm':
426                         if (!isdigit((unsigned char)*buf))
427                                 return 0;
428
429                         len = 2;
430                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
431                                 i *= 10;
432                                 i += *buf - '0';
433                                 len--;
434                         }
435                         if (i < 1 || i > 12)
436                                 return 0;
437
438                         tm->tm_mon = i - 1;
439
440                         if (*buf != 0 && isspace((unsigned char)*buf))
441                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
442                                         ptr++;
443                         break;
444
445                 case 's':
446                         {
447                         char *cp;
448                         time_t t;
449
450                         t = strtol(buf, &cp, 10);
451                         if (t == LONG_MAX)
452                                 return 0;
453                         buf = cp;
454                         gmtime_r(&t, tm);
455                         got_GMT = 1;
456                         }
457                         break;
458
459                 case 'Y':
460                 case 'y':
461                         if (*buf == 0 || isspace((unsigned char)*buf))
462                                 break;
463
464                         if (!isdigit((unsigned char)*buf))
465                                 return 0;
466
467                         len = (c == 'Y') ? 4 : 2;
468                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
469                                 i *= 10;
470                                 i += *buf - '0';
471                                 len--;
472                         }
473                         if (c == 'Y')
474                                 i -= 1900;
475                         if (c == 'y' && i < 69)
476                                 i += 100;
477                         if (i < 0)
478                                 return 0;
479
480                         tm->tm_year = i;
481
482                         if (*buf != 0 && isspace((unsigned char)*buf))
483                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
484                                         ptr++;
485                         break;
486
487                 case 'Z':
488                         {
489                         const char *cp;
490                         char *zonestr;
491
492                         for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) {/*empty*/}
493                         if (cp - buf) {
494                                 zonestr = alloca(cp - buf + 1);
495                                 strncpy(zonestr, buf, cp - buf);
496                                 zonestr[cp - buf] = '\0';
497                                 tzset();
498                                 if (0 == strcmp(zonestr, "GMT")) {
499                                     got_GMT = 1;
500                                 } else if (0 == strcmp(zonestr, tzname[0])) {
501                                     tm->tm_isdst = 0;
502                                 } else if (0 == strcmp(zonestr, tzname[1])) {
503                                     tm->tm_isdst = 1;
504                                 } else {
505                                     return 0;
506                                 }
507                                 buf += cp - buf;
508                         }
509                         }
510                         break;
511                 }
512         }
513         return (char *)buf;
514 }
515
516
517 char *
518 strptime(const char *buf, const char *fmt, struct tm *tm)
519 {
520         char *ret;
521
522 #ifdef  _THREAD_SAFE
523         pthread_mutex_lock(&gotgmt_mutex);
524 #endif
525
526         got_GMT = 0;
527         ret = _strptime(buf, fmt, tm);
528         if (ret && got_GMT) {
529                 time_t t = timegm(tm);
530             localtime_r(&t, tm);
531                 got_GMT = 0;
532         }
533
534 #ifdef  _THREAD_SAFE
535         pthread_mutex_unlock(&gotgmt_mutex);
536 #endif
537
538         return ret;
539 }