File Coverage

Piece.xs
Criterion Covered Total %
statement 463 616 75.1
branch 273 476 57.3
condition n/a
subroutine n/a
pod n/a
total 736 1092 67.4


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