X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fmisc.c;h=b56a916b1557a87175c5ea57e308a186c3259b67;hb=1bcbbcf1ff2c537ffa970dbf82e3843d4ad110e5;hp=e6e5c85b8a001949de4cf3be5a8959bbb9d2860a;hpb=3971a56b22b61befbda689d375b359dec84a980d;p=chaz%2Ftar diff --git a/src/misc.c b/src/misc.c index e6e5c85..b56a916 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 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 @@ -15,16 +15,25 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "common.h" #include #include +#include +#include +#include +#include + +#if HAVE_STROPTS_H +# include +#endif +#if HAVE_SYS_FILIO_H +# include +#endif -static void call_arg_fatal (char const *, char const *) - __attribute__ ((noreturn)); /* Handling strings. */ @@ -127,7 +136,7 @@ unquote_string (char *string) *destination++ = '\a'; source++; break; - + case 'b': *destination++ = '\b'; source++; @@ -157,7 +166,7 @@ unquote_string (char *string) *destination++ = '\v'; source++; break; - + case '?': *destination++ = 0177; source++; @@ -206,6 +215,82 @@ 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; +} + +char * +normalize_filename (const char *name) +{ + return zap_slashes (canonicalize_filename_mode (name, CAN_MISSING)); +} + + +/* 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; + + 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; +} /* File handling. */ @@ -224,7 +309,7 @@ must_be_dot_or_slash (char const *file_name) for (;;) if (ISSLASH (file_name[1])) file_name++; - else if (file_name[1] == '.' + else if (file_name[1] == '.' && ISSLASH (file_name[2 + (file_name[2] == '.')])) file_name += 2 + (file_name[2] == '.'); else @@ -258,7 +343,7 @@ safer_rmdir (const char *file_name) return rmdir (file_name); } -/* Remove FILE_NAME, returning 1 on success. If FILE_NAME is a directory, +/* Remove FILE_NAME, returning 1 on success. If FILE_NAME is a directory, then if OPTION is RECURSIVE_REMOVE_OPTION is set remove FILE_NAME recursively; otherwise, remove it only if it is empty. If FILE_NAME is a directory that cannot be removed (e.g., because it is nonempty) @@ -268,9 +353,12 @@ safer_rmdir (const char *file_name) int remove_any_file (const char *file_name, enum remove_option option) { - /* Try unlink first if we are not root, as this saves us a system - call in the common case where we're removing a non-directory. */ - if (! we_are_root) + /* Try unlink first if we cannot unlink directories, as this saves + us a system call in the common case where we're removing a + non-directory. */ + bool try_unlink_first = cannot_unlink_dir (); + + if (try_unlink_first) { if (unlink (file_name) == 0) return 1; @@ -288,7 +376,7 @@ remove_any_file (const char *file_name, enum remove_option option) switch (errno) { case ENOTDIR: - return we_are_root && unlink (file_name) == 0; + return !try_unlink_first && unlink (file_name) == 0; case 0: case EEXIST: @@ -317,7 +405,7 @@ remove_any_file (const char *file_name, enum remove_option option) entry += entrylen + 1) { char *file_name_buffer = new_name (file_name, entry); - int r = remove_any_file (file_name_buffer, + int r = remove_any_file (file_name_buffer, RECURSIVE_REMOVE_OPTION); int e = errno; free (file_name_buffer); @@ -347,10 +435,19 @@ remove_any_file (const char *file_name, enum remove_option option) so, we do not have to backup block or character devices, nor remote entities. */ bool -maybe_backup_file (const char *file_name, int this_is_the_archive) +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)) @@ -372,14 +469,6 @@ maybe_backup_file (const char *file_name, int 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 (); @@ -433,6 +522,26 @@ deref_stat (bool deref, char const *name, struct stat *buf) return deref ? stat (name, buf) : lstat (name, buf); } +/* Set FD's (i.e., FILE's) access time to TIMESPEC[0]. If that's not + possible to do by itself, set its access and data modification + times to TIMESPEC[0] and TIMESPEC[1], respectively. */ +int +set_file_atime (int fd, char const *file, struct timespec const timespec[2]) +{ +#ifdef _FIOSATIME + if (0 <= fd) + { + struct timeval timeval; + timeval.tv_sec = timespec[0].tv_sec; + timeval.tv_usec = timespec[0].tv_nsec / 1000; + if (ioctl (fd, _FIOSATIME, &timeval) == 0) + return 0; + } +#endif + + return gl_futimens (fd, file, timespec); +} + /* A description of a working directory. */ struct wd { @@ -445,25 +554,39 @@ 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) { - wd_alloc = 2 * (wd_alloc + 1); - wd = xrealloc (wd, sizeof *wd * wd_alloc); - if (! wds) + if (wd_alloc == 0) + { + wd_alloc = 2; + wd = xmalloc (sizeof *wd * wd_alloc); + } + else + wd = x2nrealloc (wd, &wd_alloc, sizeof *wd); + + if (! wd_count) { - wd[wds].name = "."; - wd[wds].saved = 0; - wds++; + wd[wd_count].name = "."; + wd[wd_count].saved = 0; + wd_count++; } } @@ -475,12 +598,12 @@ 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++; } /* Change to directory I. If I is 0, change to the initial working @@ -497,9 +620,30 @@ chdir_do (int i) if (! prev->saved) { + int err = 0; prev->saved = 1; if (save_cwd (&prev->saved_cwd) != 0) - FATAL_ERROR ((0, 0, _("Cannot save working directory"))); + err = errno; + else if (0 <= prev->saved_cwd.desc) + { + /* 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; + } + + if (err) + FATAL_ERROR ((0, err, _("Cannot save working directory"))); } if (curr->saved) @@ -519,93 +663,6 @@ chdir_do (int i) } } -/* Decode MODE from its binary form in a stat structure, and encode it - into a 9-byte string STRING, terminated with a NUL. */ - -void -decode_mode (mode_t mode, char *string) -{ - *string++ = mode & S_IRUSR ? 'r' : '-'; - *string++ = mode & S_IWUSR ? 'w' : '-'; - *string++ = (mode & S_ISUID - ? (mode & S_IXUSR ? 's' : 'S') - : (mode & S_IXUSR ? 'x' : '-')); - *string++ = mode & S_IRGRP ? 'r' : '-'; - *string++ = mode & S_IWGRP ? 'w' : '-'; - *string++ = (mode & S_ISGID - ? (mode & S_IXGRP ? 's' : 'S') - : (mode & S_IXGRP ? 'x' : '-')); - *string++ = mode & S_IROTH ? 'r' : '-'; - *string++ = mode & S_IWOTH ? 'w' : '-'; - *string++ = (mode & S_ISVTX - ? (mode & S_IXOTH ? 't' : 'T') - : (mode & S_IXOTH ? 'x' : '-')); - *string = '\0'; -} - -/* Report an error associated with the system call CALL and the - optional name NAME. */ -static void -call_arg_error (char const *call, char const *name) -{ - int e = errno; - ERROR ((0, e, _("%s: Cannot %s"), quotearg_colon (name), call)); -} - -/* Report a fatal error associated with the system call CALL and - the optional file name NAME. */ -static void -call_arg_fatal (char const *call, char const *name) -{ - int e = errno; - FATAL_ERROR ((0, e, _("%s: Cannot %s"), quotearg_colon (name), call)); -} - -/* Report a warning associated with the system call CALL and - the optional file name NAME. */ -static void -call_arg_warn (char const *call, char const *name) -{ - int e = errno; - WARN ((0, e, _("%s: Warning: Cannot %s"), quotearg_colon (name), call)); -} - -void -chdir_fatal (char const *name) -{ - call_arg_fatal ("chdir", name); -} - -void -chmod_error_details (char const *name, mode_t mode) -{ - int e = errno; - char buf[10]; - decode_mode (mode, buf); - ERROR ((0, e, _("%s: Cannot change mode to %s"), - quotearg_colon (name), buf)); -} - -void -chown_error_details (char const *name, uid_t uid, gid_t gid) -{ - int e = errno; - ERROR ((0, e, _("%s: Cannot change ownership to uid %lu, gid %lu"), - quotearg_colon (name), (unsigned long) uid, (unsigned long) gid)); -} - -void -close_error (char const *name) -{ - call_arg_error ("close", name); -} - -void -close_warn (char const *name) -{ - call_arg_warn ("close", name); -} - void close_diag (char const *name) { @@ -615,56 +672,6 @@ close_diag (char const *name) close_error (name); } -void -exec_fatal (char const *name) -{ - call_arg_fatal ("exec", name); -} - -void -link_error (char const *target, char const *source) -{ - int e = errno; - ERROR ((0, e, _("%s: Cannot hard link to %s"), - quotearg_colon (source), quote_n (1, target))); -} - -void -mkdir_error (char const *name) -{ - call_arg_error ("mkdir", name); -} - -void -mkfifo_error (char const *name) -{ - call_arg_error ("mkfifo", name); -} - -void -mknod_error (char const *name) -{ - call_arg_error ("mknod", name); -} - -void -open_error (char const *name) -{ - call_arg_error ("open", name); -} - -void -open_fatal (char const *name) -{ - call_arg_fatal ("open", name); -} - -void -open_warn (char const *name) -{ - call_arg_warn ("open", name); -} - void open_diag (char const *name) { @@ -674,38 +681,6 @@ open_diag (char const *name) open_error (name); } -void -read_error (char const *name) -{ - call_arg_error ("read", name); -} - -void -read_error_details (char const *name, off_t offset, size_t size) -{ - char buf[UINTMAX_STRSIZE_BOUND]; - int e = errno; - ERROR ((0, e, - ngettext ("%s: Read error at byte %s, reading %lu byte", - "%s: Read error at byte %s, reading %lu bytes", - size), - quotearg_colon (name), STRINGIFY_BIGINT (offset, buf), - (unsigned long) size)); -} - -void -read_warn_details (char const *name, off_t offset, size_t size) -{ - char buf[UINTMAX_STRSIZE_BOUND]; - int e = errno; - WARN ((0, e, - ngettext ("%s: Warning: Read error at byte %s, reading %lu byte", - "%s: Warning: Read error at byte %s, reading %lu bytes", - size), - quotearg_colon (name), STRINGIFY_BIGINT (offset, buf), - (unsigned long) size)); -} - void read_diag_details (char const *name, off_t offset, size_t size) { @@ -715,37 +690,6 @@ read_diag_details (char const *name, off_t offset, size_t size) read_error_details (name, offset, size); } -void -read_fatal (char const *name) -{ - call_arg_fatal ("read", name); -} - -void -read_fatal_details (char const *name, off_t offset, size_t size) -{ - char buf[UINTMAX_STRSIZE_BOUND]; - int e = errno; - FATAL_ERROR ((0, e, - ngettext ("%s: Read error at byte %s, reading %lu byte", - "%s: Read error at byte %s, reading %lu bytes", - size), - quotearg_colon (name), STRINGIFY_BIGINT (offset, buf), - (unsigned long) size)); -} - -void -readlink_error (char const *name) -{ - call_arg_error ("readlink", name); -} - -void -readlink_warn (char const *name) -{ - call_arg_warn ("readlink", name); -} - void readlink_diag (char const *name) { @@ -755,18 +699,6 @@ readlink_diag (char const *name) readlink_error (name); } -void -savedir_error (char const *name) -{ - call_arg_error ("savedir", name); -} - -void -savedir_warn (char const *name) -{ - call_arg_warn ("savedir", name); -} - void savedir_diag (char const *name) { @@ -776,38 +708,6 @@ savedir_diag (char const *name) savedir_error (name); } -void -seek_error (char const *name) -{ - call_arg_error ("seek", name); -} - -void -seek_error_details (char const *name, off_t offset) -{ - char buf[UINTMAX_STRSIZE_BOUND]; - int e = errno; - ERROR ((0, e, _("%s: Cannot seek to %s"), - quotearg_colon (name), - STRINGIFY_BIGINT (offset, buf))); -} - -void -seek_warn (char const *name) -{ - call_arg_warn ("seek", name); -} - -void -seek_warn_details (char const *name, off_t offset) -{ - char buf[UINTMAX_STRSIZE_BOUND]; - int e = errno; - WARN ((0, e, _("%s: Warning: Cannot seek to %s"), - quotearg_colon (name), - STRINGIFY_BIGINT (offset, buf))); -} - void seek_diag_details (char const *name, off_t offset) { @@ -817,32 +717,6 @@ seek_diag_details (char const *name, off_t offset) seek_error_details (name, offset); } -void -symlink_error (char const *contents, char const *name) -{ - int e = errno; - ERROR ((0, e, _("%s: Cannot create symlink to %s"), - quotearg_colon (name), quote_n (1, contents))); -} - -void -stat_fatal (char const *name) -{ - call_arg_fatal ("stat", name); -} - -void -stat_error (char const *name) -{ - call_arg_error ("stat", name); -} - -void -stat_warn (char const *name) -{ - call_arg_warn ("stat", name); -} - void stat_diag (char const *name) { @@ -852,61 +726,6 @@ stat_diag (char const *name) stat_error (name); } -void -truncate_error (char const *name) -{ - call_arg_error ("truncate", name); -} - -void -truncate_warn (char const *name) -{ - call_arg_warn ("truncate", name); -} - -void -unlink_error (char const *name) -{ - call_arg_error ("unlink", name); -} - -void -utime_error (char const *name) -{ - call_arg_error ("utime", name); -} - -void -waitpid_error (char const *name) -{ - call_arg_error ("waitpid", name); -} - -void -write_error (char const *name) -{ - call_arg_error ("write", name); -} - -void -write_error_details (char const *name, size_t status, size_t size) -{ - if (status == 0) - write_error (name); - else - ERROR ((0, 0, - ngettext ("%s: Wrote only %lu of %lu byte", - "%s: Wrote only %lu of %lu bytes", - size), - name, (unsigned long int) status, (unsigned long int) size)); -} - -void -write_fatal (char const *name) -{ - call_arg_fatal ("write", name); -} - void write_fatal_details (char const *name, ssize_t status, size_t size) { @@ -914,7 +733,6 @@ write_fatal_details (char const *name, ssize_t status, size_t size) fatal_exit (); } - /* Fork, aborting if unsuccessful. */ pid_t xfork (void)