uClibc machine match for EM_X86_64 (will now compile in a 64bit environment)
[tomato.git] / tools-src / uClibc / libc / misc / time / time.c
blobad5abe6a218866e9f8b508fb60ae7922aebec9df
1 /* Copyright (C) 2002 Manuel Novoa III
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.
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.
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.
18 /* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION!
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
27 * ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! */
29 /* June 15, 2002 Initial Notes:
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.
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
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.
42 * Differences from glibc worth noting:
44 * Leap seconds are not considered here.
46 * glibc stores additional timezone info the struct tm, whereas we don't.
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.
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.
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.
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.
69 * TODO - Implement getdate? tzfile? struct tm extensions?
71 * TODO - Rework _time_mktime to remove the dependency on long long.
74 /* Oct 28, 2002
76 * Fixed allowed char check for std and dst TZ fields.
78 * Added several options concerned with timezone support. The names will
79 * probably change once Erik gets the new config system in place.
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
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.
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.
94 * Nov 21, 2002 Fix an error return case in _time_mktime.
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.
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>
115 #ifndef __isleap
116 #define __isleap(y) ( !((y) % 4) && ( ((y) % 100) || !((y) % 400) ) )
117 #endif
119 #ifndef TZNAME_MAX
120 #define TZNAME_MAX _POSIX_TZNAME_MAX
121 #endif
123 /**********************************************************************/
125 /* The era code is currently unfinished. */
126 /* #define ENABLE_ERA_CODE */
128 #define __TIME_TZ_FILE
129 /* #define __TIME_TZ_FILE_ONCE */
131 #define __TIME_TZ_OPT_SPEED
133 #define TZ_BUFLEN (2*TZNAME_MAX + 56)
135 #ifdef __TIME_TZ_FILE
136 #include <sys/stat.h>
137 #include <fcntl.h>
138 #include <unistd.h>
139 #include "paths.h"
140 /* ":<tzname>+hh:mm:ss<tzname>+hh:mm:ss,Mmm.w.d/hh:mm:ss,Mmm.w.d/hh:mm:ss" + nul */
141 /* 1 + 2*(1+TZNAME_MAX+1 + 9 + 7 + 9) + 1 = 2*TZNAME_MAX + 56 */
142 #else /* __TIME_TZ_FILE */
143 #undef __TIME_TZ_FILE_ONCE
144 #endif /* __TIME_TZ_FILE */
146 /**********************************************************************/
148 extern struct tm __time_tm;
150 typedef struct {
151 long gmt_offset;
152 long dst_offset;
153 short day; /* for J or normal */
154 short week;
155 short month;
156 short rule_type; /* J, M, \0 */
157 char tzname[TZNAME_MAX+1];
158 } rule_struct;
160 #ifdef __UCLIBC_HAS_THREADS__
162 #include <pthread.h>
164 extern pthread_mutex_t _time_tzlock;
166 #define TZLOCK pthread_mutex_lock(&_time_tzlock)
167 #define TZUNLOCK pthread_mutex_unlock(&_time_tzlock)
169 #else
171 #define TZLOCK ((void) 0)
172 #define TZUNLOCK ((void) 0)
174 #endif
176 extern rule_struct _time_tzinfo[2];
178 extern struct tm *_time_t2tm(const time_t *__restrict timer,
179 int offset, struct tm *__restrict result);
181 extern time_t _time_mktime(struct tm *timeptr, int store_on_success);
183 /**********************************************************************/
184 #ifdef L_asctime
186 static char __time_str[26];
188 char *asctime(const struct tm *__restrict ptm)
190 return asctime_r(ptm, __time_str);
193 #endif
194 /**********************************************************************/
195 #ifdef L_asctime_r
197 /* Strictly speaking, this implementation isn't correct. ANSI/ISO specifies
198 * that the implementation of asctime() be equivalent to
200 * char *asctime(const struct tm *timeptr)
202 * static char wday_name[7][3] = {
203 * "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
204 * };
205 * static char mon_name[12][3] = {
206 * "Jan", "Feb", "Mar", "Apr", "May", "Jun",
207 * "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
208 * };
209 * static char result[26];
211 * sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
212 * wday_name[timeptr->tm_wday],
213 * mon_name[timeptr->tm_mon],
214 * timeptr->tm_mday, timeptr->tm_hour,
215 * timeptr->tm_min, timeptr->tm_sec,
216 * 1900 + timeptr->tm_year);
217 * return result;
220 * but the above is either inherently unsafe, or carries with it the implicit
221 * assumption that all fields of timeptr fall within their usual ranges, and
222 * that the tm_year value falls in the range [-2899,8099] to avoid overflowing
223 * the static buffer.
225 * If we take the implicit assumption as given, then the implementation below
226 * is still incorrect for tm_year values < -900, as there will be either
227 * 0-padding and/or a missing negative sign for the year conversion . But given
228 * the ususal use of asctime(), I think it isn't unreasonable to restrict correct
229 * operation to the domain of years between 1000 and 9999.
232 /* This is generally a good thing, but if you're _sure_ any data passed will be
233 * in range, you can #undef this. */
234 #define SAFE_ASCTIME_R 1
236 static const unsigned char at_data[] = {
237 'S', 'u', 'n', 'M', 'o', 'n', 'T', 'u', 'e', 'W', 'e', 'd',
238 'T', 'h', 'u', 'F', 'r', 'i', 'S', 'a', 't',
240 'J', 'a', 'n', 'F', 'e', 'b', 'M', 'a', 'r', 'A', 'p', 'r',
241 'M', 'a', 'y', 'J', 'u', 'n', 'J', 'u', 'l', 'A', 'u', 'g',
242 'S', 'e', 'p', 'O', 'c', 't', 'N', 'o', 'v', 'D', 'e', 'c',
244 #ifdef SAFE_ASCTIME_R
245 '?', '?', '?',
246 #endif
247 ' ', '?', '?', '?',
248 ' ', '0',
249 offsetof(struct tm, tm_mday),
250 ' ', '0',
251 offsetof(struct tm, tm_hour),
252 ':', '0',
253 offsetof(struct tm, tm_min),
254 ':', '0',
255 offsetof(struct tm, tm_sec),
256 ' ', '?', '?', '?', '?', '\n', 0
259 char *asctime_r(register const struct tm *__restrict ptm,
260 register char *__restrict buffer)
262 int tmp;
264 assert(ptm);
265 assert(buffer);
267 #ifdef SAFE_ASCTIME_R
268 memcpy(buffer, at_data + 3*(7 + 12), sizeof(at_data) - 3*(7 + 12));
270 if (((unsigned int)(ptm->tm_wday)) <= 6) {
271 memcpy(buffer, at_data + 3 * ptm->tm_wday, 3);
274 if (((unsigned int)(ptm->tm_mon)) <= 11) {
275 memcpy(buffer + 4, at_data + 3*7 + 3 * ptm->tm_mon, 3);
277 #else
278 assert(((unsigned int)(ptm->tm_wday)) <= 6);
279 assert(((unsigned int)(ptm->tm_mon)) <= 11);
281 memcpy(buffer, at_data + 3*(7 + 12) - 3, sizeof(at_data) + 3 - 3*(7 + 12));
283 memcpy(buffer, at_data + 3 * ptm->tm_wday, 3);
284 memcpy(buffer + 4, at_data + 3*7 + 3 * ptm->tm_mon, 3);
285 #endif
287 #ifdef SAFE_ASCTIME_R
288 buffer += 19;
289 tmp = ptm->tm_year + 1900;
290 if (((unsigned int) tmp) < 10000) {
291 buffer += 4;
292 do {
293 *buffer = '0' + (tmp % 10);
294 tmp /= 10;
295 } while (*--buffer == '?');
297 #else /* SAFE_ASCTIME_R */
298 buffer += 23;
299 tmp = ptm->tm_year + 1900;
300 assert( ((unsigned int) tmp) < 10000 );
301 do {
302 *buffer = '0' + (tmp % 10);
303 tmp /= 10;
304 } while (*--buffer == '?');
305 #endif /* SAFE_ASCTIME_R */
307 do {
308 --buffer;
309 tmp = *((int *)(((const char *) ptm) + (int) *buffer));
310 #ifdef SAFE_ASCTIME_R
311 if (((unsigned int) tmp) >= 100) { /* Just check 2 digit non-neg. */
312 buffer[-1] = *buffer = '?';
313 } else
314 #else /* SAFE_ASCTIME_R */
315 assert(((unsigned int) tmp) < 100); /* Just check 2 digit non-neg. */
316 #endif /* SAFE_ASCTIME_R */
318 *buffer = '0' + (tmp % 10);
319 #ifdef __BCC__
320 buffer[-1] = '0' + (tmp/10);
321 #else /* __BCC__ */
322 buffer[-1] += (tmp/10);
323 #endif /* __BCC__ */
325 } while ((buffer -= 2)[-2] == '0');
327 if (*++buffer == '0') { /* Space-pad day of month. */
328 *buffer = ' ';
331 return buffer - 8;
334 #endif
335 /**********************************************************************/
336 #ifdef L_clock
338 #include <sys/times.h>
340 /* Note: According to glibc...
341 * CAE XSH, Issue 4, Version 2: <time.h>
342 * The value of CLOCKS_PER_SEC is required to be 1 million on all
343 * XSI-conformant systems.
346 #ifndef __BCC__
347 #if CLOCKS_PER_SEC != 1000000L
348 #error unexpected value for CLOCKS_PER_SEC!
349 #endif
350 #endif
352 clock_t clock(void)
354 struct tms xtms;
355 unsigned long t;
357 times(&xtms);
358 t = ((unsigned long) xtms.tms_utime) + xtms.tms_stime;
360 #ifndef __UCLIBC_CLK_TCK_CONST
361 #error __UCLIBC_CLK_TCK_CONST not defined!
362 #endif
364 #undef CLK_TCK
365 #define CLK_TCK __UCLIBC_CLK_TCK_CONST
367 #if CLK_TCK > CLOCKS_PER_SEC
368 #error __UCLIBC_CLK_TCK_CONST > CLOCKS_PER_SEC!
369 #elif CLK_TCK < 1
370 #error __UCLIBC_CLK_TCK_CONST < 1!
371 #endif
373 #if (CLK_TCK == CLOCKS_PER_SEC)
374 return (t <= LONG_MAX) ? t : -1;
375 #elif (CLOCKS_PER_SEC % CLK_TCK) == 0
376 return (t <= (LONG_MAX / (CLOCKS_PER_SEC/CLK_TCK)))
377 ? t * (CLOCKS_PER_SEC/CLK_TCK)
378 : -1;
379 #else
380 return (t <= ((LONG_MAX / CLOCKS_PER_SEC) * CLK_TCK
381 + ((LONG_MAX % CLOCKS_PER_SEC) * CLK_TCK) / CLOCKS_PER_SEC))
382 ? (((t / CLK_TCK) * CLOCKS_PER_SEC)
383 + (((t % CLK_TCK) * CLOCKS_PER_SEC) / CLK_TCK))
384 : -1;
385 #endif
388 #endif
389 /**********************************************************************/
390 #ifdef L_ctime
392 char *ctime(const time_t *clock)
394 /* ANSI/ISO/SUSv3 say that ctime is equivalent to the following. */
395 return asctime(localtime(clock));
398 #endif
399 /**********************************************************************/
400 #ifdef L_ctime_r
402 char *ctime_r(const time_t *clock, char *buf)
404 struct tm xtms;
406 return asctime_r(localtime_r(clock, &xtms), buf);
409 #endif
410 /**********************************************************************/
411 #ifdef L_difftime
413 #include <float.h>
415 #if FLT_RADIX != 2
416 #error difftime implementation assumptions violated for you arch!
417 #endif
419 double difftime(time_t time1, time_t time0)
421 #if (LONG_MAX >> DBL_MANT_DIG) == 0
423 /* time_t fits in the mantissa of a double. */
424 return ((double) time1) - time0;
426 #elif ((LONG_MAX >> DBL_MANT_DIG) >> DBL_MANT_DIG) == 0
428 /* time_t can overflow the mantissa of a double. */
429 time_t t1, t0, d;
431 d = ((time_t) 1) << DBL_MANT_DIG;
432 t1 = time1 / d;
433 time1 -= (t1 * d);
434 t0 = time0 / d;
435 time0 -= (t0*d);
437 /* Since FLT_RADIX==2 and d is a power of 2, the only possible
438 * rounding error in the expression below would occur from the
439 * addition. */
440 return (((double) t1) - t0) * d + (((double) time1) - time0);
442 #else
443 #error difftime needs special implementation on your arch.
444 #endif
447 #endif
448 /**********************************************************************/
449 #ifdef L_gmtime
451 struct tm *gmtime(const time_t *timer)
453 register struct tm *ptm = &__time_tm;
455 _time_t2tm(timer, 0, ptm); /* Can return NULL... */
457 return ptm;
460 #endif
461 /**********************************************************************/
462 #ifdef L_gmtime_r
464 struct tm *gmtime_r(const time_t *__restrict timer,
465 struct tm *__restrict result)
467 return _time_t2tm(timer, 0, result);
470 #endif
471 /**********************************************************************/
472 #ifdef L_localtime
474 struct tm *localtime(const time_t *timer)
476 register struct tm *ptm = &__time_tm;
478 /* In this implementation, tzset() is called by localtime_r(). */
480 localtime_r(timer, ptm); /* Can return NULL... */
482 return ptm;
485 #endif
486 /**********************************************************************/
487 #ifdef L_localtime_r
489 static const unsigned char day_cor[] = { /* non-leap */
490 31, 31, 34, 34, 35, 35, 36, 36, 36, 37, 37, 38, 38
491 /* 0, 0, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 7 */
492 /* 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 */
495 /* Note: timezone locking is done by localtime_r. */
497 static int tm_isdst(register const struct tm *__restrict ptm)
499 register rule_struct *r = _time_tzinfo;
500 long sec;
501 int i, isdst, isleap, day, day0, monlen, mday;
502 int oday; /* Note: oday can be uninitialized. */
504 isdst = 0;
505 if (r[1].tzname[0] != 0) {
506 /* First, get the current seconds offset from the start of the year.
507 * Fields of ptm are assumed to be in their normal ranges. */
508 sec = ptm->tm_sec
509 + 60 * (ptm->tm_min
510 + 60 * (long)(ptm->tm_hour
511 + 24 * ptm->tm_yday));
512 /* Do some prep work. */
513 i = (ptm->tm_year % 400) + 1900; /* Make sure we don't overflow. */
514 isleap = __isleap(i);
515 --i;
516 day0 = (1
517 + i /* Normal years increment 1 wday. */
518 + (i/4)
519 - (i/100)
520 + (i/400) ) % 7;
521 i = 0;
522 do {
523 day = r->day; /* Common for 'J' and # case. */
524 if (r->rule_type == 'J') {
525 if (!isleap || (day < (31+29))) {
526 --day;
528 } else if (r->rule_type == 'M') {
529 /* Find 0-based day number for 1st of the month. */
530 day = 31*r->month - day_cor[r->month -1];
531 if (isleap && (day >= 59)) {
532 ++day;
534 monlen = 31 + day_cor[r->month -1] - day_cor[r->month];
535 if (isleap && (r->month > 1)) {
536 ++monlen;
538 /* Wweekday (0 is Sunday) of 1st of the month
539 * is (day0 + day) % 7. */
540 if ((mday = r->day - ((day0 + day) % 7)) >= 0) {
541 mday -= 7; /* Back up into prev month since r->week>0. */
543 if ((mday += 7 * r->week) >= monlen) {
544 mday -= 7;
546 /* So, 0-based day number is... */
547 day += mday;
550 if (i != 0) {
551 /* Adjust sec since dst->std change time is in dst. */
552 sec += (r[-1].gmt_offset - r->gmt_offset);
553 if (oday > day) {
554 ++isdst; /* Year starts in dst. */
557 oday = day;
559 /* Now convert day to seconds and add offset and compare. */
560 if (sec >= (day * 86400L) + r->dst_offset) {
561 ++isdst;
563 ++r;
564 } while (++i < 2);
567 return (isdst & 1);
570 struct tm *localtime_r(register const time_t *__restrict timer,
571 register struct tm *__restrict result)
573 time_t x[1];
574 long offset;
575 int days, dst;
577 TZLOCK;
579 tzset();
581 dst = 0;
582 do {
583 days = -7;
584 offset = 604800L - _time_tzinfo[dst].gmt_offset;
585 if (*timer > (LONG_MAX - 604800L)) {
586 days = -days;
587 offset = -offset;
589 *x = *timer + offset;
591 _time_t2tm(x, days, result);
593 if (dst) {
594 result->tm_isdst = dst;
595 break;
597 ++dst;
598 } while ((result->tm_isdst = tm_isdst(result)) != 0);
600 TZUNLOCK;
602 return result;
605 #endif
606 /**********************************************************************/
607 #ifdef L_mktime
609 time_t mktime(struct tm *timeptr)
611 return _time_mktime(timeptr, 1);
614 #endif
615 /**********************************************************************/
616 #ifdef L_strftime
618 #define NO_E_MOD 0x80
619 #define NO_O_MOD 0x40
621 #define ILLEGAL_SPEC 0x3f
623 #define INT_SPEC 0x00 /* must be 0x00!! */
624 #define STRING_SPEC 0x10 /* must be 0x10!! */
625 #define CALC_SPEC 0x20
626 #define STACKED_SPEC 0x30
628 #define MASK_SPEC 0x30
630 /* Compatibility:
632 * No alternate digit (%O?) handling. Always uses 0-9.
633 * Alternate locale format (%E?) handling is broken for nontrivial ERAs.
634 * glibc's %P is currently faked by %p. This means it doesn't do lower case.
635 * glibc's %k, %l, and %s are handled.
636 * glibc apparently allows (and ignores) extraneous 'E' and 'O' modifiers,
637 * while they are flagged as illegal conversions here.
640 /* Warning: Assumes ASCII values! (as do lots of other things in the lib...) */
641 static const unsigned char spec[] = {
642 /* A */ 0x03 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
643 /* B */ 0x04 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
644 /* C */ 0x0a | INT_SPEC | NO_O_MOD,
645 /* D */ 0x02 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
646 /* E */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
647 /* F */ 0x03 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
648 /* G */ 0x03 | CALC_SPEC | NO_E_MOD | NO_O_MOD,
649 /* H */ 0x0b | INT_SPEC | NO_E_MOD,
650 /* I */ 0x0c | INT_SPEC | NO_E_MOD,
651 /* J */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
652 /* K */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
653 /* L */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
654 /* M */ 0x0d | INT_SPEC | NO_E_MOD,
655 /* N */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
656 /* O */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
657 /* P */ 0x05 | STRING_SPEC | NO_E_MOD | NO_O_MOD, /* glibc ; use %p */
658 /* Q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
659 /* R */ 0x04 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
660 /* S */ 0x0e | INT_SPEC | NO_E_MOD,
661 /* T */ 0x05 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
662 /* U */ 0x04 | CALC_SPEC | NO_E_MOD,
663 /* V */ 0x05 | CALC_SPEC | NO_E_MOD,
664 /* W */ 0x06 | CALC_SPEC | NO_E_MOD,
665 /* X */ 0x0a | STACKED_SPEC | NO_O_MOD,
666 /* Y */ 0x0f | INT_SPEC | NO_O_MOD,
667 /* Z */ 0x01 | CALC_SPEC | NO_E_MOD | NO_O_MOD,
668 '?', /* 26 */
669 '?', /* 27 */
670 '?', /* 28 */
671 '?', /* 29 */
672 0, /* 30 */
673 0, /* 31 */
674 /* a */ 0x00 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
675 /* b */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
676 /* c */ 0x08 | STACKED_SPEC | NO_O_MOD,
677 /* d */ 0x00 | INT_SPEC | NO_E_MOD,
678 /* e */ 0x01 | INT_SPEC | NO_E_MOD,
679 /* f */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
680 /* g */ 0x02 | CALC_SPEC | NO_E_MOD | NO_O_MOD,
681 /* h */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD, /* same as b */
682 /* i */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
683 /* j */ 0x08 | INT_SPEC | NO_E_MOD | NO_O_MOD,
684 /* k */ 0x03 | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
685 /* l */ 0x04 | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
686 /* m */ 0x05 | INT_SPEC | NO_E_MOD,
687 /* n */ 0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
688 /* o */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
689 /* p */ 0x02 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
690 /* q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
691 /* r */ 0x0b | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
692 /* s */ 0x07 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
693 /* t */ 0x01 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
694 /* u */ 0x07 | INT_SPEC | NO_E_MOD,
695 /* v */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
696 /* w */ 0x02 | INT_SPEC | NO_E_MOD,
697 /* x */ 0x09 | STACKED_SPEC | NO_O_MOD,
698 /* y */ 0x09 | INT_SPEC,
699 /* z */ 0x00 | CALC_SPEC | NO_E_MOD | NO_O_MOD,
702 /* WARNING!!! These are dependent on the layout of struct tm!!! */
703 #define FIELD_MAX (26+6+26)
704 60 /* 61? */, 59, 23, 31, 11, 0 /* 9999 */, 6, 0 /* 365 */,
706 #define TP_OFFSETS (FIELD_MAX+8)
707 3, /* d */
708 3, /* e */
709 6, /* w */
710 2, /* k */
711 2, /* l */
712 4, /* m */
713 0, /* CURRENTLY UNUSED */
714 /* NOTE: u,j,y order must be preserved as 6,7,5 seq is used in the code! */
715 #define CALC_OFFSETS (TP_OFFSETS + 7)
716 6, /* u */
717 7, /* j */
718 5, /* y */
719 5, /* C */
720 2, /* H */
721 2, /* I */
722 1, /* M */
723 0, /* S */
724 5, /* Y */
725 6, /* a */
726 4, /* b, h */
727 2, /* p */
728 6, /* A */
729 4, /* B */
730 2, /* P */
732 #define TP_CODES (TP_OFFSETS + 16 + 6)
733 2 | 16, /* d */
734 2, /* e */
735 0 | 16, /* w */
736 2, /* k */
737 2 | 32 | 0, /* l */
738 2 | 16 | 1, /* m */
739 0, /* CURRENTLY UNUSED */
740 0 | 16 | 8 , /* u */
741 4 | 16 | 1, /* j */
742 2 | 128 | 32 | 16 , /* y */
743 2 | 128 | 64 | 32 | 16 , /* C */
744 2 | 16, /* H */
745 2 | 32 | 16 | 0, /* I */
746 2 | 16, /* M */
747 2 | 16, /* S */
748 6 | 16, /* Y */
749 2, /* a */
750 2, /* b, h */
751 2 | 64, /* p */
752 2, /* A */
753 2, /* B */
754 2 | 64, /* P */
756 #define STRINGS_NL_ITEM_START (TP_CODES + 16 + 6)
757 _NL_ITEM_INDEX(ABDAY_1), /* a */
758 _NL_ITEM_INDEX(ABMON_1), /* b, h */
759 _NL_ITEM_INDEX(AM_STR), /* p */
760 _NL_ITEM_INDEX(DAY_1), /* A */
761 _NL_ITEM_INDEX(MON_1), /* B */
762 _NL_ITEM_INDEX(AM_STR), /* P -- wrong! need lower case */
764 #define STACKED_STRINGS_START (STRINGS_NL_ITEM_START+6)
765 6, 7, 8, 16, 24, 29, /* 6 - offsets from offset-count to strings */
766 '\n', 0, /* 2 */
767 '\t', 0, /* 2 */
768 '%', 'm', '/', '%', 'd', '/', '%', 'y', 0, /* 9 - %D */
769 '%', 'Y', '-', '%', 'm', '-', '%', 'd', 0, /* 9 - %F (glibc extension) */
770 '%', 'H', ':', '%', 'M', 0, /* 6 - %R*/
771 '%', 'H', ':', '%', 'M', ':', '%', 'S', 0, /* 9 - %T */
773 #define STACKED_STRINGS_NL_ITEM_START (STACKED_STRINGS_START + 43)
774 _NL_ITEM_INDEX(D_T_FMT), /* c */
775 _NL_ITEM_INDEX(D_FMT), /* x */
776 _NL_ITEM_INDEX(T_FMT), /* X */
777 _NL_ITEM_INDEX(T_FMT_AMPM), /* r */
778 #ifdef ENABLE_ERA_CODE
779 _NL_ITEM_INDEX(ERA_D_T_FMT), /* Ec */
780 _NL_ITEM_INDEX(ERA_D_FMT), /* Ex */
781 _NL_ITEM_INDEX(ERA_T_FMT), /* EX */
782 #endif
785 static int load_field(int k, const struct tm *__restrict timeptr)
787 int r;
788 int r_max;
790 r = ((int *) timeptr)[k];
792 r_max = spec[FIELD_MAX + k];
794 if (k == 7) {
795 r_max = 365;
796 } else if (k == 5) {
797 r += 1900;
798 r_max = 9999;
801 if ((((unsigned int) r) > r_max) || ((k == 3) && !r)) {
802 r = -1;
805 return r;
808 #define MAX_PUSH 4
810 size_t strftime(char *__restrict s, size_t maxsize,
811 const char *__restrict format,
812 const struct tm *__restrict timeptr)
814 long tzo;
815 register const char *p;
816 register const char *o;
817 const rule_struct *rsp;
818 const char *stack[MAX_PUSH];
819 size_t count;
820 size_t o_count;
821 int field_val, i, j, lvl;
822 int x[3]; /* wday, yday, year */
823 int isofm, days;
824 char buf[__UIM_BUFLEN_LONG];
825 unsigned char mod;
826 unsigned char code;
828 tzset(); /* We'll, let's get this out of the way. */
830 lvl = 0;
831 p = format;
832 count = maxsize;
834 LOOP:
835 if (!count) {
836 return 0;
838 if (!*p) {
839 if (lvl == 0) {
840 *s = 0; /* nul-terminate */
841 return maxsize - count;
843 p = stack[--lvl];
844 goto LOOP;
847 o_count = 1;
848 if ((*(o = p) == '%') && (*++p != '%')) {
849 o_count = 2;
850 mod = ILLEGAL_SPEC;
851 if ((*p == 'O') || (*p == 'E')) { /* modifier */
852 mod |= ((*p == 'O') ? NO_O_MOD : NO_E_MOD);
853 ++o_count;
854 ++p;
856 if ((((unsigned char)(((*p) | 0x20) - 'a')) >= 26)
857 || (((code = spec[(int)(*p - 'A')]) & mod) >= ILLEGAL_SPEC)
859 if (!*p) {
860 --p;
861 --o_count;
863 goto OUTPUT;
865 code &= ILLEGAL_SPEC; /* modifiers are preserved in mod var. */
867 if ((code & MASK_SPEC) == STACKED_SPEC) {
868 if (lvl == MAX_PUSH) {
869 goto OUTPUT; /* Stack full so treat as illegal spec. */
871 stack[lvl++] = ++p;
872 if ((code &= 0xf) < 8) {
873 p = ((const char *) spec) + STACKED_STRINGS_START + code;
874 p += *((unsigned char *)p);
875 goto LOOP;
877 p = ((const char *) spec) + STACKED_STRINGS_NL_ITEM_START
878 + (code & 7);
879 #ifdef ENABLE_ERA_CODE
880 if ((mod & NO_E_MOD) /* Actually, this means E modifier present. */
881 && (*(o = nl_langinfo(_NL_ITEM(LC_TIME,
882 (int)(((unsigned char *)p)[4]))
885 p = o;
886 goto LOOP;
888 #endif
889 p = nl_langinfo(_NL_ITEM(LC_TIME,
890 (int)(*((unsigned char *)p))));
891 goto LOOP;
894 o = spec + 26; /* set to "????" */
895 if ((code & MASK_SPEC) == CALC_SPEC) {
897 if (*p == 's') {
898 time_t t;
900 /* Use a cast to silence the warning since *timeptr won't
901 * be changed. */
902 if ((t = _time_mktime((struct tm *) timeptr, 0))
903 == ((time_t) -1)
905 o_count = 1;
906 goto OUTPUT;
908 #ifdef TIME_T_IS_UNSIGNED
909 o = _uintmaxtostr(buf + sizeof(buf) - 1,
910 (uintmax_t) t,
911 10, __UIM_DECIMAL);
912 #else
913 o = _uintmaxtostr(buf + sizeof(buf) - 1,
914 (uintmax_t) t,
915 -10, __UIM_DECIMAL);
916 #endif
917 o_count = sizeof(buf);
918 goto OUTPUT;
919 } else if (((*p) | 0x20) == 'z') { /* 'z' or 'Z' */
921 if (timeptr->tm_isdst < 0) {
922 /* SUSv3 specifies this behavior for 'z', but we'll also
923 * treat it as "no timezone info" for 'Z' too. */
924 o_count = 0;
925 goto OUTPUT;
928 TZLOCK;
930 rsp = _time_tzinfo;
931 if (timeptr->tm_isdst > 0) {
932 ++rsp;
935 if (*p == 'Z') {
936 o = rsp->tzname;
937 assert(o != NULL);
938 #if 0
939 if (!o) { /* PARANOIA */
940 o = spec+30; /* empty string */
942 #endif
943 o_count = SIZE_MAX;
944 TZUNLOCK;
945 goto OUTPUT;
946 } else { /* z */
947 *s = '+';
948 if ((tzo = -rsp->gmt_offset) < 0) {
949 tzo = -tzo;
950 *s = '-';
952 TZUNLOCK;
953 ++s;
954 --count;
956 i = tzo / 60;
957 field_val = ((i / 60) * 100) + (i % 60);
959 i = 16 + 6; /* 0-fill, width = 4 */
961 } else {
962 /* TODO: don't need year for U, W */
963 for (i=0 ; i < 3 ; i++) {
964 if ((x[i] = load_field(spec[CALC_OFFSETS+i],timeptr)) < 0) {
965 goto OUTPUT;
969 i = 16 + 2; /* 0-fill, width = 2 */
971 if ((*p == 'U') || (*p == 'W')) {
972 field_val = ((x[1] - x[0]) + 7);
973 if (*p == 'W') {
974 ++field_val;
976 field_val /= 7;
977 if ((*p == 'W') && !x[0]) {
978 --field_val;
980 } else { /* ((*p == 'g') || (*p == 'G') || (*p == 'V')) */
981 ISO_LOOP:
982 isofm = (((x[1] - x[0]) + 11) % 7) - 3; /* [-3,3] */
984 if (x[1] < isofm) { /* belongs to previous year */
985 --x[2];
986 x[1] += 365 + __isleap(x[2]);
987 goto ISO_LOOP;
990 field_val = ((x[1] - isofm) / 7) + 1; /* week # */
991 days = 365 + __isleap(x[2]);
992 isofm = ((isofm + 7*53 + 3 - days)) %7 + days - 3; /* next year */
993 if (x[1] >= isofm) { /* next year */
994 x[1] -= days;
995 ++x[2];
996 goto ISO_LOOP;
999 if (*p != 'V') { /* need year */
1000 field_val = x[2]; /* TODO: what if x[2] now 10000 ?? */
1001 if (*p == 'g') {
1002 field_val %= 100;
1003 } else {
1004 i = 16 + 6; /* 0-fill, width = 4 */
1009 } else {
1010 i = TP_OFFSETS + (code & 0x1f);
1011 if ((field_val = load_field(spec[i],timeptr)) < 0) {
1012 goto OUTPUT;
1015 i = spec[i+(TP_CODES - TP_OFFSETS)];
1017 j = (i & 128) ? 100: 12;
1018 if (i & 64) {
1019 field_val /= j;;
1021 if (i & 32) {
1022 field_val %= j;
1023 if (((i&128) + field_val) == 0) { /* mod 12? == 0 */
1024 field_val = j; /* set to 12 */
1027 field_val += (i & 1);
1028 if ((i & 8) && !field_val) {
1029 field_val += 7;
1033 if ((code & MASK_SPEC) == STRING_SPEC) {
1034 o_count = SIZE_MAX;
1035 field_val += spec[STRINGS_NL_ITEM_START + (code & 0xf)];
1036 o = nl_langinfo(_NL_ITEM(LC_TIME, field_val));
1037 } else {
1038 o_count = ((i >> 1) & 3) + 1;
1039 o = buf + o_count;
1040 do {
1041 *(char *)(--o) = '0' + (field_val % 10);
1042 field_val /= 10;
1043 } while (o > buf);
1044 if (*buf == '0') {
1045 *buf = ' ' + (i & 16);
1050 OUTPUT:
1051 ++p;
1052 while (o_count && count && *o) {
1053 *s++ = *o++;
1054 --o_count;
1055 --count;
1057 goto LOOP;
1060 #endif
1061 /**********************************************************************/
1062 #ifdef L_strptime
1064 /* TODO:
1065 * 1) %l and %k are space-padded, so "%l" by itself fails while " %l" succeeds.
1066 * Both work for glibc. So, should we always strip spaces?
1067 * 2) %Z
1070 /* Notes:
1071 * There are several differences between this strptime and glibc's strptime.
1072 * 1) glibc strips leading space before numeric conversions.
1073 * 2) glibc will read fields without whitespace in between. SUSv3 states
1074 * that you must have whitespace between conversion operators. Besides,
1075 * how do you know how long a number should be if there are leading 0s?
1076 * 3) glibc attempts to compute some the struct tm fields based on the
1077 * data retrieved; tm_wday in particular. I don't as I consider it
1078 * another glibc attempt at mind-reading...
1081 #define NO_E_MOD 0x80
1082 #define NO_O_MOD 0x40
1084 #define ILLEGAL_SPEC 0x3f
1086 #define INT_SPEC 0x00 /* must be 0x00!! */
1087 #define STRING_SPEC 0x10 /* must be 0x10!! */
1088 #define CALC_SPEC 0x20
1089 #define STACKED_SPEC 0x30
1091 #define MASK_SPEC 0x30
1093 /* Warning: Assumes ASCII values! (as do lots of other things in the lib...) */
1094 static const unsigned char spec[] = {
1095 /* A */ 0x02 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1096 /* B */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1097 /* C */ 0x08 | INT_SPEC | NO_O_MOD,
1098 /* D */ 0x01 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1099 /* E */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1100 /* F */ 0x02 | STACKED_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1101 /* G */ 0x0f | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1102 /* H */ 0x06 | INT_SPEC | NO_E_MOD,
1103 /* I */ 0x07 | INT_SPEC | NO_E_MOD,
1104 /* J */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1105 /* K */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1106 /* L */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1107 /* M */ 0x04 | INT_SPEC | NO_E_MOD,
1108 /* N */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1109 /* O */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1110 /* P */ 0x00 | STRING_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1111 /* Q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1112 /* R */ 0x03 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1113 /* S */ 0x05 | INT_SPEC | NO_E_MOD,
1114 /* T */ 0x04 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1115 /* U */ 0x0c | INT_SPEC | NO_E_MOD,
1116 /* V */ 0x0d | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1117 /* W */ 0x0c | INT_SPEC | NO_E_MOD,
1118 /* X */ 0x0a | STACKED_SPEC | NO_O_MOD,
1119 /* Y */ 0x0a | INT_SPEC | NO_O_MOD,
1120 /* Z */ 0x02 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1122 /* WARNING! This assumes orderings:
1123 * AM,PM
1124 * ABDAY_1-ABDAY-7,DAY_1-DAY_7
1125 * ABMON_1-ABMON_12,MON_1-MON12
1126 * Also, there are exactly 6 bytes between 'Z' and 'a'.
1128 #define STRINGS_NL_ITEM_START (26)
1129 _NL_ITEM_INDEX(AM_STR), /* p (P) */
1130 _NL_ITEM_INDEX(ABMON_1), /* B, b */
1131 _NL_ITEM_INDEX(ABDAY_1), /* A, a */
1136 /* a */ 0x02 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1137 /* b */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1138 /* c */ 0x08 | STACKED_SPEC | NO_O_MOD,
1139 /* d */ 0x00 | INT_SPEC | NO_E_MOD,
1140 /* e */ 0x00 | INT_SPEC | NO_E_MOD,
1141 /* f */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1142 /* g */ 0x0e | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1143 /* h */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1144 /* i */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1145 /* j */ 0x01 | INT_SPEC | NO_E_MOD | NO_O_MOD,
1146 /* k */ 0x06 | INT_SPEC | NO_E_MOD, /* glibc */
1147 /* l */ 0x07 | INT_SPEC | NO_E_MOD, /* glibc */
1148 /* m */ 0x02 | INT_SPEC | NO_E_MOD,
1149 /* n */ 0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1150 /* o */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1151 /* p */ 0x00 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1152 /* q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1153 /* r */ 0x0b | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1154 /* s */ 0x00 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1155 /* t */ 0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1156 /* u */ 0x0b | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1157 /* v */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1158 /* w */ 0x03 | INT_SPEC | NO_E_MOD,
1159 /* x */ 0x09 | STACKED_SPEC | NO_O_MOD,
1160 /* y */ 0x09 | INT_SPEC,
1161 /* z */ 0x01 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1163 #define INT_FIELD_START (26+6+26)
1164 /* (field #) << 3 + lower bound (0|1) + correction 0:none, 2:-1, 4:-1900
1165 * followed by upper bound prior to correction with 1=>366 and 2=>9999. */
1166 /* d, e */ (3 << 3) + 1 + 0, 31,
1167 /* j */ (7 << 3) + 1 + 2, /* 366 */ 1,
1168 /* m */ (4 << 3) + 1 + 2, 12,
1169 /* w */ (6 << 3) + 0 + 0, 6,
1170 /* M */ (1 << 3) + 0 + 0, 59,
1171 /* S */ 0 + 0 + 0, 60,
1172 /* H (k) */ (2 << 3) + 0 + 0, 23,
1173 /* I (l) */ (9 << 3) + 1 + 0, 12, /* goes with 8 -- am/pm */
1174 /* C */ (10<< 3) + 0 + 0, 99,
1175 /* y */ (11<< 3) + 0 + 0, 99,
1176 /* Y */ (5 << 3) + 0 + 4, /* 9999 */ 2,
1177 /* u */ (6 << 3) + 1 + 0, 7,
1178 /* The following are processed and range-checked, but ignored otherwise. */
1179 /* U, W */ (12<< 3) + 0 + 0, 53,
1180 /* V */ (12<< 3) + 1 + 0, 53,
1181 /* g */ (12<< 3) + 0 + 0, 99,
1182 /* G */ (12<< 3) + 0 /*+ 4*/, /* 9999 */ 2, /* Note: -1 or 10000? */
1184 #define STACKED_STRINGS_START (INT_FIELD_START+32)
1185 5, 6, 14, 22, 27, /* 5 - offsets from offset-count to strings */
1186 ' ', 0, /* 2 - %n or %t */
1187 '%', 'm', '/', '%', 'd', '/', '%', 'y', 0, /* 9 - %D */
1188 '%', 'Y', '-', '%', 'm', '-', '%', 'd', 0, /* 9 - %F (glibc extension) */
1189 '%', 'H', ':', '%', 'M', 0, /* 6 - %R*/
1190 '%', 'H', ':', '%', 'M', ':', '%', 'S', 0, /* 9 - %T */
1192 #define STACKED_STRINGS_NL_ITEM_START (STACKED_STRINGS_START + 40)
1193 _NL_ITEM_INDEX(D_T_FMT), /* c */
1194 _NL_ITEM_INDEX(D_FMT), /* x */
1195 _NL_ITEM_INDEX(T_FMT), /* X */
1196 _NL_ITEM_INDEX(T_FMT_AMPM), /* r */
1197 #ifdef ENABLE_ERA_CODE
1198 _NL_ITEM_INDEX(ERA_D_T_FMT), /* Ec */
1199 _NL_ITEM_INDEX(ERA_D_FMT), /* Ex */
1200 _NL_ITEM_INDEX(ERA_T_FMT), /* EX */
1201 #endif
1204 #define MAX_PUSH 4
1206 char *strptime(const char *__restrict buf, const char *__restrict format,
1207 struct tm *__restrict tm)
1209 register const char *p;
1210 char *o;
1211 const char *stack[MAX_PUSH];
1212 int i, j, lvl;
1213 int fields[13];
1214 unsigned char mod;
1215 unsigned char code;
1217 i = 0;
1218 do {
1219 fields[i] = INT_MIN;
1220 } while (++i < 13);
1222 lvl = 0;
1223 p = format;
1225 LOOP:
1226 if (!*p) {
1227 if (lvl == 0) { /* Done. */
1228 if (fields[6] == 7) { /* Cleanup for %u here since just once. */
1229 fields[6] = 0; /* Don't use mod in case unset. */
1232 i = 0;
1233 do { /* Store the values into tm. */
1234 ((int *) tm)[i] = fields[i];
1235 } while (++i < 8);
1237 return (char *) buf; /* Success. */
1239 p = stack[--lvl];
1240 goto LOOP;
1243 if ((*p == '%') && (*++p != '%')) {
1244 mod = ILLEGAL_SPEC;
1245 if ((*p == 'O') || (*p == 'E')) { /* Modifier? */
1246 mod |= ((*p == 'O') ? NO_O_MOD : NO_E_MOD);
1247 ++p;
1250 if (!*p
1251 || (((unsigned char)(((*p) | 0x20) - 'a')) >= 26)
1252 || (((code = spec[(int)(*p - 'A')]) & mod) >= ILLEGAL_SPEC)
1254 return NULL; /* Illegal spec. */
1257 if ((code & MASK_SPEC) == STACKED_SPEC) {
1258 if (lvl == MAX_PUSH) {
1259 return NULL; /* Stack full so treat as illegal spec. */
1261 stack[lvl++] = ++p;
1262 if ((code &= 0xf) < 8) {
1263 p = ((const char *) spec) + STACKED_STRINGS_START + code;
1264 p += *((unsigned char *)p);
1265 goto LOOP;
1268 p = ((const char *) spec) + STACKED_STRINGS_NL_ITEM_START
1269 + (code & 7);
1270 #ifdef ENABLE_ERA_CODE
1271 if ((mod & NO_E_MOD) /* Actually, this means E modifier present. */
1272 && (*(o = nl_langinfo(_NL_ITEM(LC_TIME,
1273 (int)(((unsigned char *)p)[4]))
1276 p = o;
1277 goto LOOP;
1279 #endif
1280 p = nl_langinfo(_NL_ITEM(LC_TIME,
1281 (int)(*((unsigned char *)p))));
1282 goto LOOP;
1285 ++p;
1287 if ((code & MASK_SPEC) == STRING_SPEC) {
1288 code &= 0xf;
1289 j = spec[STRINGS_NL_ITEM_START + 3 + code];
1290 i = _NL_ITEM(LC_TIME, spec[STRINGS_NL_ITEM_START + code]);
1291 /* Go backwards to check full names before abreviations. */
1292 do {
1293 --j;
1294 o = nl_langinfo(i+j);
1295 if (!strncasecmp(buf,o,strlen(o)) && *o) { /* Found a match. */
1296 do {
1297 ++buf;
1298 } while (*++o);
1299 if (!code) { /* am/pm */
1300 fields[8] = j * 12;
1301 if (fields[9] >= 0) { /* We have a previous %I or %l. */
1302 fields[2] = fields[9] + fields[8];
1304 } else { /* day (4) or month (6) */
1305 fields[2 + (code << 1)]
1306 = j % (spec[STRINGS_NL_ITEM_START + 3 + code] >> 1);
1308 goto LOOP;
1310 } while (j);
1311 return NULL; /* Failed to match. */
1314 if ((code & MASK_SPEC) == CALC_SPEC) {
1315 if ((code &= 0xf) < 1) { /* s or z*/
1316 time_t t;
1318 o = (char *) buf;
1319 i = errno;
1320 __set_errno(0);
1321 if (!isspace(*buf)) { /* Signal an error if whitespace. */
1322 #ifdef TIME_T_IS_UNSIGNED
1323 t = strtoul(buf, &o, 10);
1324 #else
1325 t = strtol(buf, &o, 10);
1326 #endif
1328 if ((o == buf) || errno) { /* Not a number or overflow. */
1329 return NULL;
1331 __set_errno(i); /* Restore errno. */
1332 buf = o;
1334 if (!code) { /* s */
1335 localtime_r(&t, tm); /* TODO: check for failure? */
1336 i = 0;
1337 do { /* Now copy values from tm to fields. */
1338 fields[i] = ((int *) tm)[i];
1339 } while (++i < 8);
1342 /* TODO: glibc treats %Z as a nop. For now, do the same. */
1343 goto LOOP;
1346 assert((code & MASK_SPEC) == INT_SPEC);
1348 register const unsigned char *x;
1349 code &= 0xf;
1350 x = spec + INT_FIELD_START + (code << 1);
1351 if ((j = x[1]) < 3) { /* upper bound (inclusive) */
1352 j = ((j==1) ? 366 : 9999);
1354 i = -1;
1355 while (isdigit(*buf)) {
1356 if (i < 0) {
1357 i = 0;
1359 if ((i = 10*i + (*buf - '0')) > j) { /* Overflow. */
1360 return NULL;
1362 ++buf;
1364 if (i < (*x & 1)) { /* This catches no-digit case too. */
1365 return NULL;
1367 if (*x & 2) {
1368 --i;
1370 if (*x & 4) {
1371 i -= 1900;
1374 if (*x == (9 << 3) + 1 + 0) { /* %I or %l */
1375 if (i == 12) {
1376 i = 0;
1378 if (fields[8] >= 0) { /* We have a previous %p or %P. */
1379 fields[2] = i + fields[8];
1383 fields[(*x) >> 3] = i;
1385 if (((unsigned char)(*x - (10<< 3) + 0 + 0)) <= 8) { /* %C or %y */
1386 if ((j = fields[10]) < 0) { /* No %C, so i must be %y data. */
1387 if (i <= 68) { /* Map [0-68] to 2000+i */
1388 i += 100;
1390 } else { /* Have %C data, but what about %y? */
1391 if ((i = fields[11]) < 0) { /* No %y data. */
1392 i = 0; /* Treat %y val as 0 following glibc's example. */
1394 i += 100*(j - 19);
1396 fields[5] = i;
1399 goto LOOP;
1400 } else if (isspace(*p)) {
1401 ++p;
1402 while (isspace(*buf)) {
1403 ++buf;
1405 goto LOOP;
1406 } else if (*buf++ == *p++) {
1407 goto LOOP;
1409 return NULL;
1412 #endif
1413 /**********************************************************************/
1414 #ifdef L_time
1416 #ifndef __BCC__
1417 #error The uClibc version of time is in sysdeps/linux/common.
1418 #endif
1420 time_t time(register time_t *tloc)
1422 struct timeval tv;
1423 register struct timeval *p = &tv;
1425 gettimeofday(p, NULL); /* This should never fail... */
1427 if (tloc) {
1428 *tloc = p->tv_sec;
1431 return p->tv_sec;
1434 #endif
1435 /**********************************************************************/
1436 #ifdef L_tzset
1438 static const char vals[] = {
1439 'T', 'Z', 0, /* 3 */
1440 'U', 'T', 'C', 0, /* 4 */
1441 25, 60, 60, 1, /* 4 */
1442 '.', 1, /* M */
1443 5, '.', 1,
1444 6, 0, 0, /* Note: overloaded for non-M non-J case... */
1445 0, 1, 0, /* J */
1446 ',', 'M', '4', '.', '1', '.', '0',
1447 ',', 'M', '1', '0', '.', '5', '.', '0', 0
1450 #define TZ vals
1451 #define UTC (vals + 3)
1452 #define RANGE (vals + 7)
1453 #define RULE (vals + 11 - 1)
1454 #define DEFAULT_RULES (vals + 22)
1456 /* Initialize to UTC. */
1457 int daylight = 0;
1458 long timezone = 0;
1459 char *tzname[2] = { (char *) UTC, (char *) (UTC-1) };
1461 #ifdef __UCLIBC_HAS_THREADS__
1462 pthread_mutex_t _time_tzlock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
1463 #endif
1465 rule_struct _time_tzinfo[2];
1467 static const char *getoffset(register const char *e, long *pn)
1469 register const char *s = RANGE-1;
1470 long n;
1471 int f;
1473 n = 0;
1474 f = -1;
1475 do {
1476 ++s;
1477 if (isdigit(*e)) {
1478 f = *e++ - '0';
1480 if (isdigit(*e)) {
1481 f = 10 * f + (*e++ - '0');
1483 if (((unsigned int)f) >= *s) {
1484 return NULL;
1486 n = (*s) * n + f;
1487 f = 0;
1488 if (*e == ':') {
1489 ++e;
1490 --f;
1492 } while (*s > 1);
1494 *pn = n;
1495 return e;
1498 static const char *getnumber(register const char *e, int *pn)
1500 #ifdef __BCC__
1501 /* bcc can optimize the counter if it thinks it is a pointer... */
1502 register const char *n = (const char *) 3;
1503 int f;
1505 f = 0;
1506 while (n && isdigit(*e)) {
1507 f = 10 * f + (*e++ - '0');
1508 --n;
1511 *pn = f;
1512 return (n == (const char *) 3) ? NULL : e;
1513 #else /* __BCC__ */
1514 int n, f;
1516 n = 3;
1517 f = 0;
1518 while (n && isdigit(*e)) {
1519 f = 10 * f + (*e++ - '0');
1520 --n;
1523 *pn = f;
1524 return (n == 3) ? NULL : e;
1525 #endif /* __BCC__ */
1528 #ifdef __TIME_TZ_FILE
1530 #ifdef __TIME_TZ_FILE_ONCE
1531 static int TZ_file_read; /* Let BSS initialization set this to 0. */
1532 #endif /* __TIME_TZ_FILE_ONCE */
1534 static char *read_TZ_file(char *buf)
1536 int fd;
1537 ssize_t r;
1538 size_t todo;
1539 char *p = NULL;
1541 if ((fd = open(_PATH_TZ, O_RDONLY)) >= 0) {
1542 todo = TZ_BUFLEN;
1543 p = buf;
1544 do {
1545 if ((r = read(fd, p, todo)) < 0) {
1546 goto ERROR;
1548 if (r == 0) {
1549 break;
1551 p += r;
1552 todo -= r;
1553 } while (todo);
1555 if ((p > buf) && (p[-1] == '\n')) { /* Must end with newline. */
1556 p[-1] = 0;
1557 p = buf;
1558 #ifdef __TIME_TZ_FILE_ONCE
1559 ++TZ_file_read;
1560 #endif /* __TIME_TZ_FILE_ONCE */
1561 } else {
1562 ERROR:
1563 p = NULL;
1565 close(fd);
1567 return p;
1570 #endif /* __TIME_TZ_FILE */
1572 void tzset(void)
1574 register const char *e;
1575 register char *s;
1576 long off;
1577 short *p;
1578 rule_struct new_rules[2];
1579 int n, count, f;
1580 char c;
1581 #ifdef __TIME_TZ_FILE
1582 char buf[TZ_BUFLEN];
1583 #endif /* __TIME_TZ_FILE */
1584 #ifdef __TIME_TZ_OPT_SPEED
1585 static char oldval[TZ_BUFLEN]; /* BSS-zero'd. */
1586 #endif /* __TIME_TZ_OPT_SPEED */
1588 TZLOCK;
1590 e = getenv(TZ); /* TZ env var always takes precedence. */
1592 #ifdef __TIME_TZ_FILE_ONCE
1593 /* Put this inside the lock to prevent the possiblity of two different
1594 * timezones being used in a threaded app. */
1596 if (e != NULL) {
1597 TZ_file_read = 0; /* Reset if the TZ env var is set. */
1598 } else if (TZ_file_read > 0) {
1599 goto FAST_DONE;
1601 #endif /* __TIME_TZ_FILE_ONCE */
1603 /* Warning!!! Since uClibc doesn't do lib locking, the following is
1604 * potentially unsafe in a multi-threaded program since it is remotely
1605 * possible that another thread could call setenv() for TZ and overwrite
1606 * the string being parsed. So, don't do that... */
1608 if ((!e /* TZ env var not set... */
1609 #ifdef __TIME_TZ_FILE
1610 && !(e = read_TZ_file(buf)) /* and no file or invalid file */
1611 #endif /* __TIME_TZ_FILE */
1612 ) || !*e) { /* or set to empty string. */
1613 ILLEGAL: /* TODO: Clean up the following... */
1614 #ifdef __TIME_TZ_OPT_SPEED
1615 *oldval = 0; /* Set oldval tonnn empty string. */
1616 #endif /* __TIME_TZ_OPT_SPEED */
1617 s = _time_tzinfo[0].tzname;
1618 *s = 'U';
1619 *++s = 'T';
1620 *++s = 'C';
1621 *++s =
1622 *_time_tzinfo[1].tzname = 0;
1623 _time_tzinfo[0].gmt_offset = 0;
1624 goto DONE;
1627 if (*e == ':') { /* Ignore leading ':'. */
1628 ++e;
1631 #ifdef __TIME_TZ_OPT_SPEED
1632 if (strcmp(e, oldval) == 0) { /* Same string as last time... */
1633 goto FAST_DONE; /* So nothing to do. */
1635 /* Make a copy of the TZ env string. It won't be nul-terminated if
1636 * it is too long, but it that case it will be illegal and will be reset
1637 * to the empty string anyway. */
1638 strncpy(oldval, e, TZ_BUFLEN);
1639 #endif /* __TIME_TZ_OPT_SPEED */
1641 count = 0;
1642 new_rules[1].tzname[0] = 0;
1643 LOOP:
1644 /* Get std or dst name. */
1645 c = 0;
1646 if (*e == '<') {
1647 ++e;
1648 c = '>';
1651 s = new_rules[count].tzname;
1652 n = 0;
1653 while (*e
1654 && isascii(*e) /* SUSv3 requires char in portable char set. */
1655 && (isalpha(*e)
1656 || (c && (isalnum(*e) || (*e == '+') || (*e == '-'))))
1658 *s++ = *e++;
1659 if (++n > TZNAME_MAX) {
1660 goto ILLEGAL;
1663 *s = 0;
1665 if ((n < 3) /* Check for minimum length. */
1666 || (c && (*e++ != c)) /* Match any quoting '<'. */
1668 goto ILLEGAL;
1671 /* Get offset */
1672 s = (char *) e;
1673 if ((*e != '-') && (*e != '+')) {
1674 if (count && !isdigit(*e)) {
1675 off -= 3600; /* Default to 1 hour ahead of std. */
1676 goto SKIP_OFFSET;
1678 --e;
1681 ++e;
1682 if (!(e = getoffset(e, &off))) {
1683 goto ILLEGAL;
1686 if (*s == '-') {
1687 off = -off; /* Save off in case needed for dst default. */
1689 SKIP_OFFSET:
1690 new_rules[count].gmt_offset = off;
1692 if (!count) {
1693 if (*e) {
1694 ++count;
1695 goto LOOP;
1697 } else { /* OK, we have dst, so get some rules. */
1698 count = 0;
1699 if (!*e) { /* No rules so default to US rules. */
1700 e = DEFAULT_RULES;
1703 do {
1704 if (*e++ != ',') {
1705 goto ILLEGAL;
1708 n = 365;
1709 s = (char *) RULE;
1710 if ((c = *e++) == 'M') {
1711 n = 12;
1712 } else if (c == 'J') {
1713 s += 8;
1714 } else {
1715 --e;
1716 c = 0;
1717 s += 6;
1720 *(p = &new_rules[count].rule_type) = c;
1721 if (c != 'M') {
1722 p -= 2;
1725 do {
1726 ++s;
1727 if (!(e = getnumber(e, &f))
1728 || (((unsigned int)(f - s[1])) > n)
1729 || (*s && (*e++ != *s))
1731 goto ILLEGAL;
1733 *--p = f;
1734 } while ((n = *(s += 2)) > 0);
1736 off = 2 * 60 * 60; /* Default to 2:00:00 */
1737 if (*e == '/') {
1738 ++e;
1739 if (!(e = getoffset(e, &off))) {
1740 goto ILLEGAL;
1743 new_rules[count].dst_offset = off;
1744 } while (++count < 2);
1746 if (*e) {
1747 goto ILLEGAL;
1751 memcpy(_time_tzinfo, new_rules, sizeof(new_rules));
1752 DONE:
1753 tzname[0] = _time_tzinfo[0].tzname;
1754 tzname[1] = _time_tzinfo[1].tzname;
1755 daylight = !!_time_tzinfo[1].tzname[0];
1756 timezone = _time_tzinfo[0].gmt_offset;
1758 FAST_DONE:
1759 TZUNLOCK;
1762 #endif
1763 /**********************************************************************/
1764 /* #ifdef L_utime */
1766 /* utime is a syscall in both linux and elks. */
1767 /* int utime(const char *path, const struct utimbuf *times) */
1769 /* #endif */
1770 /**********************************************************************/
1771 /* Non-SUSv3 */
1772 /**********************************************************************/
1773 #ifdef L_utimes
1775 #ifndef __BCC__
1776 #error The uClibc version of utimes is in sysdeps/linux/common.
1777 #endif
1779 #include <utime.h>
1780 #include <sys/time.h>
1782 int utimes(const char *filename, register const struct timeval *tvp)
1784 register struct utimbuf *p = NULL;
1785 struct utimbuf utb;
1787 if (tvp) {
1788 p = &utb;
1789 p->actime = tvp[0].tv_sec;
1790 p->modtime = tvp[1].tv_sec;
1792 return utime(filename, p);
1795 #endif
1796 /**********************************************************************/
1797 #ifdef L__time_t2tm
1799 static const uint16_t vals[] = {
1800 60, 60, 24, 7 /* special */, 36524, 1461, 365, 0
1803 static const unsigned char days[] = {
1804 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, /* non-leap */
1808 /* Notes:
1809 * If time_t is 32 bits, then no overflow is possible.
1810 * It time_t is > 32 bits, this needs to be adjusted to deal with overflow.
1813 /* Note: offset is the correction in _days_ to *timer! */
1815 struct tm *_time_t2tm(const time_t *__restrict timer,
1816 int offset, struct tm *__restrict result)
1818 register int *p;
1819 time_t t1, t, v;
1820 int wday; /* Note: wday can be uninitialized. */
1823 register const uint16_t *vp;
1824 t = *timer;
1825 p = (int *) result;
1826 p[7] = 0;
1827 vp = vals;
1828 do {
1829 if ((v = *vp) == 7) {
1830 /* Overflow checking, assuming time_t is long int... */
1831 #if (LONG_MAX > INT_MAX) && (LONG_MAX > 2147483647L)
1832 #if (INT_MAX == 2147483647L) && (LONG_MAX == 9223372036854775807L)
1833 /* Valid range for t is [-784223472856L, 784223421720L].
1834 * Outside of this range, the tm_year field will overflow. */
1835 if (((unsigned long)(t + offset- -784223472856L))
1836 > (784223421720L - -784223472856L)
1838 return NULL;
1840 #else
1841 #error overflow conditions unknown
1842 #endif
1843 #endif
1845 /* We have days since the epoch, so caluclate the weekday. */
1846 #if defined(__BCC__) && TIME_T_IS_UNSIGNED
1847 wday = (t + 4) % (*vp); /* t is unsigned */
1848 #else
1849 wday = ((int)((t % (*vp)) + 11)) % ((int)(*vp)); /* help bcc */
1850 #endif
1851 /* Set divisor to days in 400 years. Be kind to bcc... */
1852 v = ((time_t)(vp[1])) << 2;
1853 ++v;
1854 /* Change to days since 1/1/1601 so that for 32 bit time_t
1855 * values, we'll have t >= 0. This should be changed for
1856 * archs with larger time_t types.
1857 * Also, correct for offset since a multiple of 7. */
1859 /* TODO: Does this still work on archs with time_t > 32 bits? */
1860 t += (135140L - 366) + offset; /* 146097 - (365*30 + 7) -366 */
1862 #if defined(__BCC__) && TIME_T_IS_UNSIGNED
1863 t -= ((t1 = t / v) * v);
1864 #else
1865 if ((t -= ((t1 = t / v) * v)) < 0) {
1866 t += v;
1867 --t1;
1869 #endif
1871 if ((*vp == 7) && (t == v-1)) {
1872 --t; /* Correct for 400th year leap case */
1873 ++p[4]; /* Stash the extra day... */
1876 #if defined(__BCC__) && 0
1877 *p = t1;
1878 if (v <= 60) {
1879 *p = t;
1880 t = t1;
1882 ++p;
1883 #else
1884 if (v <= 60) {
1885 *p++ = t;
1886 t = t1;
1887 } else {
1888 *p++ = t1;
1890 #endif
1891 } while (*++vp);
1894 if (p[-1] == 4) {
1895 --p[-1];
1896 t = 365;
1900 *p += ((int) t); /* result[7] .. tm_yday */
1902 p -= 2; /* at result[5] */
1904 #if (LONG_MAX > INT_MAX) && (LONG_MAX > 2147483647L)
1905 /* Protect against overflow. TODO: Unecessary if int arith wraps? */
1906 *p = ((((p[-2]<<2) + p[-1])*25 + p[0])<< 2) + (p[1] - 299); /* tm_year */
1907 #else
1908 *p = ((((p[-2]<<2) + p[-1])*25 + p[0])<< 2) + p[1] - 299; /* tm_year */
1909 #endif
1911 p[1] = wday; /* result[6] .. tm_wday */
1914 register const unsigned char *d = days;
1916 wday = 1900 + *p;
1917 if (__isleap(wday)) {
1918 d += 11;
1921 wday = p[2] + 1; /* result[7] .. tm_yday */
1922 *--p = 0; /* at result[4] .. tm_mon */
1923 while (wday > *d) {
1924 wday -= *d;
1925 if (*d == 29) {
1926 d -= 11; /* Backup to non-leap Feb. */
1928 ++d;
1929 ++*p; /* Increment tm_mon. */
1931 p[-1] = wday; /* result[3] .. tm_mday */
1933 /* TODO -- should this be 0? */
1934 p[4] = 0; /* result[8] .. tm_isdst */
1936 return result;
1939 #endif
1940 /**********************************************************************/
1941 #ifdef L___time_tm
1943 struct tm __time_tm; /* Global shared by gmtime() and localtime(). */
1945 #endif
1946 /**********************************************************************/
1947 #ifdef L__time_mktime
1949 static const unsigned char vals[] = {
1950 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, /* non-leap */
1954 time_t _time_mktime(struct tm *timeptr, int store_on_success)
1956 #ifdef __BCC__
1957 long days, secs;
1958 #else
1959 long long secs;
1960 #endif
1961 time_t t;
1962 struct tm x;
1963 /* 0:sec 1:min 2:hour 3:mday 4:mon 5:year 6:wday 7:yday 8:isdst */
1964 register int *p = (int *) &x;
1965 register const unsigned char *s;
1966 int d;
1968 tzset();
1970 memcpy(p, timeptr, sizeof(struct tm));
1972 d = 400;
1973 p[5] = (p[5] - ((p[6] = p[5]/d) * d)) + (p[7] = p[4]/12);
1974 if ((p[4] -= 12 * p[7]) < 0) {
1975 p[4] += 12;
1976 --p[5];
1979 s = vals;
1980 d = (p[5] += 1900); /* Correct year. Now between 1900 and 2300. */
1981 if (__isleap(d)) {
1982 s += 11;
1985 p[7] = 0;
1986 d = p[4];
1987 while (d) {
1988 p[7] += *s;
1989 if (*s == 29) {
1990 s -= 11; /* Backup to non-leap Feb. */
1992 ++s;
1993 --d;
1996 #ifdef __BCC__
1997 d = p[5] - 1;
1998 days = -719163L + ((long)d)*365 + ((d/4) - (d/100) + (d/400) + p[3] + p[7]);
1999 secs = p[0] + 60*( p[1] + 60*((long)(p[2])) )
2000 + _time_tzinfo[timeptr->tm_isdst > 0].gmt_offset;
2001 if (secs < 0) {
2002 secs += 120009600L;
2003 days -= 1389;
2005 if ( ((unsigned long)(days + secs/86400L)) > 49710L) {
2006 return -1;
2008 secs += (days * 86400L);
2009 #else
2010 TZLOCK;
2011 d = p[5] - 1;
2012 d = -719163L + d*365 + (d/4) - (d/100) + (d/400);
2013 secs = p[0]
2014 + _time_tzinfo[timeptr->tm_isdst > 0].gmt_offset
2015 + 60*( p[1]
2016 + 60*(p[2]
2017 + 24*(((146073L * ((long long)(p[6])) + d)
2018 + p[3]) + p[7])));
2019 TZUNLOCK;
2020 if (((unsigned long long)(secs - LONG_MIN))
2021 > (((unsigned long long)LONG_MAX) - LONG_MIN)
2023 return -1;
2025 #endif
2027 t = secs;
2029 localtime_r(&t, (struct tm *)p);
2031 if (t < 0) {
2032 return -1;
2035 if (store_on_success) {
2036 memcpy(timeptr, p, sizeof(struct tm));
2039 return t;
2042 #endif
2043 /**********************************************************************/