File Coverage

Piece.xs
Criterion Covered Total %
statement 232 517 44.8
branch 129 384 33.5
condition n/a
subroutine n/a
pod n/a
total 361 901 40.0


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 47           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 47           tzset();
155 47           }
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 27           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 27           year = 1900 + ptm->tm_year;
172 27           month = ptm->tm_mon;
173 27           mday = ptm->tm_mday;
174             /* allow given yday with no month & mday to dominate the result */
175 27 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 26           jday = 0;
182             }
183 27 100         if (month >= 2)
184 14           month+=2;
185             else
186 13           month+=14, year--;
187              
188 27           yearday = DAYS_PER_YEAR * year + year/4 - year/100 + year/400;
189 27           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 27 50         if ((unsigned) ptm->tm_sec <= 60) {
197 27           secs = 0;
198             }
199             else {
200 0           secs = ptm->tm_sec;
201 0           ptm->tm_sec = 0;
202             }
203 27           secs += 60 * ptm->tm_min;
204 27           secs += SECS_PER_HOUR * ptm->tm_hour;
205 27 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 27 50         else if (secs >= SECS_PER_DAY) {
218 0           yearday += (secs/SECS_PER_DAY);
219 0           secs %= SECS_PER_DAY;
220             }
221 27           ptm->tm_hour = secs/SECS_PER_HOUR;
222 27           secs %= SECS_PER_HOUR;
223 27           ptm->tm_min = secs/60;
224 27           secs %= 60;
225 27           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 27           jday = yearday; /* save for later fixup vis-a-vis Jan 1 */
236 27           yearday -= YEAR_ADJUST;
237 27           year = (yearday / DAYS_PER_QCENT) * 400;
238 27           yearday %= DAYS_PER_QCENT;
239 27           odd_cent = yearday / DAYS_PER_CENT;
240 27           year += odd_cent * 100;
241 27           yearday %= DAYS_PER_CENT;
242 27           year += (yearday / DAYS_PER_QYEAR) * 4;
243 27           yearday %= DAYS_PER_QYEAR;
244 27           odd_year = yearday / DAYS_PER_YEAR;
245 27           year += odd_year;
246 27           yearday %= DAYS_PER_YEAR;
247 27 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 26           yearday += YEAR_ADJUST; /* recover March 1st crock */
253 26           month = yearday*DAYS_TO_MONTH;
254 26           yearday -= month*MONTH_TO_DAYS;
255             /* recover other leap-year adjustment */
256 26 100         if (month > 13) {
257 10           month-=14;
258 10           year++;
259             }
260             else {
261 16           month-=2;
262             }
263             }
264 27           ptm->tm_year = year - 1900;
265 27 50         if (yearday) {
266 27           ptm->tm_mday = yearday;
267 27           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 27           year--;
275 27           yearday = year*DAYS_PER_YEAR + year/4 - year/100 + year/400;
276 27           yearday += 14*MONTH_TO_DAYS + 1;
277 27           ptm->tm_yday = jday - yearday;
278             /* fix tm_wday if not overridden by caller */
279 27           ptm->tm_wday = (jday + WEEKDAY_BIAS) % 7;
280 27           }
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 20           _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 20           ptr = fmt;
359 135 100         while (*ptr != 0) {
360 115 50         if (*buf == 0)
361 0           break;
362              
363 115           c = *ptr++;
364            
365 115 100         if (c != '%') {
366 47 100         if (isspace((unsigned char)c))
367 24 50         while (*buf != 0 && isspace((unsigned char)*buf))
    100          
368 12           buf++;
369 35 50         else if (c != *buf++)
370 0           return 0;
371 47           continue;
372             }
373              
374 68           Ealternative = 0;
375 68           Oalternative = 0;
376             label:
377 68           c = *ptr++;
378 68           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 0           buf = _strptime(aTHX_ buf, "%Y-%m-%d", tm, got_GMT);
436 0 0         if (buf == 0)
437 0           return 0;
438 0           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 0           buf = _strptime(aTHX_ buf, "%H:%M:%S", tm, got_GMT);
462 0 0         if (buf == 0)
463 0           return 0;
464 0           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 0           buf = _strptime(aTHX_ buf, "%a %d %b %Y", tm, got_GMT);
474 0 0         if (buf == 0)
475 0           return 0;
476 0           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 5 50         if (*buf == 0 || isspace((unsigned char)*buf))
    50          
498             break;
499              
500 5 50         if (!isdigit((unsigned char)*buf))
501 0           return 0;
502              
503 5           len = 2;
504 15 100         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
    50          
    50          
505 10           i *= 10;
506 10           i += *buf - '0';
507 10           len--;
508             }
509              
510 5 100         if (c == 'M') {
511 4 50         if (i > 59)
512 0           return 0;
513 4           tm->tm_min = i;
514             } else {
515 1 50         if (i > 60)
516 0           return 0;
517 1           tm->tm_sec = i;
518             }
519              
520 5 100         if (*buf != 0 && isspace((unsigned char)*buf))
    50          
521 0 0         while (*ptr != 0 && !isspace((unsigned char)*ptr))
    0          
522 0           ptr++;
523 5           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 11 50         if (!isdigit((unsigned char)*buf))
538 0           return 0;
539              
540 11           len = 2;
541 30 100         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
    100          
    50          
542 19           i *= 10;
543 19           i += *buf - '0';
544 19           len--;
545             }
546 11 50         if (c == 'H' || c == 'k') {
    0          
547 11 50         if (i > 23)
548 0           return 0;
549 0 0         } else if (i > 12)
550 0           return 0;
551              
552 11           tm->tm_hour = i;
553              
554 11 100         if (*buf != 0 && isspace((unsigned char)*buf))
    50          
555 0 0         while (*ptr != 0 && !isspace((unsigned char)*ptr))
    0          
556 0           ptr++;
557 11           break;
558              
559             case 'p':
560             case 'P':
561             /*
562             * XXX This is bogus if parsed before hour-related
563             * specifiers.
564             */
565 0           len = strlen(Locale->am);
566 0 0         if (strncasecmp(buf, Locale->am, len) == 0 ||
    0          
567 0           strncasecmp(buf, Locale->AM, len) == 0) {
568 0 0         if (tm->tm_hour > 12)
569 0           return 0;
570 0 0         if (tm->tm_hour == 12)
571 0           tm->tm_hour = 0;
572 0           buf += len;
573 0           break;
574             }
575              
576 0           len = strlen(Locale->pm);
577 0 0         if (strncasecmp(buf, Locale->pm, len) == 0 ||
    0          
578 0           strncasecmp(buf, Locale->PM, len) == 0) {
579 0 0         if (tm->tm_hour > 12)
580 0           return 0;
581 0 0         if (tm->tm_hour != 12)
582 0           tm->tm_hour += 12;
583 0           buf += len;
584 0           break;
585             }
586              
587 0           return 0;
588              
589             case 'A':
590             case 'a':
591 0 0         for (i = 0; i < (int)asizeof(Locale->weekday); i++) {
592 0 0         if (c == 'A') {
593 0           len = strlen(Locale->weekday[i]);
594 0 0         if (strncasecmp(buf,
595 0           Locale->weekday[i],
596             len) == 0)
597 0           break;
598             } else {
599 0           len = strlen(Locale->wday[i]);
600 0 0         if (strncasecmp(buf,
601 0           Locale->wday[i],
602             len) == 0)
603 0           break;
604             }
605             }
606 0 0         if (i == (int)asizeof(Locale->weekday))
607 0           return 0;
608              
609 0           tm->tm_wday = i;
610 0           buf += len;
611 0           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 16 50         if (!isdigit((unsigned char)*buf))
669 0           return 0;
670              
671 16           len = 2;
672 48 100         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
    50          
    50          
673 32           i *= 10;
674 32           i += *buf - '0';
675 32           len--;
676             }
677 16 50         if (i > 31)
678 0           return 0;
679              
680 16           tm->tm_mday = i;
681              
682 16 100         if (*buf != 0 && isspace((unsigned char)*buf))
    50          
683 11 50         while (*ptr != 0 && !isspace((unsigned char)*ptr))
    50          
684 0           ptr++;
685 16           break;
686              
687             case 'B':
688             case 'b':
689             case 'h':
690 0 0         for (i = 0; i < (int)asizeof(Locale->month); i++) {
691 0 0         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 0 0         if (c == 'B') {
701 0           len = strlen(Locale->month[i]);
702 0 0         if (strncasecmp(buf,
703 0           Locale->month[i],
704             len) == 0)
705 0           break;
706             } else {
707 0           len = strlen(Locale->mon[i]);
708 0 0         if (strncasecmp(buf,
709 0           Locale->mon[i],
710             len) == 0)
711 0           break;
712             }
713             }
714             }
715 0 0         if (i == (int)asizeof(Locale->month))
716 0           return 0;
717              
718 0           tm->tm_mon = i;
719 0           buf += len;
720 0           break;
721              
722             case 'm':
723 16 50         if (!isdigit((unsigned char)*buf))
724 0           return 0;
725              
726 16           len = 2;
727 48 100         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
    50          
    50          
728 32           i *= 10;
729 32           i += *buf - '0';
730 32           len--;
731             }
732 16 50         if (i < 1 || i > 12)
    50          
733 0           return 0;
734              
735 16           tm->tm_mon = i - 1;
736              
737 16 50         if (*buf != 0 && isspace((unsigned char)*buf))
    100          
738 1 50         while (*ptr != 0 && !isspace((unsigned char)*ptr))
    50          
739 0           ptr++;
740 16           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 0           sverrno = errno;
751 0           errno = 0;
752 0           n = strtol(buf, &cp, 10);
753 0 0         if (errno == ERANGE || (long)(t = n) != n) {
    0          
754 0           errno = sverrno;
755 0           return 0;
756             }
757 0           errno = sverrno;
758 0           buf = cp;
759 0           memset(&mytm, 0, sizeof(mytm));
760              
761 0 0         if(*got_GMT == 1)
762 0           mytm = *localtime(&t);
763             else
764 0           mytm = *gmtime(&t);
765              
766 0           tm->tm_sec = mytm.tm_sec;
767 0           tm->tm_min = mytm.tm_min;
768 0           tm->tm_hour = mytm.tm_hour;
769 0           tm->tm_mday = mytm.tm_mday;
770 0           tm->tm_mon = mytm.tm_mon;
771 0           tm->tm_year = mytm.tm_year;
772 0           tm->tm_wday = mytm.tm_wday;
773 0           tm->tm_yday = mytm.tm_yday;
774 0           tm->tm_isdst = mytm.tm_isdst;
775             }
776 0           break;
777              
778             case 'Y':
779             case 'y':
780 19 50         if (*buf == 0 || isspace((unsigned char)*buf))
    50          
781             break;
782              
783 19 50         if (!isdigit((unsigned char)*buf))
784 0           return 0;
785              
786 19 100         len = (c == 'Y') ? 4 : 2;
787 93 100         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
    50          
    50          
788 74           i *= 10;
789 74           i += *buf - '0';
790 74           len--;
791             }
792 19 100         if (c == 'Y')
793 18           i -= 1900;
794 19 100         if (c == 'y' && i < 69)
    50          
795 1           i += 100;
796 19 50         if (i < 0)
797 0           return 0;
798              
799 19           tm->tm_year = i;
800              
801 19 100         if (*buf != 0 && isspace((unsigned char)*buf))
    50          
802 0 0         while (*ptr != 0 && !isspace((unsigned char)*ptr))
    0          
803 0           ptr++;
804 19           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 20           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 33           push_common_tm(pTHX_ SV ** SP, struct tm *mytm)
876             {
877 33           PUSHs(newSViv(mytm->tm_sec));
878 33           PUSHs(newSViv(mytm->tm_min));
879 33           PUSHs(newSViv(mytm->tm_hour));
880 33           PUSHs(newSViv(mytm->tm_mday));
881 33           PUSHs(newSViv(mytm->tm_mon));
882 33           PUSHs(newSViv(mytm->tm_year));
883 33           PUSHs(newSViv(mytm->tm_wday));
884 33           PUSHs(newSViv(mytm->tm_yday));
885 33           PUSHs(newSViv(mytm->tm_isdst));
886 33           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 27           return_11part_tm(pTHX_ SV ** SP, struct tm *mytm)
898             {
899 27           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 27 50         EXTEND(SP, 11);
903 27           SP = push_common_tm(aTHX_ SP, mytm);
904             /* epoch */
905 27           PUSHs(newSViv(0));
906             /* islocal */
907 27           PUSHs(newSViv(0));
908 27           PUTBACK;
909             {
910 27           SV ** endsp = SP; /* the SV * under SP needs to be mortaled */
911 27           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 297           sv_2mortal(*SP++);
916 297 100         } while(SP <= endsp);
917             }
918 27           return;
919             }
920              
921              
922 20           static void _populate_C_time_locale(pTHX_ HV* locales )
923             {
924 20           AV* alt_names = (AV *) SvRV( *hv_fetch(locales, "alt_month", 9, 0) );
925 20           AV* long_names = (AV *) SvRV( *hv_fetch(locales, "month", 5, 0) );
926 20           AV* short_names = (AV *) SvRV( *hv_fetch(locales, "mon", 3, 0) );
927             int i;
928              
929 260 100         for (i = 0; i < 1 + (int) av_len( long_names ); i++) {
930 240 50         Locale->alt_month[i] = SvPV_nolen( (SV *) *av_fetch(alt_names, i, 0) );
931 240 50         Locale->month[i] = SvPV_nolen( (SV *) *av_fetch(long_names, i, 0) );
932 240 50         Locale->mon[i] = SvPV_nolen( (SV *) *av_fetch(short_names, i, 0) );
933             }
934              
935 20           long_names = (AV *) SvRV( *hv_fetch(locales, "weekday", 7, 0) );
936 20           short_names = (AV *) SvRV( *hv_fetch(locales, "wday", 4, 0) );
937              
938 160 100         for (i = 0; i < 1 + (int) av_len( long_names ); i++) {
939 140 50         Locale->wday[i] = SvPV_nolen( (SV *) *av_fetch(short_names, i, 0) );
940 140 50         Locale->weekday[i] = SvPV_nolen( (SV *) *av_fetch(long_names, i, 0) );
941             }
942              
943 20 50         Locale->am = SvPV_nolen( (SV *) *hv_fetch(locales, "am", 2, 0) );
944 20 50         Locale->pm = SvPV_nolen( (SV *) *hv_fetch(locales, "pm", 2, 0) );
945 20 50         Locale->AM = SvPV_nolen( (SV *) *hv_fetch(locales, "AM", 2, 0) );
946 20 50         Locale->PM = SvPV_nolen( (SV *) *hv_fetch(locales, "PM", 2, 0) );
947              
948 20           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 40 100         if(islocal == 1)
967 5           mytm = *localtime(&epoch);
968             else
969 35           mytm = *gmtime(&epoch);
970              
971 40           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 40 50         if ((len > 0 && len < TP_BUF_SIZE) || (len == 0 && *fmt == '\0'))
    50          
    0          
    0          
987 40           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 47           PUTBACK; /* makes rest of this function tailcall friendly */
1022 47           my_tzset(aTHX);
1023 47           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 20           memset(&mytm, 0, sizeof(mytm));
1036              
1037             /* sensible defaults. */
1038 20           mytm.tm_mday = 1;
1039 20           mytm.tm_year = 70;
1040 20           mytm.tm_wday = 4;
1041 20           mytm.tm_isdst = -1; /* -1 means we don't know */
1042              
1043 20 50         if( SvTYPE(SvRV( localization )) == SVt_PVHV ){
1044 20           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 20           _populate_C_time_locale(aTHX_ locales );
1052              
1053 20           remainder = (char *)_strptime(aTHX_ string, format, &mytm, &got_GMT);
1054 20 50         if (remainder == NULL) {
1055 0           croak("Error parsing time");
1056             }
1057 20 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 20           return_11part_tm(aTHX_ SP, &mytm);
1063 20           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 7           t = 0;
1072 7           mytm = *gmtime(&t);
1073              
1074 7           mytm.tm_sec = sec;
1075 7           mytm.tm_min = min;
1076 7           mytm.tm_hour = hour;
1077 7           mytm.tm_mday = mday;
1078 7           mytm.tm_mon = mon;
1079 7           mytm.tm_year = year;
1080              
1081 7           return_11part_tm(aTHX_ SP, &mytm);
1082 7           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 0           HV* locales = newHV();
1114 0           AV* wdays = newAV();
1115 0           AV* weekdays = newAV();
1116 0           AV* mons = newAV();
1117 0           AV* months = newAV();
1118             SV** tmp;
1119             size_t len;
1120             char buf[TP_BUF_SIZE];
1121             size_t i;
1122 0           time_t t = 1325386800; /*1325386800 = Sun, 01 Jan 2012 03:00:00 GMT*/
1123 0           struct tm mytm = *gmtime(&t);
1124             CODE:
1125              
1126 0 0         for(i = 0; i < 7; ++i){
1127              
1128 0           len = strftime(buf, TP_BUF_SIZE, "%a", &mytm);
1129 0           av_push(wdays, (SV *) newSVpvn(buf, len));
1130              
1131 0           len = strftime(buf, TP_BUF_SIZE, "%A", &mytm);
1132 0           av_push(weekdays, (SV *) newSVpvn(buf, len));
1133              
1134 0           ++mytm.tm_wday;
1135             }
1136              
1137 0 0         for(i = 0; i < 12; ++i){
1138              
1139 0           len = strftime(buf, TP_BUF_SIZE, "%b", &mytm);
1140 0           av_push(mons, (SV *) newSVpvn(buf, len));
1141              
1142 0           len = strftime(buf, TP_BUF_SIZE, "%B", &mytm);
1143 0           av_push(months, (SV *) newSVpvn(buf, len));
1144              
1145 0           ++mytm.tm_mon;
1146             }
1147              
1148 0           tmp = hv_store(locales, "wday", 4, newRV_noinc((SV *) wdays), 0);
1149 0           tmp = hv_store(locales, "weekday", 7, newRV_noinc((SV *) weekdays), 0);
1150 0           tmp = hv_store(locales, "mon", 3, newRV_noinc((SV *) mons), 0);
1151 0           tmp = hv_store(locales, "month", 5, newRV_noinc((SV *) months), 0);
1152 0           tmp = hv_store(locales, "alt_month", 9, newRV((SV *) months), 0);
1153              
1154 0           len = strftime(buf, TP_BUF_SIZE, "%p", &mytm);
1155 0           tmp = hv_store(locales, "AM", 2, newSVpvn(buf,len), 0);
1156 0           mytm.tm_hour = 18;
1157 0           len = strftime(buf, TP_BUF_SIZE, "%p", &mytm);
1158 0           tmp = hv_store(locales, "PM", 2, newSVpvn(buf,len), 0);
1159              
1160 0 0         if(tmp == NULL || !SvOK( (SV *) *tmp)){
    0          
    0          
    0          
1161 0           croak("Failed to get localization.");
1162             }
1163              
1164 0           RETVAL = newRV_noinc((SV *)locales);
1165             OUTPUT:
1166             RETVAL