~ubuntu-branches/ubuntu/lucid/patch/lucid

« back to all changes in this revision

Viewing changes to src/partime.c

  • Committer: Bazaar Package Importer
  • Author(s): Christoph Berg
  • Date: 2009-12-02 10:25:26 UTC
  • mfrom: (5.1.1 sid)
  • Revision ID: james.westby@ubuntu.com-20091202102526-5luk0zsqhghu58l2
Tags: 2.6-2
* Update watch file.
* Section: vcs.
* Suggests: diffutils-doc instead of diff-doc, thanks Christoph Anton
  Mitterer for spotting. Closes: #558974.

Show diffs side-by-side

added added

removed removed

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