xref: /netbsd-src/external/gpl2/rcs/dist/src/partime.c (revision fa28c6faa16e0b00edee7acdcaf4899797043def)
1*fa28c6faSchristos /*	$NetBSD: partime.c,v 1.2 2016/01/14 04:22:39 christos Exp $	*/
27bdc2678Schristos 
37bdc2678Schristos /* Parse a string, yielding a struct partime that describes it.  */
47bdc2678Schristos 
57bdc2678Schristos /* Copyright 1993, 1994, 1995 Paul Eggert
67bdc2678Schristos    Distributed under license by the Free Software Foundation, Inc.
77bdc2678Schristos 
87bdc2678Schristos This file is part of RCS.
97bdc2678Schristos 
107bdc2678Schristos RCS is free software; you can redistribute it and/or modify
117bdc2678Schristos it under the terms of the GNU General Public License as published by
127bdc2678Schristos the Free Software Foundation; either version 2, or (at your option)
137bdc2678Schristos any later version.
147bdc2678Schristos 
157bdc2678Schristos RCS is distributed in the hope that it will be useful,
167bdc2678Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
177bdc2678Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
187bdc2678Schristos GNU General Public License for more details.
197bdc2678Schristos 
207bdc2678Schristos You should have received a copy of the GNU General Public License
217bdc2678Schristos along with RCS; see the file COPYING.
227bdc2678Schristos If not, write to the Free Software Foundation,
237bdc2678Schristos 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
247bdc2678Schristos 
257bdc2678Schristos Report problems and direct all questions to:
267bdc2678Schristos 
277bdc2678Schristos     rcs-bugs@cs.purdue.edu
287bdc2678Schristos 
297bdc2678Schristos */
307bdc2678Schristos 
317bdc2678Schristos #if has_conf_h
327bdc2678Schristos #	include "conf.h"
337bdc2678Schristos #else
347bdc2678Schristos #	ifdef __STDC__
357bdc2678Schristos #		define P(x) x
367bdc2678Schristos #	else
377bdc2678Schristos #		define const
387bdc2678Schristos #		define P(x) ()
397bdc2678Schristos #	endif
407bdc2678Schristos #	include <limits.h>
417bdc2678Schristos #	include <time.h>
427bdc2678Schristos #endif
437bdc2678Schristos 
447bdc2678Schristos #include <ctype.h>
457bdc2678Schristos #undef isdigit
467bdc2678Schristos #define isdigit(c) (((unsigned)(c)-'0') <= 9) /* faster than stock */
477bdc2678Schristos 
487bdc2678Schristos #include "partime.h"
497bdc2678Schristos 
507bdc2678Schristos char const partimeId[]
517bdc2678Schristos   = "Id: partime.c,v 5.13 1995/06/16 06:19:24 eggert Exp ";
527bdc2678Schristos 
537bdc2678Schristos 
547bdc2678Schristos /* Lookup tables for names of months, weekdays, time zones.  */
557bdc2678Schristos 
567bdc2678Schristos #define NAME_LENGTH_MAXIMUM 4
577bdc2678Schristos 
587bdc2678Schristos struct name_val {
597bdc2678Schristos 	char name[NAME_LENGTH_MAXIMUM];
607bdc2678Schristos 	int val;
617bdc2678Schristos };
627bdc2678Schristos 
637bdc2678Schristos 
647bdc2678Schristos static char const *parse_decimal P((char const*,int,int,int,int,int*,int*));
657bdc2678Schristos static char const *parse_fixed P((char const*,int,int*));
667bdc2678Schristos static char const *parse_pattern_letter P((char const*,int,struct partime*));
677bdc2678Schristos static char const *parse_prefix P((char const*,struct partime*,int*));
687bdc2678Schristos static char const *parse_ranged P((char const*,int,int,int,int*));
697bdc2678Schristos static int lookup P((char const*,struct name_val const[]));
707bdc2678Schristos static int merge_partime P((struct partime*, struct partime const*));
717bdc2678Schristos static void undefine P((struct partime*));
727bdc2678Schristos 
737bdc2678Schristos 
747bdc2678Schristos static struct name_val const month_names[] = {
757bdc2678Schristos 	{"jan",0}, {"feb",1}, {"mar",2}, {"apr",3}, {"may",4}, {"jun",5},
767bdc2678Schristos 	{"jul",6}, {"aug",7}, {"sep",8}, {"oct",9}, {"nov",10}, {"dec",11},
777bdc2678Schristos 	{"", TM_UNDEFINED}
787bdc2678Schristos };
797bdc2678Schristos 
807bdc2678Schristos static struct name_val const weekday_names[] = {
817bdc2678Schristos 	{"sun",0}, {"mon",1}, {"tue",2}, {"wed",3}, {"thu",4}, {"fri",5}, {"sat",6},
827bdc2678Schristos 	{"", TM_UNDEFINED}
837bdc2678Schristos };
847bdc2678Schristos 
857bdc2678Schristos #define hr60nonnegative(t)  ((t)/100 * 60  +  (t)%100)
867bdc2678Schristos #define hr60(t)  ((t)<0 ? -hr60nonnegative(-(t)) : hr60nonnegative(t))
877bdc2678Schristos #define zs(t,s)  {s, hr60(t)}
887bdc2678Schristos #define zd(t,s,d)  zs(t, s),  zs((t)+100, d)
897bdc2678Schristos 
907bdc2678Schristos static struct name_val const zone_names[] = {
917bdc2678Schristos 	zs(-1000, "hst"),		/* Hawaii */
927bdc2678Schristos 	zd(-1000,"hast","hadt"),/* Hawaii-Aleutian */
937bdc2678Schristos 	zd(- 900,"akst","akdt"),/* Alaska */
947bdc2678Schristos 	zd(- 800, "pst", "pdt"),/* Pacific */
957bdc2678Schristos 	zd(- 700, "mst", "mdt"),/* Mountain */
967bdc2678Schristos 	zd(- 600, "cst", "cdt"),/* Central */
977bdc2678Schristos 	zd(- 500, "est", "edt"),/* Eastern */
987bdc2678Schristos 	zd(- 400, "ast", "adt"),/* Atlantic */
997bdc2678Schristos 	zd(- 330, "nst", "ndt"),/* Newfoundland */
1007bdc2678Schristos 	zs(  000, "utc"),		/* Coordinated Universal */
1017bdc2678Schristos 	zs(  000, "cut"),		/* " */
1027bdc2678Schristos 	zs(  000,  "ut"),		/* Universal */
1037bdc2678Schristos 	zs(  000,   "z"),		/* Zulu (required by ISO 8601) */
1047bdc2678Schristos 	zd(  000, "gmt", "bst"),/* Greenwich Mean, British Summer */
1057bdc2678Schristos 	zs(  000, "wet"),		/* Western Europe */
1067bdc2678Schristos 	zs(  100, "met"),		/* Middle Europe */
1077bdc2678Schristos 	zs(  100, "cet"),		/* Central Europe */
1087bdc2678Schristos 	zs(  200, "eet"),		/* Eastern Europe */
1097bdc2678Schristos 	zs(  530, "ist"),		/* India */
1107bdc2678Schristos 	zd(  900, "jst", "jdt"),/* Japan */
1117bdc2678Schristos 	zd(  900, "kst", "kdt"),/* Korea */
1127bdc2678Schristos 	zd( 1200,"nzst","nzdt"),/* New Zealand */
1137bdc2678Schristos 	{ "lt", 1 },
1147bdc2678Schristos #if 0
1157bdc2678Schristos 	/* The following names are duplicates or are not well attested.  */
1167bdc2678Schristos 	zs(-1100, "sst"),		/* Samoa */
1177bdc2678Schristos 	zs(-1000, "tht"),		/* Tahiti */
1187bdc2678Schristos 	zs(- 930, "mqt"),		/* Marquesas */
1197bdc2678Schristos 	zs(- 900, "gbt"),		/* Gambier */
1207bdc2678Schristos 	zd(- 900, "yst", "ydt"),/* Yukon - name is no longer used */
1217bdc2678Schristos 	zs(- 830, "pit"),		/* Pitcairn */
1227bdc2678Schristos 	zd(- 500, "cst", "cdt"),/* Cuba */
1237bdc2678Schristos 	zd(- 500, "ast", "adt"),/* Acre */
1247bdc2678Schristos 	zd(- 400, "wst", "wdt"),/* Western Brazil */
1257bdc2678Schristos 	zd(- 400, "ast", "adt"),/* Andes */
1267bdc2678Schristos 	zd(- 400, "cst", "cdt"),/* Chile */
1277bdc2678Schristos 	zs(- 300, "wgt"),		/* Western Greenland */
1287bdc2678Schristos 	zd(- 300, "est", "edt"),/* Eastern South America */
1297bdc2678Schristos 	zs(- 300, "mgt"),		/* Middle Greenland */
1307bdc2678Schristos 	zd(- 200, "fst", "fdt"),/* Fernando de Noronha */
1317bdc2678Schristos 	zs(- 100, "egt"),		/* Eastern Greenland */
1327bdc2678Schristos 	zs(- 100, "aat"),		/* Atlantic Africa */
1337bdc2678Schristos 	zs(- 100, "act"),		/* Azores and Canaries */
1347bdc2678Schristos 	zs(  000, "wat"),		/* West Africa */
1357bdc2678Schristos 	zs(  100, "cat"),		/* Central Africa */
1367bdc2678Schristos 	zd(  100, "mez","mesz"),/* Mittel-Europaeische Zeit */
1377bdc2678Schristos 	zs(  200, "sat"),		/* South Africa */
1387bdc2678Schristos 	zd(  200, "ist", "idt"),/* Israel */
1397bdc2678Schristos 	zs(  300, "eat"),		/* East Africa */
1407bdc2678Schristos 	zd(  300, "ast", "adt"),/* Arabia */
1417bdc2678Schristos 	zd(  300, "msk", "msd"),/* Moscow */
1427bdc2678Schristos 	zd(  330, "ist", "idt"),/* Iran */
1437bdc2678Schristos 	zs(  400, "gst"),		/* Gulf */
1447bdc2678Schristos 	zs(  400, "smt"),		/* Seychelles & Mascarene */
1457bdc2678Schristos 	zd(  400, "esk", "esd"),/* Yekaterinburg */
1467bdc2678Schristos 	zd(  400, "bsk", "bsd"),/* Baku */
1477bdc2678Schristos 	zs(  430, "aft"),		/* Afghanistan */
1487bdc2678Schristos 	zd(  500, "osk", "osd"),/* Omsk */
1497bdc2678Schristos 	zs(  500, "pkt"),		/* Pakistan */
1507bdc2678Schristos 	zd(  500, "tsk", "tsd"),/* Tashkent */
1517bdc2678Schristos 	zs(  545, "npt"),		/* Nepal */
1527bdc2678Schristos 	zs(  600, "bgt"),		/* Bangladesh */
1537bdc2678Schristos 	zd(  600, "nsk", "nsd"),/* Novosibirsk */
1547bdc2678Schristos 	zs(  630, "bmt"),		/* Burma */
1557bdc2678Schristos 	zs(  630, "cct"),		/* Cocos */
1567bdc2678Schristos 	zs(  700, "ict"),		/* Indochina */
1577bdc2678Schristos 	zs(  700, "jvt"),		/* Java */
1587bdc2678Schristos 	zd(  700, "isk", "isd"),/* Irkutsk */
1597bdc2678Schristos 	zs(  800, "hkt"),		/* Hong Kong */
1607bdc2678Schristos 	zs(  800, "pst"),		/* Philippines */
1617bdc2678Schristos 	zs(  800, "sgt"),		/* Singapore */
1627bdc2678Schristos 	zd(  800, "cst", "cdt"),/* China */
1637bdc2678Schristos 	zd(  800, "ust", "udt"),/* Ulan Bator */
1647bdc2678Schristos 	zd(  800, "wst", "wst"),/* Western Australia */
1657bdc2678Schristos 	zd(  800, "ysk", "ysd"),/* Yakutsk */
1667bdc2678Schristos 	zs(  900, "blt"),		/* Belau */
1677bdc2678Schristos 	zs(  900, "mlt"),		/* Moluccas */
1687bdc2678Schristos 	zd(  900, "vsk", "vsd"),/* Vladivostok */
1697bdc2678Schristos 	zd(  930, "cst", "cst"),/* Central Australia */
1707bdc2678Schristos 	zs( 1000, "gst"),		/* Guam */
1717bdc2678Schristos 	zd( 1000, "gsk", "gsd"),/* Magadan */
1727bdc2678Schristos 	zd( 1000, "est", "est"),/* Eastern Australia */
1737bdc2678Schristos 	zd( 1100,"lhst","lhst"),/* Lord Howe */
1747bdc2678Schristos 	zd( 1100, "psk", "psd"),/* Petropavlovsk-Kamchatski */
1757bdc2678Schristos 	zs( 1100,"ncst"),		/* New Caledonia */
1767bdc2678Schristos 	zs( 1130,"nrft"),		/* Norfolk */
1777bdc2678Schristos 	zd( 1200, "ask", "asd"),/* Anadyr */
1787bdc2678Schristos 	zs( 1245,"nz-chat"),	/* Chatham */
1797bdc2678Schristos 	zs( 1300, "tgt"),		/* Tongatapu */
1807bdc2678Schristos #endif
1817bdc2678Schristos 	{"", -1}
1827bdc2678Schristos };
1837bdc2678Schristos 
1847bdc2678Schristos 	static int
lookup(s,table)1857bdc2678Schristos lookup (s, table)
1867bdc2678Schristos 	char const *s;
1877bdc2678Schristos 	struct name_val const table[];
1887bdc2678Schristos /* Look for a prefix of S in TABLE, returning val for first matching entry.  */
1897bdc2678Schristos {
1907bdc2678Schristos 	int j;
1917bdc2678Schristos 	char buf[NAME_LENGTH_MAXIMUM];
1927bdc2678Schristos 
1937bdc2678Schristos 	for (j = 0;  j < NAME_LENGTH_MAXIMUM;  j++) {
1947bdc2678Schristos 		unsigned char c = *s++;
1957bdc2678Schristos 		buf[j] = isupper (c) ? tolower (c) : c;
1967bdc2678Schristos 		if (!isalpha (c))
1977bdc2678Schristos 			break;
1987bdc2678Schristos 	}
1997bdc2678Schristos 	for (;  table[0].name[0];  table++)
2007bdc2678Schristos 		for (j = 0;  buf[j] == table[0].name[j];  )
2017bdc2678Schristos 			if (++j == NAME_LENGTH_MAXIMUM  ||  !table[0].name[j])
2027bdc2678Schristos 				goto done;
2037bdc2678Schristos   done:
2047bdc2678Schristos 	return table[0].val;
2057bdc2678Schristos }
2067bdc2678Schristos 
2077bdc2678Schristos 
2087bdc2678Schristos 	static void
undefine(t)2097bdc2678Schristos undefine (t) struct partime *t;
2107bdc2678Schristos /* Set *T to ``undefined'' values.  */
2117bdc2678Schristos {
2127bdc2678Schristos 	t->tm.tm_sec = t->tm.tm_min = t->tm.tm_hour = t->tm.tm_mday = t->tm.tm_mon
2137bdc2678Schristos 		= t->tm.tm_year = t->tm.tm_wday = t->tm.tm_yday
2147bdc2678Schristos 		= t->ymodulus = t->yweek
2157bdc2678Schristos 		= TM_UNDEFINED;
2167bdc2678Schristos 	t->zone = TM_UNDEFINED_ZONE;
2177bdc2678Schristos }
2187bdc2678Schristos 
2197bdc2678Schristos /*
2207bdc2678Schristos * Array of patterns to look for in a date string.
2217bdc2678Schristos * Order is important: we look for the first matching pattern
2227bdc2678Schristos * whose values do not contradict values that we already know about.
2237bdc2678Schristos * See `parse_pattern_letter' below for the meaning of the pattern codes.
2247bdc2678Schristos */
2257bdc2678Schristos static char const * const patterns[] = {
2267bdc2678Schristos 	/*
2277bdc2678Schristos 	* These traditional patterns must come first,
2287bdc2678Schristos 	* to prevent an ISO 8601 format from misinterpreting their prefixes.
2297bdc2678Schristos 	*/
2307bdc2678Schristos 	"E_n_y", "x", /* RFC 822 */
2317bdc2678Schristos 	"E_n", "n_E", "n", "t:m:s_A", "t:m_A", "t_A", /* traditional */
2327bdc2678Schristos 	"y/N/D$", /* traditional RCS */
2337bdc2678Schristos 
2347bdc2678Schristos 	/* ISO 8601:1988 formats, generalized a bit.  */
2357bdc2678Schristos 	"y-N-D$", "4ND$", "Y-N$",
2367bdc2678Schristos 	"RND$", "-R=N$", "-R$", "--N=D$", "N=DT",
2377bdc2678Schristos 	"--N$", "---D$", "DT",
2387bdc2678Schristos 	"Y-d$", "4d$", "R=d$", "-d$", "dT",
2397bdc2678Schristos 	"y-W-X", "yWX", "y=W",
2407bdc2678Schristos 	"-r-W-X", "r-W-XT", "-rWX", "rWXT", "-W=X", "W=XT", "-W",
2417bdc2678Schristos 	"-w-X", "w-XT", "---X$", "XT", "4$",
2427bdc2678Schristos 	"T",
2437bdc2678Schristos 	"h:m:s$", "hms$", "h:m$", "hm$", "h$", "-m:s$", "-ms$", "-m$", "--s$",
2447bdc2678Schristos 	"Y", "Z",
2457bdc2678Schristos 
2467bdc2678Schristos 	0
2477bdc2678Schristos };
2487bdc2678Schristos 
2497bdc2678Schristos 	static char const *
parse_prefix(str,t,pi)2507bdc2678Schristos parse_prefix (str, t, pi) char const *str; struct partime *t; int *pi;
2517bdc2678Schristos /*
2527bdc2678Schristos * Parse an initial prefix of STR, setting *T accordingly.
2537bdc2678Schristos * Return the first character after the prefix, or 0 if it couldn't be parsed.
2547bdc2678Schristos * Start with pattern *PI; if success, set *PI to the next pattern to try.
2557bdc2678Schristos * Set *PI to -1 if we know there are no more patterns to try;
2567bdc2678Schristos * if *PI is initially negative, give up immediately.
2577bdc2678Schristos */
2587bdc2678Schristos {
2597bdc2678Schristos 	int i = *pi;
2607bdc2678Schristos 	char const *pat;
2617bdc2678Schristos 	unsigned char c;
2627bdc2678Schristos 
2637bdc2678Schristos 	if (i < 0)
2647bdc2678Schristos 		return 0;
2657bdc2678Schristos 
2667bdc2678Schristos 	/* Remove initial noise.  */
2677bdc2678Schristos 	while (!isalnum (c = *str)  &&  c != '-'  &&  c != '+') {
2687bdc2678Schristos 		if (!c) {
2697bdc2678Schristos 			undefine (t);
2707bdc2678Schristos 			*pi = -1;
2717bdc2678Schristos 			return str;
2727bdc2678Schristos 		}
2737bdc2678Schristos 		str++;
2747bdc2678Schristos 	}
2757bdc2678Schristos 
2767bdc2678Schristos 	/* Try a pattern until one succeeds.  */
2777bdc2678Schristos 	while ((pat = patterns[i++]) != 0) {
2787bdc2678Schristos 		char const *s = str;
2797bdc2678Schristos 		undefine (t);
2807bdc2678Schristos 		do {
2817bdc2678Schristos 			if (!(c = *pat++)) {
2827bdc2678Schristos 				*pi = i;
2837bdc2678Schristos 				return s;
2847bdc2678Schristos 			}
2857bdc2678Schristos 		} while ((s = parse_pattern_letter (s, c, t)) != 0);
2867bdc2678Schristos 	}
2877bdc2678Schristos 
2887bdc2678Schristos 	return 0;
2897bdc2678Schristos }
2907bdc2678Schristos 
2917bdc2678Schristos 	static char const *
parse_fixed(s,digits,res)2927bdc2678Schristos parse_fixed (s, digits, res) char const *s; int digits, *res;
2937bdc2678Schristos /*
2947bdc2678Schristos * Parse an initial prefix of S of length DIGITS; it must be a number.
2957bdc2678Schristos * Store the parsed number into *RES.
2967bdc2678Schristos * Return the first character after the prefix, or 0 if it couldn't be parsed.
2977bdc2678Schristos */
2987bdc2678Schristos {
2997bdc2678Schristos 	int n = 0;
3007bdc2678Schristos 	char const *lim = s + digits;
3017bdc2678Schristos 	while (s < lim) {
3027bdc2678Schristos 		unsigned d = *s++ - '0';
3037bdc2678Schristos 		if (9 < d)
3047bdc2678Schristos 			return 0;
3057bdc2678Schristos 		n = 10*n + d;
3067bdc2678Schristos 	}
3077bdc2678Schristos 	*res = n;
3087bdc2678Schristos 	return s;
3097bdc2678Schristos }
3107bdc2678Schristos 
3117bdc2678Schristos 	static char const *
parse_ranged(s,digits,lo,hi,res)3127bdc2678Schristos parse_ranged (s, digits, lo, hi, res) char const *s; int digits, lo, hi, *res;
3137bdc2678Schristos /*
3147bdc2678Schristos * Parse an initial prefix of S of length DIGITS;
3157bdc2678Schristos * it must be a number in the range LO through HI.
3167bdc2678Schristos * Store the parsed number into *RES.
3177bdc2678Schristos * Return the first character after the prefix, or 0 if it couldn't be parsed.
3187bdc2678Schristos */
3197bdc2678Schristos {
3207bdc2678Schristos 	s = parse_fixed (s, digits, res);
3217bdc2678Schristos 	return  s && lo<=*res && *res<=hi  ?  s  :  0;
3227bdc2678Schristos }
3237bdc2678Schristos 
3247bdc2678Schristos 	static char const *
parse_decimal(s,digits,lo,hi,resolution,res,fres)3257bdc2678Schristos parse_decimal (s, digits, lo, hi, resolution, res, fres)
3267bdc2678Schristos 	char const *s;
3277bdc2678Schristos 	int digits, lo, hi, resolution, *res, *fres;
3287bdc2678Schristos /*
3297bdc2678Schristos * Parse an initial prefix of S of length DIGITS;
3307bdc2678Schristos * it must be a number in the range LO through HI
3317bdc2678Schristos * and it may be followed by a fraction that is to be computed using RESOLUTION.
3327bdc2678Schristos * Store the parsed number into *RES; store the fraction times RESOLUTION,
3337bdc2678Schristos * rounded to the nearest integer, into *FRES.
3347bdc2678Schristos * Return the first character after the prefix, or 0 if it couldn't be parsed.
3357bdc2678Schristos */
3367bdc2678Schristos {
3377bdc2678Schristos 	s = parse_fixed (s, digits, res);
3387bdc2678Schristos 	if (s && lo<=*res && *res<=hi) {
3397bdc2678Schristos 		int f = 0;
3407bdc2678Schristos 		if ((s[0]==',' || s[0]=='.')  &&  isdigit ((unsigned char) s[1])) {
3417bdc2678Schristos 			char const *s1 = ++s;
3427bdc2678Schristos 			int num10 = 0, denom10 = 10, product;
3437bdc2678Schristos 			while (isdigit ((unsigned char) *++s))
3447bdc2678Schristos 				denom10 *= 10;
3457bdc2678Schristos 			s = parse_fixed (s1, s - s1, &num10);
3467bdc2678Schristos 			product = num10*resolution;
3477bdc2678Schristos 			f = (product + (denom10>>1)) / denom10;
3487bdc2678Schristos 			f -= f & (product%denom10 == denom10>>1); /* round to even */
3497bdc2678Schristos 			if (f < 0  ||  product/resolution != num10)
3507bdc2678Schristos 				return 0; /* overflow */
3517bdc2678Schristos 		}
3527bdc2678Schristos 		*fres = f;
3537bdc2678Schristos 		return s;
3547bdc2678Schristos 	}
3557bdc2678Schristos 	return 0;
3567bdc2678Schristos }
3577bdc2678Schristos 
3587bdc2678Schristos 	char *
parzone(s,zone)3597bdc2678Schristos parzone (s, zone) char const *s; long *zone;
3607bdc2678Schristos /*
3617bdc2678Schristos * Parse an initial prefix of S; it must denote a time zone.
3627bdc2678Schristos * Set *ZONE to the number of seconds east of GMT,
3637bdc2678Schristos * or to TM_LOCAL_ZONE if it is the local time zone.
3647bdc2678Schristos * Return the first character after the prefix, or 0 if it couldn't be parsed.
3657bdc2678Schristos */
3667bdc2678Schristos {
3677bdc2678Schristos 	char sign;
3687bdc2678Schristos 	int hh, mm, ss;
3697bdc2678Schristos 	int minutesEastOfUTC;
3707bdc2678Schristos 	long offset, z;
3717bdc2678Schristos 
3727bdc2678Schristos 	/*
3737bdc2678Schristos 	* The formats are LT, n, n DST, nDST, no, o
3747bdc2678Schristos 	* where n is a time zone name
3757bdc2678Schristos 	* and o is a time zone offset of the form [-+]hh[:mm[:ss]].
3767bdc2678Schristos 	*/
3777bdc2678Schristos 	switch (*s) {
3787bdc2678Schristos 		case '-': case '+':
3797bdc2678Schristos 			z = 0;
3807bdc2678Schristos 			break;
3817bdc2678Schristos 
3827bdc2678Schristos 		default:
3837bdc2678Schristos 			minutesEastOfUTC = lookup (s, zone_names);
3847bdc2678Schristos 			if (minutesEastOfUTC == -1)
3857bdc2678Schristos 				return 0;
3867bdc2678Schristos 
3877bdc2678Schristos 			/* Don't bother to check rest of spelling.  */
3887bdc2678Schristos 			while (isalpha ((unsigned char) *s))
3897bdc2678Schristos 				s++;
3907bdc2678Schristos 
3917bdc2678Schristos 			/* Don't modify LT.  */
3927bdc2678Schristos 			if (minutesEastOfUTC == 1) {
3937bdc2678Schristos 				*zone = TM_LOCAL_ZONE;
3947bdc2678Schristos 				return (char *) s;
3957bdc2678Schristos 			}
3967bdc2678Schristos 
3977bdc2678Schristos 			z = minutesEastOfUTC * 60L;
3987bdc2678Schristos 
3997bdc2678Schristos 			/* Look for trailing " DST".  */
4007bdc2678Schristos 			if (
4017bdc2678Schristos 				(s[-1]=='T' || s[-1]=='t') &&
4027bdc2678Schristos 				(s[-2]=='S' || s[-2]=='s') &&
4037bdc2678Schristos 				(s[-3]=='D' || s[-3]=='t')
4047bdc2678Schristos 			)
4057bdc2678Schristos 				goto trailing_dst;
4067bdc2678Schristos 			while (isspace ((unsigned char) *s))
4077bdc2678Schristos 				s++;
4087bdc2678Schristos 			if (
4097bdc2678Schristos 				(s[0]=='D' || s[0]=='d') &&
4107bdc2678Schristos 				(s[1]=='S' || s[1]=='s') &&
4117bdc2678Schristos 				(s[2]=='T' || s[2]=='t')
4127bdc2678Schristos 			) {
4137bdc2678Schristos 				s += 3;
4147bdc2678Schristos 			  trailing_dst:
4157bdc2678Schristos 				*zone = z + 60*60;
4167bdc2678Schristos 				return (char *) s;
4177bdc2678Schristos 			}
4187bdc2678Schristos 
4197bdc2678Schristos 			switch (*s) {
4207bdc2678Schristos 				case '-': case '+': break;
4217bdc2678Schristos 				default: return (char *) s;
4227bdc2678Schristos 			}
4237bdc2678Schristos 	}
4247bdc2678Schristos 	sign = *s++;
4257bdc2678Schristos 
4267bdc2678Schristos 	if (!(s = parse_ranged (s, 2, 0, 23, &hh)))
4277bdc2678Schristos 		return 0;
4287bdc2678Schristos 	mm = ss = 0;
4297bdc2678Schristos 	if (*s == ':')
4307bdc2678Schristos 		s++;
4317bdc2678Schristos 	if (isdigit ((unsigned char) *s)) {
4327bdc2678Schristos 		if (!(s = parse_ranged (s, 2, 0, 59, &mm)))
4337bdc2678Schristos 			return 0;
4347bdc2678Schristos 		if (*s==':' && s[-3]==':' && isdigit ((unsigned char) s[1])) {
4357bdc2678Schristos 			if (!(s = parse_ranged (s + 1, 2, 0, 59, &ss)))
4367bdc2678Schristos 				return 0;
4377bdc2678Schristos 		}
4387bdc2678Schristos 	}
4397bdc2678Schristos 	if (isdigit ((unsigned char) *s))
4407bdc2678Schristos 		return 0;
4417bdc2678Schristos 	offset = (hh*60 + mm)*60L + ss;
4427bdc2678Schristos 	*zone = z + (sign=='-' ? -offset : offset);
4437bdc2678Schristos 	/*
4447bdc2678Schristos 	* ?? Are fractions allowed here?
4457bdc2678Schristos 	* If so, they're not implemented.
4467bdc2678Schristos 	*/
4477bdc2678Schristos 	return (char *) s;
4487bdc2678Schristos }
4497bdc2678Schristos 
4507bdc2678Schristos 	static char const *
parse_pattern_letter(s,c,t)4517bdc2678Schristos parse_pattern_letter (s, c, t) char const *s; int c; struct partime *t;
4527bdc2678Schristos /*
4537bdc2678Schristos * Parse an initial prefix of S, matching the pattern whose code is C.
4547bdc2678Schristos * Set *T accordingly.
4557bdc2678Schristos * Return the first character after the prefix, or 0 if it couldn't be parsed.
4567bdc2678Schristos */
4577bdc2678Schristos {
4587bdc2678Schristos 	switch (c) {
4597bdc2678Schristos 		case '$': /* The next character must be a non-digit.  */
4607bdc2678Schristos 			if (isdigit ((unsigned char) *s))
4617bdc2678Schristos 				return 0;
4627bdc2678Schristos 			break;
4637bdc2678Schristos 
4647bdc2678Schristos 		case '-': case '/': case ':':
4657bdc2678Schristos 			/* These characters stand for themselves.  */
4667bdc2678Schristos 			if (*s++ != c)
4677bdc2678Schristos 				return 0;
4687bdc2678Schristos 			break;
4697bdc2678Schristos 
4707bdc2678Schristos 		case '4': /* 4-digit year */
4717bdc2678Schristos 			s = parse_fixed (s, 4, &t->tm.tm_year);
4727bdc2678Schristos 			break;
4737bdc2678Schristos 
4747bdc2678Schristos 		case '=': /* optional '-' */
4757bdc2678Schristos 			s  +=  *s == '-';
4767bdc2678Schristos 			break;
4777bdc2678Schristos 
4787bdc2678Schristos 		case 'A': /* AM or PM */
4797bdc2678Schristos 			/*
4807bdc2678Schristos 			* This matches the regular expression [AaPp][Mm]?.
4817bdc2678Schristos 			* It must not be followed by a letter or digit;
4827bdc2678Schristos 			* otherwise it would match prefixes of strings like "PST".
4837bdc2678Schristos 			*/
4847bdc2678Schristos 			switch (*s++) {
4857bdc2678Schristos 				case 'A': case 'a':
4867bdc2678Schristos 					if (t->tm.tm_hour == 12)
4877bdc2678Schristos 						t->tm.tm_hour = 0;
4887bdc2678Schristos 					break;
4897bdc2678Schristos 
4907bdc2678Schristos 				case 'P': case 'p':
4917bdc2678Schristos 					if (t->tm.tm_hour != 12)
4927bdc2678Schristos 						t->tm.tm_hour += 12;
4937bdc2678Schristos 					break;
4947bdc2678Schristos 
4957bdc2678Schristos 				default: return 0;
4967bdc2678Schristos 			}
4977bdc2678Schristos 			switch (*s) {
4987bdc2678Schristos 				case 'M': case 'm': s++; break;
4997bdc2678Schristos 			}
5007bdc2678Schristos 			if (isalnum (*s))
5017bdc2678Schristos 				return 0;
5027bdc2678Schristos 			break;
5037bdc2678Schristos 
5047bdc2678Schristos 		case 'D': /* day of month [01-31] */
5057bdc2678Schristos 			s = parse_ranged (s, 2, 1, 31, &t->tm.tm_mday);
5067bdc2678Schristos 			break;
5077bdc2678Schristos 
5087bdc2678Schristos 		case 'd': /* day of year [001-366] */
5097bdc2678Schristos 			s = parse_ranged (s, 3, 1, 366, &t->tm.tm_yday);
5107bdc2678Schristos 			t->tm.tm_yday--;
5117bdc2678Schristos 			break;
5127bdc2678Schristos 
5137bdc2678Schristos 		case 'E': /* extended day of month [1-9, 01-31] */
5147bdc2678Schristos 			s = parse_ranged (s, (
5157bdc2678Schristos 				isdigit ((unsigned char) s[0]) &&
5167bdc2678Schristos 				isdigit ((unsigned char) s[1])
5177bdc2678Schristos 			) + 1, 1, 31, &t->tm.tm_mday);
5187bdc2678Schristos 			break;
5197bdc2678Schristos 
5207bdc2678Schristos 		case 'h': /* hour [00-23 followed by optional fraction] */
5217bdc2678Schristos 			{
5227bdc2678Schristos 				int frac;
5237bdc2678Schristos 				s = parse_decimal (s, 2, 0, 23, 60*60, &t->tm.tm_hour, &frac);
5247bdc2678Schristos 				t->tm.tm_min = frac / 60;
5257bdc2678Schristos 				t->tm.tm_sec = frac % 60;
5267bdc2678Schristos 			}
5277bdc2678Schristos 			break;
5287bdc2678Schristos 
5297bdc2678Schristos 		case 'm': /* minute [00-59 followed by optional fraction] */
5307bdc2678Schristos 			s = parse_decimal (s, 2, 0, 59, 60, &t->tm.tm_min, &t->tm.tm_sec);
5317bdc2678Schristos 			break;
5327bdc2678Schristos 
5337bdc2678Schristos 		case 'n': /* month name [e.g. "Jan"] */
5347bdc2678Schristos 			if (!TM_DEFINED (t->tm.tm_mon = lookup (s, month_names)))
5357bdc2678Schristos 				return 0;
5367bdc2678Schristos 			/* Don't bother to check rest of spelling.  */
5377bdc2678Schristos 			while (isalpha ((unsigned char) *s))
5387bdc2678Schristos 				s++;
5397bdc2678Schristos 			break;
5407bdc2678Schristos 
5417bdc2678Schristos 		case 'N': /* month [01-12] */
5427bdc2678Schristos 			s = parse_ranged (s, 2, 1, 12, &t->tm.tm_mon);
5437bdc2678Schristos 			t->tm.tm_mon--;
5447bdc2678Schristos 			break;
5457bdc2678Schristos 
5467bdc2678Schristos 		case 'r': /* year % 10 (remainder in origin-0 decade) [0-9] */
5477bdc2678Schristos 			s = parse_fixed (s, 1, &t->tm.tm_year);
5487bdc2678Schristos 			t->ymodulus = 10;
5497bdc2678Schristos 			break;
5507bdc2678Schristos 
5517bdc2678Schristos 		case_R:
5527bdc2678Schristos 		case 'R': /* year % 100 (remainder in origin-0 century) [00-99] */
5537bdc2678Schristos 			s = parse_fixed (s, 2, &t->tm.tm_year);
5547bdc2678Schristos 			t->ymodulus = 100;
5557bdc2678Schristos 			break;
5567bdc2678Schristos 
5577bdc2678Schristos 		case 's': /* second [00-60 followed by optional fraction] */
5587bdc2678Schristos 			{
5597bdc2678Schristos 				int frac;
5607bdc2678Schristos 				s = parse_decimal (s, 2, 0, 60, 1, &t->tm.tm_sec, &frac);
5617bdc2678Schristos 				t->tm.tm_sec += frac;
5627bdc2678Schristos 			}
5637bdc2678Schristos 			break;
5647bdc2678Schristos 
5657bdc2678Schristos 		case 'T': /* 'T' or 't' */
5667bdc2678Schristos 			switch (*s++) {
5677bdc2678Schristos 				case 'T': case 't': break;
5687bdc2678Schristos 				default: return 0;
5697bdc2678Schristos 			}
5707bdc2678Schristos 			break;
5717bdc2678Schristos 
5727bdc2678Schristos 		case 't': /* traditional hour [1-9 or 01-12] */
5737bdc2678Schristos 			s = parse_ranged (s, (
5747bdc2678Schristos 				isdigit ((unsigned char) s[0]) && isdigit ((unsigned char) s[1])
5757bdc2678Schristos 			) + 1, 1, 12, &t->tm.tm_hour);
5767bdc2678Schristos 			break;
5777bdc2678Schristos 
5787bdc2678Schristos 		case 'w': /* 'W' or 'w' only (stands for current week) */
5797bdc2678Schristos 			switch (*s++) {
5807bdc2678Schristos 				case 'W': case 'w': break;
5817bdc2678Schristos 				default: return 0;
5827bdc2678Schristos 			}
5837bdc2678Schristos 			break;
5847bdc2678Schristos 
5857bdc2678Schristos 		case 'W': /* 'W' or 'w', followed by a week of year [00-53] */
5867bdc2678Schristos 			switch (*s++) {
5877bdc2678Schristos 				case 'W': case 'w': break;
5887bdc2678Schristos 				default: return 0;
5897bdc2678Schristos 			}
5907bdc2678Schristos 			s = parse_ranged (s, 2, 0, 53, &t->yweek);
5917bdc2678Schristos 			break;
5927bdc2678Schristos 
5937bdc2678Schristos 		case 'X': /* weekday (1=Mon ... 7=Sun) [1-7] */
5947bdc2678Schristos 			s = parse_ranged (s, 1, 1, 7, &t->tm.tm_wday);
5957bdc2678Schristos 			t->tm.tm_wday--;
5967bdc2678Schristos 			break;
5977bdc2678Schristos 
5987bdc2678Schristos 		case 'x': /* weekday name [e.g. "Sun"] */
5997bdc2678Schristos 			if (!TM_DEFINED (t->tm.tm_wday = lookup (s, weekday_names)))
6007bdc2678Schristos 				return 0;
6017bdc2678Schristos 			/* Don't bother to check rest of spelling.  */
6027bdc2678Schristos 			while (isalpha ((unsigned char) *s))
6037bdc2678Schristos 				s++;
6047bdc2678Schristos 			break;
6057bdc2678Schristos 
6067bdc2678Schristos 		case 'y': /* either R or Y */
6077bdc2678Schristos 			if (
6087bdc2678Schristos 				isdigit ((unsigned char) s[0]) &&
6097bdc2678Schristos 				isdigit ((unsigned char) s[1]) &&
6107bdc2678Schristos 				!isdigit ((unsigned char) s[2])
6117bdc2678Schristos 			)
6127bdc2678Schristos 				goto case_R;
6137bdc2678Schristos 			/* fall into */
6147bdc2678Schristos 		case 'Y': /* year in full [4 or more digits] */
6157bdc2678Schristos 			{
6167bdc2678Schristos 				int len = 0;
6177bdc2678Schristos 				while (isdigit ((unsigned char) s[len]))
6187bdc2678Schristos 					len++;
6197bdc2678Schristos 				if (len < 4)
6207bdc2678Schristos 					return 0;
6217bdc2678Schristos 				s = parse_fixed (s, len, &t->tm.tm_year);
6227bdc2678Schristos 			}
6237bdc2678Schristos 			break;
6247bdc2678Schristos 
6257bdc2678Schristos 		case 'Z': /* time zone */
6267bdc2678Schristos 			s = parzone (s, &t->zone);
6277bdc2678Schristos 			break;
6287bdc2678Schristos 
6297bdc2678Schristos 		case '_': /* possibly empty sequence of non-alphanumerics */
6307bdc2678Schristos 			while (!isalnum (*s)  &&  *s)
6317bdc2678Schristos 				s++;
6327bdc2678Schristos 			break;
6337bdc2678Schristos 
6347bdc2678Schristos 		default: /* bad pattern */
6357bdc2678Schristos 			return 0;
6367bdc2678Schristos 	}
6377bdc2678Schristos 	return s;
6387bdc2678Schristos }
6397bdc2678Schristos 
6407bdc2678Schristos 	static int
merge_partime(t,u)6417bdc2678Schristos merge_partime (t, u) struct partime *t; struct partime const *u;
6427bdc2678Schristos /*
6437bdc2678Schristos * If there is no conflict, merge into *T the additional information in *U
6447bdc2678Schristos * and return 0.  Otherwise do nothing and return -1.
6457bdc2678Schristos */
6467bdc2678Schristos {
6477bdc2678Schristos #	define conflict(a,b) ((a) != (b)  &&  TM_DEFINED (a)  &&  TM_DEFINED (b))
6487bdc2678Schristos 	if (
6497bdc2678Schristos 		conflict (t->tm.tm_sec, u->tm.tm_sec) ||
6507bdc2678Schristos 		conflict (t->tm.tm_min, u->tm.tm_min) ||
6517bdc2678Schristos 		conflict (t->tm.tm_hour, u->tm.tm_hour) ||
6527bdc2678Schristos 		conflict (t->tm.tm_mday, u->tm.tm_mday) ||
6537bdc2678Schristos 		conflict (t->tm.tm_mon, u->tm.tm_mon) ||
6547bdc2678Schristos 		conflict (t->tm.tm_year, u->tm.tm_year) ||
6557bdc2678Schristos 		conflict (t->tm.tm_wday, u->tm.tm_yday) ||
6567bdc2678Schristos 		conflict (t->ymodulus, u->ymodulus) ||
6577bdc2678Schristos 		conflict (t->yweek, u->yweek) ||
6587bdc2678Schristos 		(
6597bdc2678Schristos 			t->zone != u->zone &&
6607bdc2678Schristos 			t->zone != TM_UNDEFINED_ZONE &&
6617bdc2678Schristos 			u->zone != TM_UNDEFINED_ZONE
6627bdc2678Schristos 		)
6637bdc2678Schristos 	)
6647bdc2678Schristos 		return -1;
6657bdc2678Schristos #	undef conflict
6667bdc2678Schristos #	define merge_(a,b) if (TM_DEFINED (b)) (a) = (b);
6677bdc2678Schristos 	merge_ (t->tm.tm_sec, u->tm.tm_sec)
6687bdc2678Schristos 	merge_ (t->tm.tm_min, u->tm.tm_min)
6697bdc2678Schristos 	merge_ (t->tm.tm_hour, u->tm.tm_hour)
6707bdc2678Schristos 	merge_ (t->tm.tm_mday, u->tm.tm_mday)
6717bdc2678Schristos 	merge_ (t->tm.tm_mon, u->tm.tm_mon)
6727bdc2678Schristos 	merge_ (t->tm.tm_year, u->tm.tm_year)
6737bdc2678Schristos 	merge_ (t->tm.tm_wday, u->tm.tm_yday)
6747bdc2678Schristos 	merge_ (t->ymodulus, u->ymodulus)
6757bdc2678Schristos 	merge_ (t->yweek, u->yweek)
6767bdc2678Schristos #	undef merge_
6777bdc2678Schristos 	if (u->zone != TM_UNDEFINED_ZONE) t->zone = u->zone;
6787bdc2678Schristos 	return 0;
6797bdc2678Schristos }
6807bdc2678Schristos 
6817bdc2678Schristos 	char *
partime(s,t)6827bdc2678Schristos partime (s, t) char const *s; struct partime *t;
6837bdc2678Schristos /*
6847bdc2678Schristos * Parse a date/time prefix of S, putting the parsed result into *T.
6857bdc2678Schristos * Return the first character after the prefix.
6867bdc2678Schristos * The prefix may contain no useful information;
6877bdc2678Schristos * in that case, *T will contain only undefined values.
6887bdc2678Schristos */
6897bdc2678Schristos {
6907bdc2678Schristos 	struct partime p;
6917bdc2678Schristos 
6927bdc2678Schristos 	undefine (t);
6937bdc2678Schristos 	while (*s) {
6947bdc2678Schristos 		int i = 0;
6957bdc2678Schristos 		char const *s1;
6967bdc2678Schristos 		do {
6977bdc2678Schristos 			if (!(s1 = parse_prefix (s, &p, &i)))
6987bdc2678Schristos 				return (char *) s;
6997bdc2678Schristos 		} while (merge_partime (t, &p) != 0);
7007bdc2678Schristos 		s = s1;
7017bdc2678Schristos 	}
7027bdc2678Schristos 	return (char *) s;
7037bdc2678Schristos }
704