1 /* Copyright (C) 2002-2004 Manuel Novoa III <mjn3@codepoet.org>
3 * GNU Library General Public License (LGPL) version 2 or later.
5 * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details.
8 /* June 15, 2002 Initial Notes:
10 * Note: It is assumed throught that time_t is either long or unsigned long.
11 * Similarly, clock_t is assumed to be long int.
13 * Warning: Assumptions are made about the layout of struct tm! It is
14 * assumed that the initial fields of struct tm are (in order):
15 * tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday
17 * Reached the inital goal of supporting the ANSI/ISO C99 time functions
18 * as well as SUSv3's strptime. All timezone info is obtained from the
21 * Differences from glibc worth noting:
23 * Leap seconds are not considered here.
25 * glibc stores additional timezone info the struct tm, whereas we don't.
27 * Alternate digits and era handling are not currently implemented.
28 * The modifiers are accepted, and tested for validity with the following
29 * specifier, but are ignored otherwise.
31 * strftime does not implement glibc extension modifiers or widths for
32 * conversion specifiers. However it does implement the glibc
33 * extension specifiers %l, %k, and %s. It also recognizes %P, but
34 * treats it as a synonym for %p; i.e. doesn't convert to lower case.
36 * strptime implements the glibc extension specifiers. However, it follows
37 * SUSv3 in requiring at least one non-alphanumeric char between
38 * conversion specifiers. Also, strptime only sets struct tm fields
39 * for which format specifiers appear and does not try to infer other
40 * fields (such as wday) as glibc's version does.
42 * TODO - Since glibc's %l and %k can space-pad their output in strftime,
43 * it might be reasonable to eat whitespace first for those specifiers.
44 * This could be done by pushing " %I" and " %H" respectively so that
45 * leading whitespace is consumed. This is really only an issue if %l
46 * or %k occurs at the start of the format string.
48 * TODO - Implement getdate? tzfile? struct tm extensions?
50 * TODO - Rework _time_mktime to remove the dependency on long long.
55 * Fixed allowed char check for std and dst TZ fields.
57 * Added several options concerned with timezone support. The names will
58 * probably change once Erik gets the new config system in place.
60 * Defining __TIME_TZ_FILE causes tzset() to attempt to read the TZ value
61 * from the file /etc/TZ if the TZ env variable isn't set. The file contents
62 * must be the intended value of TZ, followed by a newline. No other chars,
63 * spacing, etc is allowed. As an example, an easy way for me to init
64 * /etc/TZ appropriately would be: echo CST6CDT > /etc/TZ
66 * Defining __TIME_TZ_FILE_ONCE will cause all further accesses of /etc/TZ
67 * to be skipped once a legal value has been read.
69 * Defining __TIME_TZ_OPT_SPEED will cause a tzset() to keep a copy of the
70 * last TZ setting string and do a "fast out" if the current string is the
73 * Nov 21, 2002 Fix an error return case in _time_mktime.
75 * Nov 26, 2002 Fix bug in setting daylight and timezone when no (valid) TZ.
76 * Bug reported by Arne Bernin <arne@alamut.de> in regards to freeswan.
78 * July 27, 2003 Adjust the struct tm extension field support.
79 * Change __tm_zone back to a ptr and add the __tm_tzname[] buffer for
80 * __tm_zone to point to. This gets around complaints from g++.
81 * Who knows... it might even fix the PPC timezone init problem.
83 * July 29, 2003 Fix a bug in mktime behavior when tm_isdst was -1.
84 * Bug reported by "Sid Wade" <sid@vivato.net> in regards to busybox.
86 * NOTE: uClibc mktime behavior is different than glibc's when
87 * the struct tm has tm_isdst == -1 and also had fields outside of
90 * Apparently, glibc examines (at least) tm_sec and guesses the app's
91 * intention of assuming increasing or decreasing time when entering an
92 * ambiguous time period at the dst<->st boundaries.
94 * The uClibc behavior is to always normalize the struct tm and then
95 * try to determing the dst setting.
97 * As long as tm_isdst != -1 or the time specifiec by struct tm is
98 * unambiguous (not falling in the dst<->st transition region) both
99 * uClibc and glibc should produce the same result for mktime.
101 * Oct 31, 2003 Kill the seperate __tm_zone and __tm_tzname[] and which
102 * doesn't work if you want the memcpy the struct. Sigh... I didn't
103 * think about that. So now, when the extensions are enabled, we
104 * malloc space when necessary and keep the timezone names in a linked
107 * Fix a dst-related bug which resulted in use of uninitialized data.
109 * Nov 15, 2003 I forgot to update the thread locking in the last dst fix.
111 * Dec 14, 2003 Fix some dst issues in _time_mktime().
112 * Normalize the tm_isdst value to -1, 0, or 1.
113 * If no dst for this timezone, then reset tm_isdst to 0.
116 * Change clock() to allow wrapping.
117 * Add timegm() function.
118 * Make lookup_tzname() static (as it should have been).
119 * Have strftime() get timezone information from the passed struct
120 * for the %z and %Z conversions when using struct tm extensions.
123 * Fix 2 bugs in strftime related to glibc struct tm extensions.
124 * 1) Need to negate tm_gmtoff field value when used. (bug 336).
125 * 2) Deal with NULL ptr case for tm_zone field, which was causing
126 * segfaults in both the NIST/PCTS tests and the Python 2.4.1
128 * NOTE: We set uninitialized timezone names to "???", and this
129 * differs (intentionally) from glibc's behavior.
132 #define strnlen __strnlen
133 #define gettimeofday __gettimeofday
145 #include <langinfo.h>
147 #include <bits/uClibc_uintmaxtostr.h>
149 extern void __tzset (void) __THROW attribute_hidden;
151 extern long int __strtol (__const char *__restrict __nptr,
152 char **__restrict __endptr, int __base)
153 __THROW __nonnull ((1)) __wur attribute_hidden;
155 #ifdef __UCLIBC_HAS_XLOCALE__
157 extern long int __strtol_l (__const char *__restrict __nptr,
158 char **__restrict __endptr, int __base,
159 __locale_t __loc) __THROW __nonnull ((1, 4)) __wur attribute_hidden;
160 extern int __strncasecmp_l (__const char *__s1, __const char *__s2,
161 size_t __n, __locale_t __loc)
162 __THROW __attribute_pure__ __nonnull ((1, 2, 4)) attribute_hidden;
163 extern size_t __strftime_l (char *__restrict __s, size_t __maxsize,
164 __const char *__restrict __format,
165 __const struct tm *__restrict __tp,
166 __locale_t __loc) __THROW attribute_hidden;
168 extern char *__strptime_l (__const char *__restrict __s,
169 __const char *__restrict __fmt, struct tm *__tp,
170 __locale_t __loc) __THROW attribute_hidden;
174 #define __isleap(y) ( !((y) % 4) && ( ((y) % 100) || !((y) % 400) ) )
178 #define TZNAME_MAX _POSIX_TZNAME_MAX
181 /**********************************************************************/
182 /* The era code is currently unfinished. */
183 /* #define ENABLE_ERA_CODE */
185 #define TZ_BUFLEN (2*TZNAME_MAX + 56)
187 #ifdef __UCLIBC_HAS_TZ_FILE__
189 #include <sys/stat.h>
193 /* ":<tzname>+hh:mm:ss<tzname>+hh:mm:ss,Mmm.w.d/hh:mm:ss,Mmm.w.d/hh:mm:ss" + nul */
194 /* 1 + 2*(1+TZNAME_MAX+1 + 9 + 7 + 9) + 1 = 2*TZNAME_MAX + 56 */
196 #else /* __UCLIBC_HAS_TZ_FILE__ */
198 /* Probably no longer needed. */
199 #undef __UCLIBC_HAS_TZ_FILE_READ_MANY__
201 #endif /* __UCLIBC_HAS_TZ_FILE__ */
203 /**********************************************************************/
205 extern struct tm __time_tm;
207 extern struct tm *__localtime_r (__const time_t *__restrict __timer,
208 struct tm *__restrict __tp) attribute_hidden;
210 extern struct tm *__localtime (__const time_t *__timer) attribute_hidden;
215 short day; /* for J or normal */
218 short rule_type; /* J, M, \0 */
219 char tzname[TZNAME_MAX+1];
222 #ifdef __UCLIBC_HAS_THREADS__
223 # include <pthread.h>
224 extern pthread_mutex_t _time_tzlock;
226 #define TZLOCK __pthread_mutex_lock(&_time_tzlock)
227 #define TZUNLOCK __pthread_mutex_unlock(&_time_tzlock)
229 extern rule_struct _time_tzinfo[2];
231 extern struct tm *_time_t2tm(const time_t *__restrict timer,
232 int offset, struct tm *__restrict result) attribute_hidden;
234 extern time_t _time_mktime(struct tm *timeptr, int store_on_success) attribute_hidden;
236 extern struct tm *__time_localtime_tzi(const time_t *__restrict timer,
237 struct tm *__restrict result,
238 rule_struct *tzi) attribute_hidden;
240 extern time_t _time_mktime_tzi(struct tm *timeptr, int store_on_success,
241 rule_struct *tzi) attribute_hidden;
243 extern char *__asctime (__const struct tm *__tp) attribute_hidden;
245 extern char *__asctime_r (__const struct tm *__restrict __tp,
246 char *__restrict __buf) attribute_hidden;
248 /**********************************************************************/
251 static char __time_str[26];
253 char attribute_hidden *__asctime(const struct tm *ptm)
255 return __asctime_r(ptm, __time_str);
257 strong_alias(__asctime,asctime)
260 /**********************************************************************/
263 /* Strictly speaking, this implementation isn't correct. ANSI/ISO specifies
264 * that the implementation of asctime() be equivalent to
266 * char *asctime(const struct tm *timeptr)
268 * static char wday_name[7][3] = {
269 * "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
271 * static char mon_name[12][3] = {
272 * "Jan", "Feb", "Mar", "Apr", "May", "Jun",
273 * "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
275 * static char result[26];
277 * __sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
278 * wday_name[timeptr->tm_wday],
279 * mon_name[timeptr->tm_mon],
280 * timeptr->tm_mday, timeptr->tm_hour,
281 * timeptr->tm_min, timeptr->tm_sec,
282 * 1900 + timeptr->tm_year);
286 * but the above is either inherently unsafe, or carries with it the implicit
287 * assumption that all fields of timeptr fall within their usual ranges, and
288 * that the tm_year value falls in the range [-2899,8099] to avoid overflowing
291 * If we take the implicit assumption as given, then the implementation below
292 * is still incorrect for tm_year values < -900, as there will be either
293 * 0-padding and/or a missing negative sign for the year conversion . But given
294 * the ususal use of asctime(), I think it isn't unreasonable to restrict correct
295 * operation to the domain of years between 1000 and 9999.
298 /* This is generally a good thing, but if you're _sure_ any data passed will be
299 * in range, you can #undef this. */
300 #define SAFE_ASCTIME_R 1
302 static const unsigned char at_data[] = {
303 'S', 'u', 'n', 'M', 'o', 'n', 'T', 'u', 'e', 'W', 'e', 'd',
304 'T', 'h', 'u', 'F', 'r', 'i', 'S', 'a', 't',
306 'J', 'a', 'n', 'F', 'e', 'b', 'M', 'a', 'r', 'A', 'p', 'r',
307 'M', 'a', 'y', 'J', 'u', 'n', 'J', 'u', 'l', 'A', 'u', 'g',
308 'S', 'e', 'p', 'O', 'c', 't', 'N', 'o', 'v', 'D', 'e', 'c',
310 #ifdef SAFE_ASCTIME_R
315 offsetof(struct tm, tm_mday),
317 offsetof(struct tm, tm_hour),
319 offsetof(struct tm, tm_min),
321 offsetof(struct tm, tm_sec),
322 ' ', '?', '?', '?', '?', '\n', 0
325 char attribute_hidden *__asctime_r(register const struct tm *__restrict ptm,
326 register char *__restrict buffer)
333 #ifdef SAFE_ASCTIME_R
334 __memcpy(buffer, at_data + 3*(7 + 12), sizeof(at_data) - 3*(7 + 12));
336 if (((unsigned int)(ptm->tm_wday)) <= 6) {
337 __memcpy(buffer, at_data + 3 * ptm->tm_wday, 3);
340 if (((unsigned int)(ptm->tm_mon)) <= 11) {
341 __memcpy(buffer + 4, at_data + 3*7 + 3 * ptm->tm_mon, 3);
344 assert(((unsigned int)(ptm->tm_wday)) <= 6);
345 assert(((unsigned int)(ptm->tm_mon)) <= 11);
347 __memcpy(buffer, at_data + 3*(7 + 12) - 3, sizeof(at_data) + 3 - 3*(7 + 12));
349 __memcpy(buffer, at_data + 3 * ptm->tm_wday, 3);
350 __memcpy(buffer + 4, at_data + 3*7 + 3 * ptm->tm_mon, 3);
353 #ifdef SAFE_ASCTIME_R
355 tmp = ptm->tm_year + 1900;
356 if (((unsigned int) tmp) < 10000) {
359 *buffer = '0' + (tmp % 10);
361 } while (*--buffer == '?');
363 #else /* SAFE_ASCTIME_R */
365 tmp = ptm->tm_year + 1900;
366 assert( ((unsigned int) tmp) < 10000 );
368 *buffer = '0' + (tmp % 10);
370 } while (*--buffer == '?');
371 #endif /* SAFE_ASCTIME_R */
375 tmp = *((int *)(((const char *) ptm) + (int) *buffer));
376 #ifdef SAFE_ASCTIME_R
377 if (((unsigned int) tmp) >= 100) { /* Just check 2 digit non-neg. */
378 buffer[-1] = *buffer = '?';
380 #else /* SAFE_ASCTIME_R */
381 assert(((unsigned int) tmp) < 100); /* Just check 2 digit non-neg. */
382 #endif /* SAFE_ASCTIME_R */
384 *buffer = '0' + (tmp % 10);
386 buffer[-1] = '0' + (tmp/10);
388 buffer[-1] += (tmp/10);
391 } while ((buffer -= 2)[-2] == '0');
393 if (*++buffer == '0') { /* Space-pad day of month. */
399 strong_alias(__asctime_r,asctime_r)
402 /**********************************************************************/
405 #define times __times
407 #include <sys/times.h>
410 #if CLOCKS_PER_SEC != 1000000L
411 #error unexpected value for CLOCKS_PER_SEC!
415 #ifdef __UCLIBC_CLK_TCK_CONST
416 # if __UCLIBC_CLK_TCK_CONST > CLOCKS_PER_SEC
417 # error __UCLIBC_CLK_TCK_CONST > CLOCKS_PER_SEC!
418 # elif __UCLIBC_CLK_TCK_CONST < 1
419 # error __UCLIBC_CLK_TCK_CONST < 1!
425 * On XSI-conformant systems, CLOCKS_PER_SEC is defined to be one million.
427 * The value returned by clock() may wrap around on some implementations.
428 * For example, on a machine with 32-bit values for clock_t, it wraps
429 * after 2147 seconds.
431 * This implies that we should bitwise and with LONG_MAX.
441 t = ((unsigned long) xtms.tms_utime) + xtms.tms_stime;
443 #ifndef __UCLIBC_CLK_TCK_CONST
445 # error __UCLIBC_CLK_TCK_CONST not defined!
447 #elif ((CLOCKS_PER_SEC % __UCLIBC_CLK_TCK_CONST) == 0)
449 /* CLOCKS_PER_SEC == k * __UCLIBC_CLK_TCK_CONST for some integer k >= 1. */
450 return ((t * (CLOCKS_PER_SEC/__UCLIBC_CLK_TCK_CONST)) & LONG_MAX);
454 /* Unlike the previous case, the scaling factor is not an integer.
455 * So when tms_utime, tms_stime, or their sum wraps, some of the
456 * "visible" bits in the return value are affected. Nothing we
457 * can really do about this though other than handle tms_utime and
458 * tms_stime seperately and then sum. But since that doesn't really
459 * buy us much, we don't bother. */
461 return ((((t / __UCLIBC_CLK_TCK_CONST) * CLOCKS_PER_SEC)
462 + ((((t % __UCLIBC_CLK_TCK_CONST) * CLOCKS_PER_SEC)
463 / __UCLIBC_CLK_TCK_CONST))
470 /**********************************************************************/
473 char attribute_hidden *__ctime(const time_t *clock)
475 /* ANSI/ISO/SUSv3 say that ctime is equivalent to the following. */
476 return __asctime(__localtime(clock));
478 strong_alias(__ctime,ctime)
480 /**********************************************************************/
483 char *ctime_r(const time_t *clock, char *buf)
487 return __asctime_r(__localtime_r(clock, &xtm), buf);
491 /**********************************************************************/
497 #error difftime implementation assumptions violated for you arch!
500 double difftime(time_t time1, time_t time0)
502 #if (LONG_MAX >> DBL_MANT_DIG) == 0
504 /* time_t fits in the mantissa of a double. */
505 return ((double) time1) - time0;
507 #elif ((LONG_MAX >> DBL_MANT_DIG) >> DBL_MANT_DIG) == 0
509 /* time_t can overflow the mantissa of a double. */
512 d = ((time_t) 1) << DBL_MANT_DIG;
518 /* Since FLT_RADIX==2 and d is a power of 2, the only possible
519 * rounding error in the expression below would occur from the
521 return (((double) t1) - t0) * d + (((double) time1) - time0);
524 #error difftime needs special implementation on your arch.
529 /**********************************************************************/
532 struct tm *gmtime(const time_t *timer)
534 register struct tm *ptm = &__time_tm;
536 _time_t2tm(timer, 0, ptm); /* Can return NULL... */
542 /**********************************************************************/
545 struct tm *gmtime_r(const time_t *__restrict timer,
546 struct tm *__restrict result)
548 return _time_t2tm(timer, 0, result);
552 /**********************************************************************/
555 struct tm attribute_hidden *__localtime(const time_t *timer)
557 register struct tm *ptm = &__time_tm;
559 /* In this implementation, tzset() is called by localtime_r(). */
561 __localtime_r(timer, ptm); /* Can return NULL... */
565 strong_alias(__localtime,localtime)
568 /**********************************************************************/
571 struct tm attribute_hidden *__localtime_r(register const time_t *__restrict timer,
572 register struct tm *__restrict result)
578 __time_localtime_tzi(timer, result, _time_tzinfo);
584 strong_alias(__localtime_r,localtime_r)
587 /**********************************************************************/
588 #ifdef L__time_localtime_tzi
590 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
592 struct ll_tzname_item;
594 typedef struct ll_tzname_item {
595 struct ll_tzname_item *next;
596 char tzname[TZNAME_MAX+1];
599 static ll_tzname_item_t ll_tzname[] = {
600 { ll_tzname + 1, "UTC" }, /* Always 1st. */
601 { NULL, "???" } /* Always 2nd. (invalid or out-of-memory) */
604 static const char *lookup_tzname(const char *key)
608 for (p=ll_tzname ; p ; p=p->next) {
609 if (!__strcmp(p->tzname, key)) {
614 /* Hmm... a new name. */
615 if (strnlen(key, TZNAME_MAX+1) < TZNAME_MAX+1) { /* Verify legal length */
616 if ((p = malloc(sizeof(ll_tzname_item_t))) != NULL) {
617 /* Insert as 3rd item in the list. */
618 p->next = ll_tzname[1].next;
619 ll_tzname[1].next = p;
620 __strcpy(p->tzname, key);
625 /* Either invalid or couldn't alloc. */
626 return ll_tzname[1].tzname;
629 #endif /* __UCLIBC_HAS_TM_EXTENSIONS__ */
631 static const unsigned char day_cor[] = { /* non-leap */
632 31, 31, 34, 34, 35, 35, 36, 36, 36, 37, 37, 38, 38
633 /* 0, 0, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 7 */
634 /* 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 */
637 /* Note: timezone locking is done by localtime_r. */
639 static int tm_isdst(register const struct tm *__restrict ptm,
640 register rule_struct *r)
643 int i, isdst, isleap, day, day0, monlen, mday;
644 int oday; /* Note: oday can be uninitialized. */
647 if (r[1].tzname[0] != 0) {
648 /* First, get the current seconds offset from the start of the year.
649 * Fields of ptm are assumed to be in their normal ranges. */
652 + 60 * (long)(ptm->tm_hour
653 + 24 * ptm->tm_yday));
654 /* Do some prep work. */
655 i = (ptm->tm_year % 400) + 1900; /* Make sure we don't overflow. */
656 isleap = __isleap(i);
659 + i /* Normal years increment 1 wday. */
665 day = r->day; /* Common for 'J' and # case. */
666 if (r->rule_type == 'J') {
667 if (!isleap || (day < (31+29))) {
670 } else if (r->rule_type == 'M') {
671 /* Find 0-based day number for 1st of the month. */
672 day = 31*r->month - day_cor[r->month -1];
673 if (isleap && (day >= 59)) {
676 monlen = 31 + day_cor[r->month -1] - day_cor[r->month];
677 if (isleap && (r->month > 1)) {
680 /* Wweekday (0 is Sunday) of 1st of the month
681 * is (day0 + day) % 7. */
682 if ((mday = r->day - ((day0 + day) % 7)) >= 0) {
683 mday -= 7; /* Back up into prev month since r->week>0. */
685 if ((mday += 7 * r->week) >= monlen) {
688 /* So, 0-based day number is... */
693 /* Adjust sec since dst->std change time is in dst. */
694 sec += (r[-1].gmt_offset - r->gmt_offset);
696 ++isdst; /* Year starts in dst. */
701 /* Now convert day to seconds and add offset and compare. */
702 if (sec >= (day * 86400L) + r->dst_offset) {
712 struct tm attribute_hidden *__time_localtime_tzi(register const time_t *__restrict timer,
713 register struct tm *__restrict result,
723 offset = 604800L - tzi[dst].gmt_offset;
724 if (*timer > (LONG_MAX - 604800L)) {
728 *x = *timer + offset;
730 _time_t2tm(x, days, result);
731 result->tm_isdst = dst;
732 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
733 result->tm_gmtoff = - tzi[dst].gmt_offset;
734 result->tm_zone = lookup_tzname(tzi[dst].tzname);
735 #endif /* __UCLIBC_HAS_TM_EXTENSIONS__ */
737 && ((result->tm_isdst = tm_isdst(result, tzi)) != 0));
743 /**********************************************************************/
746 time_t mktime(struct tm *timeptr)
748 return _time_mktime(timeptr, 1);
751 /* Another name for `mktime'. */
752 /* time_t timelocal(struct tm *tp) */
753 weak_alias(mktime,timelocal);
756 /**********************************************************************/
758 /* Like `mktime' but timeptr represents Universal Time, not local time. */
760 time_t timegm(struct tm *timeptr)
762 rule_struct gmt_tzinfo[2];
764 __memset(gmt_tzinfo, 0, sizeof(gmt_tzinfo));
765 __strcpy(gmt_tzinfo[0].tzname, "GMT"); /* Match glibc behavior here. */
767 return _time_mktime_tzi(timeptr, 1, gmt_tzinfo);
771 /**********************************************************************/
772 #if defined(L_strftime) || defined(L_strftime_l)
774 #if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE)
776 size_t attribute_hidden __strftime(char *__restrict s, size_t maxsize,
777 const char *__restrict format,
778 const struct tm *__restrict timeptr)
780 return __strftime_l(s, maxsize, format, timeptr, __UCLIBC_CURLOCALE);
782 strong_alias(__strftime,strftime)
784 #else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
786 #define NO_E_MOD 0x80
787 #define NO_O_MOD 0x40
789 #define ILLEGAL_SPEC 0x3f
791 #define INT_SPEC 0x00 /* must be 0x00!! */
792 #define STRING_SPEC 0x10 /* must be 0x10!! */
793 #define CALC_SPEC 0x20
794 #define STACKED_SPEC 0x30
796 #define MASK_SPEC 0x30
800 * No alternate digit (%O?) handling. Always uses 0-9.
801 * Alternate locale format (%E?) handling is broken for nontrivial ERAs.
802 * glibc's %P is currently faked by %p. This means it doesn't do lower case.
803 * glibc's %k, %l, and %s are handled.
804 * glibc apparently allows (and ignores) extraneous 'E' and 'O' modifiers,
805 * while they are flagged as illegal conversions here.
808 /* Warning: Assumes ASCII values! (as do lots of other things in the lib...) */
809 static const unsigned char spec[] = {
810 /* A */ 0x03 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
811 /* B */ 0x04 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
812 /* C */ 0x0a | INT_SPEC | NO_O_MOD,
813 /* D */ 0x02 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
814 /* E */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
815 /* F */ 0x03 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
816 /* G */ 0x03 | CALC_SPEC | NO_E_MOD | NO_O_MOD,
817 /* H */ 0x0b | INT_SPEC | NO_E_MOD,
818 /* I */ 0x0c | INT_SPEC | NO_E_MOD,
819 /* J */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
820 /* K */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
821 /* L */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
822 /* M */ 0x0d | INT_SPEC | NO_E_MOD,
823 /* N */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
824 /* O */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
825 /* P */ 0x05 | STRING_SPEC | NO_E_MOD | NO_O_MOD, /* glibc ; use %p */
826 /* Q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
827 /* R */ 0x04 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
828 /* S */ 0x0e | INT_SPEC | NO_E_MOD,
829 /* T */ 0x05 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
830 /* U */ 0x04 | CALC_SPEC | NO_E_MOD,
831 /* V */ 0x05 | CALC_SPEC | NO_E_MOD,
832 /* W */ 0x06 | CALC_SPEC | NO_E_MOD,
833 /* X */ 0x0a | STACKED_SPEC | NO_O_MOD,
834 /* Y */ 0x0f | INT_SPEC | NO_O_MOD,
835 /* Z */ 0x01 | CALC_SPEC | NO_E_MOD | NO_O_MOD,
842 /* a */ 0x00 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
843 /* b */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
844 /* c */ 0x08 | STACKED_SPEC | NO_O_MOD,
845 /* d */ 0x00 | INT_SPEC | NO_E_MOD,
846 /* e */ 0x01 | INT_SPEC | NO_E_MOD,
847 /* f */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
848 /* g */ 0x02 | CALC_SPEC | NO_E_MOD | NO_O_MOD,
849 /* h */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD, /* same as b */
850 /* i */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
851 /* j */ 0x08 | INT_SPEC | NO_E_MOD | NO_O_MOD,
852 /* k */ 0x03 | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
853 /* l */ 0x04 | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
854 /* m */ 0x05 | INT_SPEC | NO_E_MOD,
855 /* n */ 0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
856 /* o */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
857 /* p */ 0x02 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
858 /* q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
859 /* r */ 0x0b | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
860 /* s */ 0x07 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
861 /* t */ 0x01 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
862 /* u */ 0x07 | INT_SPEC | NO_E_MOD,
863 /* v */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
864 /* w */ 0x02 | INT_SPEC | NO_E_MOD,
865 /* x */ 0x09 | STACKED_SPEC | NO_O_MOD,
866 /* y */ 0x09 | INT_SPEC,
867 /* z */ 0x00 | CALC_SPEC | NO_E_MOD | NO_O_MOD,
870 /* WARNING!!! These are dependent on the layout of struct tm!!! */
871 #define FIELD_MAX (26+6+26)
872 60 /* 61? */, 59, 23, 31, 11, 0 /* 9999 */, 6, 0 /* 365 */,
874 #define TP_OFFSETS (FIELD_MAX+8)
881 0, /* CURRENTLY UNUSED */
882 /* NOTE: u,j,y order must be preserved as 6,7,5 seq is used in the code! */
883 #define CALC_OFFSETS (TP_OFFSETS + 7)
900 #define TP_CODES (TP_OFFSETS + 16 + 6)
907 0, /* CURRENTLY UNUSED */
910 2 | 128 | 32 | 16 , /* y */
911 2 | 128 | 64 | 32 | 16 , /* C */
913 2 | 32 | 16 | 0, /* I */
924 #define STRINGS_NL_ITEM_START (TP_CODES + 16 + 6)
925 _NL_ITEM_INDEX(ABDAY_1), /* a */
926 _NL_ITEM_INDEX(ABMON_1), /* b, h */
927 _NL_ITEM_INDEX(AM_STR), /* p */
928 _NL_ITEM_INDEX(DAY_1), /* A */
929 _NL_ITEM_INDEX(MON_1), /* B */
930 _NL_ITEM_INDEX(AM_STR), /* P -- wrong! need lower case */
932 #define STACKED_STRINGS_START (STRINGS_NL_ITEM_START+6)
933 6, 7, 8, 16, 24, 29, /* 6 - offsets from offset-count to strings */
936 '%', 'm', '/', '%', 'd', '/', '%', 'y', 0, /* 9 - %D */
937 '%', 'Y', '-', '%', 'm', '-', '%', 'd', 0, /* 9 - %F (glibc extension) */
938 '%', 'H', ':', '%', 'M', 0, /* 6 - %R*/
939 '%', 'H', ':', '%', 'M', ':', '%', 'S', 0, /* 9 - %T */
941 #define STACKED_STRINGS_NL_ITEM_START (STACKED_STRINGS_START + 43)
942 _NL_ITEM_INDEX(D_T_FMT), /* c */
943 _NL_ITEM_INDEX(D_FMT), /* x */
944 _NL_ITEM_INDEX(T_FMT), /* X */
945 _NL_ITEM_INDEX(T_FMT_AMPM), /* r */
946 #ifdef ENABLE_ERA_CODE
947 _NL_ITEM_INDEX(ERA_D_T_FMT), /* Ec */
948 _NL_ITEM_INDEX(ERA_D_FMT), /* Ex */
949 _NL_ITEM_INDEX(ERA_T_FMT), /* EX */
953 static int load_field(int k, const struct tm *__restrict timeptr)
958 r = ((int *) timeptr)[k];
960 r_max = spec[FIELD_MAX + k];
969 if ((((unsigned int) r) > r_max) || ((k == 3) && !r)) {
978 #ifdef __UCLIBC_MJN3_ONLY__
979 #warning TODO: Check multibyte format string validity.
982 size_t attribute_hidden __UCXL(strftime)(char *__restrict s, size_t maxsize,
983 const char *__restrict format,
984 const struct tm *__restrict timeptr __LOCALE_PARAM )
987 register const char *p;
988 register const char *o;
989 #ifndef __UCLIBC_HAS_TM_EXTENSIONS__
990 const rule_struct *rsp;
992 const char *stack[MAX_PUSH];
995 int field_val, i, j, lvl;
996 int x[3]; /* wday, yday, year */
998 char buf[__UIM_BUFLEN_LONG];
1002 __tzset(); /* We'll, let's get this out of the way. */
1014 *s = 0; /* nul-terminate */
1015 return maxsize - count;
1022 if ((*(o = p) == '%') && (*++p != '%')) {
1025 if ((*p == 'O') || (*p == 'E')) { /* modifier */
1026 mod |= ((*p == 'O') ? NO_O_MOD : NO_E_MOD);
1030 if ((((unsigned char)(((*p) | 0x20) - 'a')) >= 26)
1031 || (((code = spec[(int)(*p - 'A')]) & mod) >= ILLEGAL_SPEC)
1039 code &= ILLEGAL_SPEC; /* modifiers are preserved in mod var. */
1041 if ((code & MASK_SPEC) == STACKED_SPEC) {
1042 if (lvl == MAX_PUSH) {
1043 goto OUTPUT; /* Stack full so treat as illegal spec. */
1046 if ((code &= 0xf) < 8) {
1047 p = ((const char *) spec) + STACKED_STRINGS_START + code;
1048 p += *((unsigned char *)p);
1051 p = ((const char *) spec) + STACKED_STRINGS_NL_ITEM_START
1053 #ifdef ENABLE_ERA_CODE
1054 if ((mod & NO_E_MOD) /* Actually, this means E modifier present. */
1055 && (*(o = __XL(nl_langinfo)(_NL_ITEM(LC_TIME,
1056 (int)(((unsigned char *)p)[4]))
1064 p = __XL(nl_langinfo)(_NL_ITEM(LC_TIME,
1065 (int)(*((unsigned char *)p)))
1071 o = spec + 26; /* set to "????" */
1072 if ((code & MASK_SPEC) == CALC_SPEC) {
1077 /* Use a cast to silence the warning since *timeptr won't
1079 if ((t = _time_mktime((struct tm *) timeptr, 0))
1085 #ifdef TIME_T_IS_UNSIGNED
1086 o = _uintmaxtostr(buf + sizeof(buf) - 1,
1090 o = _uintmaxtostr(buf + sizeof(buf) - 1,
1092 -10, __UIM_DECIMAL);
1094 o_count = sizeof(buf);
1096 } else if (((*p) | 0x20) == 'z') { /* 'z' or 'Z' */
1098 if (timeptr->tm_isdst < 0) {
1099 /* SUSv3 specifies this behavior for 'z', but we'll also
1100 * treat it as "no timezone info" for 'Z' too. */
1105 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
1107 #define RSP_TZUNLOCK ((void) 0)
1108 #define RSP_TZNAME timeptr->tm_zone
1109 #define RSP_GMT_OFFSET (-timeptr->tm_gmtoff)
1113 #define RSP_TZUNLOCK TZUNLOCK
1114 #define RSP_TZNAME rsp->tzname
1115 #define RSP_GMT_OFFSET rsp->gmt_offset
1120 if (timeptr->tm_isdst > 0) {
1127 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
1128 /* Sigh... blasted glibc extensions. Of course we can't
1129 * count on the pointer being valid. Best we can do is
1130 * handle NULL, which looks to be all that glibc does.
1131 * At least that catches the memset() with 0 case.
1132 * NOTE: We handle this case differently than glibc!
1133 * It uses system timezone name (based on tm_isdst) in this
1134 * case... although it always seems to use the embedded
1135 * tm_gmtoff value. What we'll do instead is treat the
1136 * timezone name as unknown/invalid and return "???". */
1143 if (!o) { /* PARANOIA */
1144 o = spec+30; /* empty string */
1152 if ((tzo = -RSP_GMT_OFFSET) < 0) {
1161 field_val = ((i / 60) * 100) + (i % 60);
1163 i = 16 + 6; /* 0-fill, width = 4 */
1167 /* TODO: don't need year for U, W */
1168 for (i=0 ; i < 3 ; i++) {
1169 if ((x[i] = load_field(spec[CALC_OFFSETS+i],timeptr)) < 0) {
1174 i = 16 + 2; /* 0-fill, width = 2 */
1176 if ((*p == 'U') || (*p == 'W')) {
1177 field_val = ((x[1] - x[0]) + 7);
1182 if ((*p == 'W') && !x[0]) {
1185 } else { /* ((*p == 'g') || (*p == 'G') || (*p == 'V')) */
1187 isofm = (((x[1] - x[0]) + 11) % 7) - 3; /* [-3,3] */
1189 if (x[1] < isofm) { /* belongs to previous year */
1191 x[1] += 365 + __isleap(x[2]);
1195 field_val = ((x[1] - isofm) / 7) + 1; /* week # */
1196 days = 365 + __isleap(x[2]);
1197 isofm = ((isofm + 7*53 + 3 - days)) %7 + days - 3; /* next year */
1198 if (x[1] >= isofm) { /* next year */
1204 if (*p != 'V') { /* need year */
1205 field_val = x[2]; /* TODO: what if x[2] now 10000 ?? */
1209 i = 16 + 6; /* 0-fill, width = 4 */
1215 i = TP_OFFSETS + (code & 0x1f);
1216 if ((field_val = load_field(spec[i],timeptr)) < 0) {
1220 i = spec[i+(TP_CODES - TP_OFFSETS)];
1222 j = (i & 128) ? 100: 12;
1228 if (((i&128) + field_val) == 0) { /* mod 12? == 0 */
1229 field_val = j; /* set to 12 */
1232 field_val += (i & 1);
1233 if ((i & 8) && !field_val) {
1238 if ((code & MASK_SPEC) == STRING_SPEC) {
1240 field_val += spec[STRINGS_NL_ITEM_START + (code & 0xf)];
1241 o = __XL(nl_langinfo)(_NL_ITEM(LC_TIME, field_val) __LOCALE_ARG );
1243 o_count = ((i >> 1) & 3) + 1;
1246 *(char *)(--o) = '0' + (field_val % 10);
1250 *buf = ' ' + (i & 16);
1257 while (o_count && count && *o) {
1265 __UCXL_ALIAS(strftime)
1267 #endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
1270 /**********************************************************************/
1271 #if defined(L_strptime) || defined(L_strptime_l)
1273 #define ISDIGIT(C) __isdigit_char((C))
1275 #ifdef __UCLIBC_DO_XLOCALE
1276 #define ISSPACE(C) isspace_l((C), locale_arg)
1278 #define ISSPACE(C) isspace((C))
1281 #if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE)
1283 char attribute_hidden *__strptime(const char *__restrict buf, const char *__restrict format,
1284 struct tm *__restrict tm)
1286 return __strptime_l(buf, format, tm, __UCLIBC_CURLOCALE);
1288 strong_alias(__strptime,strptime)
1290 #else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
1293 * 1) %l and %k are space-padded, so "%l" by itself fails while " %l" succeeds.
1294 * Both work for glibc. So, should we always strip spaces?
1299 * There are several differences between this strptime and glibc's strptime.
1300 * 1) glibc strips leading space before numeric conversions.
1301 * 2) glibc will read fields without whitespace in between. SUSv3 states
1302 * that you must have whitespace between conversion operators. Besides,
1303 * how do you know how long a number should be if there are leading 0s?
1304 * 3) glibc attempts to compute some the struct tm fields based on the
1305 * data retrieved; tm_wday in particular. I don't as I consider it
1306 * another glibc attempt at mind-reading...
1309 #define NO_E_MOD 0x80
1310 #define NO_O_MOD 0x40
1312 #define ILLEGAL_SPEC 0x3f
1314 #define INT_SPEC 0x00 /* must be 0x00!! */
1315 #define STRING_SPEC 0x10 /* must be 0x10!! */
1316 #define CALC_SPEC 0x20
1317 #define STACKED_SPEC 0x30
1319 #define MASK_SPEC 0x30
1321 /* Warning: Assumes ASCII values! (as do lots of other things in the lib...) */
1322 static const unsigned char spec[] = {
1323 /* A */ 0x02 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1324 /* B */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1325 /* C */ 0x08 | INT_SPEC | NO_O_MOD,
1326 /* D */ 0x01 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1327 /* E */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1328 /* F */ 0x02 | STACKED_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1329 /* G */ 0x0f | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1330 /* H */ 0x06 | INT_SPEC | NO_E_MOD,
1331 /* I */ 0x07 | INT_SPEC | NO_E_MOD,
1332 /* J */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1333 /* K */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1334 /* L */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1335 /* M */ 0x04 | INT_SPEC | NO_E_MOD,
1336 /* N */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1337 /* O */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1338 /* P */ 0x00 | STRING_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1339 /* Q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1340 /* R */ 0x03 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1341 /* S */ 0x05 | INT_SPEC | NO_E_MOD,
1342 /* T */ 0x04 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1343 /* U */ 0x0c | INT_SPEC | NO_E_MOD,
1344 /* V */ 0x0d | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1345 /* W */ 0x0c | INT_SPEC | NO_E_MOD,
1346 /* X */ 0x0a | STACKED_SPEC | NO_O_MOD,
1347 /* Y */ 0x0a | INT_SPEC | NO_O_MOD,
1348 /* Z */ 0x02 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1350 /* WARNING! This assumes orderings:
1352 * ABDAY_1-ABDAY-7,DAY_1-DAY_7
1353 * ABMON_1-ABMON_12,MON_1-MON12
1354 * Also, there are exactly 6 bytes between 'Z' and 'a'.
1356 #define STRINGS_NL_ITEM_START (26)
1357 _NL_ITEM_INDEX(AM_STR), /* p (P) */
1358 _NL_ITEM_INDEX(ABMON_1), /* B, b */
1359 _NL_ITEM_INDEX(ABDAY_1), /* A, a */
1364 /* a */ 0x02 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1365 /* b */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1366 /* c */ 0x08 | STACKED_SPEC | NO_O_MOD,
1367 /* d */ 0x00 | INT_SPEC | NO_E_MOD,
1368 /* e */ 0x00 | INT_SPEC | NO_E_MOD,
1369 /* f */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1370 /* g */ 0x0e | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1371 /* h */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1372 /* i */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1373 /* j */ 0x01 | INT_SPEC | NO_E_MOD | NO_O_MOD,
1374 /* k */ 0x06 | INT_SPEC | NO_E_MOD, /* glibc */
1375 /* l */ 0x07 | INT_SPEC | NO_E_MOD, /* glibc */
1376 /* m */ 0x02 | INT_SPEC | NO_E_MOD,
1377 /* n */ 0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1378 /* o */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1379 /* p */ 0x00 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1380 /* q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1381 /* r */ 0x0b | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1382 /* s */ 0x00 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1383 /* t */ 0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1384 /* u */ 0x0b | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1385 /* v */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1386 /* w */ 0x03 | INT_SPEC | NO_E_MOD,
1387 /* x */ 0x09 | STACKED_SPEC | NO_O_MOD,
1388 /* y */ 0x09 | INT_SPEC,
1389 /* z */ 0x01 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1391 #define INT_FIELD_START (26+6+26)
1392 /* (field #) << 3 + lower bound (0|1) + correction 0:none, 2:-1, 4:-1900
1393 * followed by upper bound prior to correction with 1=>366 and 2=>9999. */
1394 /* d, e */ (3 << 3) + 1 + 0, 31,
1395 /* j */ (7 << 3) + 1 + 2, /* 366 */ 1,
1396 /* m */ (4 << 3) + 1 + 2, 12,
1397 /* w */ (6 << 3) + 0 + 0, 6,
1398 /* M */ (1 << 3) + 0 + 0, 59,
1399 /* S */ 0 + 0 + 0, 60,
1400 /* H (k) */ (2 << 3) + 0 + 0, 23,
1401 /* I (l) */ (9 << 3) + 1 + 0, 12, /* goes with 8 -- am/pm */
1402 /* C */ (10<< 3) + 0 + 0, 99,
1403 /* y */ (11<< 3) + 0 + 0, 99,
1404 /* Y */ (5 << 3) + 0 + 4, /* 9999 */ 2,
1405 /* u */ (6 << 3) + 1 + 0, 7,
1406 /* The following are processed and range-checked, but ignored otherwise. */
1407 /* U, W */ (12<< 3) + 0 + 0, 53,
1408 /* V */ (12<< 3) + 1 + 0, 53,
1409 /* g */ (12<< 3) + 0 + 0, 99,
1410 /* G */ (12<< 3) + 0 /*+ 4*/, /* 9999 */ 2, /* Note: -1 or 10000? */
1412 #define STACKED_STRINGS_START (INT_FIELD_START+32)
1413 5, 6, 14, 22, 27, /* 5 - offsets from offset-count to strings */
1414 ' ', 0, /* 2 - %n or %t */
1415 '%', 'm', '/', '%', 'd', '/', '%', 'y', 0, /* 9 - %D */
1416 '%', 'Y', '-', '%', 'm', '-', '%', 'd', 0, /* 9 - %F (glibc extension) */
1417 '%', 'H', ':', '%', 'M', 0, /* 6 - %R*/
1418 '%', 'H', ':', '%', 'M', ':', '%', 'S', 0, /* 9 - %T */
1420 #define STACKED_STRINGS_NL_ITEM_START (STACKED_STRINGS_START + 40)
1421 _NL_ITEM_INDEX(D_T_FMT), /* c */
1422 _NL_ITEM_INDEX(D_FMT), /* x */
1423 _NL_ITEM_INDEX(T_FMT), /* X */
1424 _NL_ITEM_INDEX(T_FMT_AMPM), /* r */
1425 #ifdef ENABLE_ERA_CODE
1426 _NL_ITEM_INDEX(ERA_D_T_FMT), /* Ec */
1427 _NL_ITEM_INDEX(ERA_D_FMT), /* Ex */
1428 _NL_ITEM_INDEX(ERA_T_FMT), /* EX */
1434 char attribute_hidden *__UCXL(strptime)(const char *__restrict buf, const char *__restrict format,
1435 struct tm *__restrict tm __LOCALE_PARAM)
1437 register const char *p;
1439 const char *stack[MAX_PUSH];
1447 fields[i] = INT_MIN;
1455 if (lvl == 0) { /* Done. */
1456 if (fields[6] == 7) { /* Cleanup for %u here since just once. */
1457 fields[6] = 0; /* Don't use mod in case unset. */
1461 do { /* Store the values into tm. */
1462 if (fields[i] != INT_MIN) {
1463 ((int *) tm)[i] = fields[i];
1467 return (char *) buf; /* Success. */
1473 if ((*p == '%') && (*++p != '%')) {
1475 if ((*p == 'O') || (*p == 'E')) { /* Modifier? */
1476 mod |= ((*p == 'O') ? NO_O_MOD : NO_E_MOD);
1481 || (((unsigned char)(((*p) | 0x20) - 'a')) >= 26)
1482 || (((code = spec[(int)(*p - 'A')]) & mod) >= ILLEGAL_SPEC)
1484 return NULL; /* Illegal spec. */
1487 if ((code & MASK_SPEC) == STACKED_SPEC) {
1488 if (lvl == MAX_PUSH) {
1489 return NULL; /* Stack full so treat as illegal spec. */
1492 if ((code &= 0xf) < 8) {
1493 p = ((const char *) spec) + STACKED_STRINGS_START + code;
1494 p += *((unsigned char *)p);
1498 p = ((const char *) spec) + STACKED_STRINGS_NL_ITEM_START
1500 #ifdef ENABLE_ERA_CODE
1501 if ((mod & NO_E_MOD) /* Actually, this means E modifier present. */
1502 && (*(o = __XL(nl_langinfo)(_NL_ITEM(LC_TIME,
1503 (int)(((unsigned char *)p)[4]))
1511 p = __XL(nl_langinfo)(_NL_ITEM(LC_TIME,
1512 (int)(*((unsigned char *)p)))
1519 if ((code & MASK_SPEC) == STRING_SPEC) {
1521 j = spec[STRINGS_NL_ITEM_START + 3 + code];
1522 i = _NL_ITEM(LC_TIME, spec[STRINGS_NL_ITEM_START + code]);
1523 /* Go backwards to check full names before abreviations. */
1526 o = __XL(nl_langinfo)(i+j __LOCALE_ARG);
1527 if (!__UCXL(strncasecmp)(buf,o,__strlen(o) __LOCALE_ARG) && *o) {
1528 do { /* Found a match. */
1531 if (!code) { /* am/pm */
1533 if (fields[9] >= 0) { /* We have a previous %I or %l. */
1534 fields[2] = fields[9] + fields[8];
1536 } else { /* day (4) or month (6) */
1537 fields[2 + (code << 1)]
1538 = j % (spec[STRINGS_NL_ITEM_START + 3 + code] >> 1);
1543 return NULL; /* Failed to match. */
1546 if ((code & MASK_SPEC) == CALC_SPEC) {
1547 if ((code &= 0xf) < 1) { /* s or z*/
1553 if (!ISSPACE(*buf)) { /* Signal an error if whitespace. */
1554 #ifdef TIME_T_IS_UNSIGNED
1555 t = __UCXL(strtoul)(buf, &o, 10 __LOCALE_ARG);
1557 t = __UCXL(strtol)(buf, &o, 10 __LOCALE_ARG);
1560 if ((o == buf) || errno) { /* Not a number or overflow. */
1563 __set_errno(i); /* Restore errno. */
1566 if (!code) { /* s */
1567 __localtime_r(&t, tm); /* TODO: check for failure? */
1569 do { /* Now copy values from tm to fields. */
1570 fields[i] = ((int *) tm)[i];
1574 /* TODO: glibc treats %Z as a nop. For now, do the same. */
1578 assert((code & MASK_SPEC) == INT_SPEC);
1580 register const unsigned char *x;
1582 x = spec + INT_FIELD_START + (code << 1);
1583 if ((j = x[1]) < 3) { /* upper bound (inclusive) */
1584 j = ((j==1) ? 366 : 9999);
1587 while (ISDIGIT(*buf)) {
1591 if ((i = 10*i + (*buf - '0')) > j) { /* Overflow. */
1596 if (i < (*x & 1)) { /* This catches no-digit case too. */
1606 if (*x == (9 << 3) + 1 + 0) { /* %I or %l */
1610 if (fields[8] >= 0) { /* We have a previous %p or %P. */
1611 fields[2] = i + fields[8];
1615 fields[(*x) >> 3] = i;
1617 if (((unsigned char)(*x - (10<< 3) + 0 + 0)) <= 8) { /* %C or %y */
1618 if ((j = fields[10]) < 0) { /* No %C, so i must be %y data. */
1619 if (i <= 68) { /* Map [0-68] to 2000+i */
1622 } else { /* Have %C data, but what about %y? */
1623 if ((i = fields[11]) < 0) { /* No %y data. */
1624 i = 0; /* Treat %y val as 0 following glibc's example. */
1632 } else if (ISSPACE(*p)) {
1634 while (ISSPACE(*buf)) {
1638 } else if (*buf++ == *p++) {
1644 __UCXL_ALIAS(strptime)
1646 #endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
1649 /**********************************************************************/
1653 #error The uClibc version of time is in sysdeps/linux/common.
1656 time_t time(register time_t *tloc)
1659 register struct timeval *p = &tv;
1661 gettimeofday(p, NULL); /* This should never fail... */
1671 /**********************************************************************/
1674 static const char vals[] = {
1675 'T', 'Z', 0, /* 3 */
1676 'U', 'T', 'C', 0, /* 4 */
1677 25, 60, 60, 1, /* 4 */
1680 6, 0, 0, /* Note: overloaded for non-M non-J case... */
1682 ',', 'M', '4', '.', '1', '.', '0',
1683 ',', 'M', '1', '0', '.', '5', '.', '0', 0
1687 #define UTC (vals + 3)
1688 #define RANGE (vals + 7)
1689 #define RULE (vals + 11 - 1)
1690 #define DEFAULT_RULES (vals + 22)
1692 /* Initialize to UTC. */
1695 char *tzname[2] = { (char *) UTC, (char *) (UTC-1) };
1697 #ifdef __UCLIBC_HAS_THREADS__
1698 pthread_mutex_t _time_tzlock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
1701 rule_struct _time_tzinfo[2];
1703 static const char *getoffset(register const char *e, long *pn)
1705 register const char *s = RANGE-1;
1713 if (__isdigit_char(*e)) {
1716 if (__isdigit_char(*e)) {
1717 f = 10 * f + (*e++ - '0');
1719 if (((unsigned int)f) >= *s) {
1734 static const char *getnumber(register const char *e, int *pn)
1737 /* bcc can optimize the counter if it thinks it is a pointer... */
1738 register const char *n = (const char *) 3;
1742 while (n && __isdigit_char(*e)) {
1743 f = 10 * f + (*e++ - '0');
1748 return (n == (const char *) 3) ? NULL : e;
1754 while (n && __isdigit_char(*e)) {
1755 f = 10 * f + (*e++ - '0');
1760 return (n == 3) ? NULL : e;
1761 #endif /* __BCC__ */
1765 #ifdef __UCLIBC_MJN3_ONLY__
1766 #warning CONSIDER: Should we preserve errno from open/read/close errors re TZ file?
1769 #ifdef __UCLIBC_HAS_TZ_FILE__
1771 #ifndef __UCLIBC_HAS_TZ_FILE_READ_MANY__
1772 static int TZ_file_read; /* Let BSS initialization set this to 0. */
1773 #endif /* __UCLIBC_HAS_TZ_FILE_READ_MANY__ */
1775 static char *read_TZ_file(char *buf)
1782 if ((fd = __open(__UCLIBC_TZ_FILE_PATH__, O_RDONLY)) >= 0) {
1786 if ((r = __read(fd, p, todo)) < 0) {
1796 if ((p > buf) && (p[-1] == '\n')) { /* Must end with newline. */
1799 #ifndef __UCLIBC_HAS_TZ_FILE_READ_MANY__
1801 #endif /* __UCLIBC_HAS_TZ_FILE_READ_MANY__ */
1811 #endif /* __UCLIBC_HAS_TZ_FILE__ */
1813 void attribute_hidden __tzset(void)
1815 register const char *e;
1819 rule_struct new_rules[2];
1822 #ifdef __UCLIBC_HAS_TZ_FILE__
1823 char buf[TZ_BUFLEN];
1824 #endif /* __UCLIBC_HAS_TZ_FILE__ */
1825 #ifdef __UCLIBC_HAS_TZ_CACHING__
1826 static char oldval[TZ_BUFLEN]; /* BSS-zero'd. */
1827 #endif /* __UCLIBC_HAS_TZ_CACHING__ */
1831 e = __getenv(TZ); /* TZ env var always takes precedence. */
1833 #if defined(__UCLIBC_HAS_TZ_FILE__) && !defined(__UCLIBC_HAS_TZ_FILE_READ_MANY__)
1834 /* Put this inside the lock to prevent the possiblity of two different
1835 * timezones being used in a threaded app. */
1838 TZ_file_read = 0; /* Reset if the TZ env var is set. */
1839 } else if (TZ_file_read > 0) {
1842 #endif /* defined(__UCLIBC_HAS_TZ_FILE__) && !defined(__UCLIBC_HAS_TZ_FILE_READ_MANY__) */
1844 /* Warning!!! Since uClibc doesn't do lib locking, the following is
1845 * potentially unsafe in a multi-threaded program since it is remotely
1846 * possible that another thread could call setenv() for TZ and overwrite
1847 * the string being parsed. So, don't do that... */
1849 if ((!e /* TZ env var not set... */
1850 #ifdef __UCLIBC_HAS_TZ_FILE__
1851 && !(e = read_TZ_file(buf)) /* and no file or invalid file */
1852 #endif /* __UCLIBC_HAS_TZ_FILE__ */
1853 ) || !*e) { /* or set to empty string. */
1854 ILLEGAL: /* TODO: Clean up the following... */
1855 #ifdef __UCLIBC_HAS_TZ_CACHING__
1856 *oldval = 0; /* Set oldval to an empty string. */
1857 #endif /* __UCLIBC_HAS_TZ_CACHING__ */
1858 __memset(_time_tzinfo, 0, 2*sizeof(rule_struct));
1859 __strcpy(_time_tzinfo[0].tzname, UTC);
1863 if (*e == ':') { /* Ignore leading ':'. */
1867 #ifdef __UCLIBC_HAS_TZ_CACHING__
1868 if (__strcmp(e, oldval) == 0) { /* Same string as last time... */
1869 goto FAST_DONE; /* So nothing to do. */
1871 /* Make a copy of the TZ env string. It won't be nul-terminated if
1872 * it is too long, but it that case it will be illegal and will be reset
1873 * to the empty string anyway. */
1874 __strncpy(oldval, e, TZ_BUFLEN);
1875 #endif /* __UCLIBC_HAS_TZ_CACHING__ */
1878 new_rules[1].tzname[0] = 0;
1880 /* Get std or dst name. */
1887 s = new_rules[count].tzname;
1890 && isascii(*e) /* SUSv3 requires char in portable char set. */
1892 || (c && (isalnum(*e) || (*e == '+') || (*e == '-'))))
1895 if (++n > TZNAME_MAX) {
1901 if ((n < 3) /* Check for minimum length. */
1902 || (c && (*e++ != c)) /* Match any quoting '<'. */
1909 if ((*e != '-') && (*e != '+')) {
1910 if (count && !__isdigit_char(*e)) {
1911 off -= 3600; /* Default to 1 hour ahead of std. */
1918 if (!(e = getoffset(e, &off))) {
1923 off = -off; /* Save off in case needed for dst default. */
1926 new_rules[count].gmt_offset = off;
1929 new_rules[1].gmt_offset = off; /* Shouldn't be needed... */
1934 } else { /* OK, we have dst, so get some rules. */
1936 if (!*e) { /* No rules so default to US rules. */
1947 if ((c = *e++) == 'M') {
1949 } else if (c == 'J') {
1957 *(p = &new_rules[count].rule_type) = c;
1964 if (!(e = getnumber(e, &f))
1965 || (((unsigned int)(f - s[1])) > n)
1966 || (*s && (*e++ != *s))
1971 } while ((n = *(s += 2)) > 0);
1973 off = 2 * 60 * 60; /* Default to 2:00:00 */
1976 if (!(e = getoffset(e, &off))) {
1980 new_rules[count].dst_offset = off;
1981 } while (++count < 2);
1988 __memcpy(_time_tzinfo, new_rules, sizeof(new_rules));
1990 tzname[0] = _time_tzinfo[0].tzname;
1991 tzname[1] = _time_tzinfo[1].tzname;
1992 daylight = !!_time_tzinfo[1].tzname[0];
1993 timezone = _time_tzinfo[0].gmt_offset;
1995 #if defined(__UCLIBC_HAS_TZ_FILE__) || defined(__UCLIBC_HAS_TZ_CACHING__)
2000 strong_alias(__tzset,tzset)
2002 /**********************************************************************/
2003 /* #ifdef L_utime */
2005 /* utime is a syscall in both linux and elks. */
2006 /* int utime(const char *path, const struct utimbuf *times) */
2009 /**********************************************************************/
2011 /**********************************************************************/
2015 #error The uClibc version of utimes is in sysdeps/linux/common.
2019 #include <sys/time.h>
2021 int utimes(const char *filename, register const struct timeval *tvp)
2023 register struct utimbuf *p = NULL;
2028 p->actime = tvp[0].tv_sec;
2029 p->modtime = tvp[1].tv_sec;
2031 return utime(filename, p);
2035 /**********************************************************************/
2038 static const uint16_t _vals[] = {
2039 60, 60, 24, 7 /* special */, 36524, 1461, 365, 0
2042 static const unsigned char days[] = {
2043 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, /* non-leap */
2047 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
2048 static const char utc_string[] = "UTC";
2052 * If time_t is 32 bits, then no overflow is possible.
2053 * It time_t is > 32 bits, this needs to be adjusted to deal with overflow.
2056 /* Note: offset is the correction in _days_ to *timer! */
2058 struct tm attribute_hidden *_time_t2tm(const time_t *__restrict timer,
2059 int offset, struct tm *__restrict result)
2063 int wday; /* Note: wday can be uninitialized. */
2066 register const uint16_t *vp;
2072 if ((v = *vp) == 7) {
2073 /* Overflow checking, assuming time_t is long int... */
2074 #if (LONG_MAX > INT_MAX) && (LONG_MAX > 2147483647L)
2075 #if (INT_MAX == 2147483647L) && (LONG_MAX == 9223372036854775807L)
2076 /* Valid range for t is [-784223472856L, 784223421720L].
2077 * Outside of this range, the tm_year field will overflow. */
2078 if (((unsigned long)(t + offset- -784223472856L))
2079 > (784223421720L - -784223472856L)
2084 #error overflow conditions unknown
2088 /* We have days since the epoch, so caluclate the weekday. */
2089 #if defined(__BCC__) && TIME_T_IS_UNSIGNED
2090 wday = (t + 4) % (*vp); /* t is unsigned */
2092 wday = ((int)((t % (*vp)) + 11)) % ((int)(*vp)); /* help bcc */
2094 /* Set divisor to days in 400 years. Be kind to bcc... */
2095 v = ((time_t)(vp[1])) << 2;
2097 /* Change to days since 1/1/1601 so that for 32 bit time_t
2098 * values, we'll have t >= 0. This should be changed for
2099 * archs with larger time_t types.
2100 * Also, correct for offset since a multiple of 7. */
2102 /* TODO: Does this still work on archs with time_t > 32 bits? */
2103 t += (135140L - 366) + offset; /* 146097 - (365*30 + 7) -366 */
2105 #if defined(__BCC__) && TIME_T_IS_UNSIGNED
2106 t -= ((t1 = t / v) * v);
2108 if ((t -= ((t1 = t / v) * v)) < 0) {
2114 if ((*vp == 7) && (t == v-1)) {
2115 --t; /* Correct for 400th year leap case */
2116 ++p[4]; /* Stash the extra day... */
2119 #if defined(__BCC__) && 0
2143 *p += ((int) t); /* result[7] .. tm_yday */
2145 p -= 2; /* at result[5] */
2147 #if (LONG_MAX > INT_MAX) && (LONG_MAX > 2147483647L)
2148 /* Protect against overflow. TODO: Unecessary if int arith wraps? */
2149 *p = ((((p[-2]<<2) + p[-1])*25 + p[0])<< 2) + (p[1] - 299); /* tm_year */
2151 *p = ((((p[-2]<<2) + p[-1])*25 + p[0])<< 2) + p[1] - 299; /* tm_year */
2154 p[1] = wday; /* result[6] .. tm_wday */
2157 register const unsigned char *d = days;
2160 if (__isleap(wday)) {
2164 wday = p[2] + 1; /* result[7] .. tm_yday */
2165 *--p = 0; /* at result[4] .. tm_mon */
2169 d -= 11; /* Backup to non-leap Feb. */
2172 ++*p; /* Increment tm_mon. */
2174 p[-1] = wday; /* result[3] .. tm_mday */
2176 /* TODO -- should this be 0? */
2177 p[4] = 0; /* result[8] .. tm_isdst */
2178 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
2179 result->tm_gmtoff = 0;
2180 result->tm_zone = utc_string;
2181 #endif /* __UCLIBC_HAS_TM_EXTENSIONS__ */
2187 /**********************************************************************/
2190 struct tm __time_tm; /* Global shared by gmtime() and localtime(). */
2193 /**********************************************************************/
2194 #ifdef L__time_mktime
2196 time_t attribute_hidden _time_mktime(struct tm *timeptr, int store_on_success)
2204 t = _time_mktime_tzi(timeptr, store_on_success, _time_tzinfo);
2212 /**********************************************************************/
2213 #ifdef L__time_mktime_tzi
2215 static const unsigned char __vals[] = {
2216 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, /* non-leap */
2220 time_t attribute_hidden _time_mktime_tzi(struct tm *timeptr, int store_on_success,
2230 /* 0:sec 1:min 2:hour 3:mday 4:mon 5:year 6:wday 7:yday 8:isdst */
2231 register int *p = (int *) &x;
2232 register const unsigned char *s;
2235 __memcpy(p, timeptr, sizeof(struct tm));
2237 if (!tzi[1].tzname[0]) { /* No dst in this timezone, */
2238 p[8] = 0; /* so set tm_isdst to 0. */
2242 if (p[8]) { /* Either dst or unknown? */
2243 default_dst = 1; /* Assume advancing (even if unknown). */
2244 p[8] = ((p[8] > 0) ? 1 : -1); /* Normalize so abs() <= 1. */
2248 p[5] = (p[5] - ((p[6] = p[5]/d) * d)) + (p[7] = p[4]/12);
2249 if ((p[4] -= 12 * p[7]) < 0) {
2255 d = (p[5] += 1900); /* Correct year. Now between 1900 and 2300. */
2265 s -= 11; /* Backup to non-leap Feb. */
2273 days = -719163L + ((long)d)*365 + ((d/4) - (d/100) + (d/400) + p[3] + p[7]);
2274 secs = p[0] + 60*( p[1] + 60*((long)(p[2])) )
2275 + tzi[default_dst].gmt_offset;
2281 if ( ((unsigned long)(days + secs/86400L)) > 49710L) {
2285 secs += (days * 86400L);
2288 d = -719163L + d*365 + (d/4) - (d/100) + (d/400);
2290 + tzi[default_dst].gmt_offset
2293 + 24*(((146073L * ((long long)(p[6])) + d)
2297 if (((unsigned long long)(secs - LONG_MIN))
2298 > (((unsigned long long)LONG_MAX) - LONG_MIN)
2305 d = ((struct tm *)p)->tm_isdst;
2308 __time_localtime_tzi(&t, (struct tm *)p, tzi);
2310 if (t == ((time_t)(-1))) { /* Remember, time_t can be unsigned. */
2314 if ((d < 0) && (((struct tm *)p)->tm_isdst != default_dst)) {
2316 secs -= (days * 86400L);
2318 secs += (tzi[1-default_dst].gmt_offset
2319 - tzi[default_dst].gmt_offset);
2324 if (store_on_success) {
2325 __memcpy(timeptr, p, sizeof(struct tm));
2334 /**********************************************************************/
2335 #if defined(L_wcsftime) || defined(L_wcsftime_l)
2337 #if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE)
2339 extern size_t __wcsftime_l (wchar_t *__restrict __s, size_t __maxsize,
2340 __const wchar_t *__restrict __format,
2341 __const struct tm *__restrict __timeptr,
2342 __locale_t __loc) __THROW attribute_hidden;
2344 size_t wcsftime(wchar_t *__restrict s, size_t maxsize,
2345 const wchar_t *__restrict format,
2346 const struct tm *__restrict timeptr)
2348 return __wcsftime_l(s, maxsize, format, timeptr, __UCLIBC_CURLOCALE);
2351 #else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
2353 size_t attribute_hidden __UCXL(wcsftime)(wchar_t *__restrict s, size_t maxsize,
2354 const wchar_t *__restrict format,
2355 const struct tm *__restrict timeptr __LOCALE_PARAM )
2357 #warning wcsftime always fails
2358 return 0; /* always fail */
2361 __UCXL_ALIAS(wcsftime)
2363 #endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
2366 /**********************************************************************/
2368 /* Return the number of days in YEAR. */
2370 int dysize(int year)
2372 return __isleap(year) ? 366 : 365;
2376 /**********************************************************************/