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