File Coverage

Piece.xs
Criterion Covered Total %
statement 339 517 65.5
branch 180 384 46.8
condition n/a
subroutine n/a
pod n/a
total 519 901 57.6


line stmt bran cond sub pod time code
1             #ifdef __cplusplus
2             extern "C" {
3             #endif
4             #define PERL_NO_GET_CONTEXT
5             #include "EXTERN.h"
6             #include "perl.h"
7             #include "XSUB.h"
8             #include
9             #ifdef __cplusplus
10             }
11             #endif
12              
13              
14             #define DAYS_PER_YEAR 365
15             #define DAYS_PER_QYEAR (4*DAYS_PER_YEAR+1)
16             #define DAYS_PER_CENT (25*DAYS_PER_QYEAR-1)
17             #define DAYS_PER_QCENT (4*DAYS_PER_CENT+1)
18             #define SECS_PER_HOUR (60*60)
19             #define SECS_PER_DAY (24*SECS_PER_HOUR)
20             /* parentheses deliberately absent on these two, otherwise they don't work */
21             #define MONTH_TO_DAYS 153/5
22             #define DAYS_TO_MONTH 5/153
23             /* offset to bias by March (month 4) 1st between month/mday & year finding */
24             #define YEAR_ADJUST (4*MONTH_TO_DAYS+1)
25             /* as used here, the algorithm leaves Sunday as day 1 unless we adjust it */
26             #define WEEKDAY_BIAS 6 /* (1+6)%7 makes Sunday 0 again */
27             #define TP_BUF_SIZE 160
28              
29             #ifdef WIN32
30              
31             /*
32             * (1) The CRT maintains its own copy of the environment, separate from
33             * the Win32API copy.
34             *
35             * (2) CRT getenv() retrieves from this copy. CRT putenv() updates this
36             * copy, and then calls SetEnvironmentVariableA() to update the Win32API
37             * copy.
38             *
39             * (3) win32_getenv() and win32_putenv() call GetEnvironmentVariableA() and
40             * SetEnvironmentVariableA() directly, bypassing the CRT copy of the
41             * environment.
42             *
43             * (4) The CRT strftime() "%Z" implementation calls __tzset(). That
44             * calls CRT tzset(), but only the first time it is called, and in turn
45             * that uses CRT getenv("TZ") to retrieve the timezone info from the CRT
46             * local copy of the environment and hence gets the original setting as
47             * perl never updates the CRT copy when assigning to $ENV{TZ}.
48             *
49             * Therefore, we need to retrieve the value of $ENV{TZ} and call CRT
50             * putenv() to update the CRT copy of the environment (if it is different)
51             * whenever we're about to call tzset().
52             *
53             * In addition to all that, when perl is built with PERL_IMPLICIT_SYS
54             * defined:
55             *
56             * (a) Each interpreter has its own copy of the environment inside the
57             * perlhost structure. That allows applications that host multiple
58             * independent Perl interpreters to isolate environment changes from
59             * each other. (This is similar to how the perlhost mechanism keeps a
60             * separate working directory for each Perl interpreter, so that calling
61             * chdir() will not affect other interpreters.)
62             *
63             * (b) Only the first Perl interpreter instantiated within a process will
64             * "write through" environment changes to the process environment.
65             *
66             * (c) Even the primary Perl interpreter won't update the CRT copy of the
67             * the environment, only the Win32API copy (it calls win32_putenv()).
68             *
69             * As with CPerlHost::Getenv() and CPerlHost::Putenv() themselves, it makes
70             * sense to only update the process environment when inside the main
71             * interpreter, but we don't have access to CPerlHost's m_bTopLevel member
72             * from here so we'll just have to check PL_curinterp instead.
73             *
74             * Therefore, we can simply #undef getenv() and putenv() so that those names
75             * always refer to the CRT functions, and explicitly call win32_getenv() to
76             * access perl's %ENV.
77             *
78             * We also #undef malloc() and free() to be sure we are using the CRT
79             * functions otherwise under PERL_IMPLICIT_SYS they are redefined to calls
80             * into VMem::Malloc() and VMem::Free() and all allocations will be freed
81             * when the Perl interpreter is being destroyed so we'd end up with a pointer
82             * into deallocated memory in environ[] if a program embedding a Perl
83             * interpreter continues to operate even after the main Perl interpreter has
84             * been destroyed.
85             *
86             * Note that we don't free() the malloc()ed memory unless and until we call
87             * malloc() again ourselves because the CRT putenv() function simply puts its
88             * pointer argument into the environ[] arrary (it doesn't make a copy of it)
89             * so this memory must otherwise be leaked.
90             */
91              
92             #undef getenv
93             #undef putenv
94             # ifdef UNDER_CE
95             # define getenv xcegetenv
96             # define putenv xceputenv
97             # endif
98             #undef malloc
99             #undef free
100              
101             static void
102             fix_win32_tzenv(void)
103             {
104             static char* oldenv = NULL;
105             char* newenv;
106             const char* perl_tz_env = win32_getenv("TZ");
107             const char* crt_tz_env = getenv("TZ");
108             if (perl_tz_env == NULL)
109             perl_tz_env = "";
110             if (crt_tz_env == NULL)
111             crt_tz_env = "";
112             if (strcmp(perl_tz_env, crt_tz_env) != 0) {
113             STRLEN perl_tz_env_len = strlen(perl_tz_env);
114             newenv = (char*)malloc(perl_tz_env_len + 4);
115             if (newenv != NULL) {
116             /* putenv with old MS CRTs will cause a double free internally if you delete
117             an env var with the CRT env that doesn't exist in Win32 env (perl %ENV only
118             modifies the Win32 env, not CRT env), so always create the env var in Win32
119             env before deleting it with CRT env api, so the error branch never executes
120             in __crtsetenv after SetEnvironmentVariableA executes inside __crtsetenv.
121              
122             VC 9/2008 and up dont have this bug, older VC (msvcrt80.dll and older) and
123             mingw (msvcrt.dll) have it see [perl #125529]
124             */
125             #if !(_MSC_VER >= 1500)
126             if(!perl_tz_env_len)
127             SetEnvironmentVariableA("TZ", "");
128             #endif
129             sprintf(newenv, "TZ=%s", perl_tz_env);
130             putenv(newenv);
131             if (oldenv != NULL)
132             free(oldenv);
133             oldenv = newenv;
134             }
135             }
136             }
137              
138             #endif
139              
140             /*
141             * my_tzset - wrapper to tzset() with a fix to make it work (better) on Win32.
142             * This code is duplicated in the POSIX module, so any changes made here
143             * should be made there too.
144             */
145             static void
146 314           my_tzset(pTHX)
147             {
148             #ifdef WIN32
149             #if defined(USE_ITHREADS) && defined(PERL_IMPLICIT_SYS)
150             if (PL_curinterp == aTHX)
151             #endif
152             fix_win32_tzenv();
153             #endif
154 314           tzset();
155 314           }
156              
157             /*
158             * my_mini_mktime - normalise struct tm values without the localtime()
159             * semantics (and overhead) of mktime(). Stolen shamelessly from Perl's
160             * Perl_mini_mktime() in util.c - for details on the algorithm, see that
161             * file.
162             */
163             static void
164 102           my_mini_mktime(struct tm *ptm)
165             {
166             int yearday;
167             int secs;
168             int month, mday, year, jday;
169             int odd_cent, odd_year;
170              
171 102           year = 1900 + ptm->tm_year;
172 102           month = ptm->tm_mon;
173 102           mday = ptm->tm_mday;
174             /* allow given yday with no month & mday to dominate the result */
175 102 50         if (ptm->tm_yday >= 0 && mday <= 0 && month <= 0) {
    100          
    50          
176 1           month = 0;
177 1           mday = 0;
178 1           jday = 1 + ptm->tm_yday;
179             }
180             else {
181 101           jday = 0;
182             }
183 102 100         if (month >= 2)
184 49           month+=2;
185             else
186 53           month+=14, year--;
187              
188 102           yearday = DAYS_PER_YEAR * year + year/4 - year/100 + year/400;
189 102           yearday += month*MONTH_TO_DAYS + mday + jday;
190             /*
191             * Note that we don't know when leap-seconds were or will be,
192             * so we have to trust the user if we get something which looks
193             * like a sensible leap-second. Wild values for seconds will
194             * be rationalised, however.
195             */
196 102 50         if ((unsigned) ptm->tm_sec <= 60) {
197 102           secs = 0;
198             }
199             else {
200 0           secs = ptm->tm_sec;
201 0           ptm->tm_sec = 0;
202             }
203 102           secs += 60 * ptm->tm_min;
204 102           secs += SECS_PER_HOUR * ptm->tm_hour;
205 102 50         if (secs < 0) {
206 0 0         if (secs-(secs/SECS_PER_DAY*SECS_PER_DAY) < 0) {
207             /* got negative remainder, but need positive time */
208             /* back off an extra day to compensate */
209 0           yearday += (secs/SECS_PER_DAY)-1;
210 0           secs -= SECS_PER_DAY * (secs/SECS_PER_DAY - 1);
211             }
212             else {
213 0           yearday += (secs/SECS_PER_DAY);
214 0           secs -= SECS_PER_DAY * (secs/SECS_PER_DAY);
215             }
216             }
217 102 50         else if (secs >= SECS_PER_DAY) {
218 0           yearday += (secs/SECS_PER_DAY);
219 0           secs %= SECS_PER_DAY;
220             }
221 102           ptm->tm_hour = secs/SECS_PER_HOUR;
222 102           secs %= SECS_PER_HOUR;
223 102           ptm->tm_min = secs/60;
224 102           secs %= 60;
225 102           ptm->tm_sec += secs;
226             /* done with time of day effects */
227             /*
228             * The algorithm for yearday has (so far) left it high by 428.
229             * To avoid mistaking a legitimate Feb 29 as Mar 1, we need to
230             * bias it by 123 while trying to figure out what year it
231             * really represents. Even with this tweak, the reverse
232             * translation fails for years before A.D. 0001.
233             * It would still fail for Feb 29, but we catch that one below.
234             */
235 102           jday = yearday; /* save for later fixup vis-a-vis Jan 1 */
236 102           yearday -= YEAR_ADJUST;
237 102           year = (yearday / DAYS_PER_QCENT) * 400;
238 102           yearday %= DAYS_PER_QCENT;
239 102           odd_cent = yearday / DAYS_PER_CENT;
240 102           year += odd_cent * 100;
241 102           yearday %= DAYS_PER_CENT;
242 102           year += (yearday / DAYS_PER_QYEAR) * 4;
243 102           yearday %= DAYS_PER_QYEAR;
244 102           odd_year = yearday / DAYS_PER_YEAR;
245 102           year += odd_year;
246 102           yearday %= DAYS_PER_YEAR;
247 102 100         if (!yearday && (odd_cent==4 || odd_year==4)) { /* catch Feb 29 */
    100          
    50          
248 1           month = 1;
249 1           yearday = 29;
250             }
251             else {
252 101           yearday += YEAR_ADJUST; /* recover March 1st crock */
253 101           month = yearday*DAYS_TO_MONTH;
254 101           yearday -= month*MONTH_TO_DAYS;
255             /* recover other leap-year adjustment */
256 101 100         if (month > 13) {
257 50           month-=14;
258 50           year++;
259             }
260             else {
261 51           month-=2;
262             }
263             }
264 102           ptm->tm_year = year - 1900;
265 102 50         if (yearday) {
266 102           ptm->tm_mday = yearday;
267 102           ptm->tm_mon = month;
268             }
269             else {
270 0           ptm->tm_mday = 31;
271 0           ptm->tm_mon = month - 1;
272             }
273             /* re-build yearday based on Jan 1 to get tm_yday */
274 102           year--;
275 102           yearday = year*DAYS_PER_YEAR + year/4 - year/100 + year/400;
276 102           yearday += 14*MONTH_TO_DAYS + 1;
277 102           ptm->tm_yday = jday - yearday;
278             /* fix tm_wday if not overridden by caller */
279 102           ptm->tm_wday = (jday + WEEKDAY_BIAS) % 7;
280 102           }
281              
282             # if defined(WIN32) || (defined(__QNX__) && defined(__WATCOMC__))
283             # define strncasecmp(x,y,n) strnicmp(x,y,n)
284             # endif
285              
286             /* strptime.c 0.1 (Powerdog) 94/03/27 */
287             /* strptime copied from freebsd with the following copyright: */
288             /*
289             * Copyright (c) 1994 Powerdog Industries. All rights reserved.
290             *
291             * Redistribution and use in source and binary forms, with or without
292             * modification, are permitted provided that the following conditions
293             * are met:
294             *
295             * 1. Redistributions of source code must retain the above copyright
296             * notice, this list of conditions and the following disclaimer.
297             *
298             * 2. Redistributions in binary form must reproduce the above copyright
299             * notice, this list of conditions and the following disclaimer
300             * in the documentation and/or other materials provided with the
301             * distribution.
302             *
303             * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
304             * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
305             * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
306             * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
307             * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
308             * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
309             * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
310             * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
311             * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
312             * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
313             * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
314             *
315             * The views and conclusions contained in the software and documentation
316             * are those of the authors and should not be interpreted as representing
317             * official policies, either expressed or implied, of Powerdog Industries.
318             */
319              
320             #include
321             #include
322             #include
323             static char * _strptime(pTHX_ const char *, const char *, struct tm *,
324             int *got_GMT);
325              
326             #define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
327              
328             struct lc_time_T {
329             char * mon[12];
330             char * month[12];
331             char * wday[7];
332             char * weekday[7];
333             char * am;
334             char * pm;
335             char * AM;
336             char * PM;
337             char * alt_month[12];
338             };
339              
340              
341             static struct lc_time_T _C_time_locale;
342              
343             #define Locale (&_C_time_locale)
344              
345             static char *
346 92           _strptime(pTHX_ const char *buf, const char *fmt, struct tm *tm, int *got_GMT)
347             {
348             char c;
349             const char *ptr;
350             int i;
351             size_t len;
352             int Ealternative, Oalternative;
353              
354             /* There seems to be a slightly improved version at
355             * http://www.opensource.apple.com/source/Libc/Libc-583/stdtime/strptime-fbsd.c
356             * which we may end up borrowing more from
357             */
358 92           ptr = fmt;
359 831 100         while (*ptr != 0) {
360 739 50         if (*buf == 0)
361 0           break;
362              
363 739           c = *ptr++;
364            
365 739 100         if (c != '%') {
366 347 100         if (isspace((unsigned char)c))
367 324 50         while (*buf != 0 && isspace((unsigned char)*buf))
    100          
368 168           buf++;
369 191 50         else if (c != *buf++)
370 0           return 0;
371 347           continue;
372             }
373              
374 392           Ealternative = 0;
375 392           Oalternative = 0;
376             label:
377 392           c = *ptr++;
378 392           switch (c) {
379             case 0:
380             case '%':
381 0 0         if (*buf++ != '%')
382 0           return 0;
383 0           break;
384              
385             case '+':
386 0           buf = _strptime(aTHX_ buf, "%c", tm, got_GMT);
387 0 0         if (buf == 0)
388 0           return 0;
389 0           break;
390              
391             case 'C':
392 0 0         if (!isdigit((unsigned char)*buf))
393 0           return 0;
394              
395             /* XXX This will break for 3-digit centuries. */
396 0           len = 2;
397 0 0         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
    0          
    0          
398 0           i *= 10;
399 0           i += *buf - '0';
400 0           len--;
401             }
402 0 0         if (i < 19)
403 0           return 0;
404              
405 0           tm->tm_year = i * 100 - 1900;
406 0           break;
407              
408             case 'c':
409             /* NOTE: c_fmt is intentionally ignored */
410              
411 0           buf = _strptime(aTHX_ buf, "%a %d %b %Y %I:%M:%S %p %Z", tm, got_GMT);
412 0 0         if (buf == 0)
413 0           return 0;
414 0           break;
415              
416             case 'D':
417 0           buf = _strptime(aTHX_ buf, "%m/%d/%y", tm, got_GMT);
418 0 0         if (buf == 0)
419 0           return 0;
420 0           break;
421              
422             case 'E':
423 0 0         if (Ealternative || Oalternative)
    0          
424             break;
425 0           Ealternative++;
426 0           goto label;
427              
428             case 'O':
429 0 0         if (Ealternative || Oalternative)
    0          
430             break;
431 0           Oalternative++;
432 0           goto label;
433              
434             case 'F':
435 6           buf = _strptime(aTHX_ buf, "%Y-%m-%d", tm, got_GMT);
436 6 50         if (buf == 0)
437 0           return 0;
438 6           break;
439              
440             case 'R':
441 0           buf = _strptime(aTHX_ buf, "%H:%M", tm, got_GMT);
442 0 0         if (buf == 0)
443 0           return 0;
444 0           break;
445              
446             case 'r':
447 0           buf = _strptime(aTHX_ buf, "%I:%M:%S %p", tm, got_GMT);
448 0 0         if (buf == 0)
449 0           return 0;
450 0           break;
451              
452             case 'n': /* whitespace */
453             case 't':
454 0 0         if (!isspace((unsigned char)*buf))
455 0           return 0;
456 0 0         while (isspace((unsigned char)*buf))
457 0           buf++;
458 0           break;
459            
460             case 'T':
461 12           buf = _strptime(aTHX_ buf, "%H:%M:%S", tm, got_GMT);
462 12 50         if (buf == 0)
463 0           return 0;
464 12           break;
465              
466             case 'X':
467 0           buf = _strptime(aTHX_ buf, "%I:%M:%S %p", tm, got_GMT);
468 0 0         if (buf == 0)
469 0           return 0;
470 0           break;
471              
472             case 'x':
473 6           buf = _strptime(aTHX_ buf, "%a %d %b %Y", tm, got_GMT);
474 6 50         if (buf == 0)
475 0           return 0;
476 6           break;
477              
478             case 'j':
479 1 50         if (!isdigit((unsigned char)*buf))
480 0           return 0;
481              
482 1           len = 3;
483 4 100         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
    50          
    50          
484 3           i *= 10;
485 3           i += *buf - '0';
486 3           len--;
487             }
488 1 50         if (i < 1 || i > 366)
    50          
489 0           return 0;
490              
491 1           tm->tm_yday = i - 1;
492 1           tm->tm_mday = 0;
493 1           break;
494              
495             case 'M':
496             case 'S':
497 89 50         if (*buf == 0 || isspace((unsigned char)*buf))
    50          
498             break;
499              
500 89 50         if (!isdigit((unsigned char)*buf))
501 0           return 0;
502              
503 89           len = 2;
504 267 100         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
    50          
    50          
505 178           i *= 10;
506 178           i += *buf - '0';
507 178           len--;
508             }
509              
510 89 100         if (c == 'M') {
511 46 50         if (i > 59)
512 0           return 0;
513 46           tm->tm_min = i;
514             } else {
515 43 50         if (i > 60)
516 0           return 0;
517 43           tm->tm_sec = i;
518             }
519              
520 89 100         if (*buf != 0 && isspace((unsigned char)*buf))
    100          
521 18 50         while (*ptr != 0 && !isspace((unsigned char)*ptr))
    50          
522 0           ptr++;
523 89           break;
524              
525             case 'H':
526             case 'I':
527             case 'k':
528             case 'l':
529             /*
530             * Of these, %l is the only specifier explicitly
531             * documented as not being zero-padded. However,
532             * there is no harm in allowing zero-padding.
533             *
534             * XXX The %l specifier may gobble one too many
535             * digits if used incorrectly.
536             */
537 53 50         if (!isdigit((unsigned char)*buf))
538 0           return 0;
539              
540 53           len = 2;
541 156 100         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
    100          
    50          
542 103           i *= 10;
543 103           i += *buf - '0';
544 103           len--;
545             }
546 53 100         if (c == 'H' || c == 'k') {
    50          
547 35 50         if (i > 23)
548 0           return 0;
549 18 50         } else if (i > 12)
550 0           return 0;
551              
552 53           tm->tm_hour = i;
553              
554 53 100         if (*buf != 0 && isspace((unsigned char)*buf))
    50          
555 0 0         while (*ptr != 0 && !isspace((unsigned char)*ptr))
    0          
556 0           ptr++;
557 53           break;
558              
559             case 'p':
560             case 'P':
561             /*
562             * XXX This is bogus if parsed before hour-related
563             * specifiers.
564             */
565 18           len = strlen(Locale->am);
566 18 100         if (strncasecmp(buf, Locale->am, len) == 0 ||
    50          
567 12           strncasecmp(buf, Locale->AM, len) == 0) {
568 6 50         if (tm->tm_hour > 12)
569 0           return 0;
570 6 50         if (tm->tm_hour == 12)
571 6           tm->tm_hour = 0;
572 6           buf += len;
573 6           break;
574             }
575              
576 12           len = strlen(Locale->pm);
577 12 50         if (strncasecmp(buf, Locale->pm, len) == 0 ||
    0          
578 0           strncasecmp(buf, Locale->PM, len) == 0) {
579 12 50         if (tm->tm_hour > 12)
580 0           return 0;
581 12 100         if (tm->tm_hour != 12)
582 6           tm->tm_hour += 12;
583 12           buf += len;
584 12           break;
585             }
586              
587 0           return 0;
588              
589             case 'A':
590             case 'a':
591 112 50         for (i = 0; i < (int)asizeof(Locale->weekday); i++) {
592 112 100         if (c == 'A') {
593 28           len = strlen(Locale->weekday[i]);
594 28 100         if (strncasecmp(buf,
595 28           Locale->weekday[i],
596             len) == 0)
597 6           break;
598             } else {
599 84           len = strlen(Locale->wday[i]);
600 84 100         if (strncasecmp(buf,
601 84           Locale->wday[i],
602             len) == 0)
603 18           break;
604             }
605             }
606 24 50         if (i == (int)asizeof(Locale->weekday))
607 0           return 0;
608              
609 24           tm->tm_wday = i;
610 24           buf += len;
611 24           break;
612              
613             case 'U':
614             case 'V':
615             case 'W':
616             /*
617             * XXX This is bogus, as we can not assume any valid
618             * information present in the tm structure at this
619             * point to calculate a real value, so just check the
620             * range for now.
621             */
622 0 0         if (!isdigit((unsigned char)*buf))
623 0           return 0;
624              
625 0           len = 2;
626 0 0         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
    0          
    0          
627 0           i *= 10;
628 0           i += *buf - '0';
629 0           len--;
630             }
631 0 0         if (i > 53)
632 0           return 0;
633              
634 0 0         if (*buf != 0 && isspace((unsigned char)*buf))
    0          
635 0 0         while (*ptr != 0 && !isspace((unsigned char)*ptr))
    0          
636 0           ptr++;
637 0           break;
638              
639             case 'u':
640             case 'w':
641 0 0         if (!isdigit((unsigned char)*buf))
642 0           return 0;
643              
644 0           i = *buf - '0';
645 0 0         if (i > 6 + (c == 'u'))
    0          
646 0           return 0;
647 0 0         if (i == 7)
648 0           i = 0;
649              
650 0           tm->tm_wday = i;
651              
652 0           buf++;
653 0 0         if (*buf != 0 && isspace((unsigned char)*buf))
    0          
654 0 0         while (*ptr != 0 && !isspace((unsigned char)*ptr))
    0          
655 0           ptr++;
656 0           break;
657              
658             case 'd':
659             case 'e':
660             /*
661             * The %e specifier is explicitly documented as not
662             * being zero-padded but there is no harm in allowing
663             * such padding.
664             *
665             * XXX The %e specifier may gobble one too many
666             * digits if used incorrectly.
667             */
668 58 50         if (!isdigit((unsigned char)*buf))
669 0           return 0;
670              
671 58           len = 2;
672 162 100         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
    50          
    100          
673 104           i *= 10;
674 104           i += *buf - '0';
675 104           len--;
676             }
677 58 50         if (i > 31)
678 0           return 0;
679              
680 58           tm->tm_mday = i;
681              
682 58 100         if (*buf != 0 && isspace((unsigned char)*buf))
    50          
683 53 100         while (*ptr != 0 && !isspace((unsigned char)*ptr))
    50          
684 0           ptr++;
685 58           break;
686              
687             case 'B':
688             case 'b':
689             case 'h':
690 48 50         for (i = 0; i < (int)asizeof(Locale->month); i++) {
691 48 50         if (Oalternative) {
692 0 0         if (c == 'B') {
693 0           len = strlen(Locale->alt_month[i]);
694 0 0         if (strncasecmp(buf,
695 0           Locale->alt_month[i],
696             len) == 0)
697 0           break;
698             }
699             } else {
700 48 100         if (c == 'B') {
701 12           len = strlen(Locale->month[i]);
702 12 100         if (strncasecmp(buf,
703 12           Locale->month[i],
704             len) == 0)
705 6           break;
706             } else {
707 36           len = strlen(Locale->mon[i]);
708 36 100         if (strncasecmp(buf,
709 36           Locale->mon[i],
710             len) == 0)
711 18           break;
712             }
713             }
714             }
715 24 50         if (i == (int)asizeof(Locale->month))
716 0           return 0;
717              
718 24           tm->tm_mon = i;
719 24           buf += len;
720 24           break;
721              
722             case 'm':
723 34 50         if (!isdigit((unsigned char)*buf))
724 0           return 0;
725              
726 34           len = 2;
727 102 100         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
    50          
    50          
728 68           i *= 10;
729 68           i += *buf - '0';
730 68           len--;
731             }
732 34 50         if (i < 1 || i > 12)
    50          
733 0           return 0;
734              
735 34           tm->tm_mon = i - 1;
736              
737 34 50         if (*buf != 0 && isspace((unsigned char)*buf))
    100          
738 1 50         while (*ptr != 0 && !isspace((unsigned char)*ptr))
    50          
739 0           ptr++;
740 34           break;
741              
742             case 's':
743             {
744             char *cp;
745             int sverrno;
746             long n;
747             time_t t;
748             struct tm mytm;
749              
750 6           sverrno = errno;
751 6           errno = 0;
752 6           n = strtol(buf, &cp, 10);
753 6 50         if (errno == ERANGE || (long)(t = n) != n) {
    50          
754 0           errno = sverrno;
755 0           return 0;
756             }
757 6           errno = sverrno;
758 6           buf = cp;
759 6           memset(&mytm, 0, sizeof(mytm));
760              
761 6 100         if(*got_GMT == 1)
762 3           mytm = *localtime(&t);
763             else
764 3           mytm = *gmtime(&t);
765              
766 6           tm->tm_sec = mytm.tm_sec;
767 6           tm->tm_min = mytm.tm_min;
768 6           tm->tm_hour = mytm.tm_hour;
769 6           tm->tm_mday = mytm.tm_mday;
770 6           tm->tm_mon = mytm.tm_mon;
771 6           tm->tm_year = mytm.tm_year;
772 6           tm->tm_wday = mytm.tm_wday;
773 6           tm->tm_yday = mytm.tm_yday;
774 6           tm->tm_isdst = mytm.tm_isdst;
775             }
776 6           break;
777              
778             case 'Y':
779             case 'y':
780 61 50         if (*buf == 0 || isspace((unsigned char)*buf))
    50          
781             break;
782              
783 61 50         if (!isdigit((unsigned char)*buf))
784 0           return 0;
785              
786 61 100         len = (c == 'Y') ? 4 : 2;
787 303 100         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
    50          
    50          
788 242           i *= 10;
789 242           i += *buf - '0';
790 242           len--;
791             }
792 61 100         if (c == 'Y')
793 60           i -= 1900;
794 61 100         if (c == 'y' && i < 69)
    50          
795 1           i += 100;
796 61 50         if (i < 0)
797 0           return 0;
798              
799 61           tm->tm_year = i;
800              
801 61 100         if (*buf != 0 && isspace((unsigned char)*buf))
    100          
802 24 100         while (*ptr != 0 && !isspace((unsigned char)*ptr))
    50          
803 0           ptr++;
804 61           break;
805              
806             case 'Z':
807             {
808             const char *cp;
809             char *zonestr;
810              
811 0 0         for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp)
    0          
812             {/*empty*/}
813 0 0         if (cp - buf) {
814 0           zonestr = (char *)malloc((size_t) (cp - buf + 1));
815 0 0         if (!zonestr) {
816 0           errno = ENOMEM;
817 0           return 0;
818             }
819 0           strncpy(zonestr, buf,(size_t) (cp - buf));
820 0           zonestr[cp - buf] = '\0';
821 0           my_tzset(aTHX);
822 0 0         if (0 == strcmp(zonestr, "GMT")) {
823 0           *got_GMT = 1;
824             }
825 0           free(zonestr);
826 0 0         if (!*got_GMT) return 0;
827 0           buf += cp - buf;
828             }
829             }
830 0           break;
831              
832             case 'z':
833             {
834 0           int sign = 1;
835              
836 0 0         if (*buf != '+') {
837 0 0         if (*buf == '-')
838 0           sign = -1;
839             else
840 0           return 0;
841             }
842              
843 0           buf++;
844 0           i = 0;
845 0 0         for (len = 4; len > 0; len--) {
846 0 0         if (isdigit((int)*buf)) {
847 0           i *= 10;
848 0           i += *buf - '0';
849 0           buf++;
850             } else
851 0           return 0;
852             }
853              
854 0           tm->tm_hour -= sign * (i / 100);
855 0           tm->tm_min -= sign * (i % 100);
856 0           *got_GMT = 1;
857             }
858 0           break;
859             }
860             }
861 92           return (char *)buf;
862             }
863              
864             /* Saves alot of machine code.
865             Takes a (auto) SP, which may or may not have been PUSHed before, puts
866             tm struct members on Perl stack, then returns new, advanced, SP to caller.
867             Assign the return of push_common_tm to your SP, so you can continue to PUSH
868             or do a PUTBACK and return eventually.
869             !!!! push_common_tm does not touch PL_stack_sp !!!!
870             !!!! do not use PUTBACK then SPAGAIN semantics around push_common_tm !!!!
871             !!!! You must mortalize whatever push_common_tm put on stack yourself to
872             avoid leaking !!!!
873             */
874             static SV **
875 108           push_common_tm(pTHX_ SV ** SP, struct tm *mytm)
876             {
877 108           PUSHs(newSViv(mytm->tm_sec));
878 108           PUSHs(newSViv(mytm->tm_min));
879 108           PUSHs(newSViv(mytm->tm_hour));
880 108           PUSHs(newSViv(mytm->tm_mday));
881 108           PUSHs(newSViv(mytm->tm_mon));
882 108           PUSHs(newSViv(mytm->tm_year));
883 108           PUSHs(newSViv(mytm->tm_wday));
884 108           PUSHs(newSViv(mytm->tm_yday));
885 108           PUSHs(newSViv(mytm->tm_isdst));
886 108           return SP;
887             }
888              
889             /* specialized common end of 2 XSUBs
890             SV ** SP -- pass your (auto) SP, which has not been PUSHed before, but was
891             reset to 0 (PPCODE only or SP -= items or XSprePUSH)
892             tm *mytm -- a tm *, will be proprocessed with my_mini_mktime
893             return -- none, after calling return_11part_tm, you must call "return;"
894             no exceptions
895             */
896             static void
897 102           return_11part_tm(pTHX_ SV ** SP, struct tm *mytm)
898             {
899 102           my_mini_mktime(mytm);
900              
901             /* warn("tm: %d-%d-%d %d:%d:%d\n", mytm->tm_year, mytm->tm_mon, mytm->tm_mday, mytm->tm_hour, mytm->tm_min, mytm->tm_sec); */
902 102 50         EXTEND(SP, 11);
903 102           SP = push_common_tm(aTHX_ SP, mytm);
904             /* epoch */
905 102           PUSHs(newSViv(0));
906             /* islocal */
907 102           PUSHs(newSViv(0));
908 102           PUTBACK;
909             {
910 102           SV ** endsp = SP; /* the SV * under SP needs to be mortaled */
911 102           SP -= (11 - 1); /* subtract 0 based count of SVs to mortal */
912             /* mortal target of SP, then increment before function call
913             so SP is already calculated before next comparison to not stall CPU */
914             do {
915 1122           sv_2mortal(*SP++);
916 1122 100         } while(SP <= endsp);
917             }
918 102           return;
919             }
920              
921              
922 68           static void _populate_C_time_locale(pTHX_ HV* locales )
923             {
924 68           AV* alt_names = (AV *) SvRV( *hv_fetch(locales, "alt_month", 9, 0) );
925 68           AV* long_names = (AV *) SvRV( *hv_fetch(locales, "month", 5, 0) );
926 68           AV* short_names = (AV *) SvRV( *hv_fetch(locales, "mon", 3, 0) );
927             int i;
928              
929 884 100         for (i = 0; i < 1 + (int) av_len( long_names ); i++) {
930 816 50         Locale->alt_month[i] = SvPV_nolen( (SV *) *av_fetch(alt_names, i, 0) );
931 816 50         Locale->month[i] = SvPV_nolen( (SV *) *av_fetch(long_names, i, 0) );
932 816 50         Locale->mon[i] = SvPV_nolen( (SV *) *av_fetch(short_names, i, 0) );
933             }
934              
935 68           long_names = (AV *) SvRV( *hv_fetch(locales, "weekday", 7, 0) );
936 68           short_names = (AV *) SvRV( *hv_fetch(locales, "wday", 4, 0) );
937              
938 544 100         for (i = 0; i < 1 + (int) av_len( long_names ); i++) {
939 476 50         Locale->wday[i] = SvPV_nolen( (SV *) *av_fetch(short_names, i, 0) );
940 476 50         Locale->weekday[i] = SvPV_nolen( (SV *) *av_fetch(long_names, i, 0) );
941             }
942              
943 68 50         Locale->am = SvPV_nolen( (SV *) *hv_fetch(locales, "am", 2, 0) );
944 68 50         Locale->pm = SvPV_nolen( (SV *) *hv_fetch(locales, "pm", 2, 0) );
945 68 50         Locale->AM = SvPV_nolen( (SV *) *hv_fetch(locales, "AM", 2, 0) );
946 68 50         Locale->PM = SvPV_nolen( (SV *) *hv_fetch(locales, "PM", 2, 0) );
947              
948 68           return;
949             }
950              
951             MODULE = Time::Piece PACKAGE = Time::Piece
952              
953             PROTOTYPES: ENABLE
954              
955             void
956             _strftime(fmt, epoch, islocal = 1)
957             char * fmt
958             time_t epoch
959             int islocal
960             CODE:
961             {
962             char tmpbuf[TP_BUF_SIZE];
963             struct tm mytm;
964             size_t len;
965              
966 262 100         if(islocal == 1)
967 116           mytm = *localtime(&epoch);
968             else
969 146           mytm = *gmtime(&epoch);
970              
971 262           len = strftime(tmpbuf, TP_BUF_SIZE, fmt, &mytm);
972             /*
973             ** The following is needed to handle to the situation where
974             ** tmpbuf overflows. Basically we want to allocate a buffer
975             ** and try repeatedly. The reason why it is so complicated
976             ** is that getting a return value of 0 from strftime can indicate
977             ** one of the following:
978             ** 1. buffer overflowed,
979             ** 2. illegal conversion specifier, or
980             ** 3. the format string specifies nothing to be returned(not
981             ** an error). This could be because format is an empty string
982             ** or it specifies %p that yields an empty string in some locale.
983             ** If there is a better way to make it portable, go ahead by
984             ** all means.
985             */
986 262 50         if ((len > 0 && len < TP_BUF_SIZE) || (len == 0 && *fmt == '\0'))
    50          
    0          
    0          
987 262           ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
988             else {
989             /* Possibly buf overflowed - try again with a bigger buf */
990 0           size_t fmtlen = strlen(fmt);
991 0           size_t bufsize = fmtlen + TP_BUF_SIZE;
992             char* buf;
993             size_t buflen;
994              
995 0           New(0, buf, bufsize, char);
996 0 0         while (buf) {
997 0           buflen = strftime(buf, bufsize, fmt, &mytm);
998 0 0         if (buflen > 0 && buflen < bufsize)
    0          
999 0           break;
1000             /* heuristic to prevent out-of-memory errors */
1001 0 0         if (bufsize > 100*fmtlen) {
1002 0           Safefree(buf);
1003 0           buf = NULL;
1004 0           break;
1005             }
1006 0           bufsize *= 2;
1007 0           Renew(buf, bufsize, char);
1008             }
1009 0 0         if (buf) {
1010 0           ST(0) = sv_2mortal(newSVpv(buf, buflen));
1011 0           Safefree(buf);
1012             }
1013             else
1014 0           ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
1015             }
1016             }
1017              
1018             void
1019             _tzset()
1020             PPCODE:
1021 314           PUTBACK; /* makes rest of this function tailcall friendly */
1022 314           my_tzset(aTHX);
1023 314           return; /* skip XSUBPP's PUTBACK */
1024              
1025             void
1026             _strptime ( string, format, got_GMT, SV* localization )
1027             char * string
1028             char * format
1029             int got_GMT
1030             PREINIT:
1031             struct tm mytm;
1032             char * remainder;
1033             HV * locales;
1034             PPCODE:
1035 68           memset(&mytm, 0, sizeof(mytm));
1036              
1037             /* sensible defaults. */
1038 68           mytm.tm_mday = 1;
1039 68           mytm.tm_year = 70;
1040 68           mytm.tm_wday = 4;
1041 68           mytm.tm_isdst = -1; /* -1 means we don't know */
1042              
1043 68 50         if( SvTYPE(SvRV( localization )) == SVt_PVHV ){
1044 68           locales = (HV *)SvRV(localization);
1045             }
1046             else{
1047 0           croak("_strptime requires a Hash Reference of locales");
1048             }
1049              
1050             /* populate our locale data struct (used for %[AaBbPp] flags) */
1051 68           _populate_C_time_locale(aTHX_ locales );
1052              
1053 68           remainder = (char *)_strptime(aTHX_ string, format, &mytm, &got_GMT);
1054 68 50         if (remainder == NULL) {
1055 0           croak("Error parsing time");
1056             }
1057 68 50         if (*remainder != '\0') {
1058 0           warn("Garbage at end of string in strptime: %s", remainder);
1059 0           warn("Perhaps a format flag did not match the actual input?");
1060             }
1061              
1062 68           return_11part_tm(aTHX_ SP, &mytm);
1063 68           return;
1064              
1065             void
1066             _mini_mktime(int sec, int min, int hour, int mday, int mon, int year)
1067             PREINIT:
1068             struct tm mytm;
1069             time_t t;
1070             PPCODE:
1071 34           t = 0;
1072 34           mytm = *gmtime(&t);
1073              
1074 34           mytm.tm_sec = sec;
1075 34           mytm.tm_min = min;
1076 34           mytm.tm_hour = hour;
1077 34           mytm.tm_mday = mday;
1078 34           mytm.tm_mon = mon;
1079 34           mytm.tm_year = year;
1080              
1081 34           return_11part_tm(aTHX_ SP, &mytm);
1082 34           return;
1083              
1084             void
1085             _crt_localtime(time_t sec)
1086             ALIAS:
1087             _crt_gmtime = 1
1088             PREINIT:
1089             struct tm mytm;
1090             PPCODE:
1091 6 100         if(ix) mytm = *gmtime(&sec);
1092 3           else mytm = *localtime(&sec);
1093             /* Need to get: $s,$n,$h,$d,$m,$y */
1094              
1095 6 50         EXTEND(SP, 10);
1096 6           SP = push_common_tm(aTHX_ SP, &mytm);
1097 6           PUSHs(newSViv(mytm.tm_isdst));
1098 6           PUTBACK;
1099             {
1100 6           SV ** endsp = SP; /* the SV * under SP needs to be mortaled */
1101 6           SP -= (10 - 1); /* subtract 0 based count of SVs to mortal */
1102             /* mortal target of SP, then increment before function call
1103             so SP is already calculated before next comparison to not stall CPU */
1104             do {
1105 60           sv_2mortal(*SP++);
1106 60 100         } while(SP <= endsp);
1107             }
1108 6           return;
1109              
1110             SV*
1111             _get_localization()
1112             INIT:
1113 1           HV* locales = newHV();
1114 1           AV* wdays = newAV();
1115 1           AV* weekdays = newAV();
1116 1           AV* mons = newAV();
1117 1           AV* months = newAV();
1118             SV** tmp;
1119             size_t len;
1120             char buf[TP_BUF_SIZE];
1121             size_t i;
1122 1           time_t t = 1325386800; /*1325386800 = Sun, 01 Jan 2012 03:00:00 GMT*/
1123 1           struct tm mytm = *gmtime(&t);
1124             CODE:
1125              
1126 8 100         for(i = 0; i < 7; ++i){
1127              
1128 7           len = strftime(buf, TP_BUF_SIZE, "%a", &mytm);
1129 7           av_push(wdays, (SV *) newSVpvn(buf, len));
1130              
1131 7           len = strftime(buf, TP_BUF_SIZE, "%A", &mytm);
1132 7           av_push(weekdays, (SV *) newSVpvn(buf, len));
1133              
1134 7           ++mytm.tm_wday;
1135             }
1136              
1137 13 100         for(i = 0; i < 12; ++i){
1138              
1139 12           len = strftime(buf, TP_BUF_SIZE, "%b", &mytm);
1140 12           av_push(mons, (SV *) newSVpvn(buf, len));
1141              
1142 12           len = strftime(buf, TP_BUF_SIZE, "%B", &mytm);
1143 12           av_push(months, (SV *) newSVpvn(buf, len));
1144              
1145 12           ++mytm.tm_mon;
1146             }
1147              
1148 1           tmp = hv_store(locales, "wday", 4, newRV_noinc((SV *) wdays), 0);
1149 1           tmp = hv_store(locales, "weekday", 7, newRV_noinc((SV *) weekdays), 0);
1150 1           tmp = hv_store(locales, "mon", 3, newRV_noinc((SV *) mons), 0);
1151 1           tmp = hv_store(locales, "month", 5, newRV_noinc((SV *) months), 0);
1152 1           tmp = hv_store(locales, "alt_month", 9, newRV((SV *) months), 0);
1153              
1154 1           len = strftime(buf, TP_BUF_SIZE, "%p", &mytm);
1155 1           tmp = hv_store(locales, "AM", 2, newSVpvn(buf,len), 0);
1156 1           mytm.tm_hour = 18;
1157 1           len = strftime(buf, TP_BUF_SIZE, "%p", &mytm);
1158 1           tmp = hv_store(locales, "PM", 2, newSVpvn(buf,len), 0);
1159              
1160 1 50         if(tmp == NULL || !SvOK( (SV *) *tmp)){
    50          
    0          
    0          
1161 0           croak("Failed to get localization.");
1162             }
1163              
1164 1           RETVAL = newRV_noinc((SV *)locales);
1165             OUTPUT:
1166             RETVAL