~ubuntu-branches/ubuntu/trusty/patch/trusty-security

« back to all changes in this revision

Viewing changes to 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 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
 
 
77
 
char 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
 
 
85
 
struct name_val
86
 
  {
87
 
    char name[NAME_LENGTH_MAXIMUM];
88
 
    int val;
89
 
  };
90
 
 
91
 
 
92
 
static char const *parse_decimal P ((char const *, int, int, int, int, int *, int *));
93
 
static char const *parse_fixed P ((char const *, int, int *));
94
 
static char const *parse_pattern_letter P ((char const *, int, struct partime *));
95
 
static char const *parse_prefix P ((char const *, char const **, struct partime *));
96
 
static char const *parse_ranged P ((char const *, int, int, int, int *));
97
 
static char const *parse_varying P ((char const *, int *));
98
 
static int lookup P ((char const *, struct name_val const[]));
99
 
static int merge_partime P ((struct partime *, struct partime const *));
100
 
static void undefine P ((struct partime *));
101
 
 
102
 
 
103
 
static 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
 
 
120
 
static 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))
136
 
static 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
 
 
149
 
static struct name_val const ago[] =
150
 
{
151
 
  {"ago", 0},
152
 
  {"", TM_UNDEFINED}
153
 
};
154
 
 
155
 
static 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
 
 
166
 
static 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.  */
222
 
static int
223
 
lookup (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.  */
253
 
static void
254
 
undefine (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.  */
270
 
static 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.  */
352
 
static char const *
353
 
parse_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.  */
403
 
static char const *
404
 
parse_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.  */
424
 
static char const *
425
 
parse_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.  */
446
 
static char const *
447
 
parse_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.  */
461
 
static char const *
462
 
parse_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.  */
498
 
char *
499
 
parzone (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.  */
607
 
static char const *
608
 
parse_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.  */
883
 
static int
884
 
merge_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.  */
931
 
char *
932
 
partime (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
 
}