+
+/* 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. */
+static 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 (const char *name)
+{
+ char *copy = NULL;
+
+ if (IS_RELATIVE_FILE_NAME (name))
+ {
+ /* Set COPY to the absolute file name if possible.
+
+ FIXME: There should be no need to get the absolute file name.
+ getcwd is slow, it might fail, and it does not necessarily
+ return a canonical name even when it succeeds. Perhaps we
+ can use dev+ino pairs instead of names? */
+ copy = xgetcwd ();
+ if (copy)
+ {
+ size_t copylen = strlen (copy);
+ bool need_separator = ! (DOUBLE_SLASH_IS_DISTINCT_ROOT
+ && copylen == 2 && ISSLASH (copy[1]));
+ copy = xrealloc (copy, copylen + need_separator + strlen (name) + 1);
+ copy[copylen] = DIRECTORY_SEPARATOR;
+ strcpy (copy + copylen + need_separator, name);
+ }
+ else
+ WARN ((0, errno, _("Cannot get working directory")));
+ }
+
+ 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. */
+
+/* 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;
+}