X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fmisc.c;h=ff7e4b23cdbbcc140440fc288a35911cc2f358dd;hb=c1d3d134939d48e921b981f400808663f4e74001;hp=328591ca4b77beb26b216a86499f82687e353282;hpb=9869d0ae1728827b68c71b33bc2a5cc2261bd35c;p=chaz%2Ftar diff --git a/src/misc.c b/src/misc.c index 328591c..ff7e4b2 100644 --- a/src/misc.c +++ b/src/misc.c @@ -1,11 +1,11 @@ /* 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 @@ -18,8 +18,6 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include -#include -#include #include #include "common.h" #include @@ -35,6 +33,10 @@ # include #endif +#ifndef DOUBLE_SLASH_IS_DISTINCT_ROOT +# define DOUBLE_SLASH_IS_DISTINCT_ROOT 0 +#endif + /* Handling strings. */ @@ -216,6 +218,135 @@ unquote_string (char *string) *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; +} + +/* Normalize NAME by resolving any relative references and + removing trailing slashes. Destructive version: modifies its argument. */ +int +normalize_filename_x (char *name) +{ + char *p, *q; + + p = name; + if (DOUBLE_SLASH_IS_DISTINCT_ROOT && ISSLASH (*p)) + p++; + + /* Remove /./, resolve /../ and compress sequences of slashes */ + for (q = p; *q; ) + { + if (ISSLASH (*q)) + { + *p++ = *q++; + while (ISSLASH (*q)) + q++; + continue; + } + else if (p == name) + { + if (*q == '.') + { + if (ISSLASH (q[1])) + { + q += 2; + continue; + } + if (q[1] == '.' && ISSLASH (q[2])) + return 1; + } + } + else + { + if (*q == '.' && ISSLASH (p[-1])) + { + if (ISSLASH (q[1])) + { + q += 2; + while (ISSLASH (*q)) + q++; + continue; + } + else if (q[1] == '.' && ISSLASH (q[2])) + { + do + { + --p; + } + while (p > name && !ISSLASH (p[-1])); + q += 3; + continue; + } + } + } + *p++ = *q++; + } + + /* Remove trailing slashes */ + while (p - 1 > name && ISSLASH (p[-1])) + p--; + + *p = 0; + return 0; +} + +/* Normalize NAME by resolving any relative references, removing trailing + slashes, and converting it to absolute file name. Return the normalized + name, or NULL in case of error. */ + +char * +normalize_filename (const char *name) +{ + char *copy; + + if (name[0] != '/') + { + copy = xgetcwd (); + copy = xrealloc (copy, strlen (copy) + strlen (name) + 2); + + strcat (copy, "/"); + strcat (copy, name); + } + else + copy = xstrdup (name); + if (normalize_filename_x (copy)) + { + free (copy); + return NULL; + } + return xrealloc (copy, strlen (copy) + 1); +} + + +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); + } +} + /* Handling numbers. */ @@ -259,6 +390,10 @@ code_timespec (struct timespec t, char sbuf[TIMESPEC_STRSIZE_BOUND]) char *np; bool negative = s < 0; + /* ignore invalid values of ns */ + if (BILLION <= ns || ns < 0) + ns = 0; + if (negative && ns != 0) { s++; @@ -419,6 +554,15 @@ maybe_backup_file (const char *file_name, bool this_is_the_archive) { 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)) @@ -440,14 +584,6 @@ maybe_backup_file (const char *file_name, bool this_is_the_archive) && (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 (); @@ -518,7 +654,7 @@ set_file_atime (int fd, char const *file, struct timespec const timespec[2]) } #endif - return futimens (fd, file, timespec); + return gl_futimens (fd, file, timespec); } /* A description of a working directory. */ @@ -533,17 +669,25 @@ struct wd 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) { @@ -553,11 +697,11 @@ chdir_arg (char const *dir) 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++; } } @@ -569,49 +713,21 @@ chdir_arg (char const *dir) 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]; @@ -619,17 +735,30 @@ chdir_do (int i) 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) @@ -712,6 +841,36 @@ stat_diag (char const *name) 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) { @@ -763,3 +922,43 @@ page_aligned_alloc (void **ptr, size_t size) *ptr = xmalloc (size1); return ptr_align (*ptr, alignment); } + + + +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; +} +