source: vendor/patch/2.5.9/partime.c@ 3729

Last change on this file since 3729 was 3444, checked in by bird, 18 years ago

patch 2.5.9

File size: 23.4 KB
Line 
1/* Parse a string, yielding a struct partime that describes it. */
2
3/* Copyright (C) 1993, 1994, 1995, 1997, 2002 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 HAVE_STDDEF_H
46# include <stddef.h>
47# endif
48# if STDC_HEADERS
49# include <stdlib.h>
50# endif
51# include <time.h>
52# ifdef __STDC__
53# define P(x) x
54# else
55# define P(x) ()
56# endif
57#endif
58
59#ifndef offsetof
60#define offsetof(aggregate, member) ((size_t) &((aggregate *) 0)->member)
61#endif
62
63#include <ctype.h>
64#if STDC_HEADERS
65# define CTYPE_DOMAIN(c) 1
66#else
67# define CTYPE_DOMAIN(c) ((unsigned) (c) <= 0177)
68#endif
69#define ISALNUM(c) (CTYPE_DOMAIN (c) && isalnum (c))
70#define ISALPHA(c) (CTYPE_DOMAIN (c) && isalpha (c))
71#define ISSPACE(c) (CTYPE_DOMAIN (c) && isspace (c))
72#define ISUPPER(c) (CTYPE_DOMAIN (c) && isupper (c))
73#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
74
75#include <partime.h>
76
77char const partime_id[] =
78 "$Id: partime.c,v 1.2 2002/02/18 07:42:58 eggert Exp $";
79
80
81/* Lookup tables for names of months, weekdays, time zones. */
82
83#define NAME_LENGTH_MAXIMUM 4
84
85struct name_val
86 {
87 char name[NAME_LENGTH_MAXIMUM];
88 int val;
89 };
90
91
92static char const *parse_decimal P ((char const *, int, int, int, int, int *, int *));
93static char const *parse_fixed P ((char const *, int, int *));
94static char const *parse_pattern_letter P ((char const *, int, struct partime *));
95static char const *parse_prefix P ((char const *, char const **, struct partime *));
96static char const *parse_ranged P ((char const *, int, int, int, int *));
97static char const *parse_varying P ((char const *, int *));
98static int lookup P ((char const *, struct name_val const[]));
99static int merge_partime P ((struct partime *, struct partime const *));
100static void undefine P ((struct partime *));
101
102
103static struct name_val const month_names[] =
104{
105 {"jan", 0},
106 {"feb", 1},
107 {"mar", 2},
108 {"apr", 3},
109 {"may", 4},
110 {"jun", 5},
111 {"jul", 6},
112 {"aug", 7},
113 {"sep", 8},
114 {"oct", 9},
115 {"nov", 10},
116 {"dec", 11},
117 {"", TM_UNDEFINED}
118};
119
120static struct name_val const weekday_names[] =
121{
122 {"sun", 0},
123 {"mon", 1},
124 {"tue", 2},
125 {"wed", 3},
126 {"thu", 4},
127 {"fri", 5},
128 {"sat", 6},
129 {"", TM_UNDEFINED}
130};
131
132#define RELATIVE_CONS(member, multiplier) \
133 (offsetof (struct tm, member) + (multiplier) * sizeof (struct tm))
134#define RELATIVE_OFFSET(c) ((c) % sizeof (struct tm))
135#define RELATIVE_MULTIPLIER(c) ((c) / sizeof (struct tm))
136static struct name_val const relative_units[] =
137{
138 {"year", RELATIVE_CONS (tm_year, 1) },
139 {"mont", RELATIVE_CONS (tm_mon , 1) },
140 {"fort", RELATIVE_CONS (tm_mday, 14) },
141 {"week", RELATIVE_CONS (tm_mday, 7) },
142 {"day" , RELATIVE_CONS (tm_mday, 1) },
143 {"hour", RELATIVE_CONS (tm_hour, 1) },
144 {"min" , RELATIVE_CONS (tm_min , 1) },
145 {"sec" , RELATIVE_CONS (tm_sec , 1) },
146 {"", TM_UNDEFINED}
147};
148
149static struct name_val const ago[] =
150{
151 {"ago", 0},
152 {"", TM_UNDEFINED}
153};
154
155static struct name_val const dst_names[] =
156{
157 {"dst", 1},
158 {"", 0}
159};
160
161#define hr60nonnegative(t) ((t)/100 * 60 + (t)%100)
162#define hr60(t) ((t) < 0 ? - hr60nonnegative (-(t)) : hr60nonnegative (t))
163#define zs(t, s) {s, hr60 (t)}
164#define zd(t, s, d) zs (t, s), zs ((t) + 100, d)
165
166static struct name_val const zone_names[] =
167{
168 zs (-1000, "hst"), /* Hawaii */
169 zd (-1000, "hast", "hadt"), /* Hawaii-Aleutian */
170 zd (- 900, "akst", "akdt"), /* Alaska */
171 zd (- 800, "pst" , "pdt" ), /* Pacific */
172 zd (- 700, "mst" , "mdt" ), /* Mountain */
173 zd (- 600, "cst" , "cdt" ), /* Central */
174 zd (- 500, "est" , "edt" ), /* Eastern */
175 zd (- 400, "ast" , "adt" ), /* Atlantic */
176 zd (- 330, "nst" , "ndt" ), /* Newfoundland */
177 zs ( 000, "utc" ), /* Coordinated Universal */
178 zs ( 000, "uct" ), /* " */
179 zs ( 000, "cut" ), /* " */
180 zs ( 000, "ut"), /* Universal */
181 zs ( 000, "z"), /* Zulu (required by ISO 8601) */
182 zd ( 000, "gmt" , "bst" ), /* Greenwich Mean, British Summer */
183 zd ( 000, "wet" , "west"), /* Western European */
184 zd ( 100, "cet" , "cest"), /* Central European */
185 zd ( 100, "met" , "mest"), /* Middle European (bug in old tz versions) */
186 zd ( 100, "mez" , "mesz"), /* Mittel-Europaeische Zeit */
187 zd ( 200, "eet" , "eest"), /* Eastern European */
188 zs ( 530, "ist" ), /* India */
189 zd ( 900, "jst" , "jdt" ), /* Japan */
190 zd ( 900, "kst" , "kdt" ), /* Korea */
191 zd ( 1200, "nzst", "nzdt"), /* New Zealand */
192 {"lt", 1},
193#if 0
194 /* The following names are duplicates or are not well attested.
195 It's not worth keeping a complete list, since alphabetic time zone names
196 are deprecated and there are lots more where these came from. */
197 zs (-1100, "sst" ), /* Samoan */
198 zd (- 900, "yst" , "ydt" ), /* Yukon - name is no longer used */
199 zd (- 500, "ast" , "adt" ), /* Acre */
200 zd (- 400, "wst" , "wdt" ), /* Western Brazil */
201 zd (- 400, "cst" , "cdt" ), /* Chile */
202 zd (- 200, "fst" , "fdt" ), /* Fernando de Noronha */
203 zs ( 000, "wat" ), /* West African */
204 zs ( 100, "cat" ), /* Central African */
205 zs ( 200, "sat" ), /* South African */
206 zd ( 200, "ist" , "idt" ), /* Israel */
207 zs ( 300, "eat" ), /* East African */
208 zd ( 300, "msk" , "msd" ), /* Moscow */
209 zd ( 330, "ist" , "idt" ), /* Iran */
210 zs ( 800, "hkt" ), /* Hong Kong */
211 zs ( 800, "sgt" ), /* Singapore */
212 zd ( 800, "cst" , "cdt" ), /* China */
213 zd ( 800, "wst" , "wst" ), /* Western Australia */
214 zd ( 930, "cst" , "cst" ), /* Central Australia */
215 zs ( 1000, "gst" ), /* Guam */
216 zd ( 1000, "est" , "est" ), /* Eastern Australia */
217#endif
218 {"", -1}
219};
220
221/* Look for a prefix of S in TABLE, returning val for first matching entry. */
222static int
223lookup (s, table)
224 char const *s;
225 struct name_val const table[];
226{
227 int j;
228 char buf[NAME_LENGTH_MAXIMUM];
229
230 for (j = 0; j < NAME_LENGTH_MAXIMUM; j++)
231 {
232 unsigned char c = *s;
233 if (! ISALPHA (c))
234 {
235 buf[j] = '\0';
236 break;
237 }
238 buf[j] = ISUPPER (c) ? tolower (c) : c;
239 s++;
240 s += *s == '.';
241 }
242
243 for (;; table++)
244 for (j = 0; ; j++)
245 if (j == NAME_LENGTH_MAXIMUM || ! table[0].name[j])
246 return table[0].val;
247 else if (buf[j] != table[0].name[j])
248 break;
249}
250
251
252/* Set *T to ``undefined'' values. */
253static void
254undefine (t)
255 struct partime *t;
256{
257 t->tm.tm_sec = t->tm.tm_min = t->tm.tm_hour = t->tm.tm_mday = t->tm.tm_mon
258 = t->tm.tm_year = t->tm.tm_wday = t->tm.tm_yday
259 = t->wday_ordinal = t->ymodulus = t->yweek
260 = TM_UNDEFINED;
261 t->tmr.tm_sec = t->tmr.tm_min = t->tmr.tm_hour =
262 t->tmr.tm_mday = t->tmr.tm_mon = t->tmr.tm_year = 0;
263 t->zone = TM_UNDEFINED_ZONE;
264}
265
266/* Patterns to look for in a time string.
267 Order is important: we look for the first matching pattern
268 whose values do not contradict values that we already know about.
269 See `parse_pattern_letter' below for the meaning of the pattern codes. */
270static char const time_patterns[] =
271{
272 /* Traditional patterns come first,
273 to prevent an ISO 8601 format from misinterpreting their prefixes. */
274
275 /* RFC 822, extended */
276 'E', '_', 'N', '_', 'y', '$', 0,
277 'x', 0,
278
279 /* traditional */
280 '4', '_', 'M', '_', 'D', '_', 'h', '_', 'm', '_', 's', '$', 0,
281 'R', '_', 'M', '_', 'D', '_', 'h', '_', 'm', '_', 's', '$', 0,
282 'E', '_', 'N', 0,
283 'N', '_', 'E', '_', 'y', ';', 0,
284 'N', '_', 'E', ';', 0,
285 'N', 0,
286 't', ':', 'm', ':', 's', '_', 'A', 0,
287 't', ':', 'm', '_', 'A', 0,
288 't', '_', 'A', 0,
289
290 /* traditional get_date */
291 'i', '_', 'x', 0,
292 'Y', '/', 'n', '/', 'E', ';', 0,
293 'n', '/', 'E', '/', 'y', ';', 0,
294 'n', '/', 'E', ';', 0,
295 'u', 0,
296
297 /* ISO 8601:1988 formats, generalized a bit. */
298 'y', '-', 'M', '-', 'D', '$', 0,
299 '4', 'M', 'D', '$', 0,
300 'Y', '-', 'M', '$', 0,
301 'R', 'M', 'D', '$', 0,
302 '-', 'R', '=', 'M', '$', 0,
303 '-', 'R', '$', 0,
304 '-', '-', 'M', '=', 'D', '$', 0,
305 'M', '=', 'D', 'T', 0,
306 '-', '-', 'M', '$', 0,
307 '-', '-', '-', 'D', '$', 0,
308 'D', 'T', 0,
309 'Y', '-', 'd', '$', 0,
310 '4', 'd', '$', 0,
311 'R', '=', 'd', '$', 0,
312 '-', 'd', '$', 0,
313 'd', 'T', 0,
314 'y', '-', 'W', '-', 'X', 0,
315 'y', 'W', 'X', 0,
316 'y', '=', 'W', 0,
317 '-', 'r', '-', 'W', '-', 'X', 0,
318 'r', '-', 'W', '-', 'X', 'T', 0,
319 '-', 'r', 'W', 'X', 0,
320 'r', 'W', 'X', 'T', 0,
321 '-', 'W', '=', 'X', 0,
322 'W', '=', 'X', 'T', 0,
323 '-', 'W', 0,
324 '-', 'w', '-', 'X', 0,
325 'w', '-', 'X', 'T', 0,
326 '-', '-', '-', 'X', '$', 0,
327 'X', 'T', 0,
328 '4', '$', 0,
329 'T', 0,
330 'h', ':', 'm', ':', 's', '$', 0,
331 'h', 'm', 's', '$', 0,
332 'h', ':', 'L', '$', 0,
333 'h', 'L', '$', 0,
334 'H', '$', 0,
335 '-', 'm', ':', 's', '$', 0,
336 '-', 'm', 's', '$', 0,
337 '-', 'L', '$', 0,
338 '-', '-', 's', '$', 0,
339 'Y', 0,
340 'Z', 0,
341
342 0
343};
344
345/* Parse an initial prefix of STR according to *PATTERNS, setting *T.
346 Return the first character after the prefix, or 0 if it couldn't be parsed.
347 *PATTERNS is a character array containing one pattern string after another;
348 it is terminated by an empty string.
349 If success, set *PATTERNS to the next pattern to try.
350 Set *PATTERNS to 0 if we know there are no more patterns to try;
351 if *PATTERNS is initially 0, give up immediately. */
352static char const *
353parse_prefix (str, patterns, t)
354 char const *str;
355 char const **patterns;
356 struct partime *t;
357{
358 char const *pat = *patterns;
359 unsigned char c;
360
361 if (! pat)
362 return 0;
363
364 /* Remove initial noise. */
365 while (! ISALNUM (c = *str) && c != '-' && c != '+')
366 {
367 if (! c)
368 {
369 undefine (t);
370 *patterns = 0;
371 return str;
372 }
373
374 str++;
375 }
376
377 /* Try a pattern until one succeeds. */
378 while (*pat)
379 {
380 char const *s = str;
381 undefine (t);
382
383 do
384 {
385 if (! (c = *pat++))
386 {
387 *patterns = pat;
388 return s;
389 }
390 }
391 while ((s = parse_pattern_letter (s, c, t)) != 0);
392
393 while (*pat++)
394 continue;
395 }
396
397 return 0;
398}
399
400/* Parse an initial prefix of S of length DIGITS; it must be a number.
401 Store the parsed number into *RES.
402 Return the first character after the prefix, or 0 if it wasn't parsed. */
403static char const *
404parse_fixed (s, digits, res)
405 char const *s;
406 int digits, *res;
407{
408 int n = 0;
409 char const *lim = s + digits;
410 while (s < lim)
411 {
412 unsigned d = *s++ - '0';
413 if (9 < d)
414 return 0;
415 n = 10 * n + d;
416 }
417 *res = n;
418 return s;
419}
420
421/* Parse a possibly empty initial prefix of S.
422 Store the parsed number into *RES.
423 Return the first character after the prefix. */
424static char const *
425parse_varying (s, res)
426 char const *s;
427 int *res;
428{
429 int n = 0;
430 for (;;)
431 {
432 unsigned d = *s - '0';
433 if (9 < d)
434 break;
435 s++;
436 n = 10 * n + d;
437 }
438 *res = n;
439 return s;
440}
441
442/* Parse an initial prefix of S of length DIGITS;
443 it must be a number in the range LO through HI.
444 Store the parsed number into *RES.
445 Return the first character after the prefix, or 0 if it wasn't parsed. */
446static char const *
447parse_ranged (s, digits, lo, hi, res)
448 char const *s;
449 int digits, lo, hi, *res;
450{
451 s = parse_fixed (s, digits, res);
452 return s && lo <= *res && *res <= hi ? s : 0;
453}
454
455/* Parse an initial prefix of S of length DIGITS;
456 it must be a number in the range LO through HI
457 and it may be followed by a fraction to be computed using RESOLUTION.
458 Store the parsed number into *RES; store the fraction times RESOLUTION,
459 rounded to the nearest integer, into *FRES.
460 Return the first character after the prefix, or 0 if it wasn't parsed. */
461static char const *
462parse_decimal (s, digits, lo, hi, resolution, res, fres)
463 char const *s;
464 int digits, lo, hi, resolution, *res, *fres;
465{
466 s = parse_fixed (s, digits, res);
467 if (s && lo <= *res && *res <= hi)
468 {
469 int f = 0;
470 if ((s[0] == ',' || s[0] == '.') && ISDIGIT (s[1]))
471 {
472 char const *s1 = ++s;
473 int num10 = 0, denom10 = 10, product;
474 while (ISDIGIT (*++s))
475 {
476 int d = denom10 * 10;
477 if (d / 10 != denom10)
478 return 0; /* overflow */
479 denom10 = d;
480 }
481 s = parse_fixed (s1, (int) (s - s1), &num10);
482 product = num10 * resolution;
483 f = (product + (denom10 >> 1)) / denom10;
484 f -= f & (product % denom10 == denom10 >> 1); /* round to even */
485 if (f < 0 || product/resolution != num10)
486 return 0; /* overflow */
487 }
488 *fres = f;
489 return s;
490 }
491 return 0;
492}
493
494/* Parse an initial prefix of S; it must denote a time zone.
495 Set *ZONE to the number of seconds east of GMT,
496 or to TM_LOCAL_ZONE if it is the local time zone.
497 Return the first character after the prefix, or 0 if it wasn't parsed. */
498char *
499parzone (s, zone)
500 char const *s;
501 long *zone;
502{
503 char const *s1;
504 char sign;
505 int hh, mm, ss;
506 int minutes_east_of_UTC;
507 int trailing_DST;
508 long offset, z;
509
510 /* The formats are LT, n, n DST, nDST, no, o
511 where n is a time zone name
512 and o is a time zone offset of the form [-+]hh[:mm[:ss]]. */
513 switch (*s)
514 {
515 case '-':
516 case '+':
517 z = 0;
518 break;
519
520 default:
521 minutes_east_of_UTC = lookup (s, zone_names);
522 if (minutes_east_of_UTC == -1)
523 return 0;
524
525 /* Don't bother to check rest of spelling,
526 but look for an embedded "DST". */
527 trailing_DST = 0;
528 while (ISALPHA ((unsigned char) *s))
529 {
530 if ((*s == 'D' || *s == 'd') && lookup (s, dst_names))
531 trailing_DST = 1;
532 s++;
533 s += *s == '.';
534 }
535
536 /* Don't modify LT. */
537 if (minutes_east_of_UTC == 1)
538 {
539 *zone = TM_LOCAL_ZONE;
540 return (char *) s;
541 }
542
543 z = minutes_east_of_UTC * 60L;
544 s1 = s;
545
546 /* Look for trailing "DST" or " DST". */
547 while (ISSPACE ((unsigned char) *s))
548 s++;
549 if (lookup (s, dst_names))
550 {
551 while (ISALPHA ((unsigned char) *s))
552 {
553 s++;
554 s += *s == '.';
555 }
556 trailing_DST = 1;
557 }
558
559 if (trailing_DST)
560 {
561 *zone = z + 60*60;
562 return (char *) s;
563 }
564
565 s = s1;
566
567 switch (*s)
568 {
569 case '-':
570 case '+':
571 break;
572
573 default:
574 *zone = z;
575 return (char *) s;
576 }
577
578 break;
579 }
580
581 sign = *s++;
582
583 if (! (s = parse_ranged (s, 2, 0, 23, &hh)))
584 return 0;
585 mm = ss = 0;
586 if (*s == ':')
587 s++;
588 if (ISDIGIT (*s))
589 {
590 if (! (s = parse_ranged (s, 2, 0, 59, &mm)))
591 return 0;
592 if (*s == ':' && s[-3] == ':' && ISDIGIT (s[1])
593 && ! (s = parse_ranged (s + 1, 2, 0, 59, &ss)))
594 return 0;
595 }
596 if (ISDIGIT (*s))
597 return 0;
598 offset = (hh * 60 + mm) * 60L + ss;
599 *zone = z + (sign == '-' ? -offset : offset);
600 /* ?? Are fractions allowed here? If so, they're not implemented. */
601 return (char *) s;
602}
603
604/* Parse an initial prefix of S, matching the pattern whose code is C.
605 Set *T accordingly.
606 Return the first character after the prefix, or 0 if it wasn't parsed. */
607static char const *
608parse_pattern_letter (s, c, t)
609 char const *s;
610 int c;
611 struct partime *t;
612{
613 char const *s0 = s;
614
615 switch (c)
616 {
617 case '$': /* The next character must be a non-digit. */
618 if (ISDIGIT (*s))
619 return 0;
620 break;
621
622 case '-':
623 case '/':
624 case ':':
625 /* These characters stand for themselves. */
626 if (*s++ != c)
627 return 0;
628 break;
629
630 case '4': /* 4-digit year */
631 s = parse_fixed (s, 4, &t->tm.tm_year);
632 break;
633
634 case ';': /* The next character must be a non-digit, and cannot be ':'. */
635 if (ISDIGIT (*s) || *s == ':')
636 return 0;
637 break;
638
639 case '=': /* optional '-' */
640 s += *s == '-';
641 break;
642
643 case 'A': /* AM or PM */
644 /* This matches the regular expression [AaPp]\.?([Mm]\.?)?.
645 It must not be followed by a letter or digit;
646 otherwise it would match prefixes of strings like "PST". */
647 switch (*s)
648 {
649 case 'A':
650 case 'a':
651 if (t->tm.tm_hour == 12)
652 t->tm.tm_hour = 0;
653 break;
654
655 case 'P':
656 case 'p':
657 if (t->tm.tm_hour != 12)
658 t->tm.tm_hour += 12;
659 break;
660
661 default:
662 return 0;
663 }
664 s++;
665 s += *s == '.';
666 switch (*s)
667 {
668 case 'M':
669 case 'm':
670 s++;
671 s += *s == '.';
672 break;
673 }
674 if (ISALNUM ((unsigned char) *s))
675 return 0;
676 break;
677
678 case 'D': /* day of month [01-31] */
679 s = parse_ranged (s, 2, 1, 31, &t->tm.tm_mday);
680 break;
681
682 case 'd': /* day of year [001-366] */
683 s = parse_ranged (s, 3, 1, 366, &t->tm.tm_yday);
684 t->tm.tm_yday--;
685 break;
686
687 case 'E': /* traditional day of month [1-9, 01-31] */
688 s = parse_ranged (s, (ISDIGIT (s[0]) && ISDIGIT (s[1])) + 1, 1, 31,
689 &t->tm.tm_mday);
690 break;
691
692 case 'h': /* hour [00-23] */
693 s = parse_ranged (s, 2, 0, 23, &t->tm.tm_hour);
694 break;
695
696 case 'H': /* hour [00-23 followed by optional fraction] */
697 {
698 int frac;
699 s = parse_decimal (s, 2, 0, 23, 60 * 60, &t->tm.tm_hour, &frac);
700 t->tm.tm_min = frac / 60;
701 t->tm.tm_sec = frac % 60;
702 }
703 break;
704
705 case 'i': /* ordinal day number, e.g. "3rd" */
706 s = parse_varying (s, &t->wday_ordinal);
707 if (s == s0)
708 return 0;
709 while (ISALPHA ((unsigned char) *s))
710 s++;
711 break;
712
713 case 'L': /* minute [00-59 followed by optional fraction] */
714 s = parse_decimal (s, 2, 0, 59, 60, &t->tm.tm_min, &t->tm.tm_sec);
715 break;
716
717 case 'm': /* minute [00-59] */
718 s = parse_ranged (s, 2, 0, 59, &t->tm.tm_min);
719 break;
720
721 case 'M': /* month [01-12] */
722 s = parse_ranged (s, 2, 1, 12, &t->tm.tm_mon);
723 t->tm.tm_mon--;
724 break;
725
726 case 'n': /* traditional month [1-9, 01-12] */
727 s = parse_ranged (s, (ISDIGIT (s[0]) && ISDIGIT (s[1])) + 1, 1, 12,
728 &t->tm.tm_mon);
729 t->tm.tm_mon--;
730 break;
731
732 case 'N': /* month name [e.g. "Jan"] */
733 if (! TM_DEFINED (t->tm.tm_mon = lookup (s, month_names)))
734 return 0;
735 /* Don't bother to check rest of spelling. */
736 while (ISALPHA ((unsigned char) *s))
737 s++;
738 break;
739
740 case 'r': /* year % 10 (remainder in origin-0 decade) [0-9] */
741 s = parse_fixed (s, 1, &t->tm.tm_year);
742 t->ymodulus = 10;
743 break;
744
745 case_R:
746 case 'R': /* year % 100 (remainder in origin-0 century) [00-99] */
747 s = parse_fixed (s, 2, &t->tm.tm_year);
748 t->ymodulus = 100;
749 break;
750
751 case 's': /* second [00-60 followed by optional fraction] */
752 {
753 int frac;
754 s = parse_decimal (s, 2, 0, 60, 1, &t->tm.tm_sec, &frac);
755 t->tm.tm_sec += frac;
756 }
757 break;
758
759 case 'T': /* 'T' or 't' */
760 switch (*s++)
761 {
762 case 'T':
763 case 't':
764 break;
765 default:
766 return 0;
767 }
768 break;
769
770 case 't': /* traditional hour [1-9 or 01-12] */
771 s = parse_ranged (s, (ISDIGIT (s[0]) && ISDIGIT (s[1])) + 1, 1, 12,
772 &t->tm.tm_hour);
773 break;
774
775 case 'u': /* relative unit */
776 {
777 int i;
778 int n;
779 int negative = 0;
780 switch (*s)
781 {
782 case '-': negative = 1;
783 /* Fall through. */
784 case '+': s++;
785 }
786 if (ISDIGIT (*s))
787 s = parse_varying (s, &n);
788 else if (s == s0)
789 n = 1;
790 else
791 return 0;
792 if (negative)
793 n = -n;
794 while (! ISALNUM ((unsigned char) *s) && *s)
795 s++;
796 i = lookup (s, relative_units);
797 if (!TM_DEFINED (i))
798 return 0;
799 * (int *) ((char *) &t->tmr + RELATIVE_OFFSET (i))
800 += n * RELATIVE_MULTIPLIER (i);
801 while (ISALPHA ((unsigned char) *s))
802 s++;
803 while (! ISALNUM ((unsigned char) *s) && *s)
804 s++;
805 if (TM_DEFINED (lookup (s, ago)))
806 {
807 t->tmr.tm_sec = - t->tmr.tm_sec;
808 t->tmr.tm_min = - t->tmr.tm_min;
809 t->tmr.tm_hour = - t->tmr.tm_hour;
810 t->tmr.tm_mday = - t->tmr.tm_mday;
811 t->tmr.tm_mon = - t->tmr.tm_mon;
812 t->tmr.tm_year = - t->tmr.tm_year;
813 while (ISALPHA ((unsigned char) *s))
814 s++;
815 }
816 break;
817 }
818
819 case 'w': /* 'W' or 'w' only (stands for current week) */
820 switch (*s++)
821 {
822 case 'W':
823 case 'w':
824 break;
825 default:
826 return 0;
827 }
828 break;
829
830 case 'W': /* 'W' or 'w', followed by a week of year [00-53] */
831 switch (*s++)
832 {
833 case 'W':
834 case 'w':
835 break;
836 default:
837 return 0;
838 }
839 s = parse_ranged (s, 2, 0, 53, &t->yweek);
840 break;
841
842 case 'X': /* weekday (1=Mon ... 7=Sun) [1-7] */
843 s = parse_ranged (s, 1, 1, 7, &t->tm.tm_wday);
844 t->tm.tm_wday--;
845 break;
846
847 case 'x': /* weekday name [e.g. "Sun"] */
848 if (! TM_DEFINED (t->tm.tm_wday = lookup (s, weekday_names)))
849 return 0;
850 /* Don't bother to check rest of spelling. */
851 while (ISALPHA ((unsigned char) *s))
852 s++;
853 break;
854
855 case 'y': /* either R or Y */
856 if (ISDIGIT (s[0]) && ISDIGIT (s[1]) && ! ISDIGIT (s[2]))
857 goto case_R;
858 /* fall into */
859 case 'Y': /* year in full [4 or more digits] */
860 s = parse_varying (s, &t->tm.tm_year);
861 if (s - s0 < 4)
862 return 0;
863 break;
864
865 case 'Z': /* time zone */
866 s = parzone (s, &t->zone);
867 break;
868
869 case '_': /* possibly empty sequence of non-alphanumerics */
870 while (! ISALNUM ((unsigned char) *s) && *s)
871 s++;
872 break;
873
874 default: /* bad pattern */
875 return 0;
876 }
877
878 return s;
879}
880
881/* If there is no conflict, merge into *T the additional information in *U
882 and return 0. Otherwise do nothing and return -1. */
883static int
884merge_partime (t, u)
885 struct partime *t;
886 struct partime const *u;
887{
888# define conflict(a,b) ((a) != (b) && TM_DEFINED (a) && TM_DEFINED (b))
889 if (conflict (t->tm.tm_sec, u->tm.tm_sec)
890 || conflict (t->tm.tm_min, u->tm.tm_min)
891 || conflict (t->tm.tm_hour, u->tm.tm_hour)
892 || conflict (t->tm.tm_mday, u->tm.tm_mday)
893 || conflict (t->tm.tm_mon, u->tm.tm_mon)
894 || conflict (t->tm.tm_year, u->tm.tm_year)
895 || conflict (t->tm.tm_wday, u->tm.tm_wday)
896 || conflict (t->tm.tm_yday, u->tm.tm_yday)
897 || conflict (t->ymodulus, u->ymodulus)
898 || conflict (t->yweek, u->yweek)
899 || (t->zone != u->zone
900 && t->zone != TM_UNDEFINED_ZONE
901 && u->zone != TM_UNDEFINED_ZONE))
902 return -1;
903# undef conflict
904# define merge_(a,b) if (TM_DEFINED (b)) (a) = (b);
905 merge_ (t->tm.tm_sec, u->tm.tm_sec)
906 merge_ (t->tm.tm_min, u->tm.tm_min)
907 merge_ (t->tm.tm_hour, u->tm.tm_hour)
908 merge_ (t->tm.tm_mday, u->tm.tm_mday)
909 merge_ (t->tm.tm_mon, u->tm.tm_mon)
910 merge_ (t->tm.tm_year, u->tm.tm_year)
911 merge_ (t->tm.tm_wday, u->tm.tm_wday)
912 merge_ (t->tm.tm_yday, u->tm.tm_yday)
913 merge_ (t->ymodulus, u->ymodulus)
914 merge_ (t->yweek, u->yweek)
915# undef merge_
916 t->tmr.tm_sec += u->tmr.tm_sec;
917 t->tmr.tm_min += u->tmr.tm_min;
918 t->tmr.tm_hour += u->tmr.tm_hour;
919 t->tmr.tm_mday += u->tmr.tm_mday;
920 t->tmr.tm_mon += u->tmr.tm_mon;
921 t->tmr.tm_year += u->tmr.tm_year;
922 if (u->zone != TM_UNDEFINED_ZONE)
923 t->zone = u->zone;
924 return 0;
925}
926
927/* Parse a date/time prefix of S, putting the parsed result into *T.
928 Return the first character after the prefix.
929 The prefix may contain no useful information;
930 in that case, *T will contain only undefined values. */
931char *
932partime (s, t)
933 char const *s;
934 struct partime *t;
935{
936 struct partime p;
937
938 undefine (t);
939
940 while (*s)
941 {
942 char const *patterns = time_patterns;
943 char const *s1;
944
945 do
946 {
947 if (! (s1 = parse_prefix (s, &patterns, &p)))
948 return (char *) s;
949 }
950 while (merge_partime (t, &p) != 0);
951
952 s = s1;
953 }
954
955 return (char *) s;
956}
Note: See TracBrowser for help on using the repository browser.