X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fmisc.c;h=5fe80f4bb56e3616010cbb9075ca06e009572b6e;hb=df7b55a8f6354e30e8da62eec7f706df033d0c81;hp=734dc4eded7adcac4f7a29d84111a0d49306cfb4;hpb=d8ac237663db9817a688af6711320ad66f1d9e94;p=chaz%2Ftar diff --git a/src/misc.c b/src/misc.c index 734dc4e..5fe80f4 100644 --- a/src/misc.c +++ b/src/misc.c @@ -1,7 +1,7 @@ /* Miscellaneous functions, not really specific to GNU tar. Copyright (C) 1988, 1992, 1994, 1995, 1996, 1997, 1999, 2000, 2001, - 2003, 2004, 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc. + 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2012 Free Software Foundation, Inc. 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 @@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#define COMMON_INLINE _GL_EXTERN_INLINE #include #include #include "common.h" @@ -325,6 +326,76 @@ replace_prefix (char **pname, const char *samp, size_t slen, /* Handling numbers. */ +/* Convert VALUE, which is converted from a system integer type whose + minimum value is MINVAL and maximum MINVAL, to an decimal + integer string. Use the storage in BUF and return a pointer to the + converted string. If VALUE is converted from a negative integer in + the range MINVAL .. -1, represent it with a string representation + of the negative integer, using leading '-'. */ +#if ! (INTMAX_MAX <= UINTMAX_MAX / 2) +# error "strtosysint accepts uintmax_t to represent intmax_t" +#endif +char * +sysinttostr (uintmax_t value, intmax_t minval, uintmax_t maxval, + char buf[SYSINT_BUFSIZE]) +{ + if (value <= maxval) + return umaxtostr (value, buf); + else + { + intmax_t i = value - minval; + return imaxtostr (i + minval, buf); + } +} + +/* Convert a prefix of the string ARG to a system integer type whose + minimum value is MINVAL and maximum MAXVAL. If MINVAL is negative, + negative integers MINVAL .. -1 are assumed to be represented using + leading '-' in the usual way. If the represented value exceeds + INTMAX_MAX, return a negative integer V such that (uintmax_t) V + yields the represented value. If ARGLIM is nonnull, store into + *ARGLIM a pointer to the first character after the prefix. + + This is the inverse of sysinttostr. + + On a normal return, set errno = 0. + On conversion error, return 0 and set errno = EINVAL. + On overflow, return an extreme value and set errno = ERANGE. */ +#if ! (INTMAX_MAX <= UINTMAX_MAX) +# error "strtosysint accepts uintmax_t to represent nonnegative intmax_t" +#endif +intmax_t +strtosysint (char const *arg, char **arglim, intmax_t minval, uintmax_t maxval) +{ + errno = 0; + if (maxval <= INTMAX_MAX) + { + if (ISDIGIT (arg[*arg == '-'])) + { + intmax_t i = strtoimax (arg, arglim, 10); + intmax_t imaxval = maxval; + if (minval <= i && i <= imaxval) + return i; + errno = ERANGE; + return i < minval ? minval : maxval; + } + } + else + { + if (ISDIGIT (*arg)) + { + uintmax_t i = strtoumax (arg, arglim, 10); + if (i <= maxval) + return represent_uintmax (i); + errno = ERANGE; + return maxval; + } + } + + errno = EINVAL; + return 0; +} + /* Output fraction and trailing digits appropriate for a nanoseconds count equal to NS, but don't output unnecessary '.' or trailing zeros. */ @@ -381,6 +452,84 @@ code_timespec (struct timespec t, char sbuf[TIMESPEC_STRSIZE_BOUND]) code_ns_fraction (ns, sbuf + UINTMAX_STRSIZE_BOUND); return np; } + +struct timespec +decode_timespec (char const *arg, char **arg_lim, bool parse_fraction) +{ + time_t s = TYPE_MINIMUM (time_t); + int ns = -1; + char const *p = arg; + bool negative = *arg == '-'; + struct timespec r; + + if (! ISDIGIT (arg[negative])) + errno = EINVAL; + else + { + errno = 0; + + if (negative) + { + intmax_t i = strtoimax (arg, arg_lim, 10); + if (TYPE_SIGNED (time_t) ? TYPE_MINIMUM (time_t) <= i : 0 <= i) + s = i; + else + errno = ERANGE; + } + else + { + uintmax_t i = strtoumax (arg, arg_lim, 10); + if (i <= TYPE_MAXIMUM (time_t)) + s = i; + else + errno = ERANGE; + } + + p = *arg_lim; + ns = 0; + + if (parse_fraction && *p == '.') + { + int digits = 0; + bool trailing_nonzero = false; + + while (ISDIGIT (*++p)) + if (digits < LOG10_BILLION) + digits++, ns = 10 * ns + (*p - '0'); + else + trailing_nonzero |= *p != '0'; + + while (digits < LOG10_BILLION) + digits++, ns *= 10; + + if (negative) + { + /* Convert "-1.10000000000001" to s == -2, ns == 89999999. + I.e., truncate time stamps towards minus infinity while + converting them to internal form. */ + ns += trailing_nonzero; + if (ns != 0) + { + if (s == TYPE_MINIMUM (time_t)) + ns = -1; + else + { + s--; + ns = BILLION - ns; + } + } + } + } + + if (errno == ERANGE) + ns = -1; + } + + *arg_lim = (char *) p; + r.tv_sec = s; + r.tv_nsec = ns; + return r; +} /* File handling. */