diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/mktime.c | 164 | ||||
| -rw-r--r-- | src/strftime.c | 6 |
2 files changed, 127 insertions, 43 deletions
diff --git a/src/mktime.c b/src/mktime.c index a0cbce9a8f1..f759c29bb96 100644 --- a/src/mktime.c +++ b/src/mktime.c | |||
| @@ -104,6 +104,9 @@ const unsigned short int __mon_yday[2][13] = | |||
| 104 | { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } | 104 | { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } |
| 105 | }; | 105 | }; |
| 106 | 106 | ||
| 107 | static struct tm *ranged_convert __P ((struct tm *(*) __P ((const time_t *, | ||
| 108 | struct tm *)), | ||
| 109 | time_t *, struct tm *)); | ||
| 107 | static time_t ydhms_tm_diff __P ((int, int, int, int, int, const struct tm *)); | 110 | static time_t ydhms_tm_diff __P ((int, int, int, int, int, const struct tm *)); |
| 108 | time_t __mktime_internal __P ((struct tm *, | 111 | time_t __mktime_internal __P ((struct tm *, |
| 109 | struct tm *(*) (const time_t *, struct tm *), | 112 | struct tm *(*) (const time_t *, struct tm *), |
| @@ -136,30 +139,36 @@ localtime_r (t, tp) | |||
| 136 | measured in seconds, ignoring leap seconds. | 139 | measured in seconds, ignoring leap seconds. |
| 137 | YEAR uses the same numbering as TM->tm_year. | 140 | YEAR uses the same numbering as TM->tm_year. |
| 138 | All values are in range, except possibly YEAR. | 141 | All values are in range, except possibly YEAR. |
| 142 | If TP is null, return a nonzero value. | ||
| 139 | If overflow occurs, yield the low order bits of the correct answer. */ | 143 | If overflow occurs, yield the low order bits of the correct answer. */ |
| 140 | static time_t | 144 | static time_t |
| 141 | ydhms_tm_diff (year, yday, hour, min, sec, tp) | 145 | ydhms_tm_diff (year, yday, hour, min, sec, tp) |
| 142 | int year, yday, hour, min, sec; | 146 | int year, yday, hour, min, sec; |
| 143 | const struct tm *tp; | 147 | const struct tm *tp; |
| 144 | { | 148 | { |
| 145 | /* Compute intervening leap days correctly even if year is negative. | 149 | if (!tp) |
| 146 | Take care to avoid int overflow. time_t overflow is OK, since | 150 | return 1; |
| 147 | only the low order bits of the correct time_t answer are needed. | 151 | else |
| 148 | Don't convert to time_t until after all divisions are done, since | 152 | { |
| 149 | time_t might be unsigned. */ | 153 | /* Compute intervening leap days correctly even if year is negative. |
| 150 | int a4 = (year >> 2) + (TM_YEAR_BASE >> 2) - ! (year & 3); | 154 | Take care to avoid int overflow. time_t overflow is OK, since |
| 151 | int b4 = (tp->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (tp->tm_year & 3); | 155 | only the low order bits of the correct time_t answer are needed. |
| 152 | int a100 = a4 / 25 - (a4 % 25 < 0); | 156 | Don't convert to time_t until after all divisions are done, since |
| 153 | int b100 = b4 / 25 - (b4 % 25 < 0); | 157 | time_t might be unsigned. */ |
| 154 | int a400 = a100 >> 2; | 158 | int a4 = (year >> 2) + (TM_YEAR_BASE >> 2) - ! (year & 3); |
| 155 | int b400 = b100 >> 2; | 159 | int b4 = (tp->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (tp->tm_year & 3); |
| 156 | int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400); | 160 | int a100 = a4 / 25 - (a4 % 25 < 0); |
| 157 | time_t years = year - (time_t) tp->tm_year; | 161 | int b100 = b4 / 25 - (b4 % 25 < 0); |
| 158 | time_t days = (365 * years + intervening_leap_days | 162 | int a400 = a100 >> 2; |
| 159 | + (yday - tp->tm_yday)); | 163 | int b400 = b100 >> 2; |
| 160 | return (60 * (60 * (24 * days + (hour - tp->tm_hour)) | 164 | int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400); |
| 161 | + (min - tp->tm_min)) | 165 | time_t years = year - (time_t) tp->tm_year; |
| 162 | + (sec - tp->tm_sec)); | 166 | time_t days = (365 * years + intervening_leap_days |
| 167 | + (yday - tp->tm_yday)); | ||
| 168 | return (60 * (60 * (24 * days + (hour - tp->tm_hour)) | ||
| 169 | + (min - tp->tm_min)) | ||
| 170 | + (sec - tp->tm_sec)); | ||
| 171 | } | ||
| 163 | } | 172 | } |
| 164 | 173 | ||
| 165 | 174 | ||
| @@ -180,6 +189,54 @@ mktime (tp) | |||
| 180 | return __mktime_internal (tp, localtime_r, &localtime_offset); | 189 | return __mktime_internal (tp, localtime_r, &localtime_offset); |
| 181 | } | 190 | } |
| 182 | 191 | ||
| 192 | /* Use CONVERT to convert *T to a broken down time in *TP. | ||
| 193 | If *T is out of range for conversion, adjust it so that | ||
| 194 | it is the nearest in-range value and then convert that. */ | ||
| 195 | static struct tm * | ||
| 196 | ranged_convert (convert, t, tp) | ||
| 197 | struct tm *(*convert) __P ((const time_t *, struct tm *)); | ||
| 198 | time_t *t; | ||
| 199 | struct tm *tp; | ||
| 200 | { | ||
| 201 | struct tm *r; | ||
| 202 | |||
| 203 | if (! (r = (*convert) (t, tp)) && *t) | ||
| 204 | { | ||
| 205 | time_t bad = *t; | ||
| 206 | time_t ok = 0; | ||
| 207 | struct tm tm; | ||
| 208 | |||
| 209 | /* BAD is a known unconvertible time_t, and OK is a known good one. | ||
| 210 | Use binary search to narrow the range between BAD and OK until | ||
| 211 | they differ by 1. */ | ||
| 212 | while (bad != ok + (bad < 0 ? -1 : 1)) | ||
| 213 | { | ||
| 214 | time_t mid = *t = (bad < 0 | ||
| 215 | ? bad + ((ok - bad) >> 1) | ||
| 216 | : ok + ((bad - ok) >> 1)); | ||
| 217 | if ((r = (*convert) (t, tp))) | ||
| 218 | { | ||
| 219 | tm = *r; | ||
| 220 | ok = mid; | ||
| 221 | } | ||
| 222 | else | ||
| 223 | bad = mid; | ||
| 224 | } | ||
| 225 | |||
| 226 | if (!r && ok) | ||
| 227 | { | ||
| 228 | /* The last conversion attempt failed; | ||
| 229 | revert to the most recent successful attempt. */ | ||
| 230 | *t = ok; | ||
| 231 | *tp = tm; | ||
| 232 | r = tp; | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | return r; | ||
| 237 | } | ||
| 238 | |||
| 239 | |||
| 183 | /* Convert *TP to a time_t value, inverting | 240 | /* Convert *TP to a time_t value, inverting |
| 184 | the monotonic and mostly-unit-linear conversion function CONVERT. | 241 | the monotonic and mostly-unit-linear conversion function CONVERT. |
| 185 | Use *OFFSET to keep track of a guess at the offset of the result, | 242 | Use *OFFSET to keep track of a guess at the offset of the result, |
| @@ -245,7 +302,8 @@ __mktime_internal (tp, convert, offset) | |||
| 245 | t0 = ydhms_tm_diff (year, yday, hour, min, sec, &tm); | 302 | t0 = ydhms_tm_diff (year, yday, hour, min, sec, &tm); |
| 246 | 303 | ||
| 247 | for (t = t0 + *offset; | 304 | for (t = t0 + *offset; |
| 248 | (dt = ydhms_tm_diff (year, yday, hour, min, sec, (*convert) (&t, &tm))); | 305 | (dt = ydhms_tm_diff (year, yday, hour, min, sec, |
| 306 | ranged_convert (convert, &t, &tm))); | ||
| 249 | t += dt) | 307 | t += dt) |
| 250 | if (--remaining_probes == 0) | 308 | if (--remaining_probes == 0) |
| 251 | return -1; | 309 | return -1; |
| @@ -265,7 +323,7 @@ __mktime_internal (tp, convert, offset) | |||
| 265 | { | 323 | { |
| 266 | struct tm otm; | 324 | struct tm otm; |
| 267 | if (! (dt = ydhms_tm_diff (year, yday, hour, min, sec, | 325 | if (! (dt = ydhms_tm_diff (year, yday, hour, min, sec, |
| 268 | (*convert) (&ot, &otm)))) | 326 | ranged_convert (convert, &ot, &otm)))) |
| 269 | { | 327 | { |
| 270 | t = ot; | 328 | t = ot; |
| 271 | tm = otm; | 329 | tm = otm; |
| @@ -285,7 +343,8 @@ __mktime_internal (tp, convert, offset) | |||
| 285 | /* Adjust time to reflect the tm_sec requested, not the normalized value. | 343 | /* Adjust time to reflect the tm_sec requested, not the normalized value. |
| 286 | Also, repair any damage from a false match due to a leap second. */ | 344 | Also, repair any damage from a false match due to a leap second. */ |
| 287 | t += sec_requested - sec + (sec == 0 && tm.tm_sec == 60); | 345 | t += sec_requested - sec + (sec == 0 && tm.tm_sec == 60); |
| 288 | (*convert) (&t, &tm); | 346 | if (! (*convert) (&t, &tm)) |
| 347 | return -1; | ||
| 289 | } | 348 | } |
| 290 | #endif | 349 | #endif |
| 291 | 350 | ||
| @@ -335,25 +394,28 @@ static void | |||
| 335 | print_tm (tp) | 394 | print_tm (tp) |
| 336 | struct tm *tp; | 395 | struct tm *tp; |
| 337 | { | 396 | { |
| 338 | printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d", | 397 | if (tp) |
| 339 | tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday, | 398 | printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d", |
| 340 | tp->tm_hour, tp->tm_min, tp->tm_sec, | 399 | tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday, |
| 341 | tp->tm_yday, tp->tm_wday, tp->tm_isdst); | 400 | tp->tm_hour, tp->tm_min, tp->tm_sec, |
| 401 | tp->tm_yday, tp->tm_wday, tp->tm_isdst); | ||
| 402 | else | ||
| 403 | printf ("0"); | ||
| 342 | } | 404 | } |
| 343 | 405 | ||
| 344 | static int | 406 | static int |
| 345 | check_result (tk, tmk, tl, tml) | 407 | check_result (tk, tmk, tl, lt) |
| 346 | time_t tk; | 408 | time_t tk; |
| 347 | struct tm tmk; | 409 | struct tm tmk; |
| 348 | time_t tl; | 410 | time_t tl; |
| 349 | struct tm tml; | 411 | struct tm *lt; |
| 350 | { | 412 | { |
| 351 | if (tk != tl || not_equal_tm (&tmk, &tml)) | 413 | if (tk != tl || !lt || not_equal_tm (&tmk, lt)) |
| 352 | { | 414 | { |
| 353 | printf ("mktime ("); | 415 | printf ("mktime ("); |
| 354 | print_tm (&tmk); | 416 | print_tm (&tmk); |
| 355 | printf (")\nyields ("); | 417 | printf (")\nyields ("); |
| 356 | print_tm (&tml); | 418 | print_tm (lt); |
| 357 | printf (") == %ld, should be %ld\n", (long) tl, (long) tk); | 419 | printf (") == %ld, should be %ld\n", (long) tl, (long) tk); |
| 358 | return 1; | 420 | return 1; |
| 359 | } | 421 | } |
| @@ -368,6 +430,7 @@ main (argc, argv) | |||
| 368 | { | 430 | { |
| 369 | int status = 0; | 431 | int status = 0; |
| 370 | struct tm tm, tmk, tml; | 432 | struct tm tm, tmk, tml; |
| 433 | struct tm *lt; | ||
| 371 | time_t tk, tl; | 434 | time_t tk, tl; |
| 372 | char trailer; | 435 | char trailer; |
| 373 | 436 | ||
| @@ -384,11 +447,16 @@ main (argc, argv) | |||
| 384 | tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]); | 447 | tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]); |
| 385 | tmk = tm; | 448 | tmk = tm; |
| 386 | tl = mktime (&tmk); | 449 | tl = mktime (&tmk); |
| 387 | tml = *localtime (&tl); | 450 | lt = localtime (&tl); |
| 451 | if (lt) | ||
| 452 | { | ||
| 453 | tml = *lt; | ||
| 454 | lt = &tml; | ||
| 455 | } | ||
| 388 | printf ("mktime returns %ld == ", (long) tl); | 456 | printf ("mktime returns %ld == ", (long) tl); |
| 389 | print_tm (&tmk); | 457 | print_tm (&tmk); |
| 390 | printf ("\n"); | 458 | printf ("\n"); |
| 391 | status = check_result (tl, tmk, tl, tml); | 459 | status = check_result (tl, tmk, tl, lt); |
| 392 | } | 460 | } |
| 393 | else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0)) | 461 | else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0)) |
| 394 | { | 462 | { |
| @@ -399,19 +467,35 @@ main (argc, argv) | |||
| 399 | if (argc == 4) | 467 | if (argc == 4) |
| 400 | for (tl = from; tl <= to; tl += by) | 468 | for (tl = from; tl <= to; tl += by) |
| 401 | { | 469 | { |
| 402 | tml = *localtime (&tl); | 470 | lt = localtime (&tl); |
| 403 | tmk = tml; | 471 | if (lt) |
| 404 | tk = mktime (&tmk); | 472 | { |
| 405 | status |= check_result (tk, tmk, tl, tml); | 473 | tmk = tml = *lt; |
| 474 | tk = mktime (&tmk); | ||
| 475 | status |= check_result (tk, tmk, tl, tml); | ||
| 476 | } | ||
| 477 | else | ||
| 478 | { | ||
| 479 | printf ("localtime (%ld) yields 0\n", (long) tl); | ||
| 480 | status = 1; | ||
| 481 | } | ||
| 406 | } | 482 | } |
| 407 | else | 483 | else |
| 408 | for (tl = from; tl <= to; tl += by) | 484 | for (tl = from; tl <= to; tl += by) |
| 409 | { | 485 | { |
| 410 | /* Null benchmark. */ | 486 | /* Null benchmark. */ |
| 411 | tml = *localtime (&tl); | 487 | lt = localtime (&tl); |
| 412 | tmk = tml; | 488 | if (lt) |
| 413 | tk = tl; | 489 | { |
| 414 | status |= check_result (tk, tmk, tl, tml); | 490 | tmk = tml = *lt; |
| 491 | tk = tl; | ||
| 492 | status |= check_result (tk, tmk, tl, tml); | ||
| 493 | } | ||
| 494 | else | ||
| 495 | { | ||
| 496 | printf ("localtime (%ld) yields 0\n", (long) tl); | ||
| 497 | status = 1; | ||
| 498 | } | ||
| 415 | } | 499 | } |
| 416 | } | 500 | } |
| 417 | else | 501 | else |
| @@ -428,6 +512,6 @@ main (argc, argv) | |||
| 428 | 512 | ||
| 429 | /* | 513 | /* |
| 430 | Local Variables: | 514 | Local Variables: |
| 431 | compile-command: "gcc -DDEBUG=1 -Wall -O -g mktime.c -o mktime" | 515 | compile-command: "gcc -DDEBUG -D__EXTENSIONS__ -DHAVE_LIMITS_H -DHAVE_LOCALTIME_R -DSTDC_HEADERS -Wall -W -O -g mktime.c -o mktime" |
| 432 | End: | 516 | End: |
| 433 | */ | 517 | */ |
diff --git a/src/strftime.c b/src/strftime.c index 50aed8ac76a..b084851a451 100644 --- a/src/strftime.c +++ b/src/strftime.c | |||
| @@ -184,7 +184,7 @@ localtime_r (t, tp) | |||
| 184 | return tp; | 184 | return tp; |
| 185 | } | 185 | } |
| 186 | # endif /* ! HAVE_LOCALTIME_R */ | 186 | # endif /* ! HAVE_LOCALTIME_R */ |
| 187 | #endif /* ! defined (_LIBC) */ | 187 | #endif /* ! defined _LIBC */ |
| 188 | 188 | ||
| 189 | 189 | ||
| 190 | #if !defined memset && !defined HAVE_MEMSET && !defined _LIBC | 190 | #if !defined memset && !defined HAVE_MEMSET && !defined _LIBC |
| @@ -204,7 +204,7 @@ static const char zeroes[16] = /* "0000000000000000" */ | |||
| 204 | do \ | 204 | do \ |
| 205 | { \ | 205 | { \ |
| 206 | int _this = _len > 16 ? 16 : _len; \ | 206 | int _this = _len > 16 ? 16 : _len; \ |
| 207 | (P) = mempcpy ((P), spaces, _this); \ | 207 | (P) = MEMPCPY ((P), spaces, _this); \ |
| 208 | _len -= _this; \ | 208 | _len -= _this; \ |
| 209 | } \ | 209 | } \ |
| 210 | while (_len > 0); \ | 210 | while (_len > 0); \ |
| @@ -217,7 +217,7 @@ static const char zeroes[16] = /* "0000000000000000" */ | |||
| 217 | do \ | 217 | do \ |
| 218 | { \ | 218 | { \ |
| 219 | int _this = _len > 16 ? 16 : _len; \ | 219 | int _this = _len > 16 ? 16 : _len; \ |
| 220 | (P) = mempcpy ((P), zeroes, _this); \ | 220 | (P) = MEMPCPY ((P), zeroes, _this); \ |
| 221 | _len -= _this; \ | 221 | _len -= _this; \ |
| 222 | } \ | 222 | } \ |
| 223 | while (_len > 0); \ | 223 | while (_len > 0); \ |