OSDN Git Service

Fix bug in setting daylight and timezone when no (valid) TZ.
[uclinux-h8/uClibc.git] / libc / misc / time / time.c
1 /*  Copyright (C) 2002     Manuel Novoa III
2  *
3  *  This library is free software; you can redistribute it and/or
4  *  modify it under the terms of the GNU Library General Public
5  *  License as published by the Free Software Foundation; either
6  *  version 2 of the License, or (at your option) any later version.
7  *
8  *  This library is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  *  Library General Public License for more details.
12  *
13  *  You should have received a copy of the GNU Library General Public
14  *  License along with this library; if not, write to the Free
15  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16  */
17
18 /*  ATTENTION!   ATTENTION!   ATTENTION!   ATTENTION!   ATTENTION!
19  *
20  *  Besides uClibc, I'm using this code in my libc for elks, which is
21  *  a 16-bit environment with a fairly limited compiler.  It would make
22  *  things much easier for me if this file isn't modified unnecessarily.
23  *  In particular, please put any new or replacement functions somewhere
24  *  else, and modify the makefile to use your version instead.
25  *  Thanks.  Manuel
26  *
27  *  ATTENTION!   ATTENTION!   ATTENTION!   ATTENTION!   ATTENTION! */
28
29 /* June 15, 2002     Initial Notes:
30  *
31  * Note: It is assumed throught that time_t is either long or unsigned long.
32  *       Similarly, clock_t is assumed to be long int.
33  *
34  * Warning: Assumptions are made about the layout of struct tm!  It is
35  *    assumed that the initial fields of struct tm are (in order):
36  *    tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday
37  *
38  * Reached the inital goal of supporting the ANSI/ISO C99 time functions
39  * as well as SUSv3's strptime.  All timezone info is obtained from the
40  * TZ env variable.
41  *
42  * Differences from glibc worth noting:
43  *
44  * Leap seconds are not considered here.
45  *
46  * glibc stores additional timezone info the struct tm, whereas we don't.
47  *
48  * Alternate digits and era handling are not currently implemented.
49  * The modifiers are accepted, and tested for validity with the following
50  * specifier, but are ignored otherwise.
51  *
52  * strftime does not implement glibc extension modifiers or widths for
53  *     conversion specifiers.  However it does implement the glibc
54  *     extension specifiers %l, %k, and %s.  It also recognizes %P, but
55  *     treats it as a synonym for %p; i.e. doesn't convert to lower case.
56  *
57  * strptime implements the glibc extension specifiers.  However, it follows
58  *     SUSv3 in requiring at least one non-alphanumeric char between
59  *     conversion specifiers.  Also, strptime only sets struct tm fields
60  *     for which format specifiers appear and does not try to infer other
61  *     fields (such as wday) as glibc's version does.
62  *
63  * TODO - Since glibc's %l and %k can space-pad their output in strftime,
64  *     it might be reasonable to eat whitespace first for those specifiers.
65  *     This could be done by pushing " %I" and " %H" respectively so that
66  *     leading whitespace is consumed.  This is really only an issue if %l
67  *     or %k occurs at the start of the format string.
68  *
69  * TODO - Implement getdate? tzfile? struct tm extensions?
70  *
71  * TODO - Rework _time_mktime to remove the dependency on long long.
72  */
73
74 /* Oct 28, 2002
75  *
76  * Fixed allowed char check for std and dst TZ fields.
77  *
78  * Added several options concerned with timezone support.  The names will
79  * probably change once Erik gets the new config system in place.
80  *
81  * Defining __TIME_TZ_FILE causes tzset() to attempt to read the TZ value
82  * from the file /etc/TZ if the TZ env variable isn't set.  The file contents
83  * must be the intended value of TZ, followed by a newline.  No other chars,
84  * spacing, etc is allowed.  As an example, an easy way for me to init
85  * /etc/TZ appropriately would be:    echo CST6CDT > /etc/TZ
86  *
87  * Defining __TIME_TZ_FILE_ONCE will cause all further accesses of /etc/TZ
88  * to be skipped once a legal value has been read.
89  *
90  * Defining __TIME_TZ_OPT_SPEED will cause a tzset() to keep a copy of the
91  * last TZ setting string and do a "fast out" if the current string is the
92  * same.
93  *
94  * Nov 21, 2002   Fix an error return case in _time_mktime.
95  *
96  * Nov 26, 2002   Fix bug in setting daylight and timezone when no (valid) TZ.
97  *   Bug reported by Arne Bernin <arne@alamut.de> in regards to freeswan.
98  */
99
100
101 #define _GNU_SOURCE
102 #define _STDIO_UTILITY
103 #include <stdio.h>
104 #include <stdlib.h>
105 #include <stddef.h>
106 #include <string.h>
107 #include <time.h>
108 #include <limits.h>
109 #include <assert.h>
110 #include <errno.h>
111 #include <ctype.h>
112 #include <langinfo.h>
113 #include <locale.h>
114
115 #ifndef __isleap
116 #define __isleap(y) ( !((y) % 4) && ( ((y) % 100) || !((y) % 400) ) )
117 #endif
118
119 #ifndef TZNAME_MAX
120 #define TZNAME_MAX _POSIX_TZNAME_MAX
121 #endif
122
123 /**********************************************************************/
124
125 /* The era code is currently unfinished. */
126 /*  #define ENABLE_ERA_CODE */
127
128 #define __TIME_TZ_FILE
129 /* #define __TIME_TZ_FILE_ONCE */
130
131 #define __TIME_TZ_OPT_SPEED
132
133 #define TZ_BUFLEN               (2*TZNAME_MAX + 56)
134
135 #ifdef __TIME_TZ_FILE
136 #include <sys/stat.h>
137 #include <fcntl.h>
138 #include <unistd.h>
139 /* ":<tzname>+hh:mm:ss<tzname>+hh:mm:ss,Mmm.w.d/hh:mm:ss,Mmm.w.d/hh:mm:ss" + nul */
140 /* 1 + 2*(1+TZNAME_MAX+1 + 9 + 7 + 9) + 1 = 2*TZNAME_MAX + 56 */
141 #else  /* __TIME_TZ_FILE */
142 #undef __TIME_TZ_FILE_ONCE
143 #endif /* __TIME_TZ_FILE */
144
145 /**********************************************************************/
146
147 extern struct tm __time_tm;
148
149 typedef struct {
150         long gmt_offset;
151         long dst_offset;
152         short day;                                      /* for J or normal */
153         short week;
154         short month;
155         short rule_type;                        /* J, M, \0 */
156         char tzname[TZNAME_MAX+1];
157 } rule_struct;
158
159 #ifdef __UCLIBC_HAS_THREADS__
160
161 #include <pthread.h>
162
163 extern pthread_mutex_t _time_tzlock;
164
165 #define TZLOCK          pthread_mutex_lock(&_time_tzlock)
166 #define TZUNLOCK        pthread_mutex_unlock(&_time_tzlock)
167
168 #else
169
170 #define TZLOCK          ((void) 0)
171 #define TZUNLOCK        ((void) 0)
172
173 #endif
174
175 extern rule_struct _time_tzinfo[2];
176
177 extern struct tm *_time_t2tm(const time_t *__restrict timer,
178                                                          int offset, struct tm *__restrict result);
179
180 extern time_t _time_mktime(struct tm *timeptr, int store_on_success);
181
182 /**********************************************************************/
183 #ifdef L_asctime
184
185 static char __time_str[26];
186
187 char *asctime(const struct tm *__restrict ptm)
188 {
189         return asctime_r(ptm, __time_str);
190 }
191
192 #endif
193 /**********************************************************************/
194 #ifdef L_asctime_r
195
196 /* Strictly speaking, this implementation isn't correct.  ANSI/ISO specifies
197  * that the implementation of asctime() be equivalent to
198  *
199  *   char *asctime(const struct tm *timeptr)
200  *   {
201  *       static char wday_name[7][3] = {
202  *           "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
203  *       };
204  *       static char mon_name[12][3] = {
205  *           "Jan", "Feb", "Mar", "Apr", "May", "Jun",
206  *           "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 
207  *       };
208  *       static char result[26];
209  *   
210  *       sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
211  *           wday_name[timeptr->tm_wday],                   
212  *           mon_name[timeptr->tm_mon],
213  *           timeptr->tm_mday, timeptr->tm_hour,
214  *           timeptr->tm_min, timeptr->tm_sec,  
215  *           1900 + timeptr->tm_year);        
216  *       return result;
217  *   }
218  *
219  * but the above is either inherently unsafe, or carries with it the implicit
220  * assumption that all fields of timeptr fall within their usual ranges, and
221  * that the tm_year value falls in the range [-2899,8099] to avoid overflowing
222  * the static buffer.
223  *
224  * If we take the implicit assumption as given, then the implementation below
225  * is still incorrect for tm_year values < -900, as there will be either
226  * 0-padding and/or a missing negative sign for the year conversion .  But given
227  * the ususal use of asctime(), I think it isn't unreasonable to restrict correct
228  * operation to the domain of years between 1000 and 9999.
229  */
230
231 /* This is generally a good thing, but if you're _sure_ any data passed will be
232  * in range, you can #undef this. */
233 #define SAFE_ASCTIME_R          1
234
235 static const unsigned char at_data[] = {
236         'S', 'u', 'n', 'M', 'o', 'n', 'T', 'u', 'e', 'W', 'e', 'd',
237         'T', 'h', 'u', 'F', 'r', 'i', 'S', 'a', 't',
238
239         'J', 'a', 'n', 'F', 'e', 'b', 'M', 'a', 'r', 'A', 'p', 'r',
240         'M', 'a', 'y', 'J', 'u', 'n', 'J', 'u', 'l', 'A', 'u', 'g',
241         'S', 'e', 'p', 'O', 'c', 't', 'N', 'o', 'v', 'D', 'e', 'c', 
242
243 #ifdef SAFE_ASCTIME_R
244         '?', '?', '?', 
245 #endif
246         ' ', '?', '?', '?',
247         ' ', '0',
248         offsetof(struct tm, tm_mday),
249         ' ', '0',
250         offsetof(struct tm, tm_hour),
251         ':', '0',
252         offsetof(struct tm, tm_min),
253         ':', '0',
254         offsetof(struct tm, tm_sec),
255         ' ', '?', '?', '?', '?', '\n', 0
256 };
257
258 char *asctime_r(register const struct tm *__restrict ptm,
259                                 register char *__restrict buffer)
260 {
261         int tmp;
262
263         assert(ptm);
264         assert(buffer);
265
266 #ifdef SAFE_ASCTIME_R
267         memcpy(buffer, at_data + 3*(7 + 12), sizeof(at_data) - 3*(7 + 12));
268
269         if (((unsigned int)(ptm->tm_wday)) <= 6) {
270                 memcpy(buffer, at_data + 3 * ptm->tm_wday, 3);
271         }
272
273         if (((unsigned int)(ptm->tm_mon)) <= 11) {
274                 memcpy(buffer + 4, at_data + 3*7 + 3 * ptm->tm_mon, 3);
275         }
276 #else
277         assert(((unsigned int)(ptm->tm_wday)) <= 6);
278         assert(((unsigned int)(ptm->tm_mon)) <= 11);
279
280         memcpy(buffer, at_data + 3*(7 + 12) - 3, sizeof(at_data) + 3 - 3*(7 + 12));
281
282         memcpy(buffer, at_data + 3 * ptm->tm_wday, 3);
283         memcpy(buffer + 4, at_data + 3*7 + 3 * ptm->tm_mon, 3);
284 #endif
285
286 #ifdef SAFE_ASCTIME_R
287         buffer += 19;
288         tmp = ptm->tm_year + 1900;
289         if (((unsigned int) tmp) < 10000) {
290                 buffer += 4;
291                 do {
292                         *buffer = '0' + (tmp % 10);
293                         tmp /= 10;
294                 } while (*--buffer == '?');
295         }
296 #else  /* SAFE_ASCTIME_R */
297         buffer += 23;
298         tmp = ptm->tm_year + 1900;
299         assert( ((unsigned int) tmp) < 10000 );
300         do {
301                 *buffer = '0' + (tmp % 10);
302                 tmp /= 10;
303         } while (*--buffer == '?');
304 #endif /* SAFE_ASCTIME_R */
305
306         do {
307                 --buffer;
308                 tmp = *((int *)(((const char *) ptm) + (int) *buffer));
309 #ifdef SAFE_ASCTIME_R
310                 if (((unsigned int) tmp) >= 100) { /* Just check 2 digit non-neg. */
311                         buffer[-1] = *buffer = '?';
312                 } else
313 #else  /* SAFE_ASCTIME_R */
314                 assert(((unsigned int) tmp) < 100); /* Just check 2 digit non-neg. */
315 #endif /* SAFE_ASCTIME_R */
316                 {
317                         *buffer = '0' + (tmp % 10);
318 #ifdef __BCC__
319                         buffer[-1] = '0' + (tmp/10);
320 #else  /* __BCC__ */
321                         buffer[-1] += (tmp/10);
322 #endif /* __BCC__ */
323                 }
324         } while ((buffer -= 2)[-2] == '0');
325
326         if (*++buffer == '0') {         /* Space-pad day of month. */
327                 *buffer = ' ';
328         }
329
330         return buffer - 8;
331 }
332
333 #endif
334 /**********************************************************************/
335 #ifdef L_clock
336
337 #include <sys/times.h>
338
339 /* Note: According to glibc...
340  *    CAE XSH, Issue 4, Version 2: <time.h>
341  *    The value of CLOCKS_PER_SEC is required to be 1 million on all
342  *    XSI-conformant systems.
343  */
344
345 #ifndef __BCC__
346 #if CLOCKS_PER_SEC != 1000000L
347 #error unexpected value for CLOCKS_PER_SEC!
348 #endif
349 #endif
350
351 clock_t clock(void)
352 {
353         struct tms xtms;
354         unsigned long t;
355
356         times(&xtms);
357         t = ((unsigned long) xtms.tms_utime) + xtms.tms_stime;
358
359 #ifndef __UCLIBC_CLK_TCK_CONST
360 #error __UCLIBC_CLK_TCK_CONST not defined!
361 #endif
362
363 #undef CLK_TCK
364 #define CLK_TCK __UCLIBC_CLK_TCK_CONST
365
366 #if CLK_TCK > CLOCKS_PER_SEC
367 #error __UCLIBC_CLK_TCK_CONST > CLOCKS_PER_SEC!
368 #elif CLK_TCK < 1
369 #error __UCLIBC_CLK_TCK_CONST < 1!
370 #endif
371
372 #if (CLK_TCK == CLOCKS_PER_SEC)
373         return (t <= LONG_MAX) ? t : -1;
374 #elif (CLOCKS_PER_SEC % CLK_TCK) == 0
375         return (t <= (LONG_MAX / (CLOCKS_PER_SEC/CLK_TCK)))
376                 ? t * (CLOCKS_PER_SEC/CLK_TCK)
377                 : -1;
378 #else
379         return (t <= ((LONG_MAX / CLOCKS_PER_SEC) * CLK_TCK
380                                   + ((LONG_MAX % CLOCKS_PER_SEC) * CLK_TCK) / CLOCKS_PER_SEC))
381                 ? (((t / CLK_TCK) * CLOCKS_PER_SEC)
382                    + (((t % CLK_TCK) * CLOCKS_PER_SEC) / CLK_TCK))
383                 : -1;
384 #endif
385 }
386
387 #endif
388 /**********************************************************************/
389 #ifdef L_ctime
390
391 char *ctime(const time_t *clock)
392 {
393         /* ANSI/ISO/SUSv3 say that ctime is equivalent to the following. */
394         return asctime(localtime(clock));
395 }
396
397 #endif
398 /**********************************************************************/
399 #ifdef L_ctime_r
400
401 char *ctime_r(const time_t *clock, char *buf)
402 {
403         struct tm xtms;
404
405         return asctime_r(localtime_r(clock, &xtms), buf);
406 }
407
408 #endif
409 /**********************************************************************/
410 #ifdef L_difftime
411
412 #include <float.h>
413
414 #if FLT_RADIX != 2
415 #error difftime implementation assumptions violated for you arch!
416 #endif
417
418 double difftime(time_t time1, time_t time0)
419 {
420 #if (LONG_MAX >> DBL_MANT_DIG) == 0
421
422         /* time_t fits in the mantissa of a double. */
423         return ((double) time1) - time0;
424
425 #elif ((LONG_MAX >> DBL_MANT_DIG) >> DBL_MANT_DIG) == 0
426
427         /* time_t can overflow the mantissa of a double. */
428         time_t t1, t0, d;
429
430         d = ((time_t) 1) << DBL_MANT_DIG;
431         t1 = time1 / d;
432         time1 -= (t1 * d);
433         t0 = time0 / d;
434         time0 -= (t0*d);
435
436         /* Since FLT_RADIX==2 and d is a power of 2, the only possible
437          * rounding error in the expression below would occur from the
438          * addition. */
439         return (((double) t1) - t0) * d + (((double) time1) - time0);
440
441 #else
442 #error difftime needs special implementation on your arch.
443 #endif
444 }
445
446 #endif
447 /**********************************************************************/
448 #ifdef L_gmtime
449
450 struct tm *gmtime(const time_t *timer)
451 {
452         register struct tm *ptm = &__time_tm;
453
454         _time_t2tm(timer, 0, ptm); /* Can return NULL... */
455
456         return ptm;
457 }
458
459 #endif
460 /**********************************************************************/
461 #ifdef L_gmtime_r
462
463 struct tm *gmtime_r(const time_t *__restrict timer,
464                                         struct tm *__restrict result)
465 {
466         return _time_t2tm(timer, 0, result);
467 }
468
469 #endif
470 /**********************************************************************/
471 #ifdef L_localtime
472
473 struct tm *localtime(const time_t *timer)
474 {
475         register struct tm *ptm = &__time_tm;
476
477         /* In this implementation, tzset() is called by localtime_r().  */
478
479         localtime_r(timer, ptm);        /* Can return NULL... */
480
481         return ptm;
482 }
483
484 #endif
485 /**********************************************************************/
486 #ifdef L_localtime_r
487
488 static const unsigned char day_cor[] = { /* non-leap */
489         31, 31, 34, 34, 35, 35, 36, 36, 36, 37, 37, 38, 38
490 /*       0,  0,  3,  3,  4,  4,  5,  5,  5,  6,  6,  7,  7 */
491 /*          31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 */
492 };
493
494 /* Note: timezone locking is done by localtime_r. */
495
496 static int tm_isdst(register const struct tm *__restrict ptm)
497 {
498         register rule_struct *r = _time_tzinfo;
499         long sec;
500         int i, isdst, isleap, day, day0, monlen, mday;
501         int oday;                                       /* Note: oday can be uninitialized. */
502
503         isdst = 0;
504         if (r[1].tzname[0] != 0) {
505                 /* First, get the current seconds offset from the start of the year.
506                  * Fields of ptm are assumed to be in their normal ranges. */
507                 sec = ptm->tm_sec
508                         + 60 * (ptm->tm_min
509                                         + 60 * (long)(ptm->tm_hour
510                                                                   + 24 * ptm->tm_yday));
511                 /* Do some prep work. */
512                 i = (ptm->tm_year % 400) + 1900; /* Make sure we don't overflow. */
513                 isleap = __isleap(i);
514                 --i;
515                 day0 = (1
516                                 + i                             /* Normal years increment 1 wday. */
517                                 + (i/4)
518                                 - (i/100)
519                                 + (i/400) ) % 7;
520                 i = 0;
521                 do {
522                         day = r->day;           /* Common for 'J' and # case. */
523                         if (r->rule_type == 'J') {
524                                 if (!isleap || (day < (31+29))) {
525                                         --day;
526                                 }
527                         } else if (r->rule_type == 'M') {
528                                 /* Find 0-based day number for 1st of the month. */
529                                 day = 31*r->month - day_cor[r->month -1];
530                                 if (isleap && (day >= 59)) {
531                                         ++day;
532                                 }
533                                 monlen = 31 + day_cor[r->month -1] - day_cor[r->month];
534                                 if (isleap && (r->month > 1)) {
535                                         ++monlen;
536                                 }
537                                 /* Wweekday (0 is Sunday) of 1st of the month
538                                  * is (day0 + day) % 7. */
539                                 if ((mday = r->day - ((day0 + day) % 7)) >= 0) {
540                                         mday -= 7;      /* Back up into prev month since r->week>0. */
541                                 }
542                                 if ((mday += 7 * r->week) >= monlen) {
543                                         mday -= 7;
544                                 }
545                                 /* So, 0-based day number is... */
546                                 day += mday;
547                         }
548
549                         if (i != 0) {
550                                 /* Adjust sec since dst->std change time is in dst. */
551                                 sec += (r[-1].gmt_offset - r->gmt_offset);
552                                 if (oday > day) {
553                                         ++isdst;        /* Year starts in dst. */
554                                 }
555                         }
556                         oday = day;
557
558                         /* Now convert day to seconds and add offset and compare. */
559                         if (sec >= (day * 86400L) + r->dst_offset) {
560                                 ++isdst;
561                         }
562                         ++r;
563                 } while (++i < 2);
564         }
565
566         return (isdst & 1);
567 }
568
569 struct tm *localtime_r(register const time_t *__restrict timer,
570                                            register struct tm *__restrict result)
571 {
572         time_t x[1];
573         long offset;
574         int days, dst;
575
576         TZLOCK;
577
578         tzset();
579
580         dst = 0;
581         do {
582                 days = -7;
583                 offset = 604800L - _time_tzinfo[dst].gmt_offset;
584                 if (*timer > (LONG_MAX - 604800L)) {
585                         days = -days;
586                         offset = -offset;
587                 }
588                 *x = *timer + offset;
589
590                 _time_t2tm(x, days, result);
591                 
592                 if (dst) {
593                         result->tm_isdst = dst;
594                         break;
595                 }
596                 ++dst;
597         } while ((result->tm_isdst = tm_isdst(result)) != 0);
598
599         TZUNLOCK;
600
601         return result;
602 }
603
604 #endif
605 /**********************************************************************/
606 #ifdef L_mktime
607
608 time_t mktime(struct tm *timeptr)
609 {
610         return  _time_mktime(timeptr, 1);
611 }
612
613 #endif
614 /**********************************************************************/
615 #ifdef L_strftime
616
617 #define NO_E_MOD                0x80
618 #define NO_O_MOD                0x40
619
620 #define ILLEGAL_SPEC    0x3f
621
622 #define INT_SPEC                0x00    /* must be 0x00!! */
623 #define STRING_SPEC             0x10    /* must be 0x10!! */
624 #define CALC_SPEC               0x20
625 #define STACKED_SPEC    0x30
626
627 #define MASK_SPEC               0x30
628
629 /* Compatibility:
630  *
631  * No alternate digit (%O?) handling.  Always uses 0-9.
632  * Alternate locale format (%E?) handling is broken for nontrivial ERAs.
633  * glibc's %P is currently faked by %p.  This means it doesn't do lower case.
634  * glibc's %k, %l, and %s are handled.
635  * glibc apparently allows (and ignores) extraneous 'E' and 'O' modifiers,
636  *   while they are flagged as illegal conversions here.
637  */
638
639 /* Warning: Assumes ASCII values! (as do lots of other things in the lib...) */
640 static const unsigned char spec[] = {
641         /* A */         0x03 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
642         /* B */         0x04 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
643         /* C */         0x0a |     INT_SPEC            | NO_O_MOD,
644         /* D */         0x02 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
645         /* E */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
646         /* F */         0x03 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
647         /* G */         0x03 |    CALC_SPEC | NO_E_MOD | NO_O_MOD,
648         /* H */         0x0b |     INT_SPEC | NO_E_MOD,
649         /* I */         0x0c |     INT_SPEC | NO_E_MOD,
650         /* J */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
651         /* K */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
652         /* L */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
653         /* M */         0x0d |     INT_SPEC | NO_E_MOD,
654         /* N */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
655         /* O */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
656         /* P */         0x05 |  STRING_SPEC | NO_E_MOD | NO_O_MOD, /* glibc ; use %p */
657         /* Q */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
658         /* R */         0x04 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
659         /* S */         0x0e |     INT_SPEC | NO_E_MOD,
660         /* T */         0x05 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
661         /* U */         0x04 |    CALC_SPEC | NO_E_MOD,
662         /* V */         0x05 |    CALC_SPEC | NO_E_MOD,
663         /* W */         0x06 |    CALC_SPEC | NO_E_MOD,
664         /* X */         0x0a | STACKED_SPEC            | NO_O_MOD,
665         /* Y */         0x0f |     INT_SPEC            | NO_O_MOD,
666         /* Z */         0x01 |    CALC_SPEC | NO_E_MOD | NO_O_MOD,
667         '?',                                            /* 26 */
668         '?',                                            /* 27 */
669         '?',                                            /* 28 */
670         '?',                                            /* 29 */
671         0,                                                      /* 30 */
672         0,                                                      /* 31 */
673         /* a */         0x00 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
674         /* b */         0x01 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
675         /* c */         0x08 | STACKED_SPEC            | NO_O_MOD,
676         /* d */         0x00 |     INT_SPEC | NO_E_MOD,
677         /* e */         0x01 |     INT_SPEC | NO_E_MOD,
678         /* f */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
679         /* g */         0x02 |    CALC_SPEC | NO_E_MOD | NO_O_MOD,
680         /* h */         0x01 |  STRING_SPEC | NO_E_MOD | NO_O_MOD, /* same as b */
681         /* i */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
682         /* j */         0x08 |     INT_SPEC | NO_E_MOD | NO_O_MOD,
683         /* k */         0x03 |     INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
684         /* l */         0x04 |     INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
685         /* m */         0x05 |     INT_SPEC | NO_E_MOD,
686         /* n */         0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
687         /* o */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
688         /* p */         0x02 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
689         /* q */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
690         /* r */         0x0b | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
691         /* s */         0x07 |    CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
692         /* t */         0x01 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
693         /* u */         0x07 |     INT_SPEC | NO_E_MOD,
694         /* v */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
695         /* w */         0x02 |     INT_SPEC | NO_E_MOD,
696         /* x */         0x09 | STACKED_SPEC            | NO_O_MOD,
697         /* y */         0x09 |     INT_SPEC,
698         /* z */         0x00 |    CALC_SPEC | NO_E_MOD | NO_O_MOD,
699
700
701         /* WARNING!!! These are dependent on the layout of struct tm!!! */
702 #define FIELD_MAX (26+6+26)
703         60 /* 61? */, 59, 23, 31, 11, 0 /* 9999 */, 6, 0 /* 365 */,
704
705 #define TP_OFFSETS (FIELD_MAX+8)
706         3, /* d */
707         3, /* e */
708         6, /* w */
709         2, /* k */
710         2, /* l */
711         4, /* m */
712         0, /* CURRENTLY UNUSED */
713         /* NOTE: u,j,y order must be preserved as 6,7,5 seq is used in the code! */
714 #define CALC_OFFSETS (TP_OFFSETS + 7)
715         6, /* u */
716         7, /* j */
717         5, /* y */
718         5, /* C */
719         2, /* H */
720         2, /* I */
721         1, /* M */
722         0, /* S */
723         5, /* Y */
724         6, /* a */
725         4, /* b, h */
726         2, /* p */
727         6, /* A */
728         4, /* B */
729         2, /* P */
730
731 #define TP_CODES (TP_OFFSETS + 16 + 6)
732         2 | 16, /* d */
733         2, /* e */
734         0 | 16, /* w */
735         2, /* k */
736         2 | 32 | 0, /* l */
737         2 | 16 | 1, /* m */
738         0, /* CURRENTLY UNUSED */
739         0 | 16 | 8 , /* u */
740         4 | 16 | 1, /* j */
741         2 | 128 | 32 | 16 , /* y */
742         2 | 128 | 64 | 32 | 16 , /* C */
743         2 | 16, /* H */
744         2 | 32 | 16 | 0, /* I */
745         2 | 16, /* M */
746         2 | 16, /* S */
747         6 | 16, /* Y */
748         2, /* a */
749         2, /* b, h */
750         2 | 64, /* p */
751         2, /* A */
752         2, /* B */
753         2 | 64, /* P */
754
755 #define STRINGS_NL_ITEM_START (TP_CODES + 16 + 6)
756         _NL_ITEM_INDEX(ABDAY_1),        /* a */
757         _NL_ITEM_INDEX(ABMON_1),        /* b, h */
758         _NL_ITEM_INDEX(AM_STR),         /* p */
759         _NL_ITEM_INDEX(DAY_1),          /* A */
760         _NL_ITEM_INDEX(MON_1),          /* B */
761         _NL_ITEM_INDEX(AM_STR),         /* P -- wrong! need lower case */
762
763 #define STACKED_STRINGS_START (STRINGS_NL_ITEM_START+6)
764         6, 7, 8, 16, 24, 29,            /* 6 - offsets from offset-count to strings */
765         '\n', 0,                                        /* 2 */
766         '\t', 0,                                        /* 2 */
767         '%', 'm', '/', '%', 'd', '/', '%', 'y', 0, /* 9 - %D */
768         '%', 'Y', '-', '%', 'm', '-', '%', 'd', 0, /* 9 - %F (glibc extension) */
769         '%', 'H', ':', '%', 'M', 0,     /* 6 - %R*/
770         '%', 'H', ':', '%', 'M', ':', '%', 'S', 0, /* 9 - %T */
771
772 #define STACKED_STRINGS_NL_ITEM_START (STACKED_STRINGS_START + 43)
773         _NL_ITEM_INDEX(D_T_FMT),        /* c */
774         _NL_ITEM_INDEX(D_FMT),          /* x */
775         _NL_ITEM_INDEX(T_FMT),          /* X */
776         _NL_ITEM_INDEX(T_FMT_AMPM), /* r */
777 #ifdef ENABLE_ERA_CODE
778         _NL_ITEM_INDEX(ERA_D_T_FMT), /* Ec */
779         _NL_ITEM_INDEX(ERA_D_FMT),      /* Ex */
780         _NL_ITEM_INDEX(ERA_T_FMT),      /* EX */
781 #endif
782 };
783
784 static int load_field(int k, const struct tm *__restrict timeptr)
785 {
786         int r;
787         int r_max;
788
789         r = ((int *) timeptr)[k];
790
791         r_max = spec[FIELD_MAX + k];
792
793         if (k == 7) {
794                 r_max = 365;
795         } else if (k == 5) {
796                 r += 1900;
797                 r_max = 9999;
798         }
799
800         if ((((unsigned int) r) > r_max) || ((k == 3) && !r)) {
801                 r = -1;
802         }
803
804         return r;
805 }
806
807 #define MAX_PUSH 4
808
809 size_t strftime(char *__restrict s, size_t maxsize,
810                                 const char *__restrict format,
811                                 const struct tm *__restrict timeptr)
812 {
813         long tzo;
814         register const char *p;
815         register const char *o;
816         const rule_struct *rsp;
817         const char *stack[MAX_PUSH];
818         size_t count;
819         size_t o_count;
820         int field_val, i, j, lvl;
821         int x[3];                       /* wday, yday, year */
822         int isofm, days;
823         char buf[__UIM_BUFLEN_LONG];
824         unsigned char mod;
825         unsigned char code;
826
827         tzset();                                        /* We'll, let's get this out of the way. */
828
829         lvl = 0;
830         p = format;
831         count = maxsize;
832
833  LOOP:
834         if (!count) {
835                 return 0;
836         }
837         if (!*p) {
838                 if (lvl == 0) {
839                         *s = 0;                         /* nul-terminate */
840                         return maxsize - count;
841                 }
842                 p = stack[--lvl];
843                 goto LOOP;
844         }
845
846         o_count = 1;
847         if ((*(o = p) == '%') && (*++p != '%')) {
848                 o_count = 2;
849                 mod = ILLEGAL_SPEC;
850                 if ((*p == 'O') || (*p == 'E')) { /* modifier */
851                         mod |= ((*p == 'O') ? NO_O_MOD : NO_E_MOD);
852                         ++o_count;
853                         ++p;
854                 }
855                 if ((((unsigned char)(((*p) | 0x20) - 'a')) >= 26)
856                         || (((code = spec[(int)(*p - 'A')]) & mod) >= ILLEGAL_SPEC)
857                         ) {
858                         if (!*p) {
859                                 --p;
860                                 --o_count;
861                         }
862                         goto OUTPUT;
863                 }
864                 code &= ILLEGAL_SPEC;   /* modifiers are preserved in mod var. */
865
866                 if ((code & MASK_SPEC) == STACKED_SPEC) {
867                         if (lvl == MAX_PUSH) {
868                                 goto OUTPUT;    /* Stack full so treat as illegal spec. */
869                         }
870                         stack[lvl++] = ++p;
871                         if ((code &= 0xf) < 8) {
872                                 p = ((const char *) spec) + STACKED_STRINGS_START + code;
873                                 p += *((unsigned char *)p);
874                                 goto LOOP;
875                         }
876                         p = ((const char *) spec) + STACKED_STRINGS_NL_ITEM_START
877                                 + (code & 7);
878 #ifdef ENABLE_ERA_CODE
879                         if ((mod & NO_E_MOD) /* Actually, this means E modifier present. */
880                                 && (*(o = nl_langinfo(_NL_ITEM(LC_TIME,
881                                                                                            (int)(((unsigned char *)p)[4]))
882                                                                           )))
883                                 ) {
884                                 p = o;
885                                 goto LOOP;
886                         }
887 #endif
888                         p = nl_langinfo(_NL_ITEM(LC_TIME,
889                                                                          (int)(*((unsigned char *)p))));
890                         goto LOOP;
891                 }
892
893                 o = spec + 26;          /* set to "????" */
894                 if ((code & MASK_SPEC) == CALC_SPEC) {
895
896                         if (*p == 's') {
897                                 time_t t;
898
899                                 /* Use a cast to silence the warning since *timeptr won't
900                                  * be changed. */
901                                 if ((t = _time_mktime((struct tm *) timeptr, 0))
902                                         == ((time_t) -1)
903                                         ) {
904                                         o_count = 1;
905                                         goto OUTPUT;
906                                 }
907 #ifdef TIME_T_IS_UNSIGNED
908                                 o = _uintmaxtostr(buf + sizeof(buf) - 1,
909                                                                   (uintmax_t) t,
910                                                                   10, __UIM_DECIMAL);
911 #else
912                                 o = _uintmaxtostr(buf + sizeof(buf) - 1,
913                                                                   (uintmax_t) t,
914                                                                   -10, __UIM_DECIMAL);
915 #endif
916                                 o_count = sizeof(buf);
917                                 goto OUTPUT;
918                         } else if (((*p) | 0x20) == 'z') { /* 'z' or 'Z' */
919
920                                 if (timeptr->tm_isdst < 0) {
921                                         /* SUSv3 specifies this behavior for 'z', but we'll also
922                                          * treat it as "no timezone info" for 'Z' too. */
923                                         o_count = 0;
924                                         goto OUTPUT;
925                                 }
926
927                                 TZLOCK;
928
929                                 rsp = _time_tzinfo;
930                                 if (timeptr->tm_isdst > 0) {
931                                         ++rsp;
932                                 }
933
934                                 if (*p == 'Z') {
935                                         o = rsp->tzname;
936                                         assert(o != NULL);
937 #if 0
938                                         if (!o) {       /* PARANOIA */
939                                                 o = spec+30; /* empty string */
940                                         }
941 #endif
942                                         o_count = SIZE_MAX;
943                                         TZUNLOCK;
944                                         goto OUTPUT;
945                                 } else {                /* z */
946                                         *s = '+';
947                                         if ((tzo = -rsp->gmt_offset) < 0) {
948                                                 tzo = -tzo;
949                                                 *s = '-';
950                                         }
951                                         TZUNLOCK;
952                                         ++s;
953                                         --count;
954
955                                         i = tzo / 60;
956                                         field_val = ((i / 60) * 100) + (i % 60);
957                         
958                                         i = 16 + 6;     /* 0-fill, width = 4 */
959                                 }
960                         } else {
961                                 /* TODO: don't need year for U, W */
962                                 for (i=0 ; i < 3 ; i++) {
963                                         if ((x[i] = load_field(spec[CALC_OFFSETS+i],timeptr)) < 0) {
964                                                 goto OUTPUT;
965                                         }
966                                 }
967
968                                 i = 16 + 2;             /* 0-fill, width = 2 */
969
970                                 if ((*p == 'U') || (*p == 'W')) {
971                                         field_val = ((x[1] - x[0]) + 7);
972                                         if (*p == 'W') {
973                                                 ++field_val;
974                                         }
975                                         field_val /= 7;
976                                         if ((*p == 'W') && !x[0]) {
977                                                 --field_val;
978                                         }
979                                 } else {        /* ((*p == 'g') || (*p == 'G') || (*p == 'V')) */
980                                 ISO_LOOP:
981                                         isofm = (((x[1] - x[0]) + 11) % 7) - 3; /* [-3,3] */
982
983                                         if (x[1] < isofm) {     /* belongs to previous year */
984                                                 --x[2];
985                                                 x[1] += 365 + __isleap(x[2]);
986                                                 goto ISO_LOOP;
987                                         }
988
989                                         field_val = ((x[1] - isofm) / 7) + 1; /* week # */
990                                         days = 365 + __isleap(x[2]);
991                                         isofm = ((isofm + 7*53 + 3 - days)) %7 + days - 3; /* next year */
992                                         if (x[1] >= isofm) { /* next year */
993                                                 x[1] -= days;
994                                                 ++x[2];
995                                                 goto ISO_LOOP;
996                                         }
997
998                                         if (*p != 'V') { /* need year */
999                                                 field_val = x[2]; /* TODO: what if x[2] now 10000 ?? */
1000                                                 if (*p == 'g') {
1001                                                         field_val %= 100;
1002                                                 } else {
1003                                                         i = 16 + 6;     /* 0-fill, width = 4 */
1004                                                 }
1005                                         }
1006                                 }
1007                         }
1008                 } else {
1009                         i = TP_OFFSETS + (code & 0x1f);
1010                         if ((field_val = load_field(spec[i],timeptr)) < 0) {
1011                                 goto OUTPUT;
1012                         }
1013
1014                         i = spec[i+(TP_CODES - TP_OFFSETS)];
1015
1016                         j = (i & 128) ? 100: 12;
1017                         if (i & 64) {
1018                                 field_val /= j;;
1019                         }
1020                         if (i & 32) {
1021                                 field_val %= j;
1022                                 if (((i&128) + field_val) == 0) { /* mod 12? == 0 */
1023                                         field_val = j; /* set to 12 */
1024                                 }
1025                         }
1026                         field_val += (i & 1);
1027                         if ((i & 8) && !field_val) {
1028                                 field_val += 7;
1029                         }
1030                 }
1031                 
1032                 if ((code & MASK_SPEC) == STRING_SPEC) {
1033                         o_count = SIZE_MAX;
1034                         field_val += spec[STRINGS_NL_ITEM_START + (code & 0xf)];
1035                         o = nl_langinfo(_NL_ITEM(LC_TIME, field_val));
1036                 } else {
1037                         o_count = ((i >> 1) & 3) + 1;
1038                         o = buf + o_count;
1039                         do {
1040                                 *(char *)(--o) = '0' + (field_val % 10);
1041                                 field_val /= 10;
1042                         } while (o > buf);
1043                         if (*buf == '0') {
1044                                 *buf = ' ' + (i & 16);
1045                         }
1046                 }
1047         }
1048
1049  OUTPUT:
1050         ++p;
1051         while (o_count && count && *o) {
1052                 *s++ = *o++;
1053                 --o_count;
1054                 --count;
1055         }
1056         goto LOOP;
1057 }
1058
1059 #endif
1060 /**********************************************************************/
1061 #ifdef L_strptime
1062
1063 /* TODO:
1064  * 1) %l and %k are space-padded, so "%l" by itself fails while " %l" succeeds.
1065  *    Both work for glibc.  So, should we always strip spaces?
1066  * 2) %Z
1067  */
1068
1069 /* Notes:
1070  * There are several differences between this strptime and glibc's strptime.
1071  * 1) glibc strips leading space before numeric conversions.
1072  * 2) glibc will read fields without whitespace in between.  SUSv3 states
1073  *    that you must have whitespace between conversion operators.  Besides,
1074  *    how do you know how long a number should be if there are leading 0s?
1075  * 3) glibc attempts to compute some the struct tm fields based on the
1076  *    data retrieved; tm_wday in particular.  I don't as I consider it
1077  *     another glibc attempt at mind-reading...
1078  */
1079
1080 #define NO_E_MOD                0x80
1081 #define NO_O_MOD                0x40
1082
1083 #define ILLEGAL_SPEC    0x3f
1084
1085 #define INT_SPEC                0x00    /* must be 0x00!! */
1086 #define STRING_SPEC             0x10    /* must be 0x10!! */
1087 #define CALC_SPEC               0x20
1088 #define STACKED_SPEC    0x30
1089
1090 #define MASK_SPEC               0x30
1091
1092 /* Warning: Assumes ASCII values! (as do lots of other things in the lib...) */
1093 static const unsigned char spec[] = {
1094         /* A */         0x02 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
1095         /* B */         0x01 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
1096         /* C */         0x08 |     INT_SPEC            | NO_O_MOD,
1097         /* D */         0x01 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1098         /* E */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1099         /* F */         0x02 | STACKED_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1100         /* G */         0x0f |     INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1101         /* H */         0x06 |     INT_SPEC | NO_E_MOD,
1102         /* I */         0x07 |     INT_SPEC | NO_E_MOD,
1103         /* J */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1104         /* K */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1105         /* L */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1106         /* M */         0x04 |     INT_SPEC | NO_E_MOD,
1107         /* N */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1108         /* O */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1109         /* P */         0x00 |  STRING_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1110         /* Q */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1111         /* R */         0x03 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1112         /* S */         0x05 |     INT_SPEC | NO_E_MOD,
1113         /* T */         0x04 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1114         /* U */         0x0c |     INT_SPEC | NO_E_MOD,
1115         /* V */         0x0d |     INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1116         /* W */         0x0c |     INT_SPEC | NO_E_MOD,
1117         /* X */         0x0a | STACKED_SPEC            | NO_O_MOD,
1118         /* Y */         0x0a |     INT_SPEC            | NO_O_MOD,
1119         /* Z */         0x02 |    CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1120
1121         /* WARNING! This assumes orderings:
1122          *    AM,PM
1123          *    ABDAY_1-ABDAY-7,DAY_1-DAY_7
1124          *    ABMON_1-ABMON_12,MON_1-MON12
1125          * Also, there are exactly 6 bytes between 'Z' and 'a'.
1126          */
1127 #define STRINGS_NL_ITEM_START (26)
1128         _NL_ITEM_INDEX(AM_STR),         /* p (P) */
1129         _NL_ITEM_INDEX(ABMON_1),        /* B, b */
1130         _NL_ITEM_INDEX(ABDAY_1),        /* A, a */
1131         2,
1132         24,
1133         14,
1134
1135         /* a */         0x02 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
1136         /* b */         0x01 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
1137         /* c */         0x08 | STACKED_SPEC            | NO_O_MOD,
1138         /* d */         0x00 |     INT_SPEC | NO_E_MOD,
1139         /* e */         0x00 |     INT_SPEC | NO_E_MOD,
1140         /* f */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1141         /* g */         0x0e |     INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1142         /* h */         0x01 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
1143         /* i */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1144         /* j */         0x01 |     INT_SPEC | NO_E_MOD | NO_O_MOD,
1145         /* k */         0x06 |     INT_SPEC | NO_E_MOD,            /* glibc */
1146         /* l */         0x07 |     INT_SPEC | NO_E_MOD,            /* glibc */
1147         /* m */         0x02 |     INT_SPEC | NO_E_MOD,
1148         /* n */         0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1149         /* o */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1150         /* p */         0x00 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
1151         /* q */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1152         /* r */         0x0b | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1153         /* s */         0x00 |    CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1154         /* t */         0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1155         /* u */         0x0b |     INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1156         /* v */                ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1157         /* w */         0x03 |     INT_SPEC | NO_E_MOD,
1158         /* x */         0x09 | STACKED_SPEC            | NO_O_MOD,
1159         /* y */         0x09 |     INT_SPEC,
1160         /* z */         0x01 |    CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1161
1162 #define INT_FIELD_START (26+6+26)
1163         /* (field #) << 3  + lower bound (0|1) + correction 0:none, 2:-1, 4:-1900
1164          * followed by upper bound prior to correction with 1=>366 and 2=>9999. */
1165         /* d, e */      (3 << 3) + 1 + 0, 31,
1166         /* j */         (7 << 3) + 1 + 2, /* 366 */ 1,
1167         /* m */         (4 << 3) + 1 + 2, 12,
1168         /* w */         (6 << 3) + 0 + 0, 6,
1169         /* M */         (1 << 3) + 0 + 0, 59,
1170         /* S */         0        + 0 + 0, 60,
1171         /* H (k) */     (2 << 3) + 0 + 0, 23,
1172         /* I (l) */     (9 << 3) + 1 + 0, 12, /* goes with 8 -- am/pm */
1173         /* C */         (10<< 3) + 0 + 0, 99,
1174         /* y */         (11<< 3) + 0 + 0, 99,
1175         /* Y */         (5 << 3) + 0 + 4, /* 9999 */ 2,
1176         /* u */         (6 << 3) + 1 + 0, 7,
1177         /* The following are processed and range-checked, but ignored otherwise. */
1178         /* U, W */      (12<< 3) + 0 + 0, 53,
1179         /* V */         (12<< 3) + 1 + 0, 53,
1180         /* g */         (12<< 3) + 0 + 0, 99,
1181         /* G */         (12<< 3) + 0 /*+ 4*/, /* 9999 */ 2, /* Note: -1 or 10000? */
1182
1183 #define STACKED_STRINGS_START (INT_FIELD_START+32)
1184         5, 6, 14, 22, 27,                       /* 5 - offsets from offset-count to strings */
1185         ' ', 0,                                         /* 2 - %n or %t */
1186         '%', 'm', '/', '%', 'd', '/', '%', 'y', 0, /* 9 - %D */
1187         '%', 'Y', '-', '%', 'm', '-', '%', 'd', 0, /* 9 - %F (glibc extension) */
1188         '%', 'H', ':', '%', 'M', 0,     /* 6 - %R*/
1189         '%', 'H', ':', '%', 'M', ':', '%', 'S', 0, /* 9 - %T */
1190
1191 #define STACKED_STRINGS_NL_ITEM_START (STACKED_STRINGS_START + 40)
1192         _NL_ITEM_INDEX(D_T_FMT),        /* c */
1193         _NL_ITEM_INDEX(D_FMT),          /* x */
1194         _NL_ITEM_INDEX(T_FMT),          /* X */
1195         _NL_ITEM_INDEX(T_FMT_AMPM), /* r */
1196 #ifdef ENABLE_ERA_CODE
1197         _NL_ITEM_INDEX(ERA_D_T_FMT), /* Ec */
1198         _NL_ITEM_INDEX(ERA_D_FMT),      /* Ex */
1199         _NL_ITEM_INDEX(ERA_T_FMT),      /* EX */
1200 #endif
1201 };
1202
1203 #define MAX_PUSH 4
1204
1205 char *strptime(const char *__restrict buf, const char *__restrict format,
1206                            struct tm *__restrict tm)
1207 {
1208         register const char *p;
1209         char *o;
1210         const char *stack[MAX_PUSH];
1211         int i, j, lvl;
1212         int fields[13];
1213         unsigned char mod;
1214         unsigned char code;
1215
1216         i = 0;
1217         do {
1218                 fields[i] = INT_MIN;
1219         } while (++i < 13);
1220
1221         lvl = 0;
1222         p = format;
1223
1224  LOOP:
1225         if (!*p) {
1226                 if (lvl == 0) {                 /* Done. */
1227                         if (fields[6] == 7) { /* Cleanup for %u here since just once. */
1228                                 fields[6] = 0;  /* Don't use mod in case unset. */
1229                         }
1230
1231                         i = 0;
1232                         do {                            /* Store the values into tm. */
1233                                 ((int *) tm)[i] = fields[i];
1234                         } while (++i < 8);
1235
1236                         return (char *) buf; /* Success. */
1237                 }
1238                 p = stack[--lvl];
1239                 goto LOOP;
1240         }
1241
1242         if ((*p == '%') && (*++p != '%')) {
1243                 mod = ILLEGAL_SPEC;
1244                 if ((*p == 'O') || (*p == 'E')) { /* Modifier? */
1245                         mod |= ((*p == 'O') ? NO_O_MOD : NO_E_MOD);
1246                         ++p;
1247                 }
1248
1249                 if (!*p
1250                         || (((unsigned char)(((*p) | 0x20) - 'a')) >= 26)
1251                         || (((code = spec[(int)(*p - 'A')]) & mod) >= ILLEGAL_SPEC)
1252                         ) {
1253                         return NULL;            /* Illegal spec. */
1254                 }
1255
1256                 if ((code & MASK_SPEC) == STACKED_SPEC) {
1257                         if (lvl == MAX_PUSH) {
1258                                 return NULL;    /* Stack full so treat as illegal spec. */
1259                         }
1260                         stack[lvl++] = ++p;
1261                         if ((code &= 0xf) < 8) {
1262                                 p = ((const char *) spec) + STACKED_STRINGS_START + code;
1263                                 p += *((unsigned char *)p);
1264                                 goto LOOP;
1265                         }
1266
1267                         p = ((const char *) spec) + STACKED_STRINGS_NL_ITEM_START
1268                                 + (code & 7);
1269 #ifdef ENABLE_ERA_CODE
1270                         if ((mod & NO_E_MOD) /* Actually, this means E modifier present. */
1271                                 && (*(o = nl_langinfo(_NL_ITEM(LC_TIME,
1272                                                                                            (int)(((unsigned char *)p)[4]))
1273                                                                           )))
1274                                 ) {
1275                                 p = o;
1276                                 goto LOOP;
1277                         }
1278 #endif
1279                         p = nl_langinfo(_NL_ITEM(LC_TIME,
1280                                                                          (int)(*((unsigned char *)p))));
1281                         goto LOOP;
1282                 }
1283
1284                 ++p;
1285
1286                 if ((code & MASK_SPEC) == STRING_SPEC) {
1287                         code &= 0xf;
1288                         j = spec[STRINGS_NL_ITEM_START + 3 + code];
1289                         i = _NL_ITEM(LC_TIME, spec[STRINGS_NL_ITEM_START + code]);
1290                         /* Go backwards to check full names before abreviations. */
1291                         do {
1292                                 --j;
1293                                 o = nl_langinfo(i+j);
1294                                 if (!strncasecmp(buf,o,strlen(o)) && *o) { /* Found a match. */
1295                                         do {
1296                                                 ++buf;
1297                                         } while (*++o);
1298                                         if (!code) { /* am/pm */
1299                                                 fields[8] = j * 12;
1300                                                 if (fields[9] >= 0) { /* We have a previous %I or %l. */
1301                                                         fields[2] = fields[9] + fields[8];
1302                                                 }
1303                                         } else {        /* day (4) or month (6) */
1304                                                 fields[2 + (code << 1)]
1305                                                         = j % (spec[STRINGS_NL_ITEM_START + 3 + code] >> 1);
1306                                         }
1307                                         goto LOOP;
1308                                 }
1309                         } while (j);
1310                         return NULL;            /* Failed to match. */
1311                 }
1312
1313                 if ((code & MASK_SPEC) == CALC_SPEC) {
1314                         if ((code &= 0xf) < 1) { /* s or z*/
1315                                 time_t t;
1316
1317                                 o = (char *) buf;
1318                                 i = errno;
1319                                 __set_errno(0);
1320                                 if (!isspace(*buf)) { /* Signal an error if whitespace. */
1321 #ifdef TIME_T_IS_UNSIGNED
1322                                         t = strtoul(buf, &o, 10);
1323 #else
1324                                         t = strtol(buf, &o, 10);
1325 #endif
1326                                 }
1327                                 if ((o == buf) || errno) { /* Not a number or overflow. */
1328                                         return NULL;
1329                                 }
1330                                 __set_errno(i); /* Restore errno. */
1331                                 buf = o;
1332
1333                                 if (!code) {    /* s */
1334                                         localtime_r(&t, tm); /* TODO: check for failure? */
1335                                         i = 0;
1336                                         do {            /* Now copy values from tm to fields. */
1337                                                  fields[i] = ((int *) tm)[i];
1338                                         } while (++i < 8);
1339                                 }
1340                         }
1341                         /* TODO: glibc treats %Z as a nop.  For now, do the same. */
1342                         goto LOOP;
1343                 }
1344
1345                 assert((code & MASK_SPEC) == INT_SPEC);
1346                 {
1347                         register const unsigned char *x;
1348                         code &= 0xf;
1349                         x = spec + INT_FIELD_START + (code << 1);
1350                         if ((j = x[1]) < 3) { /* upper bound (inclusive) */
1351                                 j = ((j==1) ? 366 : 9999);
1352                         }
1353                         i = -1;
1354                         while (isdigit(*buf)) {
1355                                 if (i < 0) {
1356                                         i = 0;
1357                                 }
1358                                 if ((i = 10*i + (*buf - '0')) > j) { /* Overflow. */
1359                                         return NULL;
1360                                 }
1361                                 ++buf;
1362                         }
1363                         if (i < (*x & 1)) {     /* This catches no-digit case too. */
1364                                 return NULL;
1365                         }
1366                         if (*x & 2) {
1367                                 --i;
1368                         }
1369                         if (*x & 4) {
1370                                 i -= 1900;
1371                         }
1372
1373                         if (*x == (9 << 3) + 1 + 0) { /* %I or %l */
1374                                 if (i == 12) {
1375                                         i = 0;
1376                                 }
1377                                 if (fields[8] >= 0) { /* We have a previous %p or %P. */
1378                                         fields[2] = i + fields[8];
1379                                 }
1380                         }
1381
1382                         fields[(*x) >> 3] = i;
1383
1384                         if (((unsigned char)(*x - (10<< 3) + 0 + 0)) <= 8) { /* %C or %y */
1385                                 if ((j = fields[10]) < 0) {     /* No %C, so i must be %y data. */
1386                                         if (i <= 68) { /* Map [0-68] to 2000+i */
1387                                                 i += 100;
1388                                         }
1389                                 } else {                /* Have %C data, but what about %y? */
1390                                         if ((i = fields[11]) < 0) {     /* No %y data. */
1391                                                 i = 0;  /* Treat %y val as 0 following glibc's example. */
1392                                         }
1393                                         i += 100*(j - 19);
1394                                 }
1395                                 fields[5] = i;
1396                         }
1397                 }
1398                 goto LOOP;
1399         } else if (isspace(*p)) {
1400                 ++p;
1401                 while (isspace(*buf)) {
1402                         ++buf;
1403                 }
1404                 goto LOOP;
1405         } else if (*buf++ == *p++) {
1406                 goto LOOP;
1407         }
1408         return NULL;
1409 }
1410
1411 #endif
1412 /**********************************************************************/
1413 #ifdef L_time
1414
1415 #ifndef __BCC__
1416 #error The uClibc version of time is in sysdeps/linux/common.
1417 #endif
1418
1419 time_t time(register time_t *tloc)
1420 {
1421         struct timeval tv;
1422         register struct timeval *p = &tv;
1423
1424         gettimeofday(p, NULL);          /* This should never fail... */
1425
1426         if (tloc) {
1427                 *tloc = p->tv_sec;
1428         }
1429
1430         return p->tv_sec;
1431 }
1432
1433 #endif
1434 /**********************************************************************/
1435 #ifdef L_tzset
1436
1437 static const char vals[] = {
1438         'T', 'Z', 0,                            /* 3 */
1439         'U', 'T', 'C', 0,                       /* 4 */
1440         25, 60, 60, 1,                          /* 4 */
1441         '.', 1,                                         /* M */
1442         5, '.', 1,
1443         6,  0,  0,                                      /* Note: overloaded for non-M non-J case... */
1444         0, 1, 0,                                        /* J */
1445         ',', 'M',      '4', '.', '1', '.', '0',
1446         ',', 'M', '1', '0', '.', '5', '.', '0', 0
1447 };
1448
1449 #define TZ    vals
1450 #define UTC   (vals + 3)
1451 #define RANGE (vals + 7)
1452 #define RULE  (vals + 11 - 1)
1453 #define DEFAULT_RULES (vals + 22)
1454
1455 /* Initialize to UTC. */
1456 int daylight = 0;
1457 long timezone = 0;
1458 char *tzname[2] = { (char *) UTC, (char *) (UTC-1) };
1459
1460 #ifdef __UCLIBC_HAS_THREADS__
1461 pthread_mutex_t _time_tzlock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
1462 #endif
1463
1464 rule_struct _time_tzinfo[2];
1465
1466 static const char *getoffset(register const char *e, long *pn)
1467 {
1468         register const char *s = RANGE-1;
1469         long n;
1470         int f;
1471
1472         n = 0;
1473         f = -1;
1474         do {
1475                 ++s;
1476                 if (isdigit(*e)) {
1477                         f = *e++ - '0';
1478                 }
1479                 if (isdigit(*e)) {
1480                         f = 10 * f + (*e++ - '0');
1481                 }
1482                 if (((unsigned int)f) >= *s) {
1483                         return NULL;
1484                 }
1485                 n = (*s) * n + f;
1486                 f = 0;
1487                 if (*e == ':') {
1488                         ++e;
1489                         --f;
1490                 }
1491         } while (*s > 1);
1492
1493         *pn = n;
1494         return e;
1495 }
1496
1497 static const char *getnumber(register const char *e, int *pn)
1498 {
1499 #ifdef __BCC__
1500         /* bcc can optimize the counter if it thinks it is a pointer... */
1501         register const char *n = (const char *) 3;
1502         int f;
1503
1504         f = 0;
1505         while (n && isdigit(*e)) {
1506                 f = 10 * f + (*e++ - '0');
1507                 --n;
1508         }
1509
1510         *pn = f;
1511         return (n == (const char *) 3) ? NULL : e;
1512 #else  /* __BCC__ */
1513         int n, f;
1514
1515         n = 3;
1516         f = 0;
1517         while (n && isdigit(*e)) {
1518                 f = 10 * f + (*e++ - '0');
1519                 --n;
1520         }
1521
1522         *pn = f;
1523         return (n == 3) ? NULL : e;
1524 #endif /* __BCC__ */
1525 }
1526
1527 #ifdef __TIME_TZ_FILE
1528
1529 #ifdef __TIME_TZ_FILE_ONCE
1530 static int TZ_file_read;                /* Let BSS initialization set this to 0. */
1531 #endif /* __TIME_TZ_FILE_ONCE */
1532
1533 static char *read_TZ_file(char *buf)
1534 {
1535         int fd;
1536         ssize_t r;
1537         size_t todo;
1538         char *p = NULL;
1539
1540         if ((fd = open("/etc/TZ", O_RDONLY)) >= 0) {
1541                 todo = TZ_BUFLEN;
1542                 p = buf;
1543                 do {
1544                         if ((r = read(fd, p, todo)) < 0) {
1545                                 goto ERROR;
1546                         }
1547                         if (r == 0) {
1548                                 break;
1549                         }
1550                         p += r;
1551                         todo -= r;
1552                 } while (todo);
1553
1554                 if ((p > buf) && (p[-1] == '\n')) {     /* Must end with newline. */
1555                         p[-1] = 0;
1556                         p = buf;
1557 #ifdef __TIME_TZ_FILE_ONCE
1558                         ++TZ_file_read;
1559 #endif /* __TIME_TZ_FILE_ONCE */
1560                 } else {
1561                 ERROR:
1562                         p = NULL;
1563                 }
1564                 close(fd);
1565         }
1566         return p;
1567 }
1568
1569 #endif /* __TIME_TZ_FILE */
1570
1571 void tzset(void)
1572 {
1573         register const char *e;
1574         register char *s;
1575         long off;
1576         short *p;
1577         rule_struct new_rules[2];
1578         int n, count, f;
1579         char c;
1580 #ifdef __TIME_TZ_FILE
1581         char buf[TZ_BUFLEN];
1582 #endif /* __TIME_TZ_FILE */
1583 #ifdef __TIME_TZ_OPT_SPEED
1584         static char oldval[TZ_BUFLEN]; /* BSS-zero'd. */
1585 #endif /* __TIME_TZ_OPT_SPEED */
1586
1587         TZLOCK;
1588
1589         e = getenv(TZ);                         /* TZ env var always takes precedence. */
1590
1591 #ifdef __TIME_TZ_FILE_ONCE
1592         /* Put this inside the lock to prevent the possiblity of two different
1593          * timezones being used in a threaded app. */
1594
1595         if (e != NULL) {
1596                 TZ_file_read = 0;               /* Reset if the TZ env var is set. */
1597         } else if (TZ_file_read > 0) {
1598                 goto FAST_DONE;
1599         }
1600 #endif /* __TIME_TZ_FILE_ONCE */
1601
1602         /* Warning!!!  Since uClibc doesn't do lib locking, the following is
1603          * potentially unsafe in a multi-threaded program since it is remotely
1604          * possible that another thread could call setenv() for TZ and overwrite
1605          * the string being parsed.  So, don't do that... */
1606
1607         if ((!e                                         /* TZ env var not set... */
1608 #ifdef __TIME_TZ_FILE
1609                  && !(e = read_TZ_file(buf)) /* and no file or invalid file */
1610 #endif /* __TIME_TZ_FILE */
1611                  ) || !*e) {                    /* or set to empty string. */
1612         ILLEGAL:                                        /* TODO: Clean up the following... */
1613 #ifdef __TIME_TZ_OPT_SPEED
1614                 *oldval = 0;                    /* Set oldval tonnn empty string. */
1615 #endif /* __TIME_TZ_OPT_SPEED */
1616                 s = _time_tzinfo[0].tzname;
1617                 *s = 'U';
1618                 *++s = 'T';
1619                 *++s = 'C';
1620                 *++s =
1621                 *_time_tzinfo[1].tzname = 0;
1622                 _time_tzinfo[0].gmt_offset = 0;
1623                 goto DONE;
1624         }
1625
1626         if (*e == ':') {                        /* Ignore leading ':'. */
1627                 ++e;
1628         }
1629
1630 #ifdef __TIME_TZ_OPT_SPEED
1631         if (strcmp(e, oldval) == 0) { /* Same string as last time... */
1632                 goto FAST_DONE;                 /* So nothing to do. */
1633         }
1634         /* Make a copy of the TZ env string.  It won't be nul-terminated if
1635          * it is too long, but it that case it will be illegal and will be reset
1636          * to the empty string anyway. */
1637         strncpy(oldval, e, TZ_BUFLEN);
1638 #endif /* __TIME_TZ_OPT_SPEED */
1639         
1640         count = 0;
1641         new_rules[1].tzname[0] = 0;
1642  LOOP:
1643         /* Get std or dst name. */
1644         c = 0;
1645         if (*e == '<') {
1646                 ++e;
1647                 c = '>';
1648         }
1649
1650         s = new_rules[count].tzname;
1651         n = 0;
1652         while (*e
1653                    && isascii(*e)               /* SUSv3 requires char in portable char set. */
1654                    && (isalpha(*e)
1655                            || (c && (isalnum(*e) || (*e == '+') || (*e == '-'))))
1656                    ) {
1657                 *s++ = *e++;
1658                 if (++n > TZNAME_MAX) {
1659                         goto ILLEGAL;
1660                 }
1661         }
1662         *s = 0;
1663
1664         if ((n < 3)                                     /* Check for minimum length. */
1665                 || (c && (*e++ != c))   /* Match any quoting '<'. */
1666                 ) {
1667                 goto ILLEGAL;
1668         }
1669
1670         /* Get offset */
1671         s = (char *) e;
1672         if ((*e != '-') && (*e != '+')) {
1673                 if (count && !isdigit(*e)) {
1674                         off -= 3600;            /* Default to 1 hour ahead of std. */
1675                         goto SKIP_OFFSET;
1676                 }
1677                 --e;
1678         }
1679
1680         ++e;
1681         if (!(e = getoffset(e, &off))) {
1682                 goto ILLEGAL;
1683         }
1684
1685         if (*s == '-') {
1686                 off = -off;                             /* Save off in case needed for dst default. */
1687         }
1688  SKIP_OFFSET:
1689         new_rules[count].gmt_offset = off;
1690
1691         if (!count) {
1692                 if (*e) {
1693                         ++count;
1694                         goto LOOP;
1695                 }
1696         } else {                                        /* OK, we have dst, so get some rules. */
1697                 count = 0;
1698                 if (!*e) {                              /* No rules so default to US rules. */
1699                         e = DEFAULT_RULES;
1700                 }
1701
1702                 do {
1703                         if (*e++ != ',') {
1704                                 goto ILLEGAL;
1705                         }
1706
1707                         n = 365;
1708                         s = (char *) RULE;
1709                         if ((c = *e++) == 'M') {
1710                                 n = 12;
1711                         } else if (c == 'J') {
1712                                 s += 8;
1713                         } else {
1714                                 --e;
1715                                 c = 0;
1716                                 s += 6;
1717                         }
1718
1719                         *(p = &new_rules[count].rule_type) = c;
1720                         if (c != 'M') {
1721                                 p -= 2;
1722                         }
1723
1724                         do {
1725                                 ++s;
1726                                 if (!(e = getnumber(e, &f))
1727                                         || (((unsigned int)(f - s[1])) > n)
1728                                         || (*s && (*e++ != *s))
1729                                         ) {
1730                                         goto ILLEGAL;
1731                                 }
1732                                 *--p = f;
1733                         } while ((n = *(s += 2)) > 0);
1734
1735                         off = 2 * 60 * 60;      /* Default to 2:00:00 */
1736                         if (*e == '/') {
1737                                 ++e;
1738                                 if (!(e = getoffset(e, &off))) {
1739                                         goto ILLEGAL;
1740                                 }
1741                         }
1742                         new_rules[count].dst_offset = off;
1743                 } while (++count < 2);
1744
1745                 if (*e) {
1746                         goto ILLEGAL;
1747                 }
1748         }
1749
1750         memcpy(_time_tzinfo, new_rules, sizeof(new_rules));
1751  DONE:
1752         tzname[0] = _time_tzinfo[0].tzname;
1753         tzname[1] = _time_tzinfo[1].tzname;
1754         daylight = !!_time_tzinfo[1].tzname[0];
1755         timezone = _time_tzinfo[0].gmt_offset;
1756
1757  FAST_DONE:
1758         TZUNLOCK;
1759 }
1760
1761 #endif
1762 /**********************************************************************/
1763 /*  #ifdef L_utime */
1764
1765 /* utime is a syscall in both linux and elks. */
1766 /*  int utime(const char *path, const struct utimbuf *times) */
1767
1768 /*  #endif */
1769 /**********************************************************************/
1770 /* Non-SUSv3 */
1771 /**********************************************************************/
1772 #ifdef L_utimes
1773
1774 #ifndef __BCC__
1775 #error The uClibc version of utimes is in sysdeps/linux/common.
1776 #endif
1777
1778 #include <utime.h>
1779 #include <sys/time.h>
1780
1781 int utimes(const char *filename, register const struct timeval *tvp)
1782 {
1783         register struct utimbuf *p = NULL;
1784         struct utimbuf utb;
1785
1786         if (tvp) {
1787                 p = &utb;
1788                 p->actime = tvp[0].tv_sec;
1789                 p->modtime = tvp[1].tv_sec;
1790         }
1791         return utime(filename, p);
1792 }
1793
1794 #endif
1795 /**********************************************************************/
1796 #ifdef L__time_t2tm
1797
1798 static const uint16_t vals[] = {
1799         60, 60, 24, 7 /* special */, 36524, 1461, 365, 0
1800 };
1801
1802 static const unsigned char days[] = {
1803         31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, /* non-leap */
1804             29,
1805 };
1806
1807 /* Notes:
1808  * If time_t is 32 bits, then no overflow is possible.
1809  * It time_t is > 32 bits, this needs to be adjusted to deal with overflow.
1810  */
1811
1812 /* Note: offset is the correction in _days_ to *timer! */
1813
1814 struct tm *_time_t2tm(const time_t *__restrict timer,
1815                                           int offset, struct tm *__restrict result)
1816 {
1817         register int *p;
1818         time_t t1, t, v;
1819         int wday;                                       /* Note: wday can be uninitialized. */
1820
1821         {
1822                 register const uint16_t *vp;
1823                 t = *timer;
1824                 p = (int *) result;
1825                 p[7] = 0;
1826                 vp = vals;
1827                 do {
1828                         if ((v = *vp) == 7) {
1829                                 /* Overflow checking, assuming time_t is long int... */
1830 #if (LONG_MAX > INT_MAX) && (LONG_MAX > 2147483647L)
1831 #if (INT_MAX == 2147483647L) && (LONG_MAX == 9223372036854775807L)
1832                                 /* Valid range for t is [-784223472856L, 784223421720L].
1833                                  * Outside of this range, the tm_year field will overflow. */
1834                                 if (((unsigned long)(t + offset- -784223472856L))
1835                                         > (784223421720L - -784223472856L)
1836                                         ) {
1837                                         return NULL;
1838                                 }
1839 #else
1840 #error overflow conditions unknown
1841 #endif
1842 #endif
1843
1844                                 /* We have days since the epoch, so caluclate the weekday. */
1845 #if defined(__BCC__) && TIME_T_IS_UNSIGNED
1846                                 wday = (t + 4) % (*vp); /* t is unsigned */
1847 #else
1848                                 wday = ((int)((t % (*vp)) + 11)) % ((int)(*vp)); /* help bcc */
1849 #endif
1850                                 /* Set divisor to days in 400 years.  Be kind to bcc... */
1851                                 v = ((time_t)(vp[1])) << 2;
1852                                 ++v;
1853                                 /* Change to days since 1/1/1601 so that for 32 bit time_t
1854                                  * values, we'll have t >= 0.  This should be changed for
1855                                  * archs with larger time_t types. 
1856                                  * Also, correct for offset since a multiple of 7. */
1857
1858                                 /* TODO: Does this still work on archs with time_t > 32 bits? */
1859                                 t += (135140L - 366) + offset; /* 146097 - (365*30 + 7) -366 */
1860                         }
1861 #if defined(__BCC__) && TIME_T_IS_UNSIGNED
1862                         t -= ((t1 = t / v) * v);
1863 #else
1864                         if ((t -= ((t1 = t / v) * v)) < 0) {
1865                                 t += v;
1866                                 --t1;
1867                         }
1868 #endif
1869
1870                         if ((*vp == 7) && (t == v-1)) {
1871                                 --t;                    /* Correct for 400th year leap case */
1872                                 ++p[4];                 /* Stash the extra day... */
1873                         }
1874
1875 #if defined(__BCC__) && 0
1876                         *p = t1;
1877                         if (v <= 60) {
1878                                 *p = t;
1879                                 t = t1;
1880                         }
1881                         ++p;
1882 #else
1883                         if (v <= 60) {
1884                                 *p++ = t;
1885                                 t = t1;
1886                         } else {
1887                                 *p++ = t1;
1888                         }
1889 #endif
1890                 } while (*++vp);
1891         }
1892
1893         if (p[-1] == 4) {
1894                 --p[-1];
1895                 t = 365;
1896         }
1897
1898
1899         *p += ((int) t);                        /* result[7] .. tm_yday */
1900
1901         p -= 2;                                         /* at result[5] */
1902
1903 #if (LONG_MAX > INT_MAX) && (LONG_MAX > 2147483647L)
1904         /* Protect against overflow.  TODO: Unecessary if int arith wraps? */
1905         *p = ((((p[-2]<<2) + p[-1])*25 + p[0])<< 2) + (p[1] - 299); /* tm_year */
1906 #else
1907         *p = ((((p[-2]<<2) + p[-1])*25 + p[0])<< 2) + p[1] - 299; /* tm_year */
1908 #endif
1909
1910         p[1] = wday;                            /* result[6] .. tm_wday */
1911
1912         {
1913                 register const unsigned char *d = days;
1914
1915                 wday = 1900 + *p;
1916                 if (__isleap(wday)) {
1917                         d += 11;
1918                 }
1919
1920                 wday = p[2] + 1;                /* result[7] .. tm_yday */
1921                 *--p = 0;                               /* at result[4] .. tm_mon */
1922                 while (wday > *d) {
1923                         wday -= *d;
1924                         if (*d == 29) {
1925                                 d -= 11;                /* Backup to non-leap Feb. */
1926                         }
1927                         ++d;
1928                         ++*p;                           /* Increment tm_mon. */
1929                 }
1930                 p[-1] = wday;                   /* result[3] .. tm_mday */
1931         }
1932         /* TODO -- should this be 0? */
1933         p[4] = 0;                                       /* result[8] .. tm_isdst */
1934
1935         return result;
1936 }
1937
1938 #endif
1939 /**********************************************************************/
1940 #ifdef L___time_tm
1941
1942 struct tm __time_tm;    /* Global shared by gmtime() and localtime(). */
1943
1944 #endif
1945 /**********************************************************************/
1946 #ifdef L__time_mktime
1947
1948 static const unsigned char vals[] = {
1949         31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, /* non-leap */
1950             29,
1951 };
1952
1953 time_t _time_mktime(struct tm *timeptr, int store_on_success)
1954 {
1955 #ifdef __BCC__
1956         long days, secs;
1957 #else
1958         long long secs;
1959 #endif
1960         time_t t;
1961         struct tm x;
1962         /* 0:sec  1:min  2:hour  3:mday  4:mon  5:year  6:wday  7:yday  8:isdst */
1963         register int *p = (int *) &x;
1964         register const unsigned char *s;
1965         int d;
1966
1967         tzset();
1968
1969         memcpy(p, timeptr, sizeof(struct tm));
1970
1971         d = 400;
1972         p[5] = (p[5] - ((p[6] = p[5]/d) * d)) + (p[7] = p[4]/12);
1973         if ((p[4] -= 12 * p[7]) < 0) {
1974                 p[4] += 12;
1975                 --p[5];
1976         }
1977
1978         s = vals;
1979         d = (p[5] += 1900);                     /* Correct year.  Now between 1900 and 2300. */
1980         if (__isleap(d)) {
1981                 s += 11;
1982         }
1983         
1984         p[7] = 0;
1985         d = p[4];
1986         while (d) {
1987                 p[7] += *s;
1988                 if (*s == 29) {
1989                         s -= 11;                        /* Backup to non-leap Feb. */
1990                 }
1991                 ++s;
1992                 --d;
1993         }
1994
1995 #ifdef __BCC__
1996         d = p[5] - 1;
1997         days = -719163L + ((long)d)*365 + ((d/4) - (d/100) + (d/400) + p[3] + p[7]);
1998         secs = p[0] + 60*( p[1] + 60*((long)(p[2])) )
1999                 + _time_tzinfo[timeptr->tm_isdst > 0].gmt_offset;
2000         if (secs < 0) {
2001                 secs += 120009600L;
2002                 days -= 1389;
2003         }
2004         if ( ((unsigned long)(days + secs/86400L)) > 49710L) {
2005                 return -1;
2006         }
2007         secs += (days * 86400L);
2008 #else
2009         TZLOCK;
2010         d = p[5] - 1;
2011         d = -719163L + d*365 + (d/4) - (d/100) + (d/400);
2012         secs = p[0]
2013                 + _time_tzinfo[timeptr->tm_isdst > 0].gmt_offset
2014                 + 60*( p[1]
2015                            + 60*(p[2]
2016                                          + 24*(((146073L * ((long long)(p[6])) + d)
2017                                                         + p[3]) + p[7])));
2018         TZUNLOCK;
2019         if (((unsigned long long)(secs - LONG_MIN))
2020                 > (((unsigned long long)LONG_MAX) - LONG_MIN)
2021                 ) {
2022                 return -1;
2023         }
2024 #endif
2025
2026         t = secs;
2027
2028         localtime_r(&t, (struct tm *)p);
2029
2030         if (t < 0) {
2031             return -1;
2032         }
2033
2034         if (store_on_success) {
2035                 memcpy(timeptr, p, sizeof(struct tm));
2036         }
2037
2038         return t;
2039 }
2040
2041 #endif
2042 /**********************************************************************/