js/src/prmjtime.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /* PR time code. */
michael@0 8
michael@0 9 #include "prmjtime.h"
michael@0 10
michael@0 11 #include "mozilla/MathAlgorithms.h"
michael@0 12
michael@0 13 #ifdef SOLARIS
michael@0 14 #define _REENTRANT 1
michael@0 15 #endif
michael@0 16 #include <string.h>
michael@0 17 #include <time.h>
michael@0 18
michael@0 19 #include "jstypes.h"
michael@0 20 #include "jsutil.h"
michael@0 21
michael@0 22 #define PRMJ_DO_MILLISECONDS 1
michael@0 23
michael@0 24 #ifdef XP_WIN
michael@0 25 #include <windef.h>
michael@0 26 #include <winbase.h>
michael@0 27 #include <mmsystem.h> /* for timeBegin/EndPeriod */
michael@0 28 /* VC++ 8.0 or later */
michael@0 29 #if _MSC_VER >= 1400
michael@0 30 #define NS_HAVE_INVALID_PARAMETER_HANDLER 1
michael@0 31 #endif
michael@0 32 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
michael@0 33 #include <crtdbg.h> /* for _CrtSetReportMode */
michael@0 34 #include <stdlib.h> /* for _set_invalid_parameter_handler */
michael@0 35 #endif
michael@0 36
michael@0 37 #ifdef JS_THREADSAFE
michael@0 38 #include "prinit.h"
michael@0 39 #endif
michael@0 40
michael@0 41 #endif
michael@0 42
michael@0 43 #ifdef XP_UNIX
michael@0 44
michael@0 45 #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
michael@0 46 extern int gettimeofday(struct timeval *tv);
michael@0 47 #endif
michael@0 48
michael@0 49 #include <sys/time.h>
michael@0 50
michael@0 51 #endif /* XP_UNIX */
michael@0 52
michael@0 53 #define PRMJ_YEAR_DAYS 365L
michael@0 54 #define PRMJ_FOUR_YEARS_DAYS (4 * PRMJ_YEAR_DAYS + 1)
michael@0 55 #define PRMJ_CENTURY_DAYS (25 * PRMJ_FOUR_YEARS_DAYS - 1)
michael@0 56 #define PRMJ_FOUR_CENTURIES_DAYS (4 * PRMJ_CENTURY_DAYS + 1)
michael@0 57 #define PRMJ_HOUR_SECONDS 3600L
michael@0 58 #define PRMJ_DAY_SECONDS (24L * PRMJ_HOUR_SECONDS)
michael@0 59 #define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * PRMJ_YEAR_DAYS)
michael@0 60 #define PRMJ_MAX_UNIX_TIMET 2145859200L /*time_t value equiv. to 12/31/2037 */
michael@0 61
michael@0 62 /* Constants for GMT offset from 1970 */
michael@0 63 #define G1970GMTMICROHI 0x00dcdcad /* micro secs to 1970 hi */
michael@0 64 #define G1970GMTMICROLOW 0x8b3fa000 /* micro secs to 1970 low */
michael@0 65
michael@0 66 #define G2037GMTMICROHI 0x00e45fab /* micro secs to 2037 high */
michael@0 67 #define G2037GMTMICROLOW 0x7a238000 /* micro secs to 2037 low */
michael@0 68
michael@0 69 #if defined(XP_WIN)
michael@0 70
michael@0 71 static const int64_t win2un = 0x19DB1DED53E8000;
michael@0 72
michael@0 73 #define FILETIME2INT64(ft) (((int64_t)ft.dwHighDateTime) << 32LL | (int64_t)ft.dwLowDateTime)
michael@0 74
michael@0 75 typedef struct CalibrationData {
michael@0 76 long double freq; /* The performance counter frequency */
michael@0 77 long double offset; /* The low res 'epoch' */
michael@0 78 long double timer_offset; /* The high res 'epoch' */
michael@0 79
michael@0 80 /* The last high res time that we returned since recalibrating */
michael@0 81 int64_t last;
michael@0 82
michael@0 83 bool calibrated;
michael@0 84
michael@0 85 #ifdef JS_THREADSAFE
michael@0 86 CRITICAL_SECTION data_lock;
michael@0 87 CRITICAL_SECTION calibration_lock;
michael@0 88 #endif
michael@0 89 } CalibrationData;
michael@0 90
michael@0 91 static CalibrationData calibration = { 0 };
michael@0 92
michael@0 93 static void
michael@0 94 NowCalibrate()
michael@0 95 {
michael@0 96 FILETIME ft, ftStart;
michael@0 97 LARGE_INTEGER liFreq, now;
michael@0 98
michael@0 99 if (calibration.freq == 0.0) {
michael@0 100 if(!QueryPerformanceFrequency(&liFreq)) {
michael@0 101 /* High-performance timer is unavailable */
michael@0 102 calibration.freq = -1.0;
michael@0 103 } else {
michael@0 104 calibration.freq = (long double) liFreq.QuadPart;
michael@0 105 }
michael@0 106 }
michael@0 107 if (calibration.freq > 0.0) {
michael@0 108 int64_t calibrationDelta = 0;
michael@0 109
michael@0 110 /* By wrapping a timeBegin/EndPeriod pair of calls around this loop,
michael@0 111 the loop seems to take much less time (1 ms vs 15ms) on Vista. */
michael@0 112 timeBeginPeriod(1);
michael@0 113 GetSystemTimeAsFileTime(&ftStart);
michael@0 114 do {
michael@0 115 GetSystemTimeAsFileTime(&ft);
michael@0 116 } while (memcmp(&ftStart,&ft, sizeof(ft)) == 0);
michael@0 117 timeEndPeriod(1);
michael@0 118
michael@0 119 /*
michael@0 120 calibrationDelta = (FILETIME2INT64(ft) - FILETIME2INT64(ftStart))/10;
michael@0 121 fprintf(stderr, "Calibration delta was %I64d us\n", calibrationDelta);
michael@0 122 */
michael@0 123
michael@0 124 QueryPerformanceCounter(&now);
michael@0 125
michael@0 126 calibration.offset = (long double) FILETIME2INT64(ft);
michael@0 127 calibration.timer_offset = (long double) now.QuadPart;
michael@0 128
michael@0 129 /* The windows epoch is around 1600. The unix epoch is around
michael@0 130 1970. win2un is the difference (in windows time units which
michael@0 131 are 10 times more highres than the JS time unit) */
michael@0 132 calibration.offset -= win2un;
michael@0 133 calibration.offset *= 0.1;
michael@0 134 calibration.last = 0;
michael@0 135
michael@0 136 calibration.calibrated = true;
michael@0 137 }
michael@0 138 }
michael@0 139
michael@0 140 #define CALIBRATIONLOCK_SPINCOUNT 0
michael@0 141 #define DATALOCK_SPINCOUNT 4096
michael@0 142 #define LASTLOCK_SPINCOUNT 4096
michael@0 143
michael@0 144 #ifdef JS_THREADSAFE
michael@0 145 static PRStatus
michael@0 146 NowInit(void)
michael@0 147 {
michael@0 148 memset(&calibration, 0, sizeof(calibration));
michael@0 149 NowCalibrate();
michael@0 150 InitializeCriticalSectionAndSpinCount(&calibration.calibration_lock, CALIBRATIONLOCK_SPINCOUNT);
michael@0 151 InitializeCriticalSectionAndSpinCount(&calibration.data_lock, DATALOCK_SPINCOUNT);
michael@0 152 return PR_SUCCESS;
michael@0 153 }
michael@0 154
michael@0 155 void
michael@0 156 PRMJ_NowShutdown()
michael@0 157 {
michael@0 158 DeleteCriticalSection(&calibration.calibration_lock);
michael@0 159 DeleteCriticalSection(&calibration.data_lock);
michael@0 160 }
michael@0 161
michael@0 162 #define MUTEX_LOCK(m) EnterCriticalSection(m)
michael@0 163 #define MUTEX_TRYLOCK(m) TryEnterCriticalSection(m)
michael@0 164 #define MUTEX_UNLOCK(m) LeaveCriticalSection(m)
michael@0 165 #define MUTEX_SETSPINCOUNT(m, c) SetCriticalSectionSpinCount((m),(c))
michael@0 166
michael@0 167 static PRCallOnceType calibrationOnce = { 0 };
michael@0 168
michael@0 169 #else
michael@0 170
michael@0 171 #define MUTEX_LOCK(m)
michael@0 172 #define MUTEX_TRYLOCK(m) 1
michael@0 173 #define MUTEX_UNLOCK(m)
michael@0 174 #define MUTEX_SETSPINCOUNT(m, c)
michael@0 175
michael@0 176 #endif
michael@0 177
michael@0 178 #endif /* XP_WIN */
michael@0 179
michael@0 180
michael@0 181 #if defined(XP_UNIX)
michael@0 182 int64_t
michael@0 183 PRMJ_Now(void)
michael@0 184 {
michael@0 185 struct timeval tv;
michael@0 186
michael@0 187 #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
michael@0 188 gettimeofday(&tv);
michael@0 189 #else
michael@0 190 gettimeofday(&tv, 0);
michael@0 191 #endif /* _SVID_GETTOD */
michael@0 192
michael@0 193 return int64_t(tv.tv_sec) * PRMJ_USEC_PER_SEC + int64_t(tv.tv_usec);
michael@0 194 }
michael@0 195
michael@0 196 #else
michael@0 197 /*
michael@0 198
michael@0 199 Win32 python-esque pseudo code
michael@0 200 Please see bug 363258 for why the win32 timing code is so complex.
michael@0 201
michael@0 202 calibration mutex : Win32CriticalSection(spincount=0)
michael@0 203 data mutex : Win32CriticalSection(spincount=4096)
michael@0 204
michael@0 205 def NowInit():
michael@0 206 init mutexes
michael@0 207 PRMJ_NowCalibration()
michael@0 208
michael@0 209 def NowCalibration():
michael@0 210 expensive up-to-15ms call
michael@0 211
michael@0 212 def PRMJ_Now():
michael@0 213 returnedTime = 0
michael@0 214 needCalibration = False
michael@0 215 cachedOffset = 0.0
michael@0 216 calibrated = False
michael@0 217 PR_CallOnce(PRMJ_NowInit)
michael@0 218 do
michael@0 219 if not global.calibrated or needCalibration:
michael@0 220 acquire calibration mutex
michael@0 221 acquire data mutex
michael@0 222
michael@0 223 // Only recalibrate if someone didn't already
michael@0 224 if cachedOffset == calibration.offset:
michael@0 225 // Have all waiting threads immediately wait
michael@0 226 set data mutex spin count = 0
michael@0 227 PRMJ_NowCalibrate()
michael@0 228 calibrated = 1
michael@0 229
michael@0 230 set data mutex spin count = default
michael@0 231 release data mutex
michael@0 232 release calibration mutex
michael@0 233
michael@0 234 calculate lowres time
michael@0 235
michael@0 236 if highres timer available:
michael@0 237 acquire data mutex
michael@0 238 calculate highres time
michael@0 239 cachedOffset = calibration.offset
michael@0 240 highres time = calibration.last = max(highres time, calibration.last)
michael@0 241 release data mutex
michael@0 242
michael@0 243 get kernel tick interval
michael@0 244
michael@0 245 if abs(highres - lowres) < kernel tick:
michael@0 246 returnedTime = highres time
michael@0 247 needCalibration = False
michael@0 248 else:
michael@0 249 if calibrated:
michael@0 250 returnedTime = lowres
michael@0 251 needCalibration = False
michael@0 252 else:
michael@0 253 needCalibration = True
michael@0 254 else:
michael@0 255 returnedTime = lowres
michael@0 256 while needCalibration
michael@0 257
michael@0 258 */
michael@0 259
michael@0 260 int64_t
michael@0 261 PRMJ_Now(void)
michael@0 262 {
michael@0 263 static int nCalls = 0;
michael@0 264 long double lowresTime, highresTimerValue;
michael@0 265 FILETIME ft;
michael@0 266 LARGE_INTEGER now;
michael@0 267 bool calibrated = false;
michael@0 268 bool needsCalibration = false;
michael@0 269 int64_t returnedTime;
michael@0 270 long double cachedOffset = 0.0;
michael@0 271
michael@0 272 /* For non threadsafe platforms, NowInit is not necessary */
michael@0 273 #ifdef JS_THREADSAFE
michael@0 274 PR_CallOnce(&calibrationOnce, NowInit);
michael@0 275 #endif
michael@0 276 do {
michael@0 277 if (!calibration.calibrated || needsCalibration) {
michael@0 278 MUTEX_LOCK(&calibration.calibration_lock);
michael@0 279 MUTEX_LOCK(&calibration.data_lock);
michael@0 280
michael@0 281 /* Recalibrate only if no one else did before us */
michael@0 282 if(calibration.offset == cachedOffset) {
michael@0 283 /* Since calibration can take a while, make any other
michael@0 284 threads immediately wait */
michael@0 285 MUTEX_SETSPINCOUNT(&calibration.data_lock, 0);
michael@0 286
michael@0 287 NowCalibrate();
michael@0 288
michael@0 289 calibrated = true;
michael@0 290
michael@0 291 /* Restore spin count */
michael@0 292 MUTEX_SETSPINCOUNT(&calibration.data_lock, DATALOCK_SPINCOUNT);
michael@0 293 }
michael@0 294 MUTEX_UNLOCK(&calibration.data_lock);
michael@0 295 MUTEX_UNLOCK(&calibration.calibration_lock);
michael@0 296 }
michael@0 297
michael@0 298
michael@0 299 /* Calculate a low resolution time */
michael@0 300 GetSystemTimeAsFileTime(&ft);
michael@0 301 lowresTime = 0.1*(long double)(FILETIME2INT64(ft) - win2un);
michael@0 302
michael@0 303 if (calibration.freq > 0.0) {
michael@0 304 long double highresTime, diff;
michael@0 305
michael@0 306 DWORD timeAdjustment, timeIncrement;
michael@0 307 BOOL timeAdjustmentDisabled;
michael@0 308
michael@0 309 /* Default to 15.625 ms if the syscall fails */
michael@0 310 long double skewThreshold = 15625.25;
michael@0 311 /* Grab high resolution time */
michael@0 312 QueryPerformanceCounter(&now);
michael@0 313 highresTimerValue = (long double)now.QuadPart;
michael@0 314
michael@0 315 MUTEX_LOCK(&calibration.data_lock);
michael@0 316 highresTime = calibration.offset + PRMJ_USEC_PER_SEC*
michael@0 317 (highresTimerValue-calibration.timer_offset)/calibration.freq;
michael@0 318 cachedOffset = calibration.offset;
michael@0 319
michael@0 320 /* On some dual processor/core systems, we might get an earlier time
michael@0 321 so we cache the last time that we returned */
michael@0 322 calibration.last = js::Max(calibration.last, int64_t(highresTime));
michael@0 323 returnedTime = calibration.last;
michael@0 324 MUTEX_UNLOCK(&calibration.data_lock);
michael@0 325
michael@0 326 /* Rather than assume the NT kernel ticks every 15.6ms, ask it */
michael@0 327 if (GetSystemTimeAdjustment(&timeAdjustment,
michael@0 328 &timeIncrement,
michael@0 329 &timeAdjustmentDisabled)) {
michael@0 330 if (timeAdjustmentDisabled) {
michael@0 331 /* timeAdjustment is in units of 100ns */
michael@0 332 skewThreshold = timeAdjustment/10.0;
michael@0 333 } else {
michael@0 334 /* timeIncrement is in units of 100ns */
michael@0 335 skewThreshold = timeIncrement/10.0;
michael@0 336 }
michael@0 337 }
michael@0 338
michael@0 339 /* Check for clock skew */
michael@0 340 diff = lowresTime - highresTime;
michael@0 341
michael@0 342 /* For some reason that I have not determined, the skew can be
michael@0 343 up to twice a kernel tick. This does not seem to happen by
michael@0 344 itself, but I have only seen it triggered by another program
michael@0 345 doing some kind of file I/O. The symptoms are a negative diff
michael@0 346 followed by an equally large positive diff. */
michael@0 347 if (mozilla::Abs(diff) > 2 * skewThreshold) {
michael@0 348 /*fprintf(stderr,"Clock skew detected (diff = %f)!\n", diff);*/
michael@0 349
michael@0 350 if (calibrated) {
michael@0 351 /* If we already calibrated once this instance, and the
michael@0 352 clock is still skewed, then either the processor(s) are
michael@0 353 wildly changing clockspeed or the system is so busy that
michael@0 354 we get switched out for long periods of time. In either
michael@0 355 case, it would be infeasible to make use of high
michael@0 356 resolution results for anything, so let's resort to old
michael@0 357 behavior for this call. It's possible that in the
michael@0 358 future, the user will want the high resolution timer, so
michael@0 359 we don't disable it entirely. */
michael@0 360 returnedTime = int64_t(lowresTime);
michael@0 361 needsCalibration = false;
michael@0 362 } else {
michael@0 363 /* It is possible that when we recalibrate, we will return a
michael@0 364 value less than what we have returned before; this is
michael@0 365 unavoidable. We cannot tell the different between a
michael@0 366 faulty QueryPerformanceCounter implementation and user
michael@0 367 changes to the operating system time. Since we must
michael@0 368 respect user changes to the operating system time, we
michael@0 369 cannot maintain the invariant that Date.now() never
michael@0 370 decreases; the old implementation has this behavior as
michael@0 371 well. */
michael@0 372 needsCalibration = true;
michael@0 373 }
michael@0 374 } else {
michael@0 375 /* No detectable clock skew */
michael@0 376 returnedTime = int64_t(highresTime);
michael@0 377 needsCalibration = false;
michael@0 378 }
michael@0 379 } else {
michael@0 380 /* No high resolution timer is available, so fall back */
michael@0 381 returnedTime = int64_t(lowresTime);
michael@0 382 }
michael@0 383 } while (needsCalibration);
michael@0 384
michael@0 385 return returnedTime;
michael@0 386 }
michael@0 387 #endif
michael@0 388
michael@0 389 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
michael@0 390 static void
michael@0 391 PRMJ_InvalidParameterHandler(const wchar_t *expression,
michael@0 392 const wchar_t *function,
michael@0 393 const wchar_t *file,
michael@0 394 unsigned int line,
michael@0 395 uintptr_t pReserved)
michael@0 396 {
michael@0 397 /* empty */
michael@0 398 }
michael@0 399 #endif
michael@0 400
michael@0 401 /* Format a time value into a buffer. Same semantics as strftime() */
michael@0 402 size_t
michael@0 403 PRMJ_FormatTime(char *buf, int buflen, const char *fmt, PRMJTime *prtm)
michael@0 404 {
michael@0 405 size_t result = 0;
michael@0 406 #if defined(XP_UNIX) || defined(XP_WIN)
michael@0 407 struct tm a;
michael@0 408 int fake_tm_year = 0;
michael@0 409 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
michael@0 410 _invalid_parameter_handler oldHandler;
michael@0 411 int oldReportMode;
michael@0 412 #endif
michael@0 413
michael@0 414 memset(&a, 0, sizeof(struct tm));
michael@0 415
michael@0 416 a.tm_sec = prtm->tm_sec;
michael@0 417 a.tm_min = prtm->tm_min;
michael@0 418 a.tm_hour = prtm->tm_hour;
michael@0 419 a.tm_mday = prtm->tm_mday;
michael@0 420 a.tm_mon = prtm->tm_mon;
michael@0 421 a.tm_wday = prtm->tm_wday;
michael@0 422
michael@0 423 /*
michael@0 424 * On systems where |struct tm| has members tm_gmtoff and tm_zone, we
michael@0 425 * must fill in those values, or else strftime will return wrong results
michael@0 426 * (e.g., bug 511726, bug 554338).
michael@0 427 */
michael@0 428 #if defined(HAVE_LOCALTIME_R) && defined(HAVE_TM_ZONE_TM_GMTOFF)
michael@0 429 {
michael@0 430 /*
michael@0 431 * Fill out |td| to the time represented by |prtm|, leaving the
michael@0 432 * timezone fields zeroed out. localtime_r will then fill in the
michael@0 433 * timezone fields for that local time according to the system's
michael@0 434 * timezone parameters.
michael@0 435 */
michael@0 436 struct tm td;
michael@0 437 memset(&td, 0, sizeof(td));
michael@0 438 td.tm_sec = prtm->tm_sec;
michael@0 439 td.tm_min = prtm->tm_min;
michael@0 440 td.tm_hour = prtm->tm_hour;
michael@0 441 td.tm_mday = prtm->tm_mday;
michael@0 442 td.tm_mon = prtm->tm_mon;
michael@0 443 td.tm_wday = prtm->tm_wday;
michael@0 444 td.tm_year = prtm->tm_year - 1900;
michael@0 445 td.tm_yday = prtm->tm_yday;
michael@0 446 td.tm_isdst = prtm->tm_isdst;
michael@0 447 time_t t = mktime(&td);
michael@0 448 localtime_r(&t, &td);
michael@0 449
michael@0 450 a.tm_gmtoff = td.tm_gmtoff;
michael@0 451 a.tm_zone = td.tm_zone;
michael@0 452 }
michael@0 453 #endif
michael@0 454
michael@0 455 /*
michael@0 456 * Years before 1900 and after 9999 cause strftime() to abort on Windows.
michael@0 457 * To avoid that we replace it with FAKE_YEAR_BASE + year % 100 and then
michael@0 458 * replace matching substrings in the strftime() result with the real year.
michael@0 459 * Note that FAKE_YEAR_BASE should be a multiple of 100 to make 2-digit
michael@0 460 * year formats (%y) work correctly (since we won't find the fake year
michael@0 461 * in that case).
michael@0 462 * e.g. new Date(1873, 0).toLocaleFormat('%Y %y') => "1873 73"
michael@0 463 * See bug 327869.
michael@0 464 */
michael@0 465 #define FAKE_YEAR_BASE 9900
michael@0 466 if (prtm->tm_year < 1900 || prtm->tm_year > 9999) {
michael@0 467 fake_tm_year = FAKE_YEAR_BASE + prtm->tm_year % 100;
michael@0 468 a.tm_year = fake_tm_year - 1900;
michael@0 469 }
michael@0 470 else {
michael@0 471 a.tm_year = prtm->tm_year - 1900;
michael@0 472 }
michael@0 473 a.tm_yday = prtm->tm_yday;
michael@0 474 a.tm_isdst = prtm->tm_isdst;
michael@0 475
michael@0 476 /*
michael@0 477 * Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff
michael@0 478 * are null. This doesn't quite work, though - the timezone is off by
michael@0 479 * tzoff + dst. (And mktime seems to return -1 for the exact dst
michael@0 480 * changeover time.)
michael@0 481 */
michael@0 482
michael@0 483 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
michael@0 484 oldHandler = _set_invalid_parameter_handler(PRMJ_InvalidParameterHandler);
michael@0 485 oldReportMode = _CrtSetReportMode(_CRT_ASSERT, 0);
michael@0 486 #endif
michael@0 487
michael@0 488 result = strftime(buf, buflen, fmt, &a);
michael@0 489
michael@0 490 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
michael@0 491 _set_invalid_parameter_handler(oldHandler);
michael@0 492 _CrtSetReportMode(_CRT_ASSERT, oldReportMode);
michael@0 493 #endif
michael@0 494
michael@0 495 if (fake_tm_year && result) {
michael@0 496 char real_year[16];
michael@0 497 char fake_year[16];
michael@0 498 size_t real_year_len;
michael@0 499 size_t fake_year_len;
michael@0 500 char* p;
michael@0 501
michael@0 502 sprintf(real_year, "%d", prtm->tm_year);
michael@0 503 real_year_len = strlen(real_year);
michael@0 504 sprintf(fake_year, "%d", fake_tm_year);
michael@0 505 fake_year_len = strlen(fake_year);
michael@0 506
michael@0 507 /* Replace the fake year in the result with the real year. */
michael@0 508 for (p = buf; (p = strstr(p, fake_year)); p += real_year_len) {
michael@0 509 size_t new_result = result + real_year_len - fake_year_len;
michael@0 510 if ((int)new_result >= buflen) {
michael@0 511 return 0;
michael@0 512 }
michael@0 513 memmove(p + real_year_len, p + fake_year_len, strlen(p + fake_year_len));
michael@0 514 memcpy(p, real_year, real_year_len);
michael@0 515 result = new_result;
michael@0 516 *(buf + result) = '\0';
michael@0 517 }
michael@0 518 }
michael@0 519 #endif
michael@0 520 return result;
michael@0 521 }

mercurial