/* Miscellaneous functions, not really specific to GNU tar.
Copyright (C) 1988, 1992, 1994, 1995, 1996, 1997, 1999, 2000, 2001,
- 2003, 2004, 2005 Free Software Foundation, Inc.
+ 2003, 2004, 2005, 2006, 2007, 2009 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
- Free Software Foundation; either version 2, or (at your option) any later
+ Free Software Foundation; either version 3, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
#include <system.h>
-#include <sys/time.h>
-#include <sys/resource.h>
#include <rmt.h>
#include "common.h"
#include <quotearg.h>
#include <xgetcwd.h>
#include <unlinkdir.h>
#include <utimens.h>
+#include <canonicalize.h>
#if HAVE_STROPTS_H
# include <stropts.h>
*destination = '\0';
return result;
}
+
+/* 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;
+}
+
+char *
+normalize_filename (const char *name)
+{
+ return zap_slashes (canonicalize_filename_mode (name, CAN_MISSING));
+}
+
+\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. */
{
struct stat file_stat;
+ assign_string (&before_backup_name, file_name);
+
+ /* A run situation may exist between Emacs or other GNU programs trying to
+ make a backup for the same file simultaneously. If theoretically
+ possible, real problems are unlikely. Doing any better would require a
+ convention, GNU-wide, for all programs doing backups. */
+
+ assign_string (&after_backup_name, 0);
+
/* Check if we really need to backup the file. */
if (this_is_the_archive && _remdev (file_name))
&& (S_ISBLK (file_stat.st_mode) || S_ISCHR (file_stat.st_mode)))
return true;
- assign_string (&before_backup_name, file_name);
-
- /* A run situation may exist between Emacs or other GNU programs trying to
- make a backup for the same file simultaneously. If theoretically
- possible, real problems are unlikely. Doing any better would require a
- convention, GNU-wide, for all programs doing backups. */
-
- assign_string (&after_backup_name, 0);
after_backup_name = find_backup_file_name (file_name, backup_type);
if (! after_backup_name)
xalloc_die ();
}
#endif
- return futimens (fd, file, timespec);
+ return gl_futimens (fd, file, timespec);
}
/* A description of a working directory. */
static struct wd *wd;
/* The number of working directories in the vector. */
-static size_t wds;
+static size_t wd_count;
/* The allocated size of the vector. */
static size_t wd_alloc;
+int
+chdir_count ()
+{
+ if (wd_count == 0)
+ return wd_count;
+ return wd_count - 1;
+}
+
/* DIR is the operand of a -C option; add it to vector of chdir targets,
and return the index of its location. */
int
chdir_arg (char const *dir)
{
- if (wds == wd_alloc)
+ if (wd_count == wd_alloc)
{
if (wd_alloc == 0)
{
else
wd = x2nrealloc (wd, &wd_alloc, sizeof *wd);
- if (! wds)
+ if (! wd_count)
{
- wd[wds].name = ".";
- wd[wds].saved = 0;
- wds++;
+ wd[wd_count].name = ".";
+ wd[wd_count].saved = 0;
+ wd_count++;
}
}
for (dir += 2; ISSLASH (*dir); dir++)
continue;
if (! dir[dir[0] == '.'])
- return wds - 1;
+ return wd_count - 1;
}
- wd[wds].name = dir;
- wd[wds].saved = 0;
- return wds++;
+ wd[wd_count].name = dir;
+ wd[wd_count].saved = 0;
+ return wd_count++;
}
-/* Return maximum number of open files */
-int
-get_max_open_files ()
-{
-#if defined _SC_OPEN_MAX
- return sysconf (_SC_OPEN_MAX);
-#elif defined RLIMIT_NOFILE
- struct rlimit rlim;
-
- if (getrlimit(RLIMIT_NOFILE, &rlim) == 0)
- return rlim.rlim_max;
-#elif defined HAVE_GETDTABLESIZE
- return getdtablesize ();
-#endif
- return -1;
-}
-
-/* Close all descriptors, except the first three */
-void
-closeopen ()
-{
- int i;
-
- for (i = get_max_open_files () - 1; i > 2; i--)
- close (i);
-}
-
/* Change to directory I. If I is 0, change to the initial working
directory; otherwise, I must be a value returned by chdir_arg. */
void
chdir_do (int i)
{
static int previous;
- static int saved_count;
-
+
if (previous != i)
{
struct wd *prev = &wd[previous];
if (! prev->saved)
{
+ int err = 0;
prev->saved = 1;
- saved_count++;
- /* Make sure we still have at least one descriptor available */
- if (saved_count >= get_max_open_files () - 4)
+ if (save_cwd (&prev->saved_cwd) != 0)
+ err = errno;
+ else if (0 <= prev->saved_cwd.desc)
{
- /* Force restore_cwd to use chdir_long */
- prev->saved_cwd.desc = -1;
- prev->saved_cwd.name = xgetcwd ();
+ /* Make sure we still have at least one descriptor available. */
+ int fd1 = prev->saved_cwd.desc;
+ int fd2 = dup (fd1);
+ if (0 <= fd2)
+ close (fd2);
+ else if (errno == EMFILE)
+ {
+ /* Force restore_cwd to use chdir_long. */
+ close (fd1);
+ prev->saved_cwd.desc = -1;
+ prev->saved_cwd.name = xgetcwd ();
+ }
+ else
+ err = errno;
}
- else if (save_cwd (&prev->saved_cwd) != 0)
- FATAL_ERROR ((0, 0, _("Cannot save working directory")));
+
+ if (err)
+ FATAL_ERROR ((0, err, _("Cannot save working directory")));
}
if (curr->saved)
stat_error (name);
}
+void
+file_removed_diag (const char *name, bool top_level,
+ void (*diagfn) (char const *name))
+{
+ if (!top_level && errno == ENOENT)
+ {
+ WARNOPT (WARN_FILE_REMOVED,
+ (0, 0, _("%s: File removed before we read it"),
+ quotearg_colon (name)));
+ set_exit_status (TAREXIT_DIFFERS);
+ }
+ else
+ diagfn (name);
+}
+
+void
+dir_removed_diag (const char *name, bool top_level,
+ void (*diagfn) (char const *name))
+{
+ if (!top_level && errno == ENOENT)
+ {
+ WARNOPT (WARN_FILE_REMOVED,
+ (0, 0, _("%s: Directory removed before we read it"),
+ quotearg_colon (name)));
+ set_exit_status (TAREXIT_DIFFERS);
+ }
+ else
+ diagfn (name);
+}
+
void
write_fatal_details (char const *name, ssize_t status, size_t size)
{
*ptr = xmalloc (size1);
return ptr_align (*ptr, alignment);
}
+
+\f
+
+struct namebuf
+{
+ char *buffer; /* directory, `/', and directory member */
+ size_t buffer_size; /* allocated size of name_buffer */
+ size_t dir_length; /* length of directory part in buffer */
+};
+
+namebuf_t
+namebuf_create (const char *dir)
+{
+ namebuf_t buf = xmalloc (sizeof (*buf));
+ buf->buffer_size = strlen (dir) + 2;
+ buf->buffer = xmalloc (buf->buffer_size);
+ strcpy (buf->buffer, dir);
+ buf->dir_length = strlen (buf->buffer);
+ if (!ISSLASH (buf->buffer[buf->dir_length - 1]))
+ buf->buffer[buf->dir_length++] = DIRECTORY_SEPARATOR;
+ return buf;
+}
+
+void
+namebuf_free (namebuf_t buf)
+{
+ free (buf->buffer);
+ free (buf);
+}
+
+char *
+namebuf_name (namebuf_t buf, const char *name)
+{
+ size_t len = strlen (name);
+ while (buf->dir_length + len + 1 >= buf->buffer_size)
+ buf->buffer = x2realloc (buf->buffer, &buf->buffer_size);
+ strcpy (buf->buffer + buf->dir_length, name);
+ return buf->buffer;
+}
+
+
+