/* Parse a string, yielding a struct partime that describes it. */ /* Copyright 1993, 1994, 1995, 1997 Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. RCS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. RCS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RCS; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Report problems and direct all questions to: rcs-bugs@cs.purdue.edu */ #if has_conf_h# include <conf.h>#else# if HAVE_CONFIG_H# include <config.h># else# ifndef __STDC__# define const# endif# endif# if HAVE_LIMITS_H# include <limits.h># endif# ifndef LONG_MIN# define LONG_MIN (-1-2147483647L)# endif# if STDC_HEADERS# include <stdlib.h># endif# include <time.h># ifdef __STDC__# define P(x) x# else# define P(x) ()# endif#endif #include <ctype.h>#if STDC_HEADERS# define CTYPE_DOMAIN(c) 1#else# define CTYPE_DOMAIN(c) ((unsigned) (c) <= 0177)#endif#define ISALNUM(c) (CTYPE_DOMAIN (c) && isalnum (c))#define ISALPHA(c) (CTYPE_DOMAIN (c) && isalpha (c))#define ISSPACE(c) (CTYPE_DOMAIN (c) && isspace (c))#define ISUPPER(c) (CTYPE_DOMAIN (c) && isupper (c))#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) #include <partime.h> char const partimeId[] = "$Id: partime.c,v 5.16 1997/05/19 06:33:53 eggert Exp $"; /* Lookup tables for names of months, weekdays, time zones. */ #define NAME_LENGTH_MAXIMUM 4 struct name_val { char name[NAME_LENGTH_MAXIMUM]; int val; }; static char const *parse_decimal P ((char const *, int, int, int, int, int *, int *));static char const *parse_fixed P ((char const *, int, int *));static char const *parse_pattern_letter P ((char const *, int, struct partime *));static char const *parse_prefix P ((char const *, struct partime *, int *));static char const *parse_ranged P ((char const *, int, int, int, int *));static int lookup P ((char const *, struct name_val const[]));static int merge_partime P ((struct partime *, struct partime const *));static void undefine P ((struct partime *)); static struct name_val const month_names[] ={ {"jan", 0}, {"feb", 1}, {"mar", 2}, {"apr", 3}, {"may", 4}, {"jun", 5}, {"jul", 6}, {"aug", 7}, {"sep", 8}, {"oct", 9}, {"nov", 10}, {"dec", 11}, {"", TM_UNDEFINED}}; static struct name_val const weekday_names[] ={ {"sun", 0}, {"mon", 1}, {"tue", 2}, {"wed", 3}, {"thu", 4}, {"fri", 5}, {"sat", 6}, {"", TM_UNDEFINED}}; #define hr60nonnegative(t) ((t)/100 * 60 + (t)%100)#define hr60(t) ((t)<0 ? -hr60nonnegative(-(t)) : hr60nonnegative(t))#define zs(t,s) {s, hr60(t)}#define zd(t,s,d) zs(t, s), zs((t)+100, d) static struct name_val const zone_names[] ={ zs (-1000, "hst"), /* Hawaii */ zd (-1000, "hast", "hadt"), /* Hawaii-Aleutian */ zd (- 900, "akst", "akdt"), /* Alaska */ zd (- 800, "pst" , "pdt" ), /* Pacific */ zd (- 700, "mst" , "mdt" ), /* Mountain */ zd (- 600, "cst" , "cdt" ), /* Central */ zd (- 500, "est" , "edt" ), /* Eastern */ zd (- 400, "ast" , "adt" ), /* Atlantic */ zd (- 330, "nst" , "ndt" ), /* Newfoundland */ zs ( 000, "utc" ), /* Coordinated Universal */ zs ( 000, "uct" ), /* " */ zs ( 000, "cut" ), /* " */ zs ( 000, "ut"), /* Universal */ zs ( 000, "z"), /* Zulu (required by ISO 8601) */ zd ( 000, "gmt" , "bst" ), /* Greenwich Mean, British Summer */ zd ( 000, "wet" , "west"), /* Western European */ zd ( 100, "cet" , "cest"), /* Central European */ zd ( 100, "met" , "mest"), /* Middle European (bug in old tz versions) */ zd ( 100, "mez" , "mesz"), /* Mittel-Europaeische Zeit */ zd ( 200, "eet" , "eest"), /* Eastern European */ zs ( 530, "ist" ), /* India */ zd ( 900, "jst" , "jdt" ), /* Japan */ zd ( 900, "kst" , "kdt" ), /* Korea */ zd ( 1200, "nzst", "nzdt"), /* New Zealand */ {"lt", 1},#if 0 /* The following names are duplicates or are not well attested. There are lots more where these came from. */ zs (-1100, "sst" ), /* Samoan */ zd (- 900, "yst" , "ydt" ), /* Yukon - name is no longer used */ zd (- 500, "ast" , "adt" ), /* Acre */ zd (- 400, "wst" , "wdt" ), /* Western Brazil */ zd (- 400, "cst" , "cdt" ), /* Chile */ zd (- 200, "fst" , "fdt" ), /* Fernando de Noronha */ zs ( 000, "wat" ), /* West African */ zs ( 100, "cat" ), /* Central African */ zs ( 200, "sat" ), /* South African */ zd ( 200, "ist" , "idt" ), /* Israel */ zs ( 300, "eat" ), /* East African */ zd ( 300, "msk" , "msd" ), /* Moscow */ zd ( 330, "ist" , "idt" ), /* Iran */ zs ( 800, "hkt" ), /* Hong Kong */ zs ( 800, "sgt" ), /* Singapore */ zd ( 800, "cst" , "cdt" ), /* China */ zd ( 800, "wst" , "wst" ), /* Western Australia */ zd ( 930, "cst" , "cst" ), /* Central Australia */ zs ( 1000, "gst" ), /* Guam */ zd ( 1000, "est" , "est" ), /* Eastern Australia */#endif {"", -1}}; /* Look for a prefix of S in TABLE, returning val for first matching entry. */static intlookup (s, table) char const *s; struct name_val const table[];{ int j; char buf[NAME_LENGTH_MAXIMUM]; for (j = 0; j < NAME_LENGTH_MAXIMUM; j++) { unsigned char c = *s++; if (! ISALPHA (c)) { buf[j] = '\0'; break; } buf[j] = ISUPPER (c) ? tolower (c) : c; } for (;; table++) for (j = 0; ; j++) if (j == NAME_LENGTH_MAXIMUM || ! table[0].name[j]) return table[0].val; else if (buf[j] != table[0].name[j]) break;} /* Set *T to ``undefined'' values. */static voidundefine (t) struct partime *t;{ t->tm.tm_sec = t->tm.tm_min = t->tm.tm_hour = t->tm.tm_mday = t->tm.tm_mon = t->tm.tm_year = t->tm.tm_wday = t->tm.tm_yday = t->ymodulus = t->yweek = TM_UNDEFINED; t->zone = TM_UNDEFINED_ZONE;} /* Array of patterns to look for in a date string. Order is important: we look for the first matching pattern whose values do not contradict values that we already know about. See `parse_pattern_letter' below for the meaning of the pattern codes. */static char const *const patterns[] ={ /* These traditional patterns must come first, to prevent an ISO 8601 format from misinterpreting their prefixes. */ "E_n_y", "x", /* RFC 822 */ "E_n", "n_E", "n", "t:m:s_A", "t:m_A", "t_A", /* traditional */ "y/N/D$", /* traditional RCS */ /* ISO 8601:1988 formats, generalized a bit. */ "y-N-D$", "4ND$", "Y-N$", "RND$", "-R=N$", "-R$", "--N=D$", "N=DT", "--N$", "---D$", "DT", "Y-d$", "4d$", "R=d$", "-d$", "dT", "y-W-X", "yWX", "y=W", "-r-W-X", "r-W-XT", "-rWX", "rWXT", "-W=X", "W=XT", "-W", "-w-X", "w-XT", "---X$", "XT", "4$", "T", "h:m:s$", "hms$", "h:m$", "hm$", "h$", "-m:s$", "-ms$", "-m$", "--s$", "Y", "Z", 0}; /* Parse an initial prefix of STR, setting *T accordingly. Return the first character after the prefix, or 0 if it couldn't be parsed. Start with pattern *PI; if success, set *PI to the next pattern to try. Set *PI to -1 if we know there are no more patterns to try; if *PI is initially negative, give up immediately. */static char const *parse_prefix (str, t, pi) char const *str; struct partime *t; int *pi;{ int i = *pi; char const *pat; unsigned char c; if (i < 0) return 0; /* Remove initial noise. */ while (! ISALNUM (c = *str) && c != '-' && c != '+') { if (! c) { undefine (t); *pi = -1; return str; } str++; } /* Try a pattern until one succeeds. */ while ((pat = patterns[i++]) != 0) { char const *s = str; undefine (t); do { if (! (c = *pat++)) { *pi = i; return s; } } while ((s = parse_pattern_letter (s, c, t)) != 0); } return 0;} /* Parse an initial prefix of S of length DIGITS; it must be a number. Store the parsed number into *RES. Return the first character after the prefix, or 0 if it wasn't parsed. */static char const *parse_fixed (s, digits, res) char const *s; int digits, *res;{ int n = 0; char const *lim = s + digits; while (s < lim) { unsigned d = *s++ - '0'; if (9 < d) return 0; n = 10 * n + d; } *res = n; return s;} /* Parse an initial prefix of S of length DIGITS; it must be a number in the range LO through HI. Store the parsed number into *RES. Return the first character after the prefix, or 0 if it wasn't parsed. */static char const *parse_ranged (s, digits, lo, hi, res) char const *s; int digits, lo, hi, *res;{ s = parse_fixed (s, digits, res); return s && lo <= *res && *res <= hi ? s : 0;} /* Parse an initial prefix of S of length DIGITS; it must be a number in the range LO through HI and it may be followed by a fraction to be computed using RESOLUTION. Store the parsed number into *RES; store the fraction times RESOLUTION, rounded to the nearest integer, into *FRES. Return the first character after the prefix, or 0 if it wasn't parsed. */static char const *parse_decimal (s, digits, lo, hi, resolution, res, fres) char const *s; int digits, lo, hi, resolution, *res, *fres;{ s = parse_fixed (s, digits, res); if (s && lo <= *res && *res <= hi) { int f = 0; if ((s[0] == ',' || s[0] == '.') && ISDIGIT (s[1])) { char const *s1 = ++s; int num10 = 0, denom10 = 10, product; while (ISDIGIT (*++s)) { int d = denom10 * 10; if (d / 10 != denom10) return 0; /* overflow */ denom10 = d; } s = parse_fixed (s1, (int) (s - s1), &num10); product = num10 * resolution; f = (product + (denom10 >> 1)) / denom10; f -= f & (product % denom10 == denom10 >> 1); /* round to even */ if (f < 0 || product/resolution != num10) return 0; /* overflow */ } *fres = f; return s; } return 0;} /* Parse an initial prefix of S; it must denote a time zone. Set *ZONE to the number of seconds east of GMT, or to TM_LOCAL_ZONE if it is the local time zone. Return the first character after the prefix, or 0 if it wasn't parsed. */char *parzone (s, zone) char const *s; long *zone;{ char sign; int hh, mm, ss; int minutesEastOfUTC; long offset, z; /* The formats are LT, n, n DST, nDST, no, o where n is a time zone name and o is a time zone offset of the form [-+]hh[:mm[:ss]]. */ switch (*s) { case '-': case '+': z = 0; break; default: minutesEastOfUTC = lookup (s, zone_names); if (minutesEastOfUTC == -1) return 0; /* Don't bother to check rest of spelling. */ while (ISALPHA ((unsigned char) *s)) s++; /* Don't modify LT. */ if (minutesEastOfUTC == 1) { *zone = TM_LOCAL_ZONE; return (char *) s; } z = minutesEastOfUTC * 60L; /* Look for trailing " DST". */ if ((s[-1] == 'T' || s[-1] == 't') && (s[-2] == 'S' || s[-2] == 's') && (s[-3] == 'D' || s[-3] == 'd')) goto trailing_dst; while (ISSPACE ((unsigned char) *s)) s++; if ((s[0] == 'D' || s[0] == 'd') && (s[1] == 'S' || s[1] == 's') && (s[2] == 'T' || s[2] == 't')) { s += 3; trailing_dst: *zone = z + 60*60; return (char *) s; } switch (*s) { case '-': case '+': break; default: *zone = z; return (char *) s; } break; } sign = *s++; if (! (s = parse_ranged (s, 2, 0, 23, &hh))) return 0; mm = ss = 0; if (*s == ':') s++; if (ISDIGIT (*s)) { if (! (s = parse_ranged (s, 2, 0, 59, &mm))) return 0; if (*s == ':' && s[-3] == ':' && ISDIGIT (s[1]) && ! (s = parse_ranged (s + 1, 2, 0, 59, &ss))) return 0; } if (ISDIGIT (*s)) return 0; offset = (hh * 60 + mm) * 60L + ss; *zone = z + (sign == '-' ? -offset : offset); /* ?? Are fractions allowed here? If so, they're not implemented. */ return (char *) s;} /* Parse an initial prefix of S, matching the pattern whose code is C. Set *T accordingly. Return the first character after the prefix, or 0 if it wasn't parsed. */static char const *parse_pattern_letter (s, c, t) char const *s; int c; struct partime *t;{ switch (c) { case '$': /* The next character must be a non-digit. */ if (ISDIGIT (*s)) return 0; break; case '-': case '/': case ':': /* These characters stand for themselves. */ if (*s++ != c) return 0; break; case '4': /* 4-digit year */ s = parse_fixed (s, 4, &t->tm.tm_year); break; case '=': /* optional '-' */ s += *s == '-'; break; Lines 1–500 of 742. Load more
/* Parse a string, yielding a struct partime that describes it. */ /* Copyright 1993, 1994, 1995, 1997 Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. RCS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. RCS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RCS; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Report problems and direct all questions to: rcs-bugs@cs.purdue.edu */ #if has_conf_h # include <conf.h> #else # if HAVE_CONFIG_H # include <config.h> # else # ifndef __STDC__ # define const # endif # endif # if HAVE_LIMITS_H # include <limits.h> # endif # ifndef LONG_MIN # define LONG_MIN (-1-2147483647L) # endif # if STDC_HEADERS # include <stdlib.h> # endif # include <time.h> # ifdef __STDC__ # define P(x) x # else # define P(x) () # endif #endif #include <ctype.h> #if STDC_HEADERS # define CTYPE_DOMAIN(c) 1 #else # define CTYPE_DOMAIN(c) ((unsigned) (c) <= 0177) #endif #define ISALNUM(c) (CTYPE_DOMAIN (c) && isalnum (c)) #define ISALPHA(c) (CTYPE_DOMAIN (c) && isalpha (c)) #define ISSPACE(c) (CTYPE_DOMAIN (c) && isspace (c)) #define ISUPPER(c) (CTYPE_DOMAIN (c) && isupper (c)) #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) #include <partime.h> char const partimeId[] = "$Id: partime.c,v 5.16 1997/05/19 06:33:53 eggert Exp $"; /* Lookup tables for names of months, weekdays, time zones. */ #define NAME_LENGTH_MAXIMUM 4 struct name_val { char name[NAME_LENGTH_MAXIMUM]; int val; }; static char const *parse_decimal P ((char const *, int, int, int, int, int *, int *)); static char const *parse_fixed P ((char const *, int, int *)); static char const *parse_pattern_letter P ((char const *, int, struct partime *)); static char const *parse_prefix P ((char const *, struct partime *, int *)); static char const *parse_ranged P ((char const *, int, int, int, int *)); static int lookup P ((char const *, struct name_val const[])); static int merge_partime P ((struct partime *, struct partime const *)); static void undefine P ((struct partime *)); static struct name_val const month_names[] = { {"jan", 0}, {"feb", 1}, {"mar", 2}, {"apr", 3}, {"may", 4}, {"jun", 5}, {"jul", 6}, {"aug", 7}, {"sep", 8}, {"oct", 9}, {"nov", 10}, {"dec", 11}, {"", TM_UNDEFINED} }; static struct name_val const weekday_names[] = { {"sun", 0}, {"mon", 1}, {"tue", 2}, {"wed", 3}, {"thu", 4}, {"fri", 5}, {"sat", 6}, {"", TM_UNDEFINED} }; #define hr60nonnegative(t) ((t)/100 * 60 + (t)%100) #define hr60(t) ((t)<0 ? -hr60nonnegative(-(t)) : hr60nonnegative(t)) #define zs(t,s) {s, hr60(t)} #define zd(t,s,d) zs(t, s), zs((t)+100, d) static struct name_val const zone_names[] = { zs (-1000, "hst"), /* Hawaii */ zd (-1000, "hast", "hadt"), /* Hawaii-Aleutian */ zd (- 900, "akst", "akdt"), /* Alaska */ zd (- 800, "pst" , "pdt" ), /* Pacific */ zd (- 700, "mst" , "mdt" ), /* Mountain */ zd (- 600, "cst" , "cdt" ), /* Central */ zd (- 500, "est" , "edt" ), /* Eastern */ zd (- 400, "ast" , "adt" ), /* Atlantic */ zd (- 330, "nst" , "ndt" ), /* Newfoundland */ zs ( 000, "utc" ), /* Coordinated Universal */ zs ( 000, "uct" ), /* " */ zs ( 000, "cut" ), /* " */ zs ( 000, "ut"), /* Universal */ zs ( 000, "z"), /* Zulu (required by ISO 8601) */ zd ( 000, "gmt" , "bst" ), /* Greenwich Mean, British Summer */ zd ( 000, "wet" , "west"), /* Western European */ zd ( 100, "cet" , "cest"), /* Central European */ zd ( 100, "met" , "mest"), /* Middle European (bug in old tz versions) */ zd ( 100, "mez" , "mesz"), /* Mittel-Europaeische Zeit */ zd ( 200, "eet" , "eest"), /* Eastern European */ zs ( 530, "ist" ), /* India */ zd ( 900, "jst" , "jdt" ), /* Japan */ zd ( 900, "kst" , "kdt" ), /* Korea */ zd ( 1200, "nzst", "nzdt"), /* New Zealand */ {"lt", 1}, #if 0 /* The following names are duplicates or are not well attested. There are lots more where these came from. */ zs (-1100, "sst" ), /* Samoan */ zd (- 900, "yst" , "ydt" ), /* Yukon - name is no longer used */ zd (- 500, "ast" , "adt" ), /* Acre */ zd (- 400, "wst" , "wdt" ), /* Western Brazil */ zd (- 400, "cst" , "cdt" ), /* Chile */ zd (- 200, "fst" , "fdt" ), /* Fernando de Noronha */ zs ( 000, "wat" ), /* West African */ zs ( 100, "cat" ), /* Central African */ zs ( 200, "sat" ), /* South African */ zd ( 200, "ist" , "idt" ), /* Israel */ zs ( 300, "eat" ), /* East African */ zd ( 300, "msk" , "msd" ), /* Moscow */ zd ( 330, "ist" , "idt" ), /* Iran */ zs ( 800, "hkt" ), /* Hong Kong */ zs ( 800, "sgt" ), /* Singapore */ zd ( 800, "cst" , "cdt" ), /* China */ zd ( 800, "wst" , "wst" ), /* Western Australia */ zd ( 930, "cst" , "cst" ), /* Central Australia */ zs ( 1000, "gst" ), /* Guam */ zd ( 1000, "est" , "est" ), /* Eastern Australia */ #endif {"", -1} }; /* Look for a prefix of S in TABLE, returning val for first matching entry. */ static int lookup (s, table) char const *s; struct name_val const table[]; { int j; char buf[NAME_LENGTH_MAXIMUM]; for (j = 0; j < NAME_LENGTH_MAXIMUM; j++) { unsigned char c = *s++; if (! ISALPHA (c)) { buf[j] = '\0'; break; } buf[j] = ISUPPER (c) ? tolower (c) : c; } for (;; table++) for (j = 0; ; j++) if (j == NAME_LENGTH_MAXIMUM || ! table[0].name[j]) return table[0].val; else if (buf[j] != table[0].name[j]) break; } /* Set *T to ``undefined'' values. */ static void undefine (t) struct partime *t; { t->tm.tm_sec = t->tm.tm_min = t->tm.tm_hour = t->tm.tm_mday = t->tm.tm_mon = t->tm.tm_year = t->tm.tm_wday = t->tm.tm_yday = t->ymodulus = t->yweek = TM_UNDEFINED; t->zone = TM_UNDEFINED_ZONE; } /* Array of patterns to look for in a date string. Order is important: we look for the first matching pattern whose values do not contradict values that we already know about. See `parse_pattern_letter' below for the meaning of the pattern codes. */ static char const *const patterns[] = { /* These traditional patterns must come first, to prevent an ISO 8601 format from misinterpreting their prefixes. */ "E_n_y", "x", /* RFC 822 */ "E_n", "n_E", "n", "t:m:s_A", "t:m_A", "t_A", /* traditional */ "y/N/D$", /* traditional RCS */ /* ISO 8601:1988 formats, generalized a bit. */ "y-N-D$", "4ND$", "Y-N$", "RND$", "-R=N$", "-R$", "--N=D$", "N=DT", "--N$", "---D$", "DT", "Y-d$", "4d$", "R=d$", "-d$", "dT", "y-W-X", "yWX", "y=W", "-r-W-X", "r-W-XT", "-rWX", "rWXT", "-W=X", "W=XT", "-W", "-w-X", "w-XT", "---X$", "XT", "4$", "T", "h:m:s$", "hms$", "h:m$", "hm$", "h$", "-m:s$", "-ms$", "-m$", "--s$", "Y", "Z", 0 }; /* Parse an initial prefix of STR, setting *T accordingly. Return the first character after the prefix, or 0 if it couldn't be parsed. Start with pattern *PI; if success, set *PI to the next pattern to try. Set *PI to -1 if we know there are no more patterns to try; if *PI is initially negative, give up immediately. */ static char const * parse_prefix (str, t, pi) char const *str; struct partime *t; int *pi; { int i = *pi; char const *pat; unsigned char c; if (i < 0) return 0; /* Remove initial noise. */ while (! ISALNUM (c = *str) && c != '-' && c != '+') { if (! c) { undefine (t); *pi = -1; return str; } str++; } /* Try a pattern until one succeeds. */ while ((pat = patterns[i++]) != 0) { char const *s = str; undefine (t); do { if (! (c = *pat++)) { *pi = i; return s; } } while ((s = parse_pattern_letter (s, c, t)) != 0); } return 0; } /* Parse an initial prefix of S of length DIGITS; it must be a number. Store the parsed number into *RES. Return the first character after the prefix, or 0 if it wasn't parsed. */ static char const * parse_fixed (s, digits, res) char const *s; int digits, *res; { int n = 0; char const *lim = s + digits; while (s < lim) { unsigned d = *s++ - '0'; if (9 < d) return 0; n = 10 * n + d; } *res = n; return s; } /* Parse an initial prefix of S of length DIGITS; it must be a number in the range LO through HI. Store the parsed number into *RES. Return the first character after the prefix, or 0 if it wasn't parsed. */ static char const * parse_ranged (s, digits, lo, hi, res) char const *s; int digits, lo, hi, *res; { s = parse_fixed (s, digits, res); return s && lo <= *res && *res <= hi ? s : 0; } /* Parse an initial prefix of S of length DIGITS; it must be a number in the range LO through HI and it may be followed by a fraction to be computed using RESOLUTION. Store the parsed number into *RES; store the fraction times RESOLUTION, rounded to the nearest integer, into *FRES. Return the first character after the prefix, or 0 if it wasn't parsed. */ static char const * parse_decimal (s, digits, lo, hi, resolution, res, fres) char const *s; int digits, lo, hi, resolution, *res, *fres; { s = parse_fixed (s, digits, res); if (s && lo <= *res && *res <= hi) { int f = 0; if ((s[0] == ',' || s[0] == '.') && ISDIGIT (s[1])) { char const *s1 = ++s; int num10 = 0, denom10 = 10, product; while (ISDIGIT (*++s)) { int d = denom10 * 10; if (d / 10 != denom10) return 0; /* overflow */ denom10 = d; } s = parse_fixed (s1, (int) (s - s1), &num10); product = num10 * resolution; f = (product + (denom10 >> 1)) / denom10; f -= f & (product % denom10 == denom10 >> 1); /* round to even */ if (f < 0 || product/resolution != num10) return 0; /* overflow */ } *fres = f; return s; } return 0; } /* Parse an initial prefix of S; it must denote a time zone. Set *ZONE to the number of seconds east of GMT, or to TM_LOCAL_ZONE if it is the local time zone. Return the first character after the prefix, or 0 if it wasn't parsed. */ char * parzone (s, zone) char const *s; long *zone; { char sign; int hh, mm, ss; int minutesEastOfUTC; long offset, z; /* The formats are LT, n, n DST, nDST, no, o where n is a time zone name and o is a time zone offset of the form [-+]hh[:mm[:ss]]. */ switch (*s) { case '-': case '+': z = 0; break; default: minutesEastOfUTC = lookup (s, zone_names); if (minutesEastOfUTC == -1) return 0; /* Don't bother to check rest of spelling. */ while (ISALPHA ((unsigned char) *s)) s++; /* Don't modify LT. */ if (minutesEastOfUTC == 1) { *zone = TM_LOCAL_ZONE; return (char *) s; } z = minutesEastOfUTC * 60L; /* Look for trailing " DST". */ if ((s[-1] == 'T' || s[-1] == 't') && (s[-2] == 'S' || s[-2] == 's') && (s[-3] == 'D' || s[-3] == 'd')) goto trailing_dst; while (ISSPACE ((unsigned char) *s)) s++; if ((s[0] == 'D' || s[0] == 'd') && (s[1] == 'S' || s[1] == 's') && (s[2] == 'T' || s[2] == 't')) { s += 3; trailing_dst: *zone = z + 60*60; return (char *) s; } switch (*s) { case '-': case '+': break; default: *zone = z; return (char *) s; } break; } sign = *s++; if (! (s = parse_ranged (s, 2, 0, 23, &hh))) return 0; mm = ss = 0; if (*s == ':') s++; if (ISDIGIT (*s)) { if (! (s = parse_ranged (s, 2, 0, 59, &mm))) return 0; if (*s == ':' && s[-3] == ':' && ISDIGIT (s[1]) && ! (s = parse_ranged (s + 1, 2, 0, 59, &ss))) return 0; } if (ISDIGIT (*s)) return 0; offset = (hh * 60 + mm) * 60L + ss; *zone = z + (sign == '-' ? -offset : offset); /* ?? Are fractions allowed here? If so, they're not implemented. */ return (char *) s; } /* Parse an initial prefix of S, matching the pattern whose code is C. Set *T accordingly. Return the first character after the prefix, or 0 if it wasn't parsed. */ static char const * parse_pattern_letter (s, c, t) char const *s; int c; struct partime *t; { switch (c) { case '$': /* The next character must be a non-digit. */ if (ISDIGIT (*s)) return 0; break; case '-': case '/': case ':': /* These characters stand for themselves. */ if (*s++ != c) return 0; break; case '4': /* 4-digit year */ s = parse_fixed (s, 4, &t->tm.tm_year); break; case '=': /* optional '-' */ s += *s == '-'; break;