+
+/* Zap trailing slashes. */
+char *
+zap_slashes (char *name)
+{
+ char *q;
+
+ if (!name || *name == 0)
+ return name;
+ q = name + strlen (name) - 1;
+ while (q > name && ISSLASH (*q))
+ *q-- = '\0';
+ return name;
+}
+
+/* Normalize FILE_NAME by removing redundant slashes and "."
+ components, including redundant trailing slashes.
+ Leave ".." alone, as it may be significant in the presence
+ of symlinks and on platforms where "/.." != "/".
+
+ Destructive version: modifies its argument. */
+void
+normalize_filename_x (char *file_name)
+{
+ char *name = file_name + FILE_SYSTEM_PREFIX_LEN (file_name);
+ char *p;
+ char const *q;
+ char c;
+
+ /* Don't squeeze leading "//" to "/", on hosts where they're distinct. */
+ name += (DOUBLE_SLASH_IS_DISTINCT_ROOT
+ && ISSLASH (*name) && ISSLASH (name[1]) && ! ISSLASH (name[2]));
+
+ /* Omit redundant leading "." components. */
+ for (q = p = name; (*p = *q) == '.' && ISSLASH (q[1]); p += !*q)
+ for (q += 2; ISSLASH (*q); q++)
+ continue;
+
+ /* Copy components from Q to P, omitting redundant slashes and
+ internal "." components. */
+ while ((*p++ = c = *q++) != '\0')
+ if (ISSLASH (c))
+ while (ISSLASH (q[*q == '.']))
+ q += (*q == '.') + 1;
+
+ /* Omit redundant trailing "." component and slash. */
+ if (2 < p - name)
+ {
+ p -= p[-2] == '.' && ISSLASH (p[-3]);
+ p -= 2 < p - name && ISSLASH (p[-2]);
+ p[-1] = '\0';
+ }
+}
+
+/* Normalize NAME by removing redundant slashes and "." components,
+ including redundant trailing slashes.
+
+ Return a normalized newly-allocated copy. */
+
+char *
+normalize_filename (int cdidx, const char *name)
+{
+ char *copy = NULL;
+
+ if (IS_RELATIVE_FILE_NAME (name))
+ {
+ /* Set COPY to the absolute path for this name.
+
+ FIXME: There should be no need to get the absolute file name.
+ tar_getcdpath does not return a true "canonical" path, so
+ this following approach may lead to situations where the same
+ file or directory is processed twice under different absolute
+ paths without that duplication being detected. Perhaps we
+ should use dev+ino pairs instead of names? */
+ const char *cdpath = tar_getcdpath (cdidx);
+ size_t copylen;
+ bool need_separator;
+
+ if (!cdpath)
+ call_arg_fatal ("getcwd", ".");
+ copylen = strlen (cdpath);
+ need_separator = ! (DOUBLE_SLASH_IS_DISTINCT_ROOT
+ && copylen == 2 && ISSLASH (cdpath[1]));
+ copy = xmalloc (copylen + need_separator + strlen (name) + 1);
+ strcpy (copy, cdpath);
+ copy[copylen] = DIRECTORY_SEPARATOR;
+ strcpy (copy + copylen + need_separator, name);
+ }
+
+ if (!copy)
+ copy = xstrdup (name);
+ normalize_filename_x (copy);
+ return copy;
+}
+
+\f
+void
+replace_prefix (char **pname, const char *samp, size_t slen,
+ const char *repl, size_t rlen)
+{
+ char *name = *pname;
+ size_t nlen = strlen (name);
+ if (nlen > slen && memcmp (name, samp, slen) == 0 && ISSLASH (name[slen]))
+ {
+ if (rlen > slen)
+ {
+ name = xrealloc (name, nlen - slen + rlen + 1);
+ *pname = name;
+ }
+ memmove (name + rlen, name + slen, nlen - slen + 1);
+ memcpy (name, repl, rlen);
+ }
+}
+
+\f
+/* 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 "sysinttostr: uintmax_t cannot represent all intmax_t values"
+#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: nonnegative intmax_t does not fit in uintmax_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. */
+
+void
+code_ns_fraction (int ns, char *p)
+{
+ if (ns == 0)
+ *p = '\0';
+ else
+ {
+ int i = 9;
+ *p++ = '.';
+
+ while (ns % 10 == 0)
+ {
+ ns /= 10;
+ i--;
+ }
+
+ p[i] = '\0';
+
+ for (;;)
+ {
+ p[--i] = '0' + ns % 10;
+ if (i == 0)
+ break;
+ ns /= 10;
+ }
+ }
+}
+
+char const *
+code_timespec (struct timespec t, char sbuf[TIMESPEC_STRSIZE_BOUND])
+{
+ time_t s = t.tv_sec;
+ int ns = t.tv_nsec;
+ char *np;
+ bool negative = s < 0;
+
+ /* ignore invalid values of ns */
+ if (BILLION <= ns || ns < 0)
+ ns = 0;
+
+ if (negative && ns != 0)
+ {
+ s++;
+ ns = BILLION - ns;
+ }
+
+ np = umaxtostr (negative ? - (uintmax_t) s : (uintmax_t) s, sbuf + 1);
+ if (negative)
+ *--np = '-';
+ 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;
+}