Mercurial > hg > octave-kai > gnulib-hg
view lib/parse-duration.c @ 16366:bb182ee4a09d
maint: replace FSF snail-mail addresses with URLs
* config/argz.mk, lib/accept4.c, lib/alignof.h, lib/alloca.in.h:
* lib/alphasort.c, lib/arcfour.c, lib/arcfour.h, lib/arctwo.c:
* lib/arctwo.h, lib/argz.c, lib/arpa_inet.in.h, lib/asnprintf.c:
* lib/asprintf.c, lib/assert.in.h, lib/base32.c, lib/base32.h:
* lib/base64.c, lib/base64.h, lib/c-ctype.c, lib/c-ctype.h:
* lib/c-strcase.h, lib/c-strcasecmp.c, lib/c-strncasecmp.c:
* lib/check-version.c, lib/check-version.h, lib/config.charset:
* lib/ctype.in.h, lib/des.c, lib/des.h, lib/dup3.c, lib/errno.in.h:
* lib/float+.h, lib/fnmatch.c, lib/fnmatch.in.h, lib/fnmatch_loop.c:
* lib/fseeko.c, lib/gai_strerror.c, lib/gc-gnulib.c:
* lib/gc-libgcrypt.c, lib/gc-pbkdf2-sha1.c, lib/gc.h:
* lib/getaddrinfo.c, lib/getdelim.c, lib/getfilecon.c, lib/getline.c:
* lib/getlogin_r.c, lib/getpass.c, lib/getpass.h, lib/gettext.h:
* lib/gettimeofday.c, lib/glob.in.h, lib/glthread/cond.c:
* lib/glthread/cond.h, lib/glthread/lock.c, lib/glthread/lock.h:
* lib/glthread/thread.c, lib/glthread/thread.h:
* lib/glthread/threadlib.c, lib/glthread/yield.h, lib/hmac-md5.c:
* lib/hmac-sha1.c, lib/hmac.h, lib/iconv.c, lib/iconv.in.h:
* lib/iconv_close.c, lib/iconv_open.c, lib/inet_ntop.c, lib/isfinite.c:
* lib/isinf.c, lib/iswblank.c, lib/langinfo.in.h, lib/link.c:
* lib/localcharset.c, lib/localcharset.h, lib/lseek.c, lib/malloc.c:
* lib/malloca.c, lib/malloca.h, lib/md2.c, lib/md2.h, lib/md4.c:
* lib/md4.h, lib/md5.c, lib/md5.h, lib/memmem.c, lib/mempcpy.c:
* lib/memset.c, lib/memxor.c, lib/memxor.h, lib/minmax.h, lib/mktime.c:
* lib/msvc-inval.c, lib/msvc-inval.h, lib/msvc-nothrow.c:
* lib/msvc-nothrow.h, lib/netdb.in.h, lib/netinet_in.in.h, lib/nproc.c:
* lib/nproc.h, lib/obstack_printf.c, lib/pathmax.h, lib/pipe.c:
* lib/pipe2.c, lib/poll.c, lib/poll.in.h, lib/printf-args.c:
* lib/printf-args.h, lib/printf-parse.c, lib/printf-parse.h:
* lib/pselect.c, lib/pthread.in.h, lib/pty-private.h, lib/pty.in.h:
* lib/read-file.c, lib/read-file.h, lib/ref-add.sin, lib/ref-del.sin:
* lib/regcomp.c, lib/regex.c, lib/regex.h, lib/regex_internal.c:
* lib/regex_internal.h, lib/regexec.c, lib/rijndael-alg-fst.c:
* lib/rijndael-alg-fst.h, lib/rijndael-api-fst.c:
* lib/rijndael-api-fst.h, lib/rint.c, lib/rintf.c, lib/rintl.c:
* lib/round.c, lib/roundf.c, lib/roundl.c, lib/scandir.c, lib/select.c:
* lib/sha1.c, lib/sha1.h, lib/size_max.h, lib/snprintf.c:
* lib/stdalign.in.h, lib/stdarg.in.h, lib/stdbool.in.h:
* lib/stddef.in.h, lib/stdint.in.h, lib/stdio.in.h, lib/str-kmp.h:
* lib/str-two-way.h, lib/strcasecmp.c, lib/strcasestr.c, lib/strdup.c:
* lib/striconv.c, lib/striconv.h, lib/string.in.h, lib/strings.in.h:
* lib/strncasecmp.c, lib/strndup.c, lib/strnlen.c, lib/strpbrk.c:
* lib/strptime.c, lib/strsep.c, lib/strstr.c, lib/strverscmp.c:
* lib/sys_file.in.h, lib/sys_ioctl.in.h, lib/sys_select.in.h:
* lib/sys_socket.in.h, lib/sys_stat.in.h, lib/sys_time.in.h:
* lib/sys_times.in.h, lib/sys_types.in.h, lib/sys_uio.in.h:
* lib/sys_utsname.in.h, lib/sys_wait.in.h, lib/tcgetsid.c:
* lib/termios.in.h, lib/time.in.h, lib/time_r.c, lib/timegm.c:
* lib/times.c, lib/unictype/3level.h, lib/unictype/3levelbit.h:
* lib/unistd.in.h, lib/vasnprintf.c, lib/vasnprintf.h, lib/vasprintf.c:
* lib/vsnprintf.c, lib/waitpid.c, lib/wchar.in.h, lib/wctype.in.h:
* lib/xsize.h, tests/test-closein.c, tests/test-des.c:
* tests/test-fclose.c, tests/test-fgetc.c, tests/test-filevercmp.c:
* tests/test-fputc.c, tests/test-fread.c, tests/test-fwrite.c:
* tests/test-gc-arcfour.c, tests/test-gc-arctwo.c, tests/test-gc-des.c:
* tests/test-gc-hmac-md5.c, tests/test-gc-hmac-sha1.c:
* tests/test-gc-md2.c, tests/test-gc-md4.c, tests/test-gc-md5.c:
* tests/test-gc-pbkdf2-sha1.c, tests/test-gc-rijndael.c:
* tests/test-gc-sha1.c, tests/test-gc.c, tests/test-getdelim.c:
* tests/test-getline.c, tests/test-getndelim2.c, tests/test-md2.c:
* tests/test-md4.c, tests/test-parse-datetime.c, tests/test-perror.c:
* tests/test-perror2.c, tests/test-pipe.c, tests/test-pipe2.c:
* tests/test-poll.c, tests/test-quotearg-simple.c:
* tests/test-quotearg.c, tests/test-quotearg.h:
* tests/test-round-ieee.c, tests/test-round1.c:
* tests/test-roundf-ieee.c, tests/test-roundf1.c:
* tests/test-roundl-ieee.c, tests/test-roundl.c:
* tests/test-safe-alloc.c, tests/test-sigpipe.c:
* tests/test-spawn-pipe-child.c, tests/test-spawn-pipe-main.c:
* tests/test-strerror.c, tests/test-strerror_r.c:
* tests/test-strsignal.c, tests/test-strverscmp.c:
* tests/test-xmemdup0.c:
Replace FSF snail mail addresses with URLs, as per GNU coding
standards. See glibc bug
<http://sourceware.org/bugzilla/show_bug.cgi?id=13673>.
author | Paul Eggert <eggert@cs.ucla.edu> |
---|---|
date | Thu, 09 Feb 2012 21:39:05 -0800 |
parents | 8250f2777afc |
children | 6fa7c7fb76b2 |
line wrap: on
line source
/* Parse a time duration and return a seconds count Copyright (C) 2008-2012 Free Software Foundation, Inc. Written by Bruce Korb <bkorb@gnu.org>, 2008. This program 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 3 of the License, or (at your option) any later version. This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ #include <config.h> /* Specification. */ #include "parse-duration.h" #include <ctype.h> #include <errno.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #ifndef NUL #define NUL '\0' #endif #define cch_t char const typedef enum { NOTHING_IS_DONE, YEAR_IS_DONE, MONTH_IS_DONE, WEEK_IS_DONE, DAY_IS_DONE, HOUR_IS_DONE, MINUTE_IS_DONE, SECOND_IS_DONE } whats_done_t; #define SEC_PER_MIN 60 #define SEC_PER_HR (SEC_PER_MIN * 60) #define SEC_PER_DAY (SEC_PER_HR * 24) #define SEC_PER_WEEK (SEC_PER_DAY * 7) #define SEC_PER_MONTH (SEC_PER_DAY * 30) #define SEC_PER_YEAR (SEC_PER_DAY * 365) #define TIME_MAX 0x7FFFFFFF /* Wrapper around strtoul that does not require a cast. */ static unsigned long inline str_const_to_ul (cch_t * str, cch_t ** ppz, int base) { return strtoul (str, (char **)ppz, base); } /* Wrapper around strtol that does not require a cast. */ static long inline str_const_to_l (cch_t * str, cch_t ** ppz, int base) { return strtol (str, (char **)ppz, base); } /* Returns BASE + VAL * SCALE, interpreting BASE = BAD_TIME with errno set as an error situation, and returning BAD_TIME with errno set in an error situation. */ static time_t inline scale_n_add (time_t base, time_t val, int scale) { if (base == BAD_TIME) { if (errno == 0) errno = EINVAL; return BAD_TIME; } if (val > TIME_MAX / scale) { errno = ERANGE; return BAD_TIME; } val *= scale; if (base > TIME_MAX - val) { errno = ERANGE; return BAD_TIME; } return base + val; } /* After a number HH has been parsed, parse subsequent :MM or :MM:SS. */ static time_t parse_hr_min_sec (time_t start, cch_t * pz) { int lpct = 0; errno = 0; /* For as long as our scanner pointer points to a colon *AND* we've not looped before, then keep looping. (two iterations max) */ while ((*pz == ':') && (lpct++ <= 1)) { unsigned long v = str_const_to_ul (pz+1, &pz, 10); if (errno != 0) return BAD_TIME; start = scale_n_add (v, start, 60); if (errno != 0) return BAD_TIME; } /* allow for trailing spaces */ while (isspace ((unsigned char)*pz)) pz++; if (*pz != NUL) { errno = EINVAL; return BAD_TIME; } return start; } /* Parses a value and returns BASE + value * SCALE, interpreting BASE = BAD_TIME with errno set as an error situation, and returning BAD_TIME with errno set in an error situation. */ static time_t parse_scaled_value (time_t base, cch_t ** ppz, cch_t * endp, int scale) { cch_t * pz = *ppz; time_t val; if (base == BAD_TIME) return base; errno = 0; val = str_const_to_ul (pz, &pz, 10); if (errno != 0) return BAD_TIME; while (isspace ((unsigned char)*pz)) pz++; if (pz != endp) { errno = EINVAL; return BAD_TIME; } *ppz = pz; return scale_n_add (base, val, scale); } /* Parses the syntax YEAR-MONTH-DAY. PS points into the string, after "YEAR", before "-MONTH-DAY". */ static time_t parse_year_month_day (cch_t * pz, cch_t * ps) { time_t res = 0; res = parse_scaled_value (0, &pz, ps, SEC_PER_YEAR); pz++; /* over the first '-' */ ps = strchr (pz, '-'); if (ps == NULL) { errno = EINVAL; return BAD_TIME; } res = parse_scaled_value (res, &pz, ps, SEC_PER_MONTH); pz++; /* over the second '-' */ ps = pz + strlen (pz); return parse_scaled_value (res, &pz, ps, SEC_PER_DAY); } /* Parses the syntax YYYYMMDD. */ static time_t parse_yearmonthday (cch_t * in_pz) { time_t res = 0; char buf[8]; cch_t * pz; if (strlen (in_pz) != 8) { errno = EINVAL; return BAD_TIME; } memcpy (buf, in_pz, 4); buf[4] = NUL; pz = buf; res = parse_scaled_value (0, &pz, buf + 4, SEC_PER_YEAR); memcpy (buf, in_pz + 4, 2); buf[2] = NUL; pz = buf; res = parse_scaled_value (res, &pz, buf + 2, SEC_PER_MONTH); memcpy (buf, in_pz + 6, 2); buf[2] = NUL; pz = buf; return parse_scaled_value (res, &pz, buf + 2, SEC_PER_DAY); } /* Parses the syntax yy Y mm M ww W dd D. */ static time_t parse_YMWD (cch_t * pz) { time_t res = 0; cch_t * ps = strchr (pz, 'Y'); if (ps != NULL) { res = parse_scaled_value (0, &pz, ps, SEC_PER_YEAR); pz++; } ps = strchr (pz, 'M'); if (ps != NULL) { res = parse_scaled_value (res, &pz, ps, SEC_PER_MONTH); pz++; } ps = strchr (pz, 'W'); if (ps != NULL) { res = parse_scaled_value (res, &pz, ps, SEC_PER_WEEK); pz++; } ps = strchr (pz, 'D'); if (ps != NULL) { res = parse_scaled_value (res, &pz, ps, SEC_PER_DAY); pz++; } while (isspace ((unsigned char)*pz)) pz++; if (*pz != NUL) { errno = EINVAL; return BAD_TIME; } return res; } /* Parses the syntax HH:MM:SS. PS points into the string, after "HH", before ":MM:SS". */ static time_t parse_hour_minute_second (cch_t * pz, cch_t * ps) { time_t res = 0; res = parse_scaled_value (0, &pz, ps, SEC_PER_HR); pz++; ps = strchr (pz, ':'); if (ps == NULL) { errno = EINVAL; return BAD_TIME; } res = parse_scaled_value (res, &pz, ps, SEC_PER_MIN); pz++; ps = pz + strlen (pz); return parse_scaled_value (res, &pz, ps, 1); } /* Parses the syntax HHMMSS. */ static time_t parse_hourminutesecond (cch_t * in_pz) { time_t res = 0; char buf[4]; cch_t * pz; if (strlen (in_pz) != 6) { errno = EINVAL; return BAD_TIME; } memcpy (buf, in_pz, 2); buf[2] = NUL; pz = buf; res = parse_scaled_value (0, &pz, buf + 2, SEC_PER_HR); memcpy (buf, in_pz + 2, 2); buf[2] = NUL; pz = buf; res = parse_scaled_value (res, &pz, buf + 2, SEC_PER_MIN); memcpy (buf, in_pz + 4, 2); buf[2] = NUL; pz = buf; return parse_scaled_value (res, &pz, buf + 2, 1); } /* Parses the syntax hh H mm M ss S. */ static time_t parse_HMS (cch_t * pz) { time_t res = 0; cch_t * ps = strchr (pz, 'H'); if (ps != NULL) { res = parse_scaled_value (0, &pz, ps, SEC_PER_HR); pz++; } ps = strchr (pz, 'M'); if (ps != NULL) { res = parse_scaled_value (res, &pz, ps, SEC_PER_MIN); pz++; } ps = strchr (pz, 'S'); if (ps != NULL) { res = parse_scaled_value (res, &pz, ps, 1); pz++; } while (isspace ((unsigned char)*pz)) pz++; if (*pz != NUL) { errno = EINVAL; return BAD_TIME; } return res; } /* Parses a time (hours, minutes, seconds) specification in either syntax. */ static time_t parse_time (cch_t * pz) { cch_t * ps; time_t res = 0; /* * Scan for a hyphen */ ps = strchr (pz, ':'); if (ps != NULL) { res = parse_hour_minute_second (pz, ps); } /* * Try for a 'H', 'M' or 'S' suffix */ else if (ps = strpbrk (pz, "HMS"), ps == NULL) { /* Its a YYYYMMDD format: */ res = parse_hourminutesecond (pz); } else res = parse_HMS (pz); return res; } /* Returns a substring of the given string, with spaces at the beginning and at the end destructively removed, per SNOBOL. */ static char * trim (char * pz) { /* trim leading white space */ while (isspace ((unsigned char)*pz)) pz++; /* trim trailing white space */ { char * pe = pz + strlen (pz); while ((pe > pz) && isspace ((unsigned char)pe[-1])) pe--; *pe = NUL; } return pz; } /* * Parse the year/months/days of a time period */ static time_t parse_period (cch_t * in_pz) { char * pT; char * ps; char * pz = strdup (in_pz); void * fptr = pz; time_t res = 0; if (pz == NULL) { errno = ENOMEM; return BAD_TIME; } pT = strchr (pz, 'T'); if (pT != NULL) { *(pT++) = NUL; pz = trim (pz); pT = trim (pT); } /* * Scan for a hyphen */ ps = strchr (pz, '-'); if (ps != NULL) { res = parse_year_month_day (pz, ps); } /* * Try for a 'Y', 'M' or 'D' suffix */ else if (ps = strpbrk (pz, "YMWD"), ps == NULL) { /* Its a YYYYMMDD format: */ res = parse_yearmonthday (pz); } else res = parse_YMWD (pz); if ((errno == 0) && (pT != NULL)) { time_t val = parse_time (pT); res = scale_n_add (res, val, 1); } free (fptr); return res; } static time_t parse_non_iso8601 (cch_t * pz) { whats_done_t whatd_we_do = NOTHING_IS_DONE; time_t res = 0; do { time_t val; errno = 0; val = str_const_to_l (pz, &pz, 10); if (errno != 0) goto bad_time; /* IF we find a colon, then we're going to have a seconds value. We will not loop here any more. We cannot already have parsed a minute value and if we've parsed an hour value, then the result value has to be less than an hour. */ if (*pz == ':') { if (whatd_we_do >= MINUTE_IS_DONE) break; val = parse_hr_min_sec (val, pz); if ((whatd_we_do == HOUR_IS_DONE) && (val >= SEC_PER_HR)) break; return scale_n_add (res, val, 1); } { unsigned int mult; /* Skip over white space following the number we just parsed. */ while (isspace ((unsigned char)*pz)) pz++; switch (*pz) { default: goto bad_time; case NUL: return scale_n_add (res, val, 1); case 'y': case 'Y': if (whatd_we_do >= YEAR_IS_DONE) goto bad_time; mult = SEC_PER_YEAR; whatd_we_do = YEAR_IS_DONE; break; case 'M': if (whatd_we_do >= MONTH_IS_DONE) goto bad_time; mult = SEC_PER_MONTH; whatd_we_do = MONTH_IS_DONE; break; case 'W': if (whatd_we_do >= WEEK_IS_DONE) goto bad_time; mult = SEC_PER_WEEK; whatd_we_do = WEEK_IS_DONE; break; case 'd': case 'D': if (whatd_we_do >= DAY_IS_DONE) goto bad_time; mult = SEC_PER_DAY; whatd_we_do = DAY_IS_DONE; break; case 'h': if (whatd_we_do >= HOUR_IS_DONE) goto bad_time; mult = SEC_PER_HR; whatd_we_do = HOUR_IS_DONE; break; case 'm': if (whatd_we_do >= MINUTE_IS_DONE) goto bad_time; mult = SEC_PER_MIN; whatd_we_do = MINUTE_IS_DONE; break; case 's': mult = 1; whatd_we_do = SECOND_IS_DONE; break; } res = scale_n_add (res, val, mult); pz++; while (isspace ((unsigned char)*pz)) pz++; if (*pz == NUL) return res; if (! isdigit ((unsigned char)*pz)) break; } } while (whatd_we_do < SECOND_IS_DONE); bad_time: errno = EINVAL; return BAD_TIME; } time_t parse_duration (char const * pz) { while (isspace ((unsigned char)*pz)) pz++; switch (*pz) { case 'P': return parse_period (pz + 1); case 'T': return parse_time (pz + 1); default: if (isdigit ((unsigned char)*pz)) return parse_non_iso8601 (pz); errno = EINVAL; return BAD_TIME; } } /* * Local Variables: * mode: C * c-file-style: "gnu" * indent-tabs-mode: nil * End: * end of parse-duration.c */