/* Miscellaneous functions, not really specific to GNU tar.
Copyright 1988, 1992, 1994-1997, 1999-2001, 2003-2007, 2009-2010,
- 2012-2013 Free Software Foundation, Inc.
+ 2012-2014 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
# define DOUBLE_SLASH_IS_DISTINCT_ROOT 0
#endif
+static const char *tar_getcdpath (int);
+
\f
/* Handling strings. */
/* Normalize FILE_NAME by removing redundant slashes and "."
components, including redundant trailing slashes.
- Leave ".." alone, as it may be significant in the presence
+ Leave ".." alone, as it may be significant in the presence
of symlinks and on platforms where "/.." != "/".
Destructive version: modifies its argument. */
Return a normalized newly-allocated copy. */
char *
-normalize_filename (const char *name)
+normalize_filename (int cdidx, const char *name)
{
char *copy = NULL;
if (IS_RELATIVE_FILE_NAME (name))
{
- /* Set COPY to the absolute file name if possible.
+ /* Set COPY to the absolute path for this name.
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? */
- const char *cwd = tar_getcwd ();
+ 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? (See listed03.at for
+ a related test case.) */
+ const char *cdpath = tar_getcdpath (cdidx);
size_t copylen;
bool need_separator;
-
- copylen = strlen (cwd);
+
+ if (!cdpath)
+ call_arg_fatal ("getcwd", ".");
+ copylen = strlen (cdpath);
need_separator = ! (DOUBLE_SLASH_IS_DISTINCT_ROOT
- && copylen == 2 && ISSLASH (cwd[1]));
+ && copylen == 2 && ISSLASH (cdpath[1]));
copy = xmalloc (copylen + need_separator + strlen (name) + 1);
- strcpy (copy, cwd);
+ strcpy (copy, cdpath);
copy[copylen] = DIRECTORY_SEPARATOR;
strcpy (copy + copylen + need_separator, name);
}
{
/* The directory's name. */
char const *name;
- /* Current working directory; initialized by tar_getcwd */
- char *cwd;
+ /* "Absolute" path representing this directory; in the contrast to
+ the real absolute pathname, it can contain /../ components (see
+ normalize_filename_x for the reason of it). It is NULL if the
+ absolute path could not be determined. */
+ char *abspath;
/* If nonzero, the file descriptor of the directory, or AT_FDCWD if
the working directory. If zero, the directory needs to be opened
to be used. */
int
chdir_arg (char const *dir)
{
+ char *absdir;
+
if (wd_count == wd_alloc)
{
if (wd_alloc == 0)
- {
- wd_alloc = 2;
- wd = xmalloc (sizeof *wd * wd_alloc);
- }
- else
- wd = x2nrealloc (wd, &wd_alloc, sizeof *wd);
+ wd_alloc = 2;
+ wd = x2nrealloc (wd, &wd_alloc, sizeof *wd);
if (! wd_count)
{
wd[wd_count].name = ".";
- wd[wd_count].cwd = NULL;
+ wd[wd_count].abspath = xgetcwd ();
wd[wd_count].fd = AT_FDCWD;
wd_count++;
}
return wd_count - 1;
}
+
+ /* If the given name is absolute, use it to represent this directory;
+ otherwise, construct a name based on the previous -C option. */
+ if (IS_ABSOLUTE_FILE_NAME (dir))
+ absdir = xstrdup (dir);
+ else if (wd[wd_count - 1].abspath)
+ {
+ namebuf_t nbuf = namebuf_create (wd[wd_count - 1].abspath);
+ namebuf_add_dir (nbuf, dir);
+ absdir = namebuf_finish (nbuf);
+ }
+ else
+ absdir = 0;
+
wd[wd_count].name = dir;
- wd[wd_count].cwd = NULL;
+ wd[wd_count].abspath = absdir;
wd[wd_count].fd = 0;
return wd_count++;
}
return wd[chdir_current].name;
}
-const char *
-tar_getcwd (void)
-{
- static char *cwd;
- namebuf_t nbuf;
- int i;
+/* Return the absolute path that represents the working
+ directory referenced by IDX.
- if (!cwd)
- cwd = xgetcwd ();
+ If wd is empty, then there were no -C options given, and
+ chdir_args() has never been called, so we simply return the
+ process's actual cwd. (Note that in this case IDX is ignored,
+ since it should always be 0.) */
+static const char *
+tar_getcdpath (int idx)
+{
if (!wd)
- return cwd;
-
- if (0 == chdir_current || !wd[chdir_current].cwd)
{
- if (IS_ABSOLUTE_FILE_NAME (wd[chdir_current].name))
- {
- wd[chdir_current].cwd = xstrdup (wd[chdir_current].name);
- return wd[chdir_current].cwd;
- }
- if (!wd[0].cwd)
- wd[0].cwd = cwd;
-
- for (i = chdir_current - 1; i > 0; i--)
- if (wd[i].cwd)
- break;
-
- nbuf = namebuf_create (wd[i].cwd);
- for (i++; i <= chdir_current; i++)
- namebuf_add_dir (nbuf, wd[i].name);
- wd[chdir_current].cwd = namebuf_finish (nbuf);
+ static char *cwd;
+ if (!cwd)
+ cwd = xgetcwd ();
+ return cwd;
}
- return wd[chdir_current].cwd;
+ return wd[idx].abspath;
}
\f
void
namebuf_finish (namebuf_t buf)
{
char *res = buf->buffer;
-
+
if (ISSLASH (buf->buffer[buf->dir_length - 1]))
buf->buffer[buf->dir_length] = 0;
free (buf);
open_error (name);
}
else if (! ((dir = fdopendir (fd))
- && (ret = streamsavedir (dir))))
+ && (ret = streamsavedir (dir, savedir_sort_order))))
savedir_error (name);
if (dir ? closedir (dir) != 0 : 0 <= fd && close (fd) != 0)