diff options
author | Josh MacDonald <josh.macdonald@gmail.com> | 2016-02-26 21:43:58 -0800 |
---|---|---|
committer | Josh MacDonald <josh.macdonald@gmail.com> | 2016-02-26 21:43:58 -0800 |
commit | d594fbe514e2a381541a510fe01041e546f56a67 (patch) | |
tree | 316072f34cf7dc94518ec14a8d955d57c20d65b7 /xdelta1/libedsio/partime.c | |
parent | 81aebf78ae67c29f528088d65743643e5355e3d3 (diff) |
Remove xdelta1 from this repo
Diffstat (limited to 'xdelta1/libedsio/partime.c')
-rw-r--r-- | xdelta1/libedsio/partime.c | 742 |
1 files changed, 0 insertions, 742 deletions
diff --git a/xdelta1/libedsio/partime.c b/xdelta1/libedsio/partime.c deleted file mode 100644 index 4cce654..0000000 --- a/xdelta1/libedsio/partime.c +++ /dev/null | |||
@@ -1,742 +0,0 @@ | |||
1 | /* Parse a string, yielding a struct partime that describes it. */ | ||
2 | |||
3 | /* Copyright 1993, 1994, 1995, 1997 Paul Eggert | ||
4 | Distributed under license by the Free Software Foundation, Inc. | ||
5 | |||
6 | This file is part of RCS. | ||
7 | |||
8 | RCS is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2, or (at your option) | ||
11 | any later version. | ||
12 | |||
13 | RCS is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License | ||
19 | along with RCS; see the file COPYING. | ||
20 | If not, write to the Free Software Foundation, | ||
21 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
22 | |||
23 | Report problems and direct all questions to: | ||
24 | |||
25 | rcs-bugs@cs.purdue.edu | ||
26 | |||
27 | */ | ||
28 | |||
29 | #if has_conf_h | ||
30 | # include <conf.h> | ||
31 | #else | ||
32 | # if HAVE_CONFIG_H | ||
33 | # include <config.h> | ||
34 | # else | ||
35 | # ifndef __STDC__ | ||
36 | # define const | ||
37 | # endif | ||
38 | # endif | ||
39 | # if HAVE_LIMITS_H | ||
40 | # include <limits.h> | ||
41 | # endif | ||
42 | # ifndef LONG_MIN | ||
43 | # define LONG_MIN (-1-2147483647L) | ||
44 | # endif | ||
45 | # if STDC_HEADERS | ||
46 | # include <stdlib.h> | ||
47 | # endif | ||
48 | # include <time.h> | ||
49 | # ifdef __STDC__ | ||
50 | # define P(x) x | ||
51 | # else | ||
52 | # define P(x) () | ||
53 | # endif | ||
54 | #endif | ||
55 | |||
56 | #include <ctype.h> | ||
57 | #if STDC_HEADERS | ||
58 | # define CTYPE_DOMAIN(c) 1 | ||
59 | #else | ||
60 | # define CTYPE_DOMAIN(c) ((unsigned) (c) <= 0177) | ||
61 | #endif | ||
62 | #define ISALNUM(c) (CTYPE_DOMAIN (c) && isalnum (c)) | ||
63 | #define ISALPHA(c) (CTYPE_DOMAIN (c) && isalpha (c)) | ||
64 | #define ISSPACE(c) (CTYPE_DOMAIN (c) && isspace (c)) | ||
65 | #define ISUPPER(c) (CTYPE_DOMAIN (c) && isupper (c)) | ||
66 | #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) | ||
67 | |||
68 | #include <partime.h> | ||
69 | |||
70 | char const partimeId[] = | ||
71 | "$Id: partime.c 1.1 Sun, 28 Jan 2007 10:02:26 -0800 jmacd $"; | ||
72 | |||
73 | |||
74 | /* Lookup tables for names of months, weekdays, time zones. */ | ||
75 | |||
76 | #define NAME_LENGTH_MAXIMUM 4 | ||
77 | |||
78 | struct name_val | ||
79 | { | ||
80 | char name[NAME_LENGTH_MAXIMUM]; | ||
81 | int val; | ||
82 | }; | ||
83 | |||
84 | |||
85 | static char const *parse_decimal P ((char const *, int, int, int, int, int *, int *)); | ||
86 | static char const *parse_fixed P ((char const *, int, int *)); | ||
87 | static char const *parse_pattern_letter P ((char const *, int, struct partime *)); | ||
88 | static char const *parse_prefix P ((char const *, struct partime *, int *)); | ||
89 | static char const *parse_ranged P ((char const *, int, int, int, int *)); | ||
90 | static int lookup P ((char const *, struct name_val const[])); | ||
91 | static int merge_partime P ((struct partime *, struct partime const *)); | ||
92 | static void undefine P ((struct partime *)); | ||
93 | |||
94 | |||
95 | static struct name_val const month_names[] = | ||
96 | { | ||
97 | {"jan", 0}, | ||
98 | {"feb", 1}, | ||
99 | {"mar", 2}, | ||
100 | {"apr", 3}, | ||
101 | {"may", 4}, | ||
102 | {"jun", 5}, | ||
103 | {"jul", 6}, | ||
104 | {"aug", 7}, | ||
105 | {"sep", 8}, | ||
106 | {"oct", 9}, | ||
107 | {"nov", 10}, | ||
108 | {"dec", 11}, | ||
109 | {"", TM_UNDEFINED} | ||
110 | }; | ||
111 | |||
112 | static struct name_val const weekday_names[] = | ||
113 | { | ||
114 | {"sun", 0}, | ||
115 | {"mon", 1}, | ||
116 | {"tue", 2}, | ||
117 | {"wed", 3}, | ||
118 | {"thu", 4}, | ||
119 | {"fri", 5}, | ||
120 | {"sat", 6}, | ||
121 | {"", TM_UNDEFINED} | ||
122 | }; | ||
123 | |||
124 | #define hr60nonnegative(t) ((t)/100 * 60 + (t)%100) | ||
125 | #define hr60(t) ((t)<0 ? -hr60nonnegative(-(t)) : hr60nonnegative(t)) | ||
126 | #define zs(t,s) {s, hr60(t)} | ||
127 | #define zd(t,s,d) zs(t, s), zs((t)+100, d) | ||
128 | |||
129 | static struct name_val const zone_names[] = | ||
130 | { | ||
131 | zs (-1000, "hst"), /* Hawaii */ | ||
132 | zd (-1000, "hast", "hadt"), /* Hawaii-Aleutian */ | ||
133 | zd (- 900, "akst", "akdt"), /* Alaska */ | ||
134 | zd (- 800, "pst" , "pdt" ), /* Pacific */ | ||
135 | zd (- 700, "mst" , "mdt" ), /* Mountain */ | ||
136 | zd (- 600, "cst" , "cdt" ), /* Central */ | ||
137 | zd (- 500, "est" , "edt" ), /* Eastern */ | ||
138 | zd (- 400, "ast" , "adt" ), /* Atlantic */ | ||
139 | zd (- 330, "nst" , "ndt" ), /* Newfoundland */ | ||
140 | zs ( 000, "utc" ), /* Coordinated Universal */ | ||
141 | zs ( 000, "uct" ), /* " */ | ||
142 | zs ( 000, "cut" ), /* " */ | ||
143 | zs ( 000, "ut"), /* Universal */ | ||
144 | zs ( 000, "z"), /* Zulu (required by ISO 8601) */ | ||
145 | zd ( 000, "gmt" , "bst" ), /* Greenwich Mean, British Summer */ | ||
146 | zd ( 000, "wet" , "west"), /* Western European */ | ||
147 | zd ( 100, "cet" , "cest"), /* Central European */ | ||
148 | zd ( 100, "met" , "mest"), /* Middle European (bug in old tz versions) */ | ||
149 | zd ( 100, "mez" , "mesz"), /* Mittel-Europaeische Zeit */ | ||
150 | zd ( 200, "eet" , "eest"), /* Eastern European */ | ||
151 | zs ( 530, "ist" ), /* India */ | ||
152 | zd ( 900, "jst" , "jdt" ), /* Japan */ | ||
153 | zd ( 900, "kst" , "kdt" ), /* Korea */ | ||
154 | zd ( 1200, "nzst", "nzdt"), /* New Zealand */ | ||
155 | {"lt", 1}, | ||
156 | #if 0 | ||
157 | /* The following names are duplicates or are not well attested. | ||
158 | There are lots more where these came from. */ | ||
159 | zs (-1100, "sst" ), /* Samoan */ | ||
160 | zd (- 900, "yst" , "ydt" ), /* Yukon - name is no longer used */ | ||
161 | zd (- 500, "ast" , "adt" ), /* Acre */ | ||
162 | zd (- 400, "wst" , "wdt" ), /* Western Brazil */ | ||
163 | zd (- 400, "cst" , "cdt" ), /* Chile */ | ||
164 | zd (- 200, "fst" , "fdt" ), /* Fernando de Noronha */ | ||
165 | zs ( 000, "wat" ), /* West African */ | ||
166 | zs ( 100, "cat" ), /* Central African */ | ||
167 | zs ( 200, "sat" ), /* South African */ | ||
168 | zd ( 200, "ist" , "idt" ), /* Israel */ | ||
169 | zs ( 300, "eat" ), /* East African */ | ||
170 | zd ( 300, "msk" , "msd" ), /* Moscow */ | ||
171 | zd ( 330, "ist" , "idt" ), /* Iran */ | ||
172 | zs ( 800, "hkt" ), /* Hong Kong */ | ||
173 | zs ( 800, "sgt" ), /* Singapore */ | ||
174 | zd ( 800, "cst" , "cdt" ), /* China */ | ||
175 | zd ( 800, "wst" , "wst" ), /* Western Australia */ | ||
176 | zd ( 930, "cst" , "cst" ), /* Central Australia */ | ||
177 | zs ( 1000, "gst" ), /* Guam */ | ||
178 | zd ( 1000, "est" , "est" ), /* Eastern Australia */ | ||
179 | #endif | ||
180 | {"", -1} | ||
181 | }; | ||
182 | |||
183 | /* Look for a prefix of S in TABLE, returning val for first matching entry. */ | ||
184 | static int | ||
185 | lookup (s, table) | ||
186 | char const *s; | ||
187 | struct name_val const table[]; | ||
188 | { | ||
189 | int j; | ||
190 | char buf[NAME_LENGTH_MAXIMUM]; | ||
191 | |||
192 | for (j = 0; j < NAME_LENGTH_MAXIMUM; j++) | ||
193 | { | ||
194 | unsigned char c = *s++; | ||
195 | if (! ISALPHA (c)) | ||
196 | { | ||
197 | buf[j] = '\0'; | ||
198 | break; | ||
199 | } | ||
200 | buf[j] = ISUPPER (c) ? tolower (c) : c; | ||
201 | } | ||
202 | |||
203 | for (;; table++) | ||
204 | for (j = 0; ; j++) | ||
205 | if (j == NAME_LENGTH_MAXIMUM || ! table[0].name[j]) | ||
206 | return table[0].val; | ||
207 | else if (buf[j] != table[0].name[j]) | ||
208 | break; | ||
209 | } | ||
210 | |||
211 | |||
212 | /* Set *T to ``undefined'' values. */ | ||
213 | static void | ||
214 | undefine (t) | ||
215 | struct partime *t; | ||
216 | { | ||
217 | t->tm.tm_sec = t->tm.tm_min = t->tm.tm_hour = t->tm.tm_mday = t->tm.tm_mon | ||
218 | = t->tm.tm_year = t->tm.tm_wday = t->tm.tm_yday | ||
219 | = t->ymodulus = t->yweek | ||
220 | = TM_UNDEFINED; | ||
221 | t->zone = TM_UNDEFINED_ZONE; | ||
222 | } | ||
223 | |||
224 | /* Array of patterns to look for in a date string. | ||
225 | Order is important: we look for the first matching pattern | ||
226 | whose values do not contradict values that we already know about. | ||
227 | See `parse_pattern_letter' below for the meaning of the pattern codes. */ | ||
228 | static char const *const patterns[] = | ||
229 | { | ||
230 | /* These traditional patterns must come first, | ||
231 | to prevent an ISO 8601 format from misinterpreting their prefixes. */ | ||
232 | "E_n_y", "x", /* RFC 822 */ | ||
233 | "E_n", "n_E", "n", "t:m:s_A", "t:m_A", "t_A", /* traditional */ | ||
234 | "y/N/D$", /* traditional RCS */ | ||
235 | |||
236 | /* ISO 8601:1988 formats, generalized a bit. */ | ||
237 | "y-N-D$", "4ND$", "Y-N$", | ||
238 | "RND$", "-R=N$", "-R$", "--N=D$", "N=DT", | ||
239 | "--N$", "---D$", "DT", | ||
240 | "Y-d$", "4d$", "R=d$", "-d$", "dT", | ||
241 | "y-W-X", "yWX", "y=W", | ||
242 | "-r-W-X", "r-W-XT", "-rWX", "rWXT", "-W=X", "W=XT", "-W", | ||
243 | "-w-X", "w-XT", "---X$", "XT", "4$", | ||
244 | "T", | ||
245 | "h:m:s$", "hms$", "h:m$", "hm$", "h$", "-m:s$", "-ms$", "-m$", "--s$", | ||
246 | "Y", "Z", | ||
247 | |||
248 | 0 | ||
249 | }; | ||
250 | |||
251 | /* Parse an initial prefix of STR, setting *T accordingly. | ||
252 | Return the first character after the prefix, or 0 if it couldn't be parsed. | ||
253 | Start with pattern *PI; if success, set *PI to the next pattern to try. | ||
254 | Set *PI to -1 if we know there are no more patterns to try; | ||
255 | if *PI is initially negative, give up immediately. */ | ||
256 | static char const * | ||
257 | parse_prefix (str, t, pi) | ||
258 | char const *str; | ||
259 | struct partime *t; | ||
260 | int *pi; | ||
261 | { | ||
262 | int i = *pi; | ||
263 | char const *pat; | ||
264 | unsigned char c; | ||
265 | |||
266 | if (i < 0) | ||
267 | return 0; | ||
268 | |||
269 | /* Remove initial noise. */ | ||
270 | while (! ISALNUM (c = *str) && c != '-' && c != '+') | ||
271 | { | ||
272 | if (! c) | ||
273 | { | ||
274 | undefine (t); | ||
275 | *pi = -1; | ||
276 | return str; | ||
277 | } | ||
278 | str++; | ||
279 | } | ||
280 | |||
281 | /* Try a pattern until one succeeds. */ | ||
282 | while ((pat = patterns[i++]) != 0) | ||
283 | { | ||
284 | char const *s = str; | ||
285 | undefine (t); | ||
286 | do | ||
287 | { | ||
288 | if (! (c = *pat++)) | ||
289 | { | ||
290 | *pi = i; | ||
291 | return s; | ||
292 | } | ||
293 | } | ||
294 | while ((s = parse_pattern_letter (s, c, t)) != 0); | ||
295 | } | ||
296 | |||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | /* Parse an initial prefix of S of length DIGITS; it must be a number. | ||
301 | Store the parsed number into *RES. | ||
302 | Return the first character after the prefix, or 0 if it wasn't parsed. */ | ||
303 | static char const * | ||
304 | parse_fixed (s, digits, res) | ||
305 | char const *s; | ||
306 | int digits, *res; | ||
307 | { | ||
308 | int n = 0; | ||
309 | char const *lim = s + digits; | ||
310 | while (s < lim) | ||
311 | { | ||
312 | unsigned d = *s++ - '0'; | ||
313 | if (9 < d) | ||
314 | return 0; | ||
315 | n = 10 * n + d; | ||
316 | } | ||
317 | *res = n; | ||
318 | return s; | ||
319 | } | ||
320 | |||
321 | /* Parse an initial prefix of S of length DIGITS; | ||
322 | it must be a number in the range LO through HI. | ||
323 | Store the parsed number into *RES. | ||
324 | Return the first character after the prefix, or 0 if it wasn't parsed. */ | ||
325 | static char const * | ||
326 | parse_ranged (s, digits, lo, hi, res) | ||
327 | char const *s; | ||
328 | int digits, lo, hi, *res; | ||
329 | { | ||
330 | s = parse_fixed (s, digits, res); | ||
331 | return s && lo <= *res && *res <= hi ? s : 0; | ||
332 | } | ||
333 | |||
334 | /* Parse an initial prefix of S of length DIGITS; | ||
335 | it must be a number in the range LO through HI | ||
336 | and it may be followed by a fraction to be computed using RESOLUTION. | ||
337 | Store the parsed number into *RES; store the fraction times RESOLUTION, | ||
338 | rounded to the nearest integer, into *FRES. | ||
339 | Return the first character after the prefix, or 0 if it wasn't parsed. */ | ||
340 | static char const * | ||
341 | parse_decimal (s, digits, lo, hi, resolution, res, fres) | ||
342 | char const *s; | ||
343 | int digits, lo, hi, resolution, *res, *fres; | ||
344 | { | ||
345 | s = parse_fixed (s, digits, res); | ||
346 | if (s && lo <= *res && *res <= hi) | ||
347 | { | ||
348 | int f = 0; | ||
349 | if ((s[0] == ',' || s[0] == '.') && ISDIGIT (s[1])) | ||
350 | { | ||
351 | char const *s1 = ++s; | ||
352 | int num10 = 0, denom10 = 10, product; | ||
353 | while (ISDIGIT (*++s)) | ||
354 | { | ||
355 | int d = denom10 * 10; | ||
356 | if (d / 10 != denom10) | ||
357 | return 0; /* overflow */ | ||
358 | denom10 = d; | ||
359 | } | ||
360 | s = parse_fixed (s1, (int) (s - s1), &num10); | ||
361 | product = num10 * resolution; | ||
362 | f = (product + (denom10 >> 1)) / denom10; | ||
363 | f -= f & (product % denom10 == denom10 >> 1); /* round to even */ | ||
364 | if (f < 0 || product/resolution != num10) | ||
365 | return 0; /* overflow */ | ||
366 | } | ||
367 | *fres = f; | ||
368 | return s; | ||
369 | } | ||
370 | return 0; | ||
371 | } | ||
372 | |||
373 | /* Parse an initial prefix of S; it must denote a time zone. | ||
374 | Set *ZONE to the number of seconds east of GMT, | ||
375 | or to TM_LOCAL_ZONE if it is the local time zone. | ||
376 | Return the first character after the prefix, or 0 if it wasn't parsed. */ | ||
377 | char * | ||
378 | parzone (s, zone) | ||
379 | char const *s; | ||
380 | long *zone; | ||
381 | { | ||
382 | char sign; | ||
383 | int hh, mm, ss; | ||
384 | int minutesEastOfUTC; | ||
385 | long offset, z; | ||
386 | |||
387 | /* The formats are LT, n, n DST, nDST, no, o | ||
388 | where n is a time zone name | ||
389 | and o is a time zone offset of the form [-+]hh[:mm[:ss]]. */ | ||
390 | switch (*s) | ||
391 | { | ||
392 | case '-': | ||
393 | case '+': | ||
394 | z = 0; | ||
395 | break; | ||
396 | |||
397 | default: | ||
398 | minutesEastOfUTC = lookup (s, zone_names); | ||
399 | if (minutesEastOfUTC == -1) | ||
400 | return 0; | ||
401 | |||
402 | /* Don't bother to check rest of spelling. */ | ||
403 | while (ISALPHA ((unsigned char) *s)) | ||
404 | s++; | ||
405 | |||
406 | /* Don't modify LT. */ | ||
407 | if (minutesEastOfUTC == 1) | ||
408 | { | ||
409 | *zone = TM_LOCAL_ZONE; | ||
410 | return (char *) s; | ||
411 | } | ||
412 | |||
413 | z = minutesEastOfUTC * 60L; | ||
414 | |||
415 | /* Look for trailing " DST". */ | ||
416 | if ((s[-1] == 'T' || s[-1] == 't') | ||
417 | && (s[-2] == 'S' || s[-2] == 's') | ||
418 | && (s[-3] == 'D' || s[-3] == 'd')) | ||
419 | goto trailing_dst; | ||
420 | while (ISSPACE ((unsigned char) *s)) | ||
421 | s++; | ||
422 | if ((s[0] == 'D' || s[0] == 'd') | ||
423 | && (s[1] == 'S' || s[1] == 's') | ||
424 | && (s[2] == 'T' || s[2] == 't')) | ||
425 | { | ||
426 | s += 3; | ||
427 | trailing_dst: | ||
428 | *zone = z + 60*60; | ||
429 | return (char *) s; | ||
430 | } | ||
431 | |||
432 | switch (*s) | ||
433 | { | ||
434 | case '-': | ||
435 | case '+': | ||
436 | break; | ||
437 | |||
438 | default: | ||
439 | *zone = z; | ||
440 | return (char *) s; | ||
441 | } | ||
442 | |||
443 | break; | ||
444 | } | ||
445 | |||
446 | sign = *s++; | ||
447 | |||
448 | if (! (s = parse_ranged (s, 2, 0, 23, &hh))) | ||
449 | return 0; | ||
450 | mm = ss = 0; | ||
451 | if (*s == ':') | ||
452 | s++; | ||
453 | if (ISDIGIT (*s)) | ||
454 | { | ||
455 | if (! (s = parse_ranged (s, 2, 0, 59, &mm))) | ||
456 | return 0; | ||
457 | if (*s == ':' && s[-3] == ':' && ISDIGIT (s[1]) | ||
458 | && ! (s = parse_ranged (s + 1, 2, 0, 59, &ss))) | ||
459 | return 0; | ||
460 | } | ||
461 | if (ISDIGIT (*s)) | ||
462 | return 0; | ||
463 | offset = (hh * 60 + mm) * 60L + ss; | ||
464 | *zone = z + (sign == '-' ? -offset : offset); | ||
465 | /* ?? Are fractions allowed here? If so, they're not implemented. */ | ||
466 | return (char *) s; | ||
467 | } | ||
468 | |||
469 | /* Parse an initial prefix of S, matching the pattern whose code is C. | ||
470 | Set *T accordingly. | ||
471 | Return the first character after the prefix, or 0 if it wasn't parsed. */ | ||
472 | static char const * | ||
473 | parse_pattern_letter (s, c, t) | ||
474 | char const *s; | ||
475 | int c; | ||
476 | struct partime *t; | ||
477 | { | ||
478 | switch (c) | ||
479 | { | ||
480 | case '$': /* The next character must be a non-digit. */ | ||
481 | if (ISDIGIT (*s)) | ||
482 | return 0; | ||
483 | break; | ||
484 | |||
485 | case '-': | ||
486 | case '/': | ||
487 | case ':': | ||
488 | /* These characters stand for themselves. */ | ||
489 | if (*s++ != c) | ||
490 | return 0; | ||
491 | break; | ||
492 | |||
493 | case '4': /* 4-digit year */ | ||
494 | s = parse_fixed (s, 4, &t->tm.tm_year); | ||
495 | break; | ||
496 | |||
497 | case '=': /* optional '-' */ | ||
498 | s += *s == '-'; | ||
499 | break; | ||
500 | |||
501 | case 'A': /* AM or PM */ | ||
502 | /* This matches the regular expression [AaPp][Mm]?. | ||
503 | It must not be followed by a letter or digit; | ||
504 | otherwise it would match prefixes of strings like "PST". */ | ||
505 | switch (*s++) | ||
506 | { | ||
507 | case 'A': | ||
508 | case 'a': | ||
509 | if (t->tm.tm_hour == 12) | ||
510 | t->tm.tm_hour = 0; | ||
511 | break; | ||
512 | |||
513 | case 'P': | ||
514 | case 'p': | ||
515 | if (t->tm.tm_hour != 12) | ||
516 | t->tm.tm_hour += 12; | ||
517 | break; | ||
518 | |||
519 | default: | ||
520 | return 0; | ||
521 | } | ||
522 | switch (*s) | ||
523 | { | ||
524 | case 'M': | ||
525 | case 'm': | ||
526 | s++; | ||
527 | break; | ||
528 | } | ||
529 | if (ISALNUM ((unsigned char) *s)) | ||
530 | return 0; | ||
531 | break; | ||
532 | |||
533 | case 'D': /* day of month [01-31] */ | ||
534 | s = parse_ranged (s, 2, 1, 31, &t->tm.tm_mday); | ||
535 | break; | ||
536 | |||
537 | case 'd': /* day of year [001-366] */ | ||
538 | s = parse_ranged (s, 3, 1, 366, &t->tm.tm_yday); | ||
539 | t->tm.tm_yday--; | ||
540 | break; | ||
541 | |||
542 | case 'E': /* extended day of month [1-9, 01-31] */ | ||
543 | s = parse_ranged (s, (ISDIGIT (s[0]) && ISDIGIT (s[1])) + 1, 1, 31, | ||
544 | &t->tm.tm_mday); | ||
545 | break; | ||
546 | |||
547 | case 'h': /* hour [00-23 followed by optional fraction] */ | ||
548 | { | ||
549 | int frac; | ||
550 | s = parse_decimal (s, 2, 0, 23, 60 * 60, &t->tm.tm_hour, &frac); | ||
551 | t->tm.tm_min = frac / 60; | ||
552 | t->tm.tm_sec = frac % 60; | ||
553 | } | ||
554 | break; | ||
555 | |||
556 | case 'm': /* minute [00-59 followed by optional fraction] */ | ||
557 | s = parse_decimal (s, 2, 0, 59, 60, &t->tm.tm_min, &t->tm.tm_sec); | ||
558 | break; | ||
559 | |||
560 | case 'n': /* month name [e.g. "Jan"] */ | ||
561 | if (! TM_DEFINED (t->tm.tm_mon = lookup (s, month_names))) | ||
562 | return 0; | ||
563 | /* Don't bother to check rest of spelling. */ | ||
564 | while (ISALPHA ((unsigned char) *s)) | ||
565 | s++; | ||
566 | break; | ||
567 | |||
568 | case 'N': /* month [01-12] */ | ||
569 | s = parse_ranged (s, 2, 1, 12, &t->tm.tm_mon); | ||
570 | t->tm.tm_mon--; | ||
571 | break; | ||
572 | |||
573 | case 'r': /* year % 10 (remainder in origin-0 decade) [0-9] */ | ||
574 | s = parse_fixed (s, 1, &t->tm.tm_year); | ||
575 | t->ymodulus = 10; | ||
576 | break; | ||
577 | |||
578 | case_R: | ||
579 | case 'R': /* year % 100 (remainder in origin-0 century) [00-99] */ | ||
580 | s = parse_fixed (s, 2, &t->tm.tm_year); | ||
581 | t->ymodulus = 100; | ||
582 | break; | ||
583 | |||
584 | case 's': /* second [00-60 followed by optional fraction] */ | ||
585 | { | ||
586 | int frac; | ||
587 | s = parse_decimal (s, 2, 0, 60, 1, &t->tm.tm_sec, &frac); | ||
588 | t->tm.tm_sec += frac; | ||
589 | } | ||
590 | break; | ||
591 | |||
592 | case 'T': /* 'T' or 't' */ | ||
593 | switch (*s++) | ||
594 | { | ||
595 | case 'T': | ||
596 | case 't': | ||
597 | break; | ||
598 | default: | ||
599 | return 0; | ||
600 | } | ||
601 | break; | ||
602 | |||
603 | case 't': /* traditional hour [1-9 or 01-12] */ | ||
604 | s = parse_ranged (s, (ISDIGIT (s[0]) && ISDIGIT (s[1])) + 1, 1, 12, | ||
605 | &t->tm.tm_hour); | ||
606 | break; | ||
607 | |||
608 | case 'w': /* 'W' or 'w' only (stands for current week) */ | ||
609 | switch (*s++) | ||
610 | { | ||
611 | case 'W': | ||
612 | case 'w': | ||
613 | break; | ||
614 | default: | ||
615 | return 0; | ||
616 | } | ||
617 | break; | ||
618 | |||
619 | case 'W': /* 'W' or 'w', followed by a week of year [00-53] */ | ||
620 | switch (*s++) | ||
621 | { | ||
622 | case 'W': | ||
623 | case 'w': | ||
624 | break; | ||
625 | default: | ||
626 | return 0; | ||
627 | } | ||
628 | s = parse_ranged (s, 2, 0, 53, &t->yweek); | ||
629 | break; | ||
630 | |||
631 | case 'X': /* weekday (1=Mon ... 7=Sun) [1-7] */ | ||
632 | s = parse_ranged (s, 1, 1, 7, &t->tm.tm_wday); | ||
633 | t->tm.tm_wday--; | ||
634 | break; | ||
635 | |||
636 | case 'x': /* weekday name [e.g. "Sun"] */ | ||
637 | if (! TM_DEFINED (t->tm.tm_wday = lookup (s, weekday_names))) | ||
638 | return 0; | ||
639 | /* Don't bother to check rest of spelling. */ | ||
640 | while (ISALPHA ((unsigned char) *s)) | ||
641 | s++; | ||
642 | break; | ||
643 | |||
644 | case 'y': /* either R or Y */ | ||
645 | if (ISDIGIT (s[0]) && ISDIGIT (s[1]) && ! ISDIGIT (s[2])) | ||
646 | goto case_R; | ||
647 | /* fall into */ | ||
648 | case 'Y': /* year in full [4 or more digits] */ | ||
649 | { | ||
650 | int len = 0; | ||
651 | while (ISDIGIT (s[len])) | ||
652 | len++; | ||
653 | if (len < 4) | ||
654 | return 0; | ||
655 | s = parse_fixed (s, len, &t->tm.tm_year); | ||
656 | } | ||
657 | break; | ||
658 | |||
659 | case 'Z': /* time zone */ | ||
660 | s = parzone (s, &t->zone); | ||
661 | break; | ||
662 | |||
663 | case '_': /* possibly empty sequence of non-alphanumerics */ | ||
664 | while (! ISALNUM ((unsigned char) *s) && *s) | ||
665 | s++; | ||
666 | break; | ||
667 | |||
668 | default: /* bad pattern */ | ||
669 | return 0; | ||
670 | } | ||
671 | |||
672 | return s; | ||
673 | } | ||
674 | |||
675 | /* If there is no conflict, merge into *T the additional information in *U | ||
676 | and return 0. Otherwise do nothing and return -1. */ | ||
677 | static int | ||
678 | merge_partime (t, u) | ||
679 | struct partime *t; | ||
680 | struct partime const *u; | ||
681 | { | ||
682 | # define conflict(a,b) ((a) != (b) && TM_DEFINED (a) && TM_DEFINED (b)) | ||
683 | if (conflict (t->tm.tm_sec, u->tm.tm_sec) | ||
684 | || conflict (t->tm.tm_min, u->tm.tm_min) | ||
685 | || conflict (t->tm.tm_hour, u->tm.tm_hour) | ||
686 | || conflict (t->tm.tm_mday, u->tm.tm_mday) | ||
687 | || conflict (t->tm.tm_mon, u->tm.tm_mon) | ||
688 | || conflict (t->tm.tm_year, u->tm.tm_year) | ||
689 | || conflict (t->tm.tm_wday, u->tm.tm_yday) | ||
690 | || conflict (t->ymodulus, u->ymodulus) | ||
691 | || conflict (t->yweek, u->yweek) | ||
692 | || (t->zone != u->zone | ||
693 | && t->zone != TM_UNDEFINED_ZONE | ||
694 | && u->zone != TM_UNDEFINED_ZONE)) | ||
695 | return -1; | ||
696 | # undef conflict | ||
697 | # define merge_(a,b) if (TM_DEFINED (b)) (a) = (b); | ||
698 | merge_ (t->tm.tm_sec, u->tm.tm_sec) | ||
699 | merge_ (t->tm.tm_min, u->tm.tm_min) | ||
700 | merge_ (t->tm.tm_hour, u->tm.tm_hour) | ||
701 | merge_ (t->tm.tm_mday, u->tm.tm_mday) | ||
702 | merge_ (t->tm.tm_mon, u->tm.tm_mon) | ||
703 | merge_ (t->tm.tm_year, u->tm.tm_year) | ||
704 | merge_ (t->tm.tm_wday, u->tm.tm_yday) | ||
705 | merge_ (t->ymodulus, u->ymodulus) | ||
706 | merge_ (t->yweek, u->yweek) | ||
707 | # undef merge_ | ||
708 | if (u->zone != TM_UNDEFINED_ZONE) | ||
709 | t->zone = u->zone; | ||
710 | return 0; | ||
711 | } | ||
712 | |||
713 | /* Parse a date/time prefix of S, putting the parsed result into *T. | ||
714 | Return the first character after the prefix. | ||
715 | The prefix may contain no useful information; | ||
716 | in that case, *T will contain only undefined values. */ | ||
717 | char * | ||
718 | partime (s, t) | ||
719 | char const *s; | ||
720 | struct partime *t; | ||
721 | { | ||
722 | struct partime p; | ||
723 | |||
724 | undefine (t); | ||
725 | |||
726 | while (*s) | ||
727 | { | ||
728 | int i = 0; | ||
729 | char const *s1; | ||
730 | |||
731 | do | ||
732 | { | ||
733 | if (! (s1 = parse_prefix (s, &p, &i))) | ||
734 | return (char *) s; | ||
735 | } | ||
736 | while (merge_partime (t, &p) != 0); | ||
737 | |||
738 | s = s1; | ||
739 | } | ||
740 | |||
741 | return (char *) s; | ||
742 | } | ||