lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame^] | 1 | /* Copyright (C) 2002-2004 Manuel Novoa III <mjn3@codepoet.org> |
| 2 | * |
| 3 | * GNU Library General Public License (LGPL) version 2 or later. |
| 4 | * |
| 5 | * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. |
| 6 | */ |
| 7 | |
| 8 | /* June 15, 2002 Initial Notes: |
| 9 | * |
| 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. |
| 12 | * |
| 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 |
| 16 | * |
| 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 |
| 19 | * TZ env variable. |
| 20 | * |
| 21 | * Differences from glibc worth noting: |
| 22 | * |
| 23 | * Leap seconds are not considered here. |
| 24 | * |
| 25 | * glibc stores additional timezone info the struct tm, whereas we don't. |
| 26 | * |
| 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. |
| 30 | * |
| 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. |
| 35 | * |
| 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. |
| 41 | * |
| 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. |
| 47 | * |
| 48 | * TODO - Implement getdate? tzfile? struct tm extensions? |
| 49 | * |
| 50 | * TODO - Rework _time_mktime to remove the dependency on long long. |
| 51 | */ |
| 52 | |
| 53 | /* Oct 28, 2002 |
| 54 | * |
| 55 | * Fixed allowed char check for std and dst TZ fields. |
| 56 | * |
| 57 | * Added several options concerned with timezone support. The names will |
| 58 | * probably change once Erik gets the new config system in place. |
| 59 | * |
| 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 |
| 65 | * |
| 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. |
| 68 | * |
| 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 |
| 71 | * same. |
| 72 | * |
| 73 | * Nov 21, 2002 Fix an error return case in _time_mktime. |
| 74 | * |
| 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. |
| 77 | * |
| 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. |
| 82 | * |
| 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. |
| 85 | * |
| 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 |
| 88 | * the normal ranges. |
| 89 | * |
| 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. |
| 93 | * |
| 94 | * The uClibc behavior is to always normalize the struct tm and then |
| 95 | * try to determing the dst setting. |
| 96 | * |
| 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. |
| 100 | * |
| 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 |
| 105 | * list. |
| 106 | * |
| 107 | * Fix a dst-related bug which resulted in use of uninitialized data. |
| 108 | * |
| 109 | * Nov 15, 2003 I forgot to update the thread locking in the last dst fix. |
| 110 | * |
| 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. |
| 114 | * |
| 115 | * May 7, 2004 |
| 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. |
| 121 | * |
| 122 | * Jul 24, 2004 |
| 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 |
| 127 | * self-test suite. |
| 128 | * NOTE: We set uninitialized timezone names to "???", and this |
| 129 | * differs (intentionally) from glibc's behavior. |
| 130 | */ |
| 131 | |
| 132 | #include <stdio.h> |
| 133 | #include <stdlib.h> |
| 134 | #include <stddef.h> |
| 135 | #include <string.h> |
| 136 | #include <time.h> |
| 137 | #include <sys/time.h> |
| 138 | #include <limits.h> |
| 139 | #include <assert.h> |
| 140 | #include <errno.h> |
| 141 | #include <ctype.h> |
| 142 | #include <langinfo.h> |
| 143 | #include <locale.h> |
| 144 | #include <fcntl.h> |
| 145 | #include <unistd.h> |
| 146 | #include <bits/uClibc_uintmaxtostr.h> |
| 147 | #include <bits/uClibc_mutex.h> |
| 148 | |
| 149 | #ifdef __UCLIBC_HAS_WCHAR__ |
| 150 | #include <wchar.h> |
| 151 | #endif |
| 152 | #ifdef __UCLIBC_HAS_XLOCALE__ |
| 153 | #include <xlocale.h> |
| 154 | #endif |
| 155 | |
| 156 | |
| 157 | #ifndef __isleap |
| 158 | #define __isleap(y) ( !((y) % 4) && ( ((y) % 100) || !((y) % 400) ) ) |
| 159 | #endif |
| 160 | |
| 161 | #ifndef TZNAME_MAX |
| 162 | #define TZNAME_MAX _POSIX_TZNAME_MAX |
| 163 | #endif |
| 164 | |
| 165 | #if defined (L_tzset) || defined (L_localtime_r) || defined(L_strftime) || \ |
| 166 | defined(L__time_mktime) || defined(L__time_mktime_tzi) || \ |
| 167 | ((defined(L_strftime) || defined(L_strftime_l)) && \ |
| 168 | defined(__UCLIBC_HAS_XLOCALE__)) |
| 169 | |
| 170 | void _time_tzset(int use_old_rules) attribute_hidden; |
| 171 | |
| 172 | #ifndef L__time_mktime |
| 173 | |
| 174 | /* Jan 1, 2007 Z - tm = 0,0,0,1,0,107,1,0,0 */ |
| 175 | |
| 176 | static const time_t new_rule_starts = 1167609600; |
| 177 | |
| 178 | #endif |
| 179 | #endif |
| 180 | |
| 181 | /**********************************************************************/ |
| 182 | /* The era code is currently unfinished. */ |
| 183 | /* #define ENABLE_ERA_CODE */ |
| 184 | |
| 185 | #define TZ_BUFLEN (2*TZNAME_MAX + 56) |
| 186 | |
| 187 | #ifdef __UCLIBC_HAS_TZ_FILE__ |
| 188 | |
| 189 | #include <sys/stat.h> |
| 190 | #include "paths.h" |
| 191 | /* ":<tzname>+hh:mm:ss<tzname>+hh:mm:ss,Mmm.w.d/hh:mm:ss,Mmm.w.d/hh:mm:ss" + nul */ |
| 192 | /* 1 + 2*(1+TZNAME_MAX+1 + 9 + 7 + 9) + 1 = 2*TZNAME_MAX + 56 */ |
| 193 | |
| 194 | #else /* __UCLIBC_HAS_TZ_FILE__ */ |
| 195 | |
| 196 | /* Probably no longer needed. */ |
| 197 | #undef __UCLIBC_HAS_TZ_FILE_READ_MANY__ |
| 198 | |
| 199 | #endif /* __UCLIBC_HAS_TZ_FILE__ */ |
| 200 | |
| 201 | /**********************************************************************/ |
| 202 | |
| 203 | extern struct tm __time_tm attribute_hidden; |
| 204 | |
| 205 | typedef struct { |
| 206 | long gmt_offset; |
| 207 | long dst_offset; |
| 208 | short day; /* for J or normal */ |
| 209 | short week; |
| 210 | short month; |
| 211 | short rule_type; /* J, M, \0 */ |
| 212 | char tzname[TZNAME_MAX+1]; |
| 213 | } rule_struct; |
| 214 | |
| 215 | __UCLIBC_MUTEX_EXTERN(_time_tzlock); |
| 216 | |
| 217 | extern rule_struct _time_tzinfo[2] attribute_hidden; |
| 218 | |
| 219 | extern struct tm *_time_t2tm(const time_t *__restrict timer, |
| 220 | int offset, struct tm *__restrict result) attribute_hidden; |
| 221 | |
| 222 | extern time_t _time_mktime(struct tm *timeptr, int store_on_success) attribute_hidden; |
| 223 | |
| 224 | extern struct tm *__time_localtime_tzi(const time_t *__restrict timer, |
| 225 | struct tm *__restrict result, |
| 226 | rule_struct *tzi) attribute_hidden; |
| 227 | |
| 228 | extern time_t _time_mktime_tzi(struct tm *timeptr, int store_on_success, |
| 229 | rule_struct *tzi) attribute_hidden; |
| 230 | |
| 231 | /**********************************************************************/ |
| 232 | #ifdef L_asctime |
| 233 | |
| 234 | static char __time_str[26]; |
| 235 | |
| 236 | char *asctime(const struct tm *ptm) |
| 237 | { |
| 238 | return asctime_r(ptm, __time_str); |
| 239 | } |
| 240 | libc_hidden_def(asctime) |
| 241 | |
| 242 | #endif |
| 243 | /**********************************************************************/ |
| 244 | #ifdef L_asctime_r |
| 245 | |
| 246 | /* Strictly speaking, this implementation isn't correct. ANSI/ISO specifies |
| 247 | * that the implementation of asctime() be equivalent to |
| 248 | * |
| 249 | * char *asctime(const struct tm *timeptr) |
| 250 | * { |
| 251 | * static char wday_name[7][3] = { |
| 252 | * "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" |
| 253 | * }; |
| 254 | * static char mon_name[12][3] = { |
| 255 | * "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
| 256 | * "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" |
| 257 | * }; |
| 258 | * static char result[26]; |
| 259 | * |
| 260 | * sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n", |
| 261 | * wday_name[timeptr->tm_wday], |
| 262 | * mon_name[timeptr->tm_mon], |
| 263 | * timeptr->tm_mday, timeptr->tm_hour, |
| 264 | * timeptr->tm_min, timeptr->tm_sec, |
| 265 | * 1900 + timeptr->tm_year); |
| 266 | * return result; |
| 267 | * } |
| 268 | * |
| 269 | * but the above is either inherently unsafe, or carries with it the implicit |
| 270 | * assumption that all fields of timeptr fall within their usual ranges, and |
| 271 | * that the tm_year value falls in the range [-2899,8099] to avoid overflowing |
| 272 | * the static buffer. |
| 273 | * |
| 274 | * If we take the implicit assumption as given, then the implementation below |
| 275 | * is still incorrect for tm_year values < -900, as there will be either |
| 276 | * 0-padding and/or a missing negative sign for the year conversion . But given |
| 277 | * the usual use of asctime(), I think it isn't unreasonable to restrict correct |
| 278 | * operation to the domain of years between 1000 and 9999. |
| 279 | */ |
| 280 | |
| 281 | /* This is generally a good thing, but if you're _sure_ any data passed will be |
| 282 | * in range, you can #undef this. */ |
| 283 | #define SAFE_ASCTIME_R 1 |
| 284 | |
| 285 | static const unsigned char at_data[] = { |
| 286 | 'S', 'u', 'n', 'M', 'o', 'n', 'T', 'u', 'e', 'W', 'e', 'd', |
| 287 | 'T', 'h', 'u', 'F', 'r', 'i', 'S', 'a', 't', |
| 288 | |
| 289 | 'J', 'a', 'n', 'F', 'e', 'b', 'M', 'a', 'r', 'A', 'p', 'r', |
| 290 | 'M', 'a', 'y', 'J', 'u', 'n', 'J', 'u', 'l', 'A', 'u', 'g', |
| 291 | 'S', 'e', 'p', 'O', 'c', 't', 'N', 'o', 'v', 'D', 'e', 'c', |
| 292 | |
| 293 | #ifdef SAFE_ASCTIME_R |
| 294 | '?', '?', '?', |
| 295 | #endif |
| 296 | ' ', '?', '?', '?', |
| 297 | ' ', '0', |
| 298 | offsetof(struct tm, tm_mday), |
| 299 | ' ', '0', |
| 300 | offsetof(struct tm, tm_hour), |
| 301 | ':', '0', |
| 302 | offsetof(struct tm, tm_min), |
| 303 | ':', '0', |
| 304 | offsetof(struct tm, tm_sec), |
| 305 | ' ', '?', '?', '?', '?', '\n', 0 |
| 306 | }; |
| 307 | |
| 308 | char *asctime_r(register const struct tm *__restrict ptm, |
| 309 | register char *__restrict buffer) |
| 310 | { |
| 311 | int tmp; |
| 312 | |
| 313 | assert(ptm); |
| 314 | assert(buffer); |
| 315 | |
| 316 | #ifdef SAFE_ASCTIME_R |
| 317 | memcpy(buffer, at_data + 3*(7 + 12), sizeof(at_data) - 3*(7 + 12)); |
| 318 | |
| 319 | if (((unsigned int)(ptm->tm_wday)) <= 6) { |
| 320 | memcpy(buffer, at_data + 3 * ptm->tm_wday, 3); |
| 321 | } |
| 322 | |
| 323 | if (((unsigned int)(ptm->tm_mon)) <= 11) { |
| 324 | memcpy(buffer + 4, at_data + 3*7 + 3 * ptm->tm_mon, 3); |
| 325 | } |
| 326 | #else |
| 327 | assert(((unsigned int)(ptm->tm_wday)) <= 6); |
| 328 | assert(((unsigned int)(ptm->tm_mon)) <= 11); |
| 329 | |
| 330 | memcpy(buffer, at_data + 3*(7 + 12) - 3, sizeof(at_data) + 3 - 3*(7 + 12)); |
| 331 | |
| 332 | memcpy(buffer, at_data + 3 * ptm->tm_wday, 3); |
| 333 | memcpy(buffer + 4, at_data + 3*7 + 3 * ptm->tm_mon, 3); |
| 334 | #endif |
| 335 | |
| 336 | #ifdef SAFE_ASCTIME_R |
| 337 | buffer += 19; |
| 338 | tmp = ptm->tm_year + 1900; |
| 339 | if (((unsigned int) tmp) < 10000) { |
| 340 | buffer += 4; |
| 341 | do { |
| 342 | *buffer = '0' + (tmp % 10); |
| 343 | tmp /= 10; |
| 344 | } while (*--buffer == '?'); |
| 345 | } |
| 346 | /* Not sure if we should even bother ... |
| 347 | } else { |
| 348 | __set_errno(EOVERFLOW); |
| 349 | return NULL; |
| 350 | } |
| 351 | */ |
| 352 | #else /* SAFE_ASCTIME_R */ |
| 353 | buffer += 23; |
| 354 | tmp = ptm->tm_year + 1900; |
| 355 | assert( ((unsigned int) tmp) < 10000 ); |
| 356 | /* Not sure if we should even bother ... |
| 357 | if ( ((unsigned int) tmp) >= 10000 ) { |
| 358 | __set_errno(EOVERFLOW); |
| 359 | return NULL; |
| 360 | } |
| 361 | */ |
| 362 | do { |
| 363 | *buffer = '0' + (tmp % 10); |
| 364 | tmp /= 10; |
| 365 | } while (*--buffer == '?'); |
| 366 | #endif /* SAFE_ASCTIME_R */ |
| 367 | |
| 368 | do { |
| 369 | --buffer; |
| 370 | tmp = *((int *)(((const char *) ptm) + (int) *buffer)); |
| 371 | #ifdef SAFE_ASCTIME_R |
| 372 | if (((unsigned int) tmp) >= 100) { /* Just check 2 digit non-neg. */ |
| 373 | buffer[-1] = *buffer = '?'; |
| 374 | } else |
| 375 | #else |
| 376 | assert(((unsigned int) tmp) < 100); /* Just check 2 digit non-neg. */ |
| 377 | #endif |
| 378 | { |
| 379 | *buffer = '0' + (tmp % 10); |
| 380 | #ifdef __BCC__ |
| 381 | buffer[-1] = '0' + (tmp/10); |
| 382 | #else |
| 383 | buffer[-1] += (tmp/10); |
| 384 | #endif |
| 385 | } |
| 386 | } while ((buffer -= 2)[-2] == '0'); |
| 387 | |
| 388 | if (*++buffer == '0') { /* Space-pad day of month. */ |
| 389 | *buffer = ' '; |
| 390 | } |
| 391 | |
| 392 | return buffer - 8; |
| 393 | } |
| 394 | libc_hidden_def(asctime_r) |
| 395 | |
| 396 | #endif |
| 397 | /**********************************************************************/ |
| 398 | #ifdef L_clock |
| 399 | |
| 400 | #include <sys/times.h> |
| 401 | |
| 402 | #ifndef __BCC__ |
| 403 | #if CLOCKS_PER_SEC != 1000000L |
| 404 | #error unexpected value for CLOCKS_PER_SEC! |
| 405 | #endif |
| 406 | #endif |
| 407 | |
| 408 | #ifdef __UCLIBC_CLK_TCK_CONST |
| 409 | # if __UCLIBC_CLK_TCK_CONST > CLOCKS_PER_SEC |
| 410 | # error __UCLIBC_CLK_TCK_CONST > CLOCKS_PER_SEC! |
| 411 | # elif __UCLIBC_CLK_TCK_CONST < 1 |
| 412 | # error __UCLIBC_CLK_TCK_CONST < 1! |
| 413 | # endif |
| 414 | #endif |
| 415 | |
| 416 | /* Note: SUSv3 notes |
| 417 | * |
| 418 | * On XSI-conformant systems, CLOCKS_PER_SEC is defined to be one million. |
| 419 | * |
| 420 | * The value returned by clock() may wrap around on some implementations. |
| 421 | * For example, on a machine with 32-bit values for clock_t, it wraps |
| 422 | * after 2147 seconds. |
| 423 | * |
| 424 | * This implies that we should bitwise and with LONG_MAX. |
| 425 | */ |
| 426 | |
| 427 | clock_t clock(void) |
| 428 | { |
| 429 | struct tms xtms; |
| 430 | unsigned long t; |
| 431 | |
| 432 | times(&xtms); |
| 433 | |
| 434 | t = ((unsigned long) xtms.tms_utime) + xtms.tms_stime; |
| 435 | |
| 436 | #ifndef __UCLIBC_CLK_TCK_CONST |
| 437 | |
| 438 | # error __UCLIBC_CLK_TCK_CONST not defined! |
| 439 | |
| 440 | #elif ((CLOCKS_PER_SEC % __UCLIBC_CLK_TCK_CONST) == 0) |
| 441 | |
| 442 | /* CLOCKS_PER_SEC == k * __UCLIBC_CLK_TCK_CONST for some integer k >= 1. */ |
| 443 | return ((t * (CLOCKS_PER_SEC/__UCLIBC_CLK_TCK_CONST)) & LONG_MAX); |
| 444 | |
| 445 | #else |
| 446 | |
| 447 | /* Unlike the previous case, the scaling factor is not an integer. |
| 448 | * So when tms_utime, tms_stime, or their sum wraps, some of the |
| 449 | * "visible" bits in the return value are affected. Nothing we |
| 450 | * can really do about this though other than handle tms_utime and |
| 451 | * tms_stime seperately and then sum. But since that doesn't really |
| 452 | * buy us much, we don't bother. */ |
| 453 | |
| 454 | return ((((t / __UCLIBC_CLK_TCK_CONST) * CLOCKS_PER_SEC) |
| 455 | + ((((t % __UCLIBC_CLK_TCK_CONST) * CLOCKS_PER_SEC) |
| 456 | / __UCLIBC_CLK_TCK_CONST)) |
| 457 | ) & LONG_MAX); |
| 458 | |
| 459 | #endif |
| 460 | } |
| 461 | |
| 462 | #endif |
| 463 | /**********************************************************************/ |
| 464 | #ifdef L_ctime |
| 465 | |
| 466 | char *ctime(const time_t *t) |
| 467 | { |
| 468 | /* ANSI/ISO/SUSv3 say that ctime is equivalent to the following: |
| 469 | * return asctime(localtime(t)); |
| 470 | * I don't think "equivalent" means "it uses the same internal buffer", |
| 471 | * it means "gives the same resultant string". |
| 472 | * |
| 473 | * I doubt anyone ever uses weird code like: |
| 474 | * struct tm *ptm = localtime(t1); ...; ctime(t2); use(ptm); |
| 475 | * which relies on the assumption that ctime's and localtime's |
| 476 | * internal static struct tm is the same. |
| 477 | * |
| 478 | * Using localtime_r instead of localtime avoids linking in |
| 479 | * localtime's static buffer: |
| 480 | */ |
| 481 | struct tm xtm; |
| 482 | memset(&xtm, 0, sizeof(xtm)); |
| 483 | |
| 484 | return asctime(localtime_r(t, &xtm)); |
| 485 | } |
| 486 | libc_hidden_def(ctime) |
| 487 | #endif |
| 488 | /**********************************************************************/ |
| 489 | #ifdef L_ctime_r |
| 490 | |
| 491 | char *ctime_r(const time_t *t, char *buf) |
| 492 | { |
| 493 | struct tm xtm; |
| 494 | |
| 495 | return asctime_r(localtime_r(t, &xtm), buf); |
| 496 | } |
| 497 | |
| 498 | #endif |
| 499 | /**********************************************************************/ |
| 500 | #ifdef L_difftime |
| 501 | |
| 502 | #include <float.h> |
| 503 | |
| 504 | #if FLT_RADIX != 2 |
| 505 | #error difftime implementation assumptions violated for you arch! |
| 506 | #endif |
| 507 | |
| 508 | double difftime(time_t time1, time_t time0) |
| 509 | { |
| 510 | #if (LONG_MAX >> DBL_MANT_DIG) == 0 |
| 511 | |
| 512 | /* time_t fits in the mantissa of a double. */ |
| 513 | return (double)time1 - (double)time0; |
| 514 | |
| 515 | #elif ((LONG_MAX >> DBL_MANT_DIG) >> DBL_MANT_DIG) == 0 |
| 516 | |
| 517 | /* time_t can overflow the mantissa of a double. */ |
| 518 | time_t t1, t0, d; |
| 519 | |
| 520 | d = ((time_t) 1) << DBL_MANT_DIG; |
| 521 | t1 = time1 / d; |
| 522 | time1 -= (t1 * d); |
| 523 | t0 = time0 / d; |
| 524 | time0 -= (t0*d); |
| 525 | |
| 526 | /* Since FLT_RADIX==2 and d is a power of 2, the only possible |
| 527 | * rounding error in the expression below would occur from the |
| 528 | * addition. */ |
| 529 | return (((double) t1) - t0) * d + (((double) time1) - time0); |
| 530 | |
| 531 | #else |
| 532 | #error difftime needs special implementation on your arch. |
| 533 | #endif |
| 534 | } |
| 535 | |
| 536 | #endif |
| 537 | /**********************************************************************/ |
| 538 | #ifdef L_gmtime |
| 539 | |
| 540 | struct tm *gmtime(const time_t *timer) |
| 541 | { |
| 542 | register struct tm *ptm = &__time_tm; |
| 543 | |
| 544 | _time_t2tm(timer, 0, ptm); /* Can return NULL... */ |
| 545 | |
| 546 | return ptm; |
| 547 | } |
| 548 | |
| 549 | #endif |
| 550 | /**********************************************************************/ |
| 551 | #ifdef L_gmtime_r |
| 552 | |
| 553 | struct tm *gmtime_r(const time_t *__restrict timer, |
| 554 | struct tm *__restrict result) |
| 555 | { |
| 556 | return _time_t2tm(timer, 0, result); |
| 557 | } |
| 558 | |
| 559 | #endif |
| 560 | /**********************************************************************/ |
| 561 | #ifdef L_localtime |
| 562 | |
| 563 | struct tm *localtime(const time_t *timer) |
| 564 | { |
| 565 | register struct tm *ptm = &__time_tm; |
| 566 | |
| 567 | /* In this implementation, tzset() is called by localtime_r(). */ |
| 568 | |
| 569 | localtime_r(timer, ptm); /* Can return NULL... */ |
| 570 | |
| 571 | return ptm; |
| 572 | } |
| 573 | libc_hidden_def(localtime) |
| 574 | |
| 575 | #endif |
| 576 | /**********************************************************************/ |
| 577 | #ifdef L_localtime_r |
| 578 | |
| 579 | struct tm *localtime_r(register const time_t *__restrict timer, |
| 580 | register struct tm *__restrict result) |
| 581 | { |
| 582 | __UCLIBC_MUTEX_LOCK(_time_tzlock); |
| 583 | |
| 584 | _time_tzset(*timer < new_rule_starts); |
| 585 | |
| 586 | __time_localtime_tzi(timer, result, _time_tzinfo); |
| 587 | |
| 588 | __UCLIBC_MUTEX_UNLOCK(_time_tzlock); |
| 589 | |
| 590 | return result; |
| 591 | } |
| 592 | libc_hidden_def(localtime_r) |
| 593 | |
| 594 | #endif |
| 595 | /**********************************************************************/ |
| 596 | #ifdef L__time_localtime_tzi |
| 597 | |
| 598 | #ifdef __UCLIBC_HAS_TM_EXTENSIONS__ |
| 599 | |
| 600 | struct ll_tzname_item; |
| 601 | |
| 602 | typedef struct ll_tzname_item { |
| 603 | struct ll_tzname_item *next; |
| 604 | char tzname[1]; |
| 605 | } ll_tzname_item_t; |
| 606 | |
| 607 | /* Structures form a list "UTC" -> "???" -> "tzname1" -> "tzname2"... */ |
| 608 | struct { |
| 609 | struct ll_tzname_item *next; |
| 610 | char tzname[4]; |
| 611 | } ll_tzname_UNKNOWN = { NULL, "???" }; |
| 612 | const struct { |
| 613 | struct ll_tzname_item *next; |
| 614 | char tzname[4]; |
| 615 | } ll_tzname_UTC = { (void*)&ll_tzname_UNKNOWN, "UTC" }; |
| 616 | |
| 617 | static const char *lookup_tzname(const char *key) |
| 618 | { |
| 619 | int len; |
| 620 | ll_tzname_item_t *p = (void*) &ll_tzname_UTC; |
| 621 | |
| 622 | do { |
| 623 | if (strcmp(p->tzname, key) == 0) |
| 624 | return p->tzname; |
| 625 | p = p->next; |
| 626 | } while (p != NULL); |
| 627 | |
| 628 | /* Hmm... a new name. */ |
| 629 | len = strnlen(key, TZNAME_MAX+1); |
| 630 | if (len < TZNAME_MAX+1) { /* Verify legal length */ |
| 631 | p = malloc(sizeof(ll_tzname_item_t) + len); |
| 632 | if (p != NULL) { |
| 633 | /* Insert as 3rd item in the list. */ |
| 634 | p->next = ll_tzname_UNKNOWN.next; |
| 635 | ll_tzname_UNKNOWN.next = p; |
| 636 | return strcpy(p->tzname, key); |
| 637 | } |
| 638 | } |
| 639 | |
| 640 | /* Either invalid or couldn't alloc. */ |
| 641 | return ll_tzname_UNKNOWN.tzname; |
| 642 | } |
| 643 | |
| 644 | #endif /* __UCLIBC_HAS_TM_EXTENSIONS__ */ |
| 645 | |
| 646 | static const unsigned char day_cor[] = { /* non-leap */ |
| 647 | 31, 31, 34, 34, 35, 35, 36, 36, 36, 37, 37, 38, 38 |
| 648 | /* 0, 0, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 7 */ |
| 649 | /* 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 */ |
| 650 | }; |
| 651 | |
| 652 | /* Note: timezone locking is done by localtime_r. */ |
| 653 | |
| 654 | static int tm_isdst(register const struct tm *__restrict ptm, |
| 655 | register rule_struct *r) |
| 656 | { |
| 657 | long sec; |
| 658 | int i, isdst, isleap, day, day0, monlen, mday; |
| 659 | int oday = oday; /* ok to be uninitialized, shutting up compiler warning */ |
| 660 | |
| 661 | isdst = 0; |
| 662 | if (r[1].tzname[0] != 0) { |
| 663 | /* First, get the current seconds offset from the start of the year. |
| 664 | * Fields of ptm are assumed to be in their normal ranges. */ |
| 665 | sec = ptm->tm_sec |
| 666 | + 60 * (ptm->tm_min |
| 667 | + 60 * (long)(ptm->tm_hour |
| 668 | + 24 * ptm->tm_yday)); |
| 669 | /* Do some prep work. */ |
| 670 | i = (ptm->tm_year % 400) + 1900; /* Make sure we don't overflow. */ |
| 671 | isleap = __isleap(i); |
| 672 | --i; |
| 673 | day0 = (1 |
| 674 | + i /* Normal years increment 1 wday. */ |
| 675 | + (i/4) |
| 676 | - (i/100) |
| 677 | + (i/400) ) % 7; |
| 678 | i = 0; |
| 679 | do { |
| 680 | day = r->day; /* Common for 'J' and # case. */ |
| 681 | if (r->rule_type == 'J') { |
| 682 | if (!isleap || (day < (31+29))) { |
| 683 | --day; |
| 684 | } |
| 685 | } else if (r->rule_type == 'M') { |
| 686 | /* Find 0-based day number for 1st of the month. */ |
| 687 | day = 31*r->month - day_cor[r->month -1]; |
| 688 | if (isleap && (day >= 59)) { |
| 689 | ++day; |
| 690 | } |
| 691 | monlen = 31 + day_cor[r->month -1] - day_cor[r->month]; |
| 692 | if (isleap && (r->month == 2)) { |
| 693 | ++monlen; |
| 694 | } |
| 695 | /* Wweekday (0 is Sunday) of 1st of the month |
| 696 | * is (day0 + day) % 7. */ |
| 697 | if ((mday = r->day - ((day0 + day) % 7)) >= 0) { |
| 698 | mday -= 7; /* Back up into prev month since r->week>0. */ |
| 699 | } |
| 700 | if ((mday += 7 * r->week) >= monlen) { |
| 701 | mday -= 7; |
| 702 | } |
| 703 | /* So, 0-based day number is... */ |
| 704 | day += mday; |
| 705 | } |
| 706 | |
| 707 | if (i != 0) { |
| 708 | /* Adjust sec since dst->std change time is in dst. */ |
| 709 | sec += (r[-1].gmt_offset - r->gmt_offset); |
| 710 | if (oday > day) { |
| 711 | ++isdst; /* Year starts in dst. */ |
| 712 | } |
| 713 | } |
| 714 | oday = day; |
| 715 | |
| 716 | /* Now convert day to seconds and add offset and compare. */ |
| 717 | if (sec >= (day * 86400L) + r->dst_offset) { |
| 718 | ++isdst; |
| 719 | } |
| 720 | ++r; |
| 721 | } while (++i < 2); |
| 722 | } |
| 723 | |
| 724 | return (isdst & 1); |
| 725 | } |
| 726 | |
| 727 | struct tm attribute_hidden *__time_localtime_tzi(register const time_t *__restrict timer, |
| 728 | register struct tm *__restrict result, |
| 729 | rule_struct *tzi) |
| 730 | { |
| 731 | time_t x[1]; |
| 732 | long offset; |
| 733 | int days, dst; |
| 734 | |
| 735 | dst = 0; |
| 736 | do { |
| 737 | days = -7; |
| 738 | offset = 604800L - tzi[dst].gmt_offset; |
| 739 | if (*timer > (LONG_MAX - 604800L)) { |
| 740 | days = -days; |
| 741 | offset = -offset; |
| 742 | } |
| 743 | *x = *timer + offset; |
| 744 | |
| 745 | _time_t2tm(x, days, result); |
| 746 | result->tm_isdst = dst; |
| 747 | #ifdef __UCLIBC_HAS_TM_EXTENSIONS__ |
| 748 | # ifdef __USE_BSD |
| 749 | result->tm_gmtoff = - tzi[dst].gmt_offset; |
| 750 | result->tm_zone = lookup_tzname(tzi[dst].tzname); |
| 751 | # else |
| 752 | result->__tm_gmtoff = - tzi[dst].gmt_offset; |
| 753 | result->__tm_zone = lookup_tzname(tzi[dst].tzname); |
| 754 | # endif |
| 755 | #endif /* __UCLIBC_HAS_TM_EXTENSIONS__ */ |
| 756 | } while ((++dst < 2) |
| 757 | && ((result->tm_isdst = tm_isdst(result, tzi)) != 0)); |
| 758 | |
| 759 | return result; |
| 760 | } |
| 761 | |
| 762 | #endif |
| 763 | /**********************************************************************/ |
| 764 | #ifdef L_mktime |
| 765 | |
| 766 | time_t mktime(struct tm *timeptr) |
| 767 | { |
| 768 | return _time_mktime(timeptr, 1); |
| 769 | } |
| 770 | |
| 771 | /* Another name for `mktime'. */ |
| 772 | /* time_t timelocal(struct tm *tp) */ |
| 773 | strong_alias(mktime,timelocal) |
| 774 | |
| 775 | #endif |
| 776 | /**********************************************************************/ |
| 777 | #ifdef L_timegm |
| 778 | /* Like `mktime' but timeptr represents Universal Time, not local time. */ |
| 779 | |
| 780 | time_t timegm(struct tm *timeptr) |
| 781 | { |
| 782 | rule_struct gmt_tzinfo[2]; |
| 783 | |
| 784 | memset(gmt_tzinfo, 0, sizeof(gmt_tzinfo)); |
| 785 | strcpy(gmt_tzinfo[0].tzname, "GMT"); /* Match glibc behavior here. */ |
| 786 | |
| 787 | return _time_mktime_tzi(timeptr, 1, gmt_tzinfo); |
| 788 | } |
| 789 | |
| 790 | #endif |
| 791 | /**********************************************************************/ |
| 792 | #if defined(L_strftime) || defined(L_strftime_l) |
| 793 | |
| 794 | #if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) |
| 795 | |
| 796 | size_t strftime(char *__restrict s, size_t maxsize, |
| 797 | const char *__restrict format, |
| 798 | const struct tm *__restrict timeptr) |
| 799 | { |
| 800 | return strftime_l(s, maxsize, format, timeptr, __UCLIBC_CURLOCALE); |
| 801 | } |
| 802 | |
| 803 | #else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ |
| 804 | |
| 805 | #define NO_E_MOD 0x80 |
| 806 | #define NO_O_MOD 0x40 |
| 807 | |
| 808 | #define ILLEGAL_SPEC 0x3f |
| 809 | |
| 810 | #define INT_SPEC 0x00 /* must be 0x00!! */ |
| 811 | #define STRING_SPEC 0x10 /* must be 0x10!! */ |
| 812 | #define CALC_SPEC 0x20 |
| 813 | #define STACKED_SPEC 0x30 |
| 814 | |
| 815 | #define MASK_SPEC 0x30 |
| 816 | |
| 817 | /* Compatibility: |
| 818 | * |
| 819 | * No alternate digit (%O?) handling. Always uses 0-9. |
| 820 | * Alternate locale format (%E?) handling is broken for nontrivial ERAs. |
| 821 | * glibc's %P is currently faked by %p. This means it doesn't do lower case. |
| 822 | * glibc's %k, %l, and %s are handled. |
| 823 | * glibc apparently allows (and ignores) extraneous 'E' and 'O' modifiers, |
| 824 | * while they are flagged as illegal conversions here. |
| 825 | */ |
| 826 | |
| 827 | /* Warning: Assumes ASCII values! (as do lots of other things in the lib...) */ |
| 828 | static const unsigned char spec[] = { |
| 829 | /* A */ 0x03 | STRING_SPEC | NO_E_MOD | NO_O_MOD, |
| 830 | /* B */ 0x04 | STRING_SPEC | NO_E_MOD | NO_O_MOD, |
| 831 | /* C */ 0x0a | INT_SPEC | NO_O_MOD, |
| 832 | /* D */ 0x02 | STACKED_SPEC | NO_E_MOD | NO_O_MOD, |
| 833 | /* E */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 834 | /* F */ 0x03 | STACKED_SPEC | NO_E_MOD | NO_O_MOD, |
| 835 | /* G */ 0x03 | CALC_SPEC | NO_E_MOD | NO_O_MOD, |
| 836 | /* H */ 0x0b | INT_SPEC | NO_E_MOD, |
| 837 | /* I */ 0x0c | INT_SPEC | NO_E_MOD, |
| 838 | /* J */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 839 | /* K */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 840 | /* L */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 841 | /* M */ 0x0d | INT_SPEC | NO_E_MOD, |
| 842 | /* N */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 843 | /* O */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 844 | /* P */ 0x05 | STRING_SPEC | NO_E_MOD | NO_O_MOD, /* glibc ; use %p */ |
| 845 | /* Q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 846 | /* R */ 0x04 | STACKED_SPEC | NO_E_MOD | NO_O_MOD, |
| 847 | /* S */ 0x0e | INT_SPEC | NO_E_MOD, |
| 848 | /* T */ 0x05 | STACKED_SPEC | NO_E_MOD | NO_O_MOD, |
| 849 | /* U */ 0x04 | CALC_SPEC | NO_E_MOD, |
| 850 | /* V */ 0x05 | CALC_SPEC | NO_E_MOD, |
| 851 | /* W */ 0x06 | CALC_SPEC | NO_E_MOD, |
| 852 | /* X */ 0x0a | STACKED_SPEC | NO_O_MOD, |
| 853 | /* Y */ 0x0f | INT_SPEC | NO_O_MOD, |
| 854 | /* Z */ 0x01 | CALC_SPEC | NO_E_MOD | NO_O_MOD, |
| 855 | '?', /* 26 */ |
| 856 | '?', /* 27 */ |
| 857 | '?', /* 28 */ |
| 858 | '?', /* 29 */ |
| 859 | 0, /* 30 */ |
| 860 | 0, /* 31 */ |
| 861 | /* a */ 0x00 | STRING_SPEC | NO_E_MOD | NO_O_MOD, |
| 862 | /* b */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD, |
| 863 | /* c */ 0x08 | STACKED_SPEC | NO_O_MOD, |
| 864 | /* d */ 0x00 | INT_SPEC | NO_E_MOD, |
| 865 | /* e */ 0x01 | INT_SPEC | NO_E_MOD, |
| 866 | /* f */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 867 | /* g */ 0x02 | CALC_SPEC | NO_E_MOD | NO_O_MOD, |
| 868 | /* h */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD, /* same as b */ |
| 869 | /* i */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 870 | /* j */ 0x08 | INT_SPEC | NO_E_MOD | NO_O_MOD, |
| 871 | /* k */ 0x03 | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */ |
| 872 | /* l */ 0x04 | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */ |
| 873 | /* m */ 0x05 | INT_SPEC | NO_E_MOD, |
| 874 | /* n */ 0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD, |
| 875 | /* o */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 876 | /* p */ 0x02 | STRING_SPEC | NO_E_MOD | NO_O_MOD, |
| 877 | /* q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 878 | /* r */ 0x0b | STACKED_SPEC | NO_E_MOD | NO_O_MOD, |
| 879 | /* s */ 0x07 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */ |
| 880 | /* t */ 0x01 | STACKED_SPEC | NO_E_MOD | NO_O_MOD, |
| 881 | /* u */ 0x07 | INT_SPEC | NO_E_MOD, |
| 882 | /* v */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 883 | /* w */ 0x02 | INT_SPEC | NO_E_MOD, |
| 884 | /* x */ 0x09 | STACKED_SPEC | NO_O_MOD, |
| 885 | /* y */ 0x09 | INT_SPEC, |
| 886 | /* z */ 0x00 | CALC_SPEC | NO_E_MOD | NO_O_MOD, |
| 887 | |
| 888 | |
| 889 | /* WARNING!!! These are dependent on the layout of struct tm!!! */ |
| 890 | #define FIELD_MAX (26+6+26) |
| 891 | 60 /* 61? */, 59, 23, 31, 11, 0 /* 9999 */, 6, 0 /* 365 */, |
| 892 | |
| 893 | #define TP_OFFSETS (FIELD_MAX+8) |
| 894 | 3, /* d */ |
| 895 | 3, /* e */ |
| 896 | 6, /* w */ |
| 897 | 2, /* k */ |
| 898 | 2, /* l */ |
| 899 | 4, /* m */ |
| 900 | 0, /* CURRENTLY UNUSED */ |
| 901 | /* NOTE: u,j,y order must be preserved as 6,7,5 seq is used in the code! */ |
| 902 | #define CALC_OFFSETS (TP_OFFSETS + 7) |
| 903 | 6, /* u */ |
| 904 | 7, /* j */ |
| 905 | 5, /* y */ |
| 906 | 5, /* C */ |
| 907 | 2, /* H */ |
| 908 | 2, /* I */ |
| 909 | 1, /* M */ |
| 910 | 0, /* S */ |
| 911 | 5, /* Y */ |
| 912 | 6, /* a */ |
| 913 | 4, /* b, h */ |
| 914 | 2, /* p */ |
| 915 | 6, /* A */ |
| 916 | 4, /* B */ |
| 917 | 2, /* P */ |
| 918 | |
| 919 | #define TP_CODES (TP_OFFSETS + 16 + 6) |
| 920 | 2 | 16, /* d */ |
| 921 | 2, /* e */ |
| 922 | 0 | 16, /* w */ |
| 923 | 2, /* k */ |
| 924 | 2 | 32 | 0, /* l */ |
| 925 | 2 | 16 | 1, /* m */ |
| 926 | 0, /* CURRENTLY UNUSED */ |
| 927 | 0 | 16 | 8 , /* u */ |
| 928 | 4 | 16 | 1, /* j */ |
| 929 | 2 | 128 | 32 | 16 , /* y */ |
| 930 | 2 | 128 | 64 | 32 | 16 , /* C */ |
| 931 | 2 | 16, /* H */ |
| 932 | 2 | 32 | 16 | 0, /* I */ |
| 933 | 2 | 16, /* M */ |
| 934 | 2 | 16, /* S */ |
| 935 | 6 | 16, /* Y */ |
| 936 | 2, /* a */ |
| 937 | 2, /* b, h */ |
| 938 | 2 | 64, /* p */ |
| 939 | 2, /* A */ |
| 940 | 2, /* B */ |
| 941 | 2 | 64, /* P */ |
| 942 | |
| 943 | #define STRINGS_NL_ITEM_START (TP_CODES + 16 + 6) |
| 944 | _NL_ITEM_INDEX(ABDAY_1), /* a */ |
| 945 | _NL_ITEM_INDEX(ABMON_1), /* b, h */ |
| 946 | _NL_ITEM_INDEX(AM_STR), /* p */ |
| 947 | _NL_ITEM_INDEX(DAY_1), /* A */ |
| 948 | _NL_ITEM_INDEX(MON_1), /* B */ |
| 949 | _NL_ITEM_INDEX(AM_STR), /* P -- wrong! need lower case */ |
| 950 | |
| 951 | #define STACKED_STRINGS_START (STRINGS_NL_ITEM_START+6) |
| 952 | 6, 7, 8, 16, 24, 29, /* 6 - offsets from offset-count to strings */ |
| 953 | '\n', 0, /* 2 */ |
| 954 | '\t', 0, /* 2 */ |
| 955 | '%', 'm', '/', '%', 'd', '/', '%', 'y', 0, /* 9 - %D */ |
| 956 | '%', 'Y', '-', '%', 'm', '-', '%', 'd', 0, /* 9 - %F (glibc extension) */ |
| 957 | '%', 'H', ':', '%', 'M', 0, /* 6 - %R*/ |
| 958 | '%', 'H', ':', '%', 'M', ':', '%', 'S', 0, /* 9 - %T */ |
| 959 | |
| 960 | #define STACKED_STRINGS_NL_ITEM_START (STACKED_STRINGS_START + 43) |
| 961 | _NL_ITEM_INDEX(D_T_FMT), /* c */ |
| 962 | _NL_ITEM_INDEX(D_FMT), /* x */ |
| 963 | _NL_ITEM_INDEX(T_FMT), /* X */ |
| 964 | _NL_ITEM_INDEX(T_FMT_AMPM), /* r */ |
| 965 | #ifdef ENABLE_ERA_CODE |
| 966 | _NL_ITEM_INDEX(ERA_D_T_FMT), /* Ec */ |
| 967 | _NL_ITEM_INDEX(ERA_D_FMT), /* Ex */ |
| 968 | _NL_ITEM_INDEX(ERA_T_FMT), /* EX */ |
| 969 | #endif |
| 970 | }; |
| 971 | |
| 972 | static int load_field(int k, const struct tm *__restrict timeptr) |
| 973 | { |
| 974 | int r; |
| 975 | int r_max; |
| 976 | |
| 977 | r = ((int *) timeptr)[k]; |
| 978 | |
| 979 | r_max = spec[FIELD_MAX + k]; |
| 980 | |
| 981 | if (k == 7) { |
| 982 | r_max = 365; |
| 983 | } else if (k == 5) { |
| 984 | r += 1900; |
| 985 | r_max = 9999; |
| 986 | } |
| 987 | |
| 988 | if ((((unsigned int) r) > r_max) || ((k == 3) && !r)) { |
| 989 | r = -1; |
| 990 | } |
| 991 | |
| 992 | return r; |
| 993 | } |
| 994 | |
| 995 | #define MAX_PUSH 4 |
| 996 | |
| 997 | #ifdef __UCLIBC_MJN3_ONLY__ |
| 998 | #warning TODO: Check multibyte format string validity. |
| 999 | #endif |
| 1000 | |
| 1001 | size_t __XL_NPP(strftime)(char *__restrict s, size_t maxsize, |
| 1002 | const char *__restrict format, |
| 1003 | const struct tm *__restrict timeptr __LOCALE_PARAM ) |
| 1004 | { |
| 1005 | long tzo; |
| 1006 | register const char *p; |
| 1007 | register const char *o; |
| 1008 | #ifndef __UCLIBC_HAS_TM_EXTENSIONS__ |
| 1009 | const rule_struct *rsp; |
| 1010 | #endif |
| 1011 | const char *stack[MAX_PUSH]; |
| 1012 | size_t count; |
| 1013 | size_t o_count; |
| 1014 | int field_val = 0, i = 0, j, lvl; |
| 1015 | int x[3]; /* wday, yday, year */ |
| 1016 | int isofm, days; |
| 1017 | char buf[__UIM_BUFLEN_LONG]; |
| 1018 | unsigned char mod; |
| 1019 | unsigned char code; |
| 1020 | |
| 1021 | /* We'll, let's get this out of the way. */ |
| 1022 | _time_tzset(_time_mktime((struct tm *) timeptr, 0) < new_rule_starts); |
| 1023 | |
| 1024 | lvl = 0; |
| 1025 | p = format; |
| 1026 | count = maxsize; |
| 1027 | |
| 1028 | LOOP: |
| 1029 | if (!count) { |
| 1030 | return 0; |
| 1031 | } |
| 1032 | if (!*p) { |
| 1033 | if (lvl == 0) { |
| 1034 | *s = 0; /* nul-terminate */ |
| 1035 | return maxsize - count; |
| 1036 | } |
| 1037 | p = stack[--lvl]; |
| 1038 | goto LOOP; |
| 1039 | } |
| 1040 | |
| 1041 | o_count = 1; |
| 1042 | if ((*(o = p) == '%') && (*++p != '%')) { |
| 1043 | o_count = 2; |
| 1044 | mod = ILLEGAL_SPEC; |
| 1045 | if ((*p == 'O') || (*p == 'E')) { /* modifier */ |
| 1046 | mod |= ((*p == 'O') ? NO_O_MOD : NO_E_MOD); |
| 1047 | ++o_count; |
| 1048 | ++p; |
| 1049 | } |
| 1050 | if ((((unsigned char)(((*p) | 0x20) - 'a')) >= 26) |
| 1051 | || (((code = spec[(int)(*p - 'A')]) & mod) >= ILLEGAL_SPEC) |
| 1052 | ) { |
| 1053 | if (!*p) { |
| 1054 | --p; |
| 1055 | --o_count; |
| 1056 | } |
| 1057 | goto OUTPUT; |
| 1058 | } |
| 1059 | code &= ILLEGAL_SPEC; /* modifiers are preserved in mod var. */ |
| 1060 | |
| 1061 | if ((code & MASK_SPEC) == STACKED_SPEC) { |
| 1062 | if (lvl == MAX_PUSH) { |
| 1063 | goto OUTPUT; /* Stack full so treat as illegal spec. */ |
| 1064 | } |
| 1065 | stack[lvl++] = ++p; |
| 1066 | if ((code &= 0xf) < 8) { |
| 1067 | p = ((const char *) spec) + STACKED_STRINGS_START + code; |
| 1068 | p += *((unsigned char *)p); |
| 1069 | goto LOOP; |
| 1070 | } |
| 1071 | p = ((const char *) spec) + STACKED_STRINGS_NL_ITEM_START |
| 1072 | + (code & 7); |
| 1073 | #ifdef ENABLE_ERA_CODE |
| 1074 | if ((mod & NO_E_MOD) /* Actually, this means E modifier present. */ |
| 1075 | && (*(o = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME, |
| 1076 | (int)(((unsigned char *)p)[4])) |
| 1077 | __LOCALE_ARG |
| 1078 | ))) |
| 1079 | ) { |
| 1080 | p = o; |
| 1081 | goto LOOP; |
| 1082 | } |
| 1083 | #endif |
| 1084 | p = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME, |
| 1085 | (int)(*((unsigned char *)p))) |
| 1086 | __LOCALE_ARG |
| 1087 | ); |
| 1088 | goto LOOP; |
| 1089 | } |
| 1090 | |
| 1091 | o = ((const char *) spec) + 26; /* set to "????" */ |
| 1092 | if ((code & MASK_SPEC) == CALC_SPEC) { |
| 1093 | |
| 1094 | if (*p == 's') { |
| 1095 | time_t t; |
| 1096 | |
| 1097 | /* Use a cast to silence the warning since *timeptr won't |
| 1098 | * be changed. */ |
| 1099 | if ((t = _time_mktime((struct tm *) timeptr, 0)) |
| 1100 | == ((time_t) -1) |
| 1101 | ) { |
| 1102 | o_count = 1; |
| 1103 | goto OUTPUT; |
| 1104 | } |
| 1105 | #ifdef TIME_T_IS_UNSIGNED |
| 1106 | o = _uintmaxtostr(buf + sizeof(buf) - 1, |
| 1107 | (uintmax_t) t, |
| 1108 | 10, __UIM_DECIMAL); |
| 1109 | #else |
| 1110 | o = _uintmaxtostr(buf + sizeof(buf) - 1, |
| 1111 | (uintmax_t) t, |
| 1112 | -10, __UIM_DECIMAL); |
| 1113 | #endif |
| 1114 | o_count = sizeof(buf); |
| 1115 | goto OUTPUT; |
| 1116 | } else if (((*p) | 0x20) == 'z') { /* 'z' or 'Z' */ |
| 1117 | |
| 1118 | if (timeptr->tm_isdst < 0) { |
| 1119 | /* SUSv3 specifies this behavior for 'z', but we'll also |
| 1120 | * treat it as "no timezone info" for 'Z' too. */ |
| 1121 | o_count = 0; |
| 1122 | goto OUTPUT; |
| 1123 | } |
| 1124 | |
| 1125 | #ifdef __UCLIBC_HAS_TM_EXTENSIONS__ |
| 1126 | |
| 1127 | # ifdef __USE_BSD |
| 1128 | # define RSP_TZNAME timeptr->tm_zone |
| 1129 | # define RSP_GMT_OFFSET (-timeptr->tm_gmtoff) |
| 1130 | # else |
| 1131 | # define RSP_TZNAME timeptr->__tm_zone |
| 1132 | # define RSP_GMT_OFFSET (-timeptr->__tm_gmtoff) |
| 1133 | # endif |
| 1134 | |
| 1135 | #else |
| 1136 | |
| 1137 | #define RSP_TZNAME rsp->tzname |
| 1138 | #define RSP_GMT_OFFSET rsp->gmt_offset |
| 1139 | |
| 1140 | __UCLIBC_MUTEX_LOCK(_time_tzlock); |
| 1141 | |
| 1142 | rsp = _time_tzinfo; |
| 1143 | if (timeptr->tm_isdst > 0) { |
| 1144 | ++rsp; |
| 1145 | } |
| 1146 | #endif |
| 1147 | |
| 1148 | if (*p == 'Z') { |
| 1149 | o = RSP_TZNAME; |
| 1150 | #ifdef __UCLIBC_HAS_TM_EXTENSIONS__ |
| 1151 | /* Sigh... blasted glibc extensions. Of course we can't |
| 1152 | * count on the pointer being valid. Best we can do is |
| 1153 | * handle NULL, which looks to be all that glibc does. |
| 1154 | * At least that catches the memset() with 0 case. |
| 1155 | * NOTE: We handle this case differently than glibc! |
| 1156 | * It uses system timezone name (based on tm_isdst) in this |
| 1157 | * case... although it always seems to use the embedded |
| 1158 | * tm_gmtoff value. What we'll do instead is treat the |
| 1159 | * timezone name as unknown/invalid and return "???". */ |
| 1160 | if (!o) { |
| 1161 | o = "???"; |
| 1162 | } |
| 1163 | #endif |
| 1164 | assert(o != NULL); |
| 1165 | #if 0 |
| 1166 | if (!o) { /* PARANOIA */ |
| 1167 | o = spec+30; /* empty string */ |
| 1168 | } |
| 1169 | #endif |
| 1170 | o_count = SIZE_MAX; |
| 1171 | #ifdef __UCLIBC_HAS_TM_EXTENSIONS__ |
| 1172 | goto OUTPUT; |
| 1173 | #endif |
| 1174 | } else { /* z */ |
| 1175 | *s = '+'; |
| 1176 | if ((tzo = -RSP_GMT_OFFSET) < 0) { |
| 1177 | tzo = -tzo; |
| 1178 | *s = '-'; |
| 1179 | } |
| 1180 | ++s; |
| 1181 | --count; |
| 1182 | |
| 1183 | i = tzo / 60; |
| 1184 | field_val = ((i / 60) * 100) + (i % 60); |
| 1185 | |
| 1186 | i = 16 + 6; /* 0-fill, width = 4 */ |
| 1187 | } |
| 1188 | #ifndef __UCLIBC_HAS_TM_EXTENSIONS__ |
| 1189 | __UCLIBC_MUTEX_UNLOCK(_time_tzlock); |
| 1190 | if (*p == 'Z') { |
| 1191 | goto OUTPUT; |
| 1192 | } |
| 1193 | #endif |
| 1194 | } else { |
| 1195 | /* TODO: don't need year for U, W */ |
| 1196 | for (i=0 ; i < 3 ; i++) { |
| 1197 | if ((x[i] = load_field(spec[CALC_OFFSETS+i],timeptr)) < 0) { |
| 1198 | goto OUTPUT; |
| 1199 | } |
| 1200 | } |
| 1201 | |
| 1202 | i = 16 + 2; /* 0-fill, width = 2 */ |
| 1203 | |
| 1204 | if ((*p == 'U') || (*p == 'W')) { |
| 1205 | field_val = ((x[1] - x[0]) + 7); |
| 1206 | if (*p == 'W') { |
| 1207 | ++field_val; |
| 1208 | } |
| 1209 | field_val /= 7; |
| 1210 | if ((*p == 'W') && !x[0]) { |
| 1211 | --field_val; |
| 1212 | } |
| 1213 | } else { /* ((*p == 'g') || (*p == 'G') || (*p == 'V')) */ |
| 1214 | ISO_LOOP: |
| 1215 | isofm = (((x[1] - x[0]) + 11) % 7) - 3; /* [-3,3] */ |
| 1216 | |
| 1217 | if (x[1] < isofm) { /* belongs to previous year */ |
| 1218 | --x[2]; |
| 1219 | x[1] += 365 + __isleap(x[2]); |
| 1220 | goto ISO_LOOP; |
| 1221 | } |
| 1222 | |
| 1223 | field_val = ((x[1] - isofm) / 7) + 1; /* week # */ |
| 1224 | days = 365 + __isleap(x[2]); |
| 1225 | isofm = ((isofm + 7*53 + 3 - days)) % 7 + days - 3; /* next year */ |
| 1226 | if (x[1] >= isofm) { /* next year */ |
| 1227 | x[1] -= days; |
| 1228 | ++x[2]; |
| 1229 | goto ISO_LOOP; |
| 1230 | } |
| 1231 | |
| 1232 | if (*p != 'V') { /* need year */ |
| 1233 | field_val = x[2]; /* TODO: what if x[2] now 10000 ?? */ |
| 1234 | if (*p == 'g') { |
| 1235 | field_val %= 100; |
| 1236 | } else { |
| 1237 | i = 16 + 6; /* 0-fill, width = 4 */ |
| 1238 | } |
| 1239 | } |
| 1240 | } |
| 1241 | } |
| 1242 | } else { |
| 1243 | i = TP_OFFSETS + (code & 0x1f); |
| 1244 | if ((field_val = load_field(spec[i], timeptr)) < 0) { |
| 1245 | goto OUTPUT; |
| 1246 | } |
| 1247 | |
| 1248 | i = spec[i+(TP_CODES - TP_OFFSETS)]; |
| 1249 | |
| 1250 | j = (i & 128) ? 100: 12; |
| 1251 | if (i & 64) { |
| 1252 | field_val /= j;; |
| 1253 | } |
| 1254 | if (i & 32) { |
| 1255 | field_val %= j; |
| 1256 | if (((i & 128) + field_val) == 0) { /* mod 12? == 0 */ |
| 1257 | field_val = j; /* set to 12 */ |
| 1258 | } |
| 1259 | } |
| 1260 | field_val += (i & 1); |
| 1261 | if ((i & 8) && !field_val) { |
| 1262 | field_val += 7; |
| 1263 | } |
| 1264 | } |
| 1265 | |
| 1266 | if ((code & MASK_SPEC) == STRING_SPEC) { |
| 1267 | o_count = SIZE_MAX; |
| 1268 | field_val += spec[STRINGS_NL_ITEM_START + (code & 0xf)]; |
| 1269 | o = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME, field_val) __LOCALE_ARG); |
| 1270 | } else { |
| 1271 | o_count = ((i >> 1) & 3) + 1; |
| 1272 | o = buf + o_count; |
| 1273 | do { |
| 1274 | *(char *)(--o) = '0' + (field_val % 10); |
| 1275 | field_val /= 10; |
| 1276 | } while (o > buf); |
| 1277 | if (*buf == '0') { |
| 1278 | *buf = ' ' + (i & 16); |
| 1279 | } |
| 1280 | } |
| 1281 | } |
| 1282 | |
| 1283 | OUTPUT: |
| 1284 | ++p; |
| 1285 | while (o_count && count && *o) { |
| 1286 | *s++ = *o++; |
| 1287 | --o_count; |
| 1288 | --count; |
| 1289 | } |
| 1290 | goto LOOP; |
| 1291 | } |
| 1292 | # ifdef L_strftime_l |
| 1293 | libc_hidden_def(strftime_l) |
| 1294 | # endif |
| 1295 | |
| 1296 | #endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ |
| 1297 | |
| 1298 | #endif |
| 1299 | /**********************************************************************/ |
| 1300 | #if defined(L_strptime) || defined(L_strptime_l) |
| 1301 | |
| 1302 | #define ISDIGIT(C) __isdigit_char((C)) |
| 1303 | |
| 1304 | #ifdef __UCLIBC_DO_XLOCALE |
| 1305 | #define ISSPACE(C) isspace_l((C), locale_arg) |
| 1306 | #else |
| 1307 | #define ISSPACE(C) isspace((C)) |
| 1308 | #endif |
| 1309 | |
| 1310 | #if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) |
| 1311 | |
| 1312 | char *strptime(const char *__restrict buf, const char *__restrict format, |
| 1313 | struct tm *__restrict tm) |
| 1314 | { |
| 1315 | return strptime_l(buf, format, tm, __UCLIBC_CURLOCALE); |
| 1316 | } |
| 1317 | |
| 1318 | #else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ |
| 1319 | |
| 1320 | /* TODO: |
| 1321 | * 1) %l and %k are space-padded, so "%l" by itself fails while " %l" succeeds. |
| 1322 | * Both work for glibc. So, should we always strip spaces? |
| 1323 | * 2) %Z |
| 1324 | */ |
| 1325 | |
| 1326 | /* Notes: |
| 1327 | * There are several differences between this strptime and glibc's strptime. |
| 1328 | * 1) glibc strips leading space before numeric conversions. |
| 1329 | * 2) glibc will read fields without whitespace in between. SUSv3 states |
| 1330 | * that you must have whitespace between conversion operators. Besides, |
| 1331 | * how do you know how long a number should be if there are leading 0s? |
| 1332 | * 3) glibc attempts to compute some the struct tm fields based on the |
| 1333 | * data retrieved; tm_wday in particular. I don't as I consider it |
| 1334 | * another glibc attempt at mind-reading... |
| 1335 | */ |
| 1336 | |
| 1337 | #define NO_E_MOD 0x80 |
| 1338 | #define NO_O_MOD 0x40 |
| 1339 | |
| 1340 | #define ILLEGAL_SPEC 0x3f |
| 1341 | |
| 1342 | #define INT_SPEC 0x00 /* must be 0x00!! */ |
| 1343 | #define STRING_SPEC 0x10 /* must be 0x10!! */ |
| 1344 | #define CALC_SPEC 0x20 |
| 1345 | #define STACKED_SPEC 0x30 |
| 1346 | |
| 1347 | #define MASK_SPEC 0x30 |
| 1348 | |
| 1349 | /* Warning: Assumes ASCII values! (as do lots of other things in the lib...) */ |
| 1350 | static const unsigned char spec[] = { |
| 1351 | /* A */ 0x02 | STRING_SPEC | NO_E_MOD | NO_O_MOD, |
| 1352 | /* B */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD, |
| 1353 | /* C */ 0x08 | INT_SPEC | NO_O_MOD, |
| 1354 | /* D */ 0x01 | STACKED_SPEC | NO_E_MOD | NO_O_MOD, |
| 1355 | /* E */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 1356 | /* F */ 0x02 | STACKED_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */ |
| 1357 | /* G */ 0x0f | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */ |
| 1358 | /* H */ 0x06 | INT_SPEC | NO_E_MOD, |
| 1359 | /* I */ 0x07 | INT_SPEC | NO_E_MOD, |
| 1360 | /* J */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 1361 | /* K */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 1362 | /* L */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 1363 | /* M */ 0x04 | INT_SPEC | NO_E_MOD, |
| 1364 | /* N */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 1365 | /* O */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 1366 | /* P */ 0x00 | STRING_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */ |
| 1367 | /* Q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 1368 | /* R */ 0x03 | STACKED_SPEC | NO_E_MOD | NO_O_MOD, |
| 1369 | /* S */ 0x05 | INT_SPEC | NO_E_MOD, |
| 1370 | /* T */ 0x04 | STACKED_SPEC | NO_E_MOD | NO_O_MOD, |
| 1371 | /* U */ 0x0c | INT_SPEC | NO_E_MOD, |
| 1372 | /* V */ 0x0d | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */ |
| 1373 | /* W */ 0x0c | INT_SPEC | NO_E_MOD, |
| 1374 | /* X */ 0x0a | STACKED_SPEC | NO_O_MOD, |
| 1375 | /* Y */ 0x0a | INT_SPEC | NO_O_MOD, |
| 1376 | /* Z */ 0x02 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */ |
| 1377 | |
| 1378 | /* WARNING! This assumes orderings: |
| 1379 | * AM,PM |
| 1380 | * ABDAY_1-ABDAY-7,DAY_1-DAY_7 |
| 1381 | * ABMON_1-ABMON_12,MON_1-MON12 |
| 1382 | * Also, there are exactly 6 bytes between 'Z' and 'a'. |
| 1383 | */ |
| 1384 | #define STRINGS_NL_ITEM_START (26) |
| 1385 | _NL_ITEM_INDEX(AM_STR), /* p (P) */ |
| 1386 | _NL_ITEM_INDEX(ABMON_1), /* B, b */ |
| 1387 | _NL_ITEM_INDEX(ABDAY_1), /* A, a */ |
| 1388 | 2, |
| 1389 | 24, |
| 1390 | 14, |
| 1391 | |
| 1392 | /* a */ 0x02 | STRING_SPEC | NO_E_MOD | NO_O_MOD, |
| 1393 | /* b */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD, |
| 1394 | /* c */ 0x08 | STACKED_SPEC | NO_O_MOD, |
| 1395 | /* d */ 0x00 | INT_SPEC | NO_E_MOD, |
| 1396 | /* e */ 0x00 | INT_SPEC | NO_E_MOD, |
| 1397 | /* f */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 1398 | /* g */ 0x0e | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */ |
| 1399 | /* h */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD, |
| 1400 | /* i */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 1401 | /* j */ 0x01 | INT_SPEC | NO_E_MOD | NO_O_MOD, |
| 1402 | /* k */ 0x06 | INT_SPEC | NO_E_MOD, /* glibc */ |
| 1403 | /* l */ 0x07 | INT_SPEC | NO_E_MOD, /* glibc */ |
| 1404 | /* m */ 0x02 | INT_SPEC | NO_E_MOD, |
| 1405 | /* n */ 0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD, |
| 1406 | /* o */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 1407 | /* p */ 0x00 | STRING_SPEC | NO_E_MOD | NO_O_MOD, |
| 1408 | /* q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 1409 | /* r */ 0x0b | STACKED_SPEC | NO_E_MOD | NO_O_MOD, |
| 1410 | /* s */ 0x00 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */ |
| 1411 | /* t */ 0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD, |
| 1412 | /* u */ 0x0b | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */ |
| 1413 | /* v */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD, |
| 1414 | /* w */ 0x03 | INT_SPEC | NO_E_MOD, |
| 1415 | /* x */ 0x09 | STACKED_SPEC | NO_O_MOD, |
| 1416 | /* y */ 0x09 | INT_SPEC, |
| 1417 | /* z */ 0x01 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */ |
| 1418 | |
| 1419 | #define INT_FIELD_START (26+6+26) |
| 1420 | /* (field #) << 3 + lower bound (0|1) + correction 0:none, 2:-1, 4:-1900 |
| 1421 | * followed by upper bound prior to correction with 1=>366 and 2=>9999. */ |
| 1422 | /* d, e */ (3 << 3) + 1 + 0, 31, |
| 1423 | /* j */ (7 << 3) + 1 + 2, /* 366 */ 1, |
| 1424 | /* m */ (4 << 3) + 1 + 2, 12, |
| 1425 | /* w */ (6 << 3) + 0 + 0, 6, |
| 1426 | /* M */ (1 << 3) + 0 + 0, 59, |
| 1427 | /* S */ 0 + 0 + 0, 60, |
| 1428 | /* H (k) */ (2 << 3) + 0 + 0, 23, |
| 1429 | /* I (l) */ (9 << 3) + 1 + 0, 12, /* goes with 8 -- am/pm */ |
| 1430 | /* C */ (10<< 3) + 0 + 0, 99, |
| 1431 | /* y */ (11<< 3) + 0 + 0, 99, |
| 1432 | /* Y */ (5 << 3) + 0 + 4, /* 9999 */ 2, |
| 1433 | /* u */ (6 << 3) + 1 + 0, 7, |
| 1434 | /* The following are processed and range-checked, but ignored otherwise. */ |
| 1435 | /* U, W */ (12<< 3) + 0 + 0, 53, |
| 1436 | /* V */ (12<< 3) + 1 + 0, 53, |
| 1437 | /* g */ (12<< 3) + 0 + 0, 99, |
| 1438 | /* G */ (12<< 3) + 0 /*+ 4*/, /* 9999 */ 2, /* Note: -1 or 10000? */ |
| 1439 | |
| 1440 | #define STACKED_STRINGS_START (INT_FIELD_START+32) |
| 1441 | 5, 6, 14, 22, 27, /* 5 - offsets from offset-count to strings */ |
| 1442 | ' ', 0, /* 2 - %n or %t */ |
| 1443 | '%', 'm', '/', '%', 'd', '/', '%', 'y', 0, /* 9 - %D */ |
| 1444 | '%', 'Y', '-', '%', 'm', '-', '%', 'd', 0, /* 9 - %F (glibc extension) */ |
| 1445 | '%', 'H', ':', '%', 'M', 0, /* 6 - %R*/ |
| 1446 | '%', 'H', ':', '%', 'M', ':', '%', 'S', 0, /* 9 - %T */ |
| 1447 | |
| 1448 | #define STACKED_STRINGS_NL_ITEM_START (STACKED_STRINGS_START + 40) |
| 1449 | _NL_ITEM_INDEX(D_T_FMT), /* c */ |
| 1450 | _NL_ITEM_INDEX(D_FMT), /* x */ |
| 1451 | _NL_ITEM_INDEX(T_FMT), /* X */ |
| 1452 | _NL_ITEM_INDEX(T_FMT_AMPM), /* r */ |
| 1453 | #ifdef ENABLE_ERA_CODE |
| 1454 | _NL_ITEM_INDEX(ERA_D_T_FMT), /* Ec */ |
| 1455 | _NL_ITEM_INDEX(ERA_D_FMT), /* Ex */ |
| 1456 | _NL_ITEM_INDEX(ERA_T_FMT), /* EX */ |
| 1457 | #endif |
| 1458 | }; |
| 1459 | |
| 1460 | #define MAX_PUSH 4 |
| 1461 | |
| 1462 | char *__XL_NPP(strptime)(const char *__restrict buf, const char *__restrict format, |
| 1463 | struct tm *__restrict tm __LOCALE_PARAM) |
| 1464 | { |
| 1465 | register const char *p; |
| 1466 | char *o; |
| 1467 | const char *stack[MAX_PUSH]; |
| 1468 | int i, j, lvl; |
| 1469 | int fields[13]; |
| 1470 | unsigned char mod; |
| 1471 | unsigned char code; |
| 1472 | |
| 1473 | i = 0; |
| 1474 | do { |
| 1475 | fields[i] = INT_MIN; |
| 1476 | } while (++i < 13); |
| 1477 | |
| 1478 | lvl = 0; |
| 1479 | p = format; |
| 1480 | |
| 1481 | LOOP: |
| 1482 | if (!*p) { |
| 1483 | if (lvl == 0) { /* Done. */ |
| 1484 | if (fields[6] == 7) { /* Cleanup for %u here since just once. */ |
| 1485 | fields[6] = 0; /* Don't use mod in case unset. */ |
| 1486 | } |
| 1487 | |
| 1488 | i = 0; |
| 1489 | do { /* Store the values into tm. */ |
| 1490 | if (fields[i] != INT_MIN) { |
| 1491 | ((int *) tm)[i] = fields[i]; |
| 1492 | } |
| 1493 | } while (++i < 8); |
| 1494 | |
| 1495 | return (char *) buf; /* Success. */ |
| 1496 | } |
| 1497 | p = stack[--lvl]; |
| 1498 | goto LOOP; |
| 1499 | } |
| 1500 | |
| 1501 | if ((*p == '%') && (*++p != '%')) { |
| 1502 | mod = ILLEGAL_SPEC; |
| 1503 | if ((*p == 'O') || (*p == 'E')) { /* Modifier? */ |
| 1504 | mod |= ((*p == 'O') ? NO_O_MOD : NO_E_MOD); |
| 1505 | ++p; |
| 1506 | } |
| 1507 | |
| 1508 | if (!*p |
| 1509 | || (((unsigned char)(((*p) | 0x20) - 'a')) >= 26) |
| 1510 | || (((code = spec[(int)(*p - 'A')]) & mod) >= ILLEGAL_SPEC) |
| 1511 | ) { |
| 1512 | return NULL; /* Illegal spec. */ |
| 1513 | } |
| 1514 | |
| 1515 | if ((code & MASK_SPEC) == STACKED_SPEC) { |
| 1516 | if (lvl == MAX_PUSH) { |
| 1517 | return NULL; /* Stack full so treat as illegal spec. */ |
| 1518 | } |
| 1519 | stack[lvl++] = ++p; |
| 1520 | if ((code &= 0xf) < 8) { |
| 1521 | p = ((const char *) spec) + STACKED_STRINGS_START + code; |
| 1522 | p += *((unsigned char *)p); |
| 1523 | goto LOOP; |
| 1524 | } |
| 1525 | |
| 1526 | p = ((const char *) spec) + STACKED_STRINGS_NL_ITEM_START |
| 1527 | + (code & 7); |
| 1528 | #ifdef ENABLE_ERA_CODE |
| 1529 | if ((mod & NO_E_MOD) /* Actually, this means E modifier present. */ |
| 1530 | && (*(o = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME, |
| 1531 | (int)(((unsigned char *)p)[4])) |
| 1532 | __LOCALE_ARG |
| 1533 | ))) |
| 1534 | ) { |
| 1535 | p = o; |
| 1536 | goto LOOP; |
| 1537 | } |
| 1538 | #endif |
| 1539 | p = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME, |
| 1540 | (int)(*((unsigned char *)p))) |
| 1541 | __LOCALE_ARG |
| 1542 | ); |
| 1543 | goto LOOP; |
| 1544 | } |
| 1545 | |
| 1546 | ++p; |
| 1547 | |
| 1548 | if ((code & MASK_SPEC) == STRING_SPEC) { |
| 1549 | code &= 0xf; |
| 1550 | j = spec[STRINGS_NL_ITEM_START + 3 + code]; |
| 1551 | i = _NL_ITEM(LC_TIME, spec[STRINGS_NL_ITEM_START + code]); |
| 1552 | /* Go backwards to check full names before abreviations. */ |
| 1553 | do { |
| 1554 | --j; |
| 1555 | o = __XL_NPP(nl_langinfo)(i+j __LOCALE_ARG); |
| 1556 | if (!__XL_NPP(strncasecmp)(buf, o, strlen(o) __LOCALE_ARG) && *o) { |
| 1557 | do { /* Found a match. */ |
| 1558 | ++buf; |
| 1559 | } while (*++o); |
| 1560 | if (!code) { /* am/pm */ |
| 1561 | fields[8] = j * 12; |
| 1562 | if (fields[9] >= 0) { /* We have a previous %I or %l. */ |
| 1563 | fields[2] = fields[9] + fields[8]; |
| 1564 | } |
| 1565 | } else { /* day (4) or month (6) */ |
| 1566 | fields[2 + (code << 1)] |
| 1567 | = j % (spec[STRINGS_NL_ITEM_START + 3 + code] >> 1); |
| 1568 | } |
| 1569 | goto LOOP; |
| 1570 | } |
| 1571 | } while (j); |
| 1572 | return NULL; /* Failed to match. */ |
| 1573 | } |
| 1574 | |
| 1575 | if ((code & MASK_SPEC) == CALC_SPEC) { |
| 1576 | if ((code &= 0xf) < 1) { /* s or z*/ |
| 1577 | time_t t; |
| 1578 | |
| 1579 | o = (char *) buf; |
| 1580 | i = errno; |
| 1581 | __set_errno(0); |
| 1582 | if (!ISSPACE(*buf)) { /* Signal an error if whitespace. */ |
| 1583 | #ifdef TIME_T_IS_UNSIGNED |
| 1584 | t = __XL_NPP(strtoul)(buf, &o, 10 __LOCALE_ARG); |
| 1585 | #else |
| 1586 | t = __XL_NPP(strtol)(buf, &o, 10 __LOCALE_ARG); |
| 1587 | #endif |
| 1588 | } |
| 1589 | if ((o == buf) || errno) { /* Not a number or overflow. */ |
| 1590 | return NULL; |
| 1591 | } |
| 1592 | __set_errno(i); /* Restore errno. */ |
| 1593 | buf = o; |
| 1594 | |
| 1595 | if (!code) { /* s */ |
| 1596 | localtime_r(&t, tm); /* TODO: check for failure? */ |
| 1597 | i = 0; |
| 1598 | do { /* Now copy values from tm to fields. */ |
| 1599 | fields[i] = ((int *) tm)[i]; |
| 1600 | } while (++i < 8); |
| 1601 | } |
| 1602 | } |
| 1603 | /* TODO: glibc treats %Z as a nop. For now, do the same. */ |
| 1604 | goto LOOP; |
| 1605 | } |
| 1606 | |
| 1607 | assert((code & MASK_SPEC) == INT_SPEC); |
| 1608 | { |
| 1609 | register const unsigned char *x; |
| 1610 | code &= 0xf; |
| 1611 | x = spec + INT_FIELD_START + (code << 1); |
| 1612 | if ((j = x[1]) < 3) { /* upper bound (inclusive) */ |
| 1613 | j = ((j==1) ? 366 : 9999); |
| 1614 | } |
| 1615 | i = -1; |
| 1616 | while (ISDIGIT(*buf)) { |
| 1617 | if (i < 0) { |
| 1618 | i = 0; |
| 1619 | } |
| 1620 | if ((i = 10*i + (*buf - '0')) > j) { /* Overflow. */ |
| 1621 | return NULL; |
| 1622 | } |
| 1623 | ++buf; |
| 1624 | } |
| 1625 | if (i < (*x & 1)) { /* This catches no-digit case too. */ |
| 1626 | return NULL; |
| 1627 | } |
| 1628 | if (*x & 2) { |
| 1629 | --i; |
| 1630 | } |
| 1631 | if (*x & 4) { |
| 1632 | i -= 1900; |
| 1633 | } |
| 1634 | |
| 1635 | if (*x == (9 << 3) + 1 + 0) { /* %I or %l */ |
| 1636 | if (i == 12) { |
| 1637 | i = 0; |
| 1638 | } |
| 1639 | if (fields[8] >= 0) { /* We have a previous %p or %P. */ |
| 1640 | fields[2] = i + fields[8]; |
| 1641 | } |
| 1642 | } |
| 1643 | |
| 1644 | fields[(*x) >> 3] = i; |
| 1645 | |
| 1646 | if (((unsigned char)(*x - (10 << 3) + 0 + 0)) <= 8) { /* %C or %y */ |
| 1647 | if ((j = fields[10]) < 0) { /* No %C, so i must be %y data. */ |
| 1648 | if (i <= 68) { /* Map [0-68] to 2000+i */ |
| 1649 | i += 100; |
| 1650 | } |
| 1651 | } else { /* Have %C data, but what about %y? */ |
| 1652 | if ((i = fields[11]) < 0) { /* No %y data. */ |
| 1653 | i = 0; /* Treat %y val as 0 following glibc's example. */ |
| 1654 | } |
| 1655 | i += 100*(j - 19); |
| 1656 | } |
| 1657 | fields[5] = i; |
| 1658 | } |
| 1659 | } |
| 1660 | goto LOOP; |
| 1661 | } else if (ISSPACE(*p)) { |
| 1662 | ++p; |
| 1663 | while (ISSPACE(*buf)) { |
| 1664 | ++buf; |
| 1665 | } |
| 1666 | goto LOOP; |
| 1667 | } else if (*buf++ == *p++) { |
| 1668 | goto LOOP; |
| 1669 | } |
| 1670 | return NULL; |
| 1671 | } |
| 1672 | # ifdef L_strptime_l |
| 1673 | libc_hidden_def(strptime_l) |
| 1674 | # endif |
| 1675 | |
| 1676 | #endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ |
| 1677 | |
| 1678 | #endif |
| 1679 | /**********************************************************************/ |
| 1680 | #ifdef L_time |
| 1681 | |
| 1682 | #ifndef __BCC__ |
| 1683 | #error The uClibc version of time is in sysdeps/linux/common. |
| 1684 | #endif |
| 1685 | |
| 1686 | time_t time(register time_t *tloc) |
| 1687 | { |
| 1688 | struct timeval tv; |
| 1689 | register struct timeval *p = &tv; |
| 1690 | |
| 1691 | gettimeofday(p, NULL); /* This should never fail... */ |
| 1692 | |
| 1693 | if (tloc) { |
| 1694 | *tloc = p->tv_sec; |
| 1695 | } |
| 1696 | |
| 1697 | return p->tv_sec; |
| 1698 | } |
| 1699 | |
| 1700 | #endif |
| 1701 | /**********************************************************************/ |
| 1702 | #ifdef L_tzset |
| 1703 | |
| 1704 | static const char vals[] = { |
| 1705 | 'T', 'Z', 0, /* 3 */ |
| 1706 | 'U', 'T', 'C', 0, /* 4 */ |
| 1707 | 25, 60, 60, 1, /* 4 */ |
| 1708 | '.', 1, /* M */ |
| 1709 | 5, '.', 1, |
| 1710 | 6, 0, 0, /* Note: overloaded for non-M non-J case... */ |
| 1711 | 0, 1, 0, /* J */ |
| 1712 | ',', 'M', '4', '.', '1', '.', '0', |
| 1713 | ',', 'M', '1', '0', '.', '5', '.', '0', 0, |
| 1714 | ',', 'M', '3', '.', '2', '.', '0', |
| 1715 | ',', 'M', '1', '1', '.', '1', '.', '0', 0 |
| 1716 | }; |
| 1717 | |
| 1718 | #define TZ vals |
| 1719 | #define UTC (vals + 3) |
| 1720 | #define RANGE (vals + 7) |
| 1721 | #define RULE (vals + 11 - 1) |
| 1722 | #define DEFAULT_RULES (vals + 22) |
| 1723 | #define DEFAULT_2007_RULES (vals + 38) |
| 1724 | |
| 1725 | /* Initialize to UTC. */ |
| 1726 | int daylight = 0; |
| 1727 | long timezone = 0; |
| 1728 | char *tzname[2] = { (char *) UTC, (char *) (UTC-1) }; |
| 1729 | |
| 1730 | __UCLIBC_MUTEX_INIT(_time_tzlock, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP); |
| 1731 | |
| 1732 | rule_struct _time_tzinfo[2]; |
| 1733 | |
| 1734 | static const char *getoffset(register const char *e, long *pn) |
| 1735 | { |
| 1736 | register const char *s = RANGE-1; |
| 1737 | long n; |
| 1738 | int f; |
| 1739 | |
| 1740 | n = 0; |
| 1741 | f = -1; |
| 1742 | do { |
| 1743 | ++s; |
| 1744 | if (__isdigit_char(*e)) { |
| 1745 | f = *e++ - '0'; |
| 1746 | } |
| 1747 | if (__isdigit_char(*e)) { |
| 1748 | f = 10 * f + (*e++ - '0'); |
| 1749 | } |
| 1750 | if (((unsigned int)f) >= *s) { |
| 1751 | return NULL; |
| 1752 | } |
| 1753 | n = (*s) * n + f; |
| 1754 | f = 0; |
| 1755 | if (*e == ':') { |
| 1756 | ++e; |
| 1757 | --f; |
| 1758 | } |
| 1759 | } while (*s > 1); |
| 1760 | |
| 1761 | *pn = n; |
| 1762 | return e; |
| 1763 | } |
| 1764 | |
| 1765 | static const char *getnumber(register const char *e, int *pn) |
| 1766 | { |
| 1767 | #ifdef __BCC__ |
| 1768 | /* bcc can optimize the counter if it thinks it is a pointer... */ |
| 1769 | register const char *n = (const char *) 3; |
| 1770 | int f; |
| 1771 | |
| 1772 | f = 0; |
| 1773 | while (n && __isdigit_char(*e)) { |
| 1774 | f = 10 * f + (*e++ - '0'); |
| 1775 | --n; |
| 1776 | } |
| 1777 | |
| 1778 | *pn = f; |
| 1779 | return (n == (const char *) 3) ? NULL : e; |
| 1780 | #else /* __BCC__ */ |
| 1781 | int n, f; |
| 1782 | |
| 1783 | n = 3; |
| 1784 | f = 0; |
| 1785 | while (n && __isdigit_char(*e)) { |
| 1786 | f = 10 * f + (*e++ - '0'); |
| 1787 | --n; |
| 1788 | } |
| 1789 | |
| 1790 | *pn = f; |
| 1791 | return (n == 3) ? NULL : e; |
| 1792 | #endif /* __BCC__ */ |
| 1793 | } |
| 1794 | |
| 1795 | |
| 1796 | #ifdef __UCLIBC_MJN3_ONLY__ |
| 1797 | #warning CONSIDER: Should we preserve errno from open/read/close errors re TZ file? |
| 1798 | #endif |
| 1799 | |
| 1800 | #ifdef __UCLIBC_HAS_TZ_FILE__ |
| 1801 | |
| 1802 | #ifndef __UCLIBC_HAS_TZ_FILE_READ_MANY__ |
| 1803 | static smallint TZ_file_read; /* Let BSS initialization set this to 0. */ |
| 1804 | #endif |
| 1805 | |
| 1806 | static char *read_TZ_file(char *buf) |
| 1807 | { |
| 1808 | int r; |
| 1809 | int fd; |
| 1810 | char *p = NULL; |
| 1811 | |
| 1812 | fd = open(__UCLIBC_TZ_FILE_PATH__, O_RDONLY); |
| 1813 | if (fd >= 0) { |
| 1814 | #if 0 |
| 1815 | /* TZ are small *files*. On files, short reads |
| 1816 | * only occur on EOF (unlike, say, pipes). |
| 1817 | * The code below is pedanticallly more correct, |
| 1818 | * but this way we always read at least twice: |
| 1819 | * 1st read is short, 2nd one is zero bytes. |
| 1820 | */ |
| 1821 | size_t todo = TZ_BUFLEN; |
| 1822 | p = buf; |
| 1823 | do { |
| 1824 | r = read(fd, p, todo); |
| 1825 | if (r < 0) |
| 1826 | goto ERROR; |
| 1827 | if (r == 0) |
| 1828 | break; |
| 1829 | p += r; |
| 1830 | todo -= r; |
| 1831 | } while (todo); |
| 1832 | #else |
| 1833 | /* Shorter, and does one fewer read syscall */ |
| 1834 | r = read(fd, buf, TZ_BUFLEN); |
| 1835 | if (r < 0) |
| 1836 | goto ERROR; |
| 1837 | p = buf + r; |
| 1838 | #endif |
| 1839 | if ((p > buf) && (p[-1] == '\n')) { /* Must end with newline */ |
| 1840 | p[-1] = 0; |
| 1841 | p = buf; |
| 1842 | #ifndef __UCLIBC_HAS_TZ_FILE_READ_MANY__ |
| 1843 | TZ_file_read = 1; |
| 1844 | #endif |
| 1845 | } else { |
| 1846 | ERROR: |
| 1847 | p = NULL; |
| 1848 | } |
| 1849 | close(fd); |
| 1850 | } |
| 1851 | #ifdef __UCLIBC_FALLBACK_TO_ETC_LOCALTIME__ |
| 1852 | else { |
| 1853 | fd = open("/etc/localtime", O_RDONLY); |
| 1854 | if (fd >= 0) { |
| 1855 | r = read(fd, buf, TZ_BUFLEN); |
| 1856 | if (r != TZ_BUFLEN |
| 1857 | || strncmp(buf, "TZif", 4) != 0 |
| 1858 | || (unsigned char)buf[4] < 2 |
| 1859 | || lseek(fd, -TZ_BUFLEN, SEEK_END) < 0 |
| 1860 | ) { |
| 1861 | goto ERROR; |
| 1862 | } |
| 1863 | /* tzfile.h from tzcode database says about TZif2+ files: |
| 1864 | ** |
| 1865 | ** If tzh_version is '2' or greater, the above is followed by a second instance |
| 1866 | ** of tzhead and a second instance of the data in which each coded transition |
| 1867 | ** time uses 8 rather than 4 chars, |
| 1868 | ** then a POSIX-TZ-environment-variable-style string for use in handling |
| 1869 | ** instants after the last transition time stored in the file |
| 1870 | ** (with nothing between the newlines if there is no POSIX representation for |
| 1871 | ** such instants). |
| 1872 | */ |
| 1873 | r = read(fd, buf, TZ_BUFLEN); |
| 1874 | if (r <= 0 || buf[--r] != '\n') |
| 1875 | goto ERROR; |
| 1876 | buf[r] = 0; |
| 1877 | while (r != 0) { |
| 1878 | if (buf[--r] == '\n') { |
| 1879 | p = buf + r + 1; |
| 1880 | #ifndef __UCLIBC_HAS_TZ_FILE_READ_MANY__ |
| 1881 | TZ_file_read = 1; |
| 1882 | #endif |
| 1883 | break; |
| 1884 | } |
| 1885 | } /* else ('\n' not found): p remains NULL */ |
| 1886 | close(fd); |
| 1887 | } |
| 1888 | } |
| 1889 | #endif /* __UCLIBC_FALLBACK_TO_ETC_LOCALTIME__ */ |
| 1890 | return p; |
| 1891 | } |
| 1892 | |
| 1893 | #endif /* __UCLIBC_HAS_TZ_FILE__ */ |
| 1894 | |
| 1895 | void tzset(void) |
| 1896 | { |
| 1897 | _time_tzset((time(NULL)) < new_rule_starts); |
| 1898 | } |
| 1899 | |
| 1900 | void _time_tzset(int use_old_rules) |
| 1901 | { |
| 1902 | register const char *e; |
| 1903 | register char *s; |
| 1904 | long off = 0; |
| 1905 | short *p; |
| 1906 | rule_struct new_rules[2]; |
| 1907 | int n, count, f; |
| 1908 | char c; |
| 1909 | #ifdef __UCLIBC_HAS_TZ_FILE__ |
| 1910 | char buf[TZ_BUFLEN]; |
| 1911 | #endif |
| 1912 | #ifdef __UCLIBC_HAS_TZ_CACHING__ |
| 1913 | static char oldval[TZ_BUFLEN]; /* BSS-zero'd. */ |
| 1914 | #endif |
| 1915 | |
| 1916 | /* Put this inside the lock to prevent the possibility of two different |
| 1917 | * timezones being used in a threaded app. */ |
| 1918 | __UCLIBC_MUTEX_LOCK(_time_tzlock); |
| 1919 | |
| 1920 | e = getenv(TZ); /* TZ env var always takes precedence. */ |
| 1921 | |
| 1922 | #if defined(__UCLIBC_HAS_TZ_FILE__) && !defined(__UCLIBC_HAS_TZ_FILE_READ_MANY__) |
| 1923 | if (e) { |
| 1924 | /* Never use TZfile if TZ env var is set. */ |
| 1925 | TZ_file_read = 0; |
| 1926 | } |
| 1927 | if (TZ_file_read) { |
| 1928 | /* We already parsed TZfile before, skip everything. */ |
| 1929 | goto FAST_DONE; |
| 1930 | } |
| 1931 | #endif |
| 1932 | |
| 1933 | /* Warning!!! Since uClibc doesn't do lib locking, the following is |
| 1934 | * potentially unsafe in a multi-threaded program since it is remotely |
| 1935 | * possible that another thread could call setenv() for TZ and overwrite |
| 1936 | * the string being parsed. So, don't do that... */ |
| 1937 | |
| 1938 | #ifdef __UCLIBC_HAS_TZ_FILE__ |
| 1939 | if (!e) |
| 1940 | e = read_TZ_file(buf); |
| 1941 | #endif |
| 1942 | if (!e /* TZ env var not set and no TZfile (or bad TZfile) */ |
| 1943 | || !*e /* or set to empty string. */ |
| 1944 | ) { |
| 1945 | goto ILLEGAL; |
| 1946 | } |
| 1947 | |
| 1948 | if (*e == ':') { /* Ignore leading ':'. */ |
| 1949 | ++e; |
| 1950 | } |
| 1951 | |
| 1952 | #ifdef __UCLIBC_HAS_TZ_CACHING__ |
| 1953 | if (strcmp(e, oldval) == 0) { |
| 1954 | /* Same string as last time... nothing to do. */ |
| 1955 | goto FAST_DONE; |
| 1956 | } |
| 1957 | /* Make a copy of the TZ env string. It won't be nul-terminated if |
| 1958 | * it is too long, but it that case it will be illegal and will be reset |
| 1959 | * to the empty string anyway. */ |
| 1960 | strncpy(oldval, e, TZ_BUFLEN); |
| 1961 | #endif |
| 1962 | |
| 1963 | count = 0; |
| 1964 | new_rules[1].tzname[0] = 0; |
| 1965 | LOOP: |
| 1966 | /* Get std or dst name. */ |
| 1967 | c = 0; |
| 1968 | if (*e == '<') { |
| 1969 | ++e; |
| 1970 | c = '>'; |
| 1971 | } |
| 1972 | |
| 1973 | s = new_rules[count].tzname; |
| 1974 | n = 0; |
| 1975 | while (*e |
| 1976 | && isascii(*e) /* SUSv3 requires char in portable char set. */ |
| 1977 | && (isalpha(*e) |
| 1978 | || (c && (isalnum(*e) || (*e == '+') || (*e == '-'))) |
| 1979 | ) |
| 1980 | ) { |
| 1981 | *s++ = *e++; |
| 1982 | if (++n > TZNAME_MAX) { |
| 1983 | goto ILLEGAL; |
| 1984 | } |
| 1985 | } |
| 1986 | *s = 0; |
| 1987 | |
| 1988 | if ((n < 3) /* Check for minimum length. */ |
| 1989 | || (c && (*e++ != c)) /* Match any quoting '<'. */ |
| 1990 | ) { |
| 1991 | goto ILLEGAL; |
| 1992 | } |
| 1993 | |
| 1994 | /* Get offset */ |
| 1995 | s = (char *) e; |
| 1996 | if ((*e != '-') && (*e != '+')) { |
| 1997 | if (count && !__isdigit_char(*e)) { |
| 1998 | off -= 3600; /* Default to 1 hour ahead of std. */ |
| 1999 | goto SKIP_OFFSET; |
| 2000 | } |
| 2001 | --e; |
| 2002 | } |
| 2003 | |
| 2004 | ++e; |
| 2005 | e = getoffset(e, &off); |
| 2006 | if (!e) { |
| 2007 | goto ILLEGAL; |
| 2008 | } |
| 2009 | |
| 2010 | if (*s == '-') { |
| 2011 | off = -off; /* Save off in case needed for dst default. */ |
| 2012 | } |
| 2013 | SKIP_OFFSET: |
| 2014 | new_rules[count].gmt_offset = off; |
| 2015 | |
| 2016 | if (!count) { |
| 2017 | new_rules[1].gmt_offset = off; /* Shouldn't be needed... */ |
| 2018 | if (*e) { |
| 2019 | ++count; |
| 2020 | goto LOOP; |
| 2021 | } |
| 2022 | } else { /* OK, we have dst, so get some rules. */ |
| 2023 | count = 0; |
| 2024 | if (!*e) { /* No rules so default to US rules. */ |
| 2025 | e = use_old_rules ? DEFAULT_RULES : DEFAULT_2007_RULES; |
| 2026 | #ifdef DEBUG_TZSET |
| 2027 | if (e == DEFAULT_RULES) |
| 2028 | printf("tzset: Using old rules.\n"); |
| 2029 | else if (e == DEFAULT_2007_RULES) |
| 2030 | printf("tzset: Using new rules\n"); |
| 2031 | else |
| 2032 | printf("tzset: Using undefined rules\n"); |
| 2033 | #endif |
| 2034 | } |
| 2035 | |
| 2036 | do { |
| 2037 | if (*e++ != ',') { |
| 2038 | goto ILLEGAL; |
| 2039 | } |
| 2040 | |
| 2041 | n = 365; |
| 2042 | s = (char *) RULE; |
| 2043 | c = *e++; |
| 2044 | if (c == 'M') { |
| 2045 | n = 12; |
| 2046 | } else if (c == 'J') { |
| 2047 | s += 8; |
| 2048 | } else { |
| 2049 | --e; |
| 2050 | c = 0; |
| 2051 | s += 6; |
| 2052 | } |
| 2053 | |
| 2054 | p = &new_rules[count].rule_type; |
| 2055 | *p = c; |
| 2056 | if (c != 'M') { |
| 2057 | p -= 2; |
| 2058 | } |
| 2059 | |
| 2060 | do { |
| 2061 | ++s; |
| 2062 | e = getnumber(e, &f); |
| 2063 | if (!e |
| 2064 | || ((unsigned int)(f - s[1]) > n) |
| 2065 | || (*s && (*e++ != *s)) |
| 2066 | ) { |
| 2067 | goto ILLEGAL; |
| 2068 | } |
| 2069 | *--p = f; |
| 2070 | s += 2; |
| 2071 | n = *s; |
| 2072 | } while (n > 0); |
| 2073 | |
| 2074 | off = 2 * 60 * 60; /* Default to 2:00:00 */ |
| 2075 | if (*e == '/') { |
| 2076 | ++e; |
| 2077 | e = getoffset(e, &off); |
| 2078 | if (!e) { |
| 2079 | goto ILLEGAL; |
| 2080 | } |
| 2081 | } |
| 2082 | new_rules[count].dst_offset = off; |
| 2083 | } while (++count < 2); |
| 2084 | |
| 2085 | if (*e) { |
| 2086 | ILLEGAL: |
| 2087 | #ifdef __UCLIBC_HAS_TZ_CACHING__ |
| 2088 | oldval[0] = 0; /* oldval = "" */ |
| 2089 | #endif |
| 2090 | memset(_time_tzinfo, 0, sizeof(_time_tzinfo)); |
| 2091 | strcpy(_time_tzinfo[0].tzname, UTC); |
| 2092 | goto DONE; |
| 2093 | } |
| 2094 | } |
| 2095 | |
| 2096 | memcpy(_time_tzinfo, new_rules, sizeof(new_rules)); |
| 2097 | DONE: |
| 2098 | tzname[0] = _time_tzinfo[0].tzname; |
| 2099 | tzname[1] = _time_tzinfo[1].tzname; |
| 2100 | daylight = !!_time_tzinfo[1].tzname[0]; |
| 2101 | timezone = _time_tzinfo[0].gmt_offset; |
| 2102 | |
| 2103 | #if defined(__UCLIBC_HAS_TZ_FILE__) || defined(__UCLIBC_HAS_TZ_CACHING__) |
| 2104 | FAST_DONE: |
| 2105 | #endif |
| 2106 | __UCLIBC_MUTEX_UNLOCK(_time_tzlock); |
| 2107 | } |
| 2108 | libc_hidden_def(tzset) |
| 2109 | #endif |
| 2110 | /**********************************************************************/ |
| 2111 | /* #ifdef L_utime */ |
| 2112 | |
| 2113 | /* utime is a syscall in both linux and elks. */ |
| 2114 | /* int utime(const char *path, const struct utimbuf *times) */ |
| 2115 | |
| 2116 | /* #endif */ |
| 2117 | /**********************************************************************/ |
| 2118 | /* Non-SUSv3 */ |
| 2119 | /**********************************************************************/ |
| 2120 | #ifdef L_utimes |
| 2121 | |
| 2122 | #ifndef __BCC__ |
| 2123 | #error The uClibc version of utimes is in sysdeps/linux/common. |
| 2124 | #endif |
| 2125 | |
| 2126 | #include <utime.h> |
| 2127 | |
| 2128 | int utimes(const char *filename, register const struct timeval *tvp) |
| 2129 | { |
| 2130 | register struct utimbuf *p = NULL; |
| 2131 | struct utimbuf utb; |
| 2132 | |
| 2133 | if (tvp) { |
| 2134 | p = &utb; |
| 2135 | p->actime = tvp[0].tv_sec; |
| 2136 | p->modtime = tvp[1].tv_sec; |
| 2137 | } |
| 2138 | return utime(filename, p); |
| 2139 | } |
| 2140 | |
| 2141 | #endif |
| 2142 | /**********************************************************************/ |
| 2143 | #ifdef L__time_t2tm |
| 2144 | |
| 2145 | static const uint16_t _vals[] = { |
| 2146 | 60, 60, 24, 7 /* special */, 36524, 1461, 365, 0 |
| 2147 | }; |
| 2148 | |
| 2149 | static const unsigned char days[] = { |
| 2150 | 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, /* non-leap */ |
| 2151 | 29, |
| 2152 | }; |
| 2153 | |
| 2154 | #ifdef __UCLIBC_HAS_TM_EXTENSIONS__ |
| 2155 | static const char utc_string[] = "UTC"; |
| 2156 | #endif |
| 2157 | |
| 2158 | /* Notes: |
| 2159 | * If time_t is 32 bits, then no overflow is possible. |
| 2160 | * It time_t is > 32 bits, this needs to be adjusted to deal with overflow. |
| 2161 | */ |
| 2162 | |
| 2163 | /* Note: offset is the correction in _days_ to *timer! */ |
| 2164 | |
| 2165 | struct tm attribute_hidden *_time_t2tm(const time_t *__restrict timer, |
| 2166 | int offset, struct tm *__restrict result) |
| 2167 | { |
| 2168 | register int *p; |
| 2169 | time_t t1, t, v; |
| 2170 | int wday = wday; /* ok to be uninitialized, shutting up warning */ |
| 2171 | |
| 2172 | { |
| 2173 | register const uint16_t *vp; |
| 2174 | t = *timer; |
| 2175 | p = (int *) result; |
| 2176 | p[7] = 0; |
| 2177 | vp = _vals; |
| 2178 | do { |
| 2179 | if ((v = *vp) == 7) { |
| 2180 | /* Overflow checking, assuming time_t is long int... */ |
| 2181 | #if (LONG_MAX > INT_MAX) && (LONG_MAX > 2147483647L) |
| 2182 | #if (INT_MAX == 2147483647L) && (LONG_MAX == 9223372036854775807L) |
| 2183 | /* Valid range for t is [-784223472856L, 784223421720L]. |
| 2184 | * Outside of this range, the tm_year field will overflow. */ |
| 2185 | if (((unsigned long)(t + offset- -784223472856L)) |
| 2186 | > (784223421720L - -784223472856L) |
| 2187 | ) { |
| 2188 | return NULL; |
| 2189 | } |
| 2190 | #else |
| 2191 | #error overflow conditions unknown |
| 2192 | #endif |
| 2193 | #endif |
| 2194 | |
| 2195 | /* We have days since the epoch, so caluclate the weekday. */ |
| 2196 | #if defined(__BCC__) && TIME_T_IS_UNSIGNED |
| 2197 | wday = (t + 4) % (*vp); /* t is unsigned */ |
| 2198 | #else |
| 2199 | wday = ((int)((t % (*vp)) + 11)) % ((int)(*vp)); /* help bcc */ |
| 2200 | #endif |
| 2201 | /* Set divisor to days in 400 years. Be kind to bcc... */ |
| 2202 | v = ((time_t)(vp[1])) << 2; |
| 2203 | ++v; |
| 2204 | /* Change to days since 1/1/1601 so that for 32 bit time_t |
| 2205 | * values, we'll have t >= 0. This should be changed for |
| 2206 | * archs with larger time_t types. |
| 2207 | * Also, correct for offset since a multiple of 7. */ |
| 2208 | |
| 2209 | /* TODO: Does this still work on archs with time_t > 32 bits? */ |
| 2210 | t += (135140L - 366) + offset; /* 146097 - (365*30 + 7) -366 */ |
| 2211 | } |
| 2212 | #if defined(__BCC__) && TIME_T_IS_UNSIGNED |
| 2213 | t -= ((t1 = t / v) * v); |
| 2214 | #else |
| 2215 | if ((t -= ((t1 = t / v) * v)) < 0) { |
| 2216 | t += v; |
| 2217 | --t1; |
| 2218 | } |
| 2219 | #endif |
| 2220 | |
| 2221 | if ((*vp == 7) && (t == v-1)) { |
| 2222 | --t; /* Correct for 400th year leap case */ |
| 2223 | ++p[4]; /* Stash the extra day... */ |
| 2224 | } |
| 2225 | |
| 2226 | #if defined(__BCC__) && 0 |
| 2227 | *p = t1; |
| 2228 | if (v <= 60) { |
| 2229 | *p = t; |
| 2230 | t = t1; |
| 2231 | } |
| 2232 | ++p; |
| 2233 | #else |
| 2234 | if (v <= 60) { |
| 2235 | *p++ = t; |
| 2236 | t = t1; |
| 2237 | } else { |
| 2238 | *p++ = t1; |
| 2239 | } |
| 2240 | #endif |
| 2241 | } while (*++vp); |
| 2242 | } |
| 2243 | |
| 2244 | if (p[-1] == 4) { |
| 2245 | --p[-1]; |
| 2246 | t = 365; |
| 2247 | } |
| 2248 | |
| 2249 | *p += ((int) t); /* result[7] .. tm_yday */ |
| 2250 | |
| 2251 | p -= 2; /* at result[5] */ |
| 2252 | |
| 2253 | #if (LONG_MAX > INT_MAX) && (LONG_MAX > 2147483647L) |
| 2254 | /* Protect against overflow. TODO: Unecessary if int arith wraps? */ |
| 2255 | *p = ((((p[-2]<<2) + p[-1])*25 + p[0])<< 2) + (p[1] - 299); /* tm_year */ |
| 2256 | #else |
| 2257 | *p = ((((p[-2]<<2) + p[-1])*25 + p[0])<< 2) + p[1] - 299; /* tm_year */ |
| 2258 | #endif |
| 2259 | |
| 2260 | p[1] = wday; /* result[6] .. tm_wday */ |
| 2261 | |
| 2262 | { |
| 2263 | register const unsigned char *d = days; |
| 2264 | |
| 2265 | wday = 1900 + *p; |
| 2266 | if (__isleap(wday)) { |
| 2267 | d += 11; |
| 2268 | } |
| 2269 | |
| 2270 | wday = p[2] + 1; /* result[7] .. tm_yday */ |
| 2271 | *--p = 0; /* at result[4] .. tm_mon */ |
| 2272 | while (wday > *d) { |
| 2273 | wday -= *d; |
| 2274 | if (*d == 29) { |
| 2275 | d -= 11; /* Backup to non-leap Feb. */ |
| 2276 | } |
| 2277 | ++d; |
| 2278 | ++*p; /* Increment tm_mon. */ |
| 2279 | } |
| 2280 | p[-1] = wday; /* result[3] .. tm_mday */ |
| 2281 | } |
| 2282 | /* TODO -- should this be 0? */ |
| 2283 | p[4] = 0; /* result[8] .. tm_isdst */ |
| 2284 | #ifdef __UCLIBC_HAS_TM_EXTENSIONS__ |
| 2285 | # ifdef __USE_BSD |
| 2286 | result->tm_gmtoff = 0; |
| 2287 | result->tm_zone = utc_string; |
| 2288 | # else |
| 2289 | result->__tm_gmtoff = 0; |
| 2290 | result->__tm_zone = utc_string; |
| 2291 | # endif |
| 2292 | #endif /* __UCLIBC_HAS_TM_EXTENSIONS__ */ |
| 2293 | |
| 2294 | return result; |
| 2295 | } |
| 2296 | |
| 2297 | #endif |
| 2298 | /**********************************************************************/ |
| 2299 | #ifdef L___time_tm |
| 2300 | |
| 2301 | struct tm __time_tm; /* Global shared by gmtime() and localtime(). */ |
| 2302 | |
| 2303 | #endif |
| 2304 | /**********************************************************************/ |
| 2305 | #ifdef L__time_mktime |
| 2306 | |
| 2307 | time_t attribute_hidden _time_mktime(struct tm *timeptr, int store_on_success) |
| 2308 | { |
| 2309 | time_t t; |
| 2310 | |
| 2311 | __UCLIBC_MUTEX_LOCK(_time_tzlock); |
| 2312 | |
| 2313 | tzset(); |
| 2314 | |
| 2315 | t = _time_mktime_tzi(timeptr, store_on_success, _time_tzinfo); |
| 2316 | |
| 2317 | __UCLIBC_MUTEX_UNLOCK(_time_tzlock); |
| 2318 | |
| 2319 | return t; |
| 2320 | } |
| 2321 | |
| 2322 | #endif |
| 2323 | /**********************************************************************/ |
| 2324 | #ifdef L__time_mktime_tzi |
| 2325 | |
| 2326 | static const unsigned char __vals[] = { |
| 2327 | 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, /* non-leap */ |
| 2328 | 29, |
| 2329 | }; |
| 2330 | |
| 2331 | time_t attribute_hidden _time_mktime_tzi(struct tm *timeptr, int store_on_success, |
| 2332 | rule_struct *tzi) |
| 2333 | { |
| 2334 | #ifdef __BCC__ |
| 2335 | long days, secs; |
| 2336 | #else |
| 2337 | long long secs; |
| 2338 | #endif |
| 2339 | time_t t; |
| 2340 | struct tm x; |
| 2341 | /* 0:sec 1:min 2:hour 3:mday 4:mon 5:year 6:wday 7:yday 8:isdst */ |
| 2342 | register int *p = (int *) &x; |
| 2343 | register const unsigned char *s; |
| 2344 | int d, default_dst; |
| 2345 | |
| 2346 | memcpy(p, timeptr, sizeof(struct tm)); |
| 2347 | |
| 2348 | if (!tzi[1].tzname[0]) { /* No dst in this timezone, */ |
| 2349 | p[8] = 0; /* so set tm_isdst to 0. */ |
| 2350 | } |
| 2351 | |
| 2352 | default_dst = 0; |
| 2353 | if (p[8]) { /* Either dst or unknown? */ |
| 2354 | default_dst = 1; /* Assume advancing (even if unknown). */ |
| 2355 | p[8] = ((p[8] > 0) ? 1 : -1); /* Normalize so abs() <= 1. */ |
| 2356 | } |
| 2357 | |
| 2358 | d = 400; |
| 2359 | p[5] = (p[5] - ((p[6] = p[5]/d) * d)) + (p[7] = p[4]/12); |
| 2360 | if ((p[4] -= 12 * p[7]) < 0) { |
| 2361 | p[4] += 12; |
| 2362 | --p[5]; |
| 2363 | } |
| 2364 | |
| 2365 | s = __vals; |
| 2366 | d = (p[5] += 1900); /* Correct year. Now between 1900 and 2300. */ |
| 2367 | if (__isleap(d)) { |
| 2368 | s += 11; |
| 2369 | } |
| 2370 | |
| 2371 | p[7] = 0; |
| 2372 | d = p[4]; |
| 2373 | while (d) { |
| 2374 | p[7] += *s; |
| 2375 | if (*s == 29) { |
| 2376 | s -= 11; /* Backup to non-leap Feb. */ |
| 2377 | } |
| 2378 | ++s; |
| 2379 | --d; |
| 2380 | } |
| 2381 | |
| 2382 | _time_tzset (x.tm_year < 2007); /* tm_year was expanded above */ |
| 2383 | |
| 2384 | #ifdef __BCC__ |
| 2385 | d = p[5] - 1; |
| 2386 | days = -719163L + ((long)d)*365 + ((d/4) - (d/100) + (d/400) + p[3] + p[7]); |
| 2387 | secs = p[0] + 60*( p[1] + 60*((long)(p[2])) ) |
| 2388 | + tzi[default_dst].gmt_offset; |
| 2389 | DST_CORRECT: |
| 2390 | if (secs < 0) { |
| 2391 | secs += 120009600L; |
| 2392 | days -= 1389; |
| 2393 | } |
| 2394 | if ( ((unsigned long)(days + secs/86400L)) > 49710L) { |
| 2395 | t = ((time_t)(-1)); |
| 2396 | goto DONE; |
| 2397 | } |
| 2398 | secs += (days * 86400L); |
| 2399 | #else |
| 2400 | d = p[5] - 1; |
| 2401 | d = -719163L + d*365 + (d/4) - (d/100) + (d/400); |
| 2402 | secs = p[0] |
| 2403 | + tzi[default_dst].gmt_offset |
| 2404 | + 60*( p[1] |
| 2405 | + 60*(p[2] |
| 2406 | + 24*(((146073L * ((long long)(p[6])) + d) |
| 2407 | + p[3]) + p[7]))); |
| 2408 | |
| 2409 | DST_CORRECT: |
| 2410 | if (((unsigned long long)(secs - LONG_MIN)) |
| 2411 | > (((unsigned long long)LONG_MAX) - LONG_MIN) |
| 2412 | ) { |
| 2413 | t = ((time_t)(-1)); |
| 2414 | goto DONE; |
| 2415 | } |
| 2416 | #endif |
| 2417 | |
| 2418 | d = ((struct tm *)p)->tm_isdst; |
| 2419 | t = secs; |
| 2420 | |
| 2421 | __time_localtime_tzi(&t, (struct tm *)p, tzi); |
| 2422 | |
| 2423 | if (t == ((time_t)(-1))) { /* Remember, time_t can be unsigned. */ |
| 2424 | goto DONE; |
| 2425 | } |
| 2426 | |
| 2427 | if ((d < 0) && (((struct tm *)p)->tm_isdst != default_dst)) { |
| 2428 | #ifdef __BCC__ |
| 2429 | secs -= (days * 86400L); |
| 2430 | #endif |
| 2431 | secs += (tzi[1-default_dst].gmt_offset |
| 2432 | - tzi[default_dst].gmt_offset); |
| 2433 | goto DST_CORRECT; |
| 2434 | } |
| 2435 | |
| 2436 | |
| 2437 | if (store_on_success) { |
| 2438 | memcpy(timeptr, p, sizeof(struct tm)); |
| 2439 | } |
| 2440 | |
| 2441 | |
| 2442 | DONE: |
| 2443 | return t; |
| 2444 | } |
| 2445 | |
| 2446 | #endif |
| 2447 | /**********************************************************************/ |
| 2448 | #if defined(L_wcsftime) || defined(L_wcsftime_l) |
| 2449 | |
| 2450 | #if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) |
| 2451 | |
| 2452 | size_t wcsftime(wchar_t *__restrict s, size_t maxsize, |
| 2453 | const wchar_t *__restrict format, |
| 2454 | const struct tm *__restrict timeptr) |
| 2455 | { |
| 2456 | return wcsftime_l(s, maxsize, format, timeptr, __UCLIBC_CURLOCALE); |
| 2457 | } |
| 2458 | |
| 2459 | #else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ |
| 2460 | |
| 2461 | size_t __XL_NPP(wcsftime)(wchar_t *__restrict s, size_t maxsize, |
| 2462 | const wchar_t *__restrict format, |
| 2463 | const struct tm *__restrict timeptr __LOCALE_PARAM ) |
| 2464 | { |
| 2465 | #warning wcsftime always fails |
| 2466 | return 0; /* always fail */ |
| 2467 | } |
| 2468 | #ifdef L_wcsftime_l |
| 2469 | libc_hidden_def(wcsftime_l) |
| 2470 | #endif |
| 2471 | |
| 2472 | #endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ |
| 2473 | |
| 2474 | #endif |
| 2475 | /**********************************************************************/ |
| 2476 | #ifdef L_dysize |
| 2477 | /* Return the number of days in YEAR. */ |
| 2478 | |
| 2479 | int dysize(int year) |
| 2480 | { |
| 2481 | return __isleap(year) ? 366 : 365; |
| 2482 | } |
| 2483 | |
| 2484 | #endif |
| 2485 | /**********************************************************************/ |