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