|
@@ -0,0 +1,1352 @@
|
|
|
+/* Tar -- a tape archiver.
|
|
|
+ Copyright (C) 1988 Free Software Foundation
|
|
|
+
|
|
|
+This file is part of GNU Tar.
|
|
|
+
|
|
|
+GNU Tar 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 version.
|
|
|
+
|
|
|
+GNU Tar is distributed in the hope that it will be useful,
|
|
|
+but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+GNU General Public License for more details.
|
|
|
+
|
|
|
+You should have received a copy of the GNU General Public License
|
|
|
+along with GNU Tar; see the file COPYING. If not, write to
|
|
|
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
|
+
|
|
|
+/*
|
|
|
+ * A tar (tape archiver) program.
|
|
|
+ *
|
|
|
+ * Written by John Gilmore, ihnp4!hoptoad!gnu, starting 25 Aug 85.
|
|
|
+ */
|
|
|
+
|
|
|
+#include <stdio.h>
|
|
|
+#include <sys/types.h> /* Needed for typedefs in tar.h */
|
|
|
+#include "getopt.h"
|
|
|
+#include "regex.h"
|
|
|
+
|
|
|
+/*
|
|
|
+ * The following causes "tar.h" to produce definitions of all the
|
|
|
+ * global variables, rather than just "extern" declarations of them.
|
|
|
+ */
|
|
|
+#define TAR_EXTERN /**/
|
|
|
+#include "tar.h"
|
|
|
+
|
|
|
+#include "port.h"
|
|
|
+
|
|
|
+#if defined(_POSIX_VERSION) || defined(DIRENT)
|
|
|
+#include <dirent.h>
|
|
|
+#ifdef direct
|
|
|
+#undef direct
|
|
|
+#endif /* direct */
|
|
|
+#define direct dirent
|
|
|
+#define DP_NAMELEN(x) strlen((x)->d_name)
|
|
|
+#endif /* _POSIX_VERSION or DIRENT */
|
|
|
+#if !defined(_POSIX_VERSION) && !defined(DIRENT) && defined(BSD42)
|
|
|
+#include <sys/dir.h>
|
|
|
+#define DP_NAMELEN(x) (x)->d_namlen
|
|
|
+#endif /* not _POSIX_VERSION and BSD42 */
|
|
|
+#ifdef __MSDOS__
|
|
|
+#include "msd_dir.h"
|
|
|
+#define DP_NAMELEN(x) (x)->d_namlen
|
|
|
+#define direct dirent
|
|
|
+#endif
|
|
|
+#if defined(USG) && !defined(_POSIX_VERSION) && !defined(DIRENT)
|
|
|
+#include <ndir.h>
|
|
|
+#define DP_NAMELEN(x) strlen((x)->d_name)
|
|
|
+#endif /* USG and not _POSIX_VERSION and not DIRENT */
|
|
|
+
|
|
|
+/*
|
|
|
+ * We should use a conversion routine that does reasonable error
|
|
|
+ * checking -- atoi doesn't. For now, punt. FIXME.
|
|
|
+ */
|
|
|
+#define intconv atoi
|
|
|
+PTR ck_malloc();
|
|
|
+PTR ck_realloc();
|
|
|
+extern int getoldopt();
|
|
|
+extern void read_and();
|
|
|
+extern void list_archive();
|
|
|
+extern void extract_archive();
|
|
|
+extern void diff_archive();
|
|
|
+extern void create_archive();
|
|
|
+extern void update_archive();
|
|
|
+extern void junk_archive();
|
|
|
+
|
|
|
+/* JF */
|
|
|
+extern time_t get_date();
|
|
|
+
|
|
|
+time_t new_time;
|
|
|
+
|
|
|
+static FILE *namef; /* File to read names from */
|
|
|
+static char **n_argv; /* Argv used by name routines */
|
|
|
+static int n_argc; /* Argc used by name routines */
|
|
|
+static char **n_ind; /* Store an array of names */
|
|
|
+static int n_indalloc; /* How big is the array? */
|
|
|
+static int n_indused; /* How many entries does it have? */
|
|
|
+static int n_indscan; /* How many of the entries have we scanned? */
|
|
|
+
|
|
|
+
|
|
|
+extern FILE *msg_file;
|
|
|
+
|
|
|
+int check_exclude();
|
|
|
+void add_exclude();
|
|
|
+void add_exclude_file();
|
|
|
+void addname();
|
|
|
+void describe();
|
|
|
+void diff_init();
|
|
|
+void extr_init();
|
|
|
+int is_regex();
|
|
|
+void name_add();
|
|
|
+void name_init();
|
|
|
+void options();
|
|
|
+char *un_quote_string();
|
|
|
+int wildmat();
|
|
|
+
|
|
|
+#ifndef S_ISLNK
|
|
|
+#define lstat stat
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifndef DEFBLOCKING
|
|
|
+#define DEFBLOCKING 20
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifndef DEF_AR_FILE
|
|
|
+#define DEF_AR_FILE "tar.out"
|
|
|
+#endif
|
|
|
+
|
|
|
+/* For long options that unconditionally set a single flag, we have getopt
|
|
|
+ do it. For the others, we share the code for the equivalent short
|
|
|
+ named option, the name of which is stored in the otherwise-unused `val'
|
|
|
+ field of the `struct option'; for long options that have no equivalent
|
|
|
+ short option, we use nongraphic characters as pseudo short option
|
|
|
+ characters, starting (for no particular reason) with character 10. */
|
|
|
+
|
|
|
+struct option long_options[] =
|
|
|
+{
|
|
|
+ {"create", 0, 0, 'c'},
|
|
|
+ {"append", 0, 0, 'r'},
|
|
|
+ {"extract", 0, 0, 'x'},
|
|
|
+ {"get", 0, 0, 'x'},
|
|
|
+ {"list", 0, 0, 't'},
|
|
|
+ {"update", 0, 0, 'u'},
|
|
|
+ {"catenate", 0, 0, 'A'},
|
|
|
+ {"concatenate", 0, 0, 'A'},
|
|
|
+ {"compare", 0, 0, 'd'},
|
|
|
+ {"diff", 0, 0, 'd'},
|
|
|
+ {"delete", 0, 0, 14},
|
|
|
+ {"help", 0, 0, 12},
|
|
|
+
|
|
|
+ {"null", 0, 0, 16},
|
|
|
+ {"directory", 1, 0, 'C'},
|
|
|
+ {"record-number", 0, &f_sayblock, 1},
|
|
|
+ {"files-from", 1, 0, 'T'},
|
|
|
+ {"label", 1, 0, 'V'},
|
|
|
+ {"exclude-from", 1, 0, 'X'},
|
|
|
+ {"exclude", 1, 0, 15},
|
|
|
+ {"file", 1, 0, 'f'},
|
|
|
+ {"block-size", 1, 0, 'b'},
|
|
|
+ {"version", 0, 0, 11},
|
|
|
+ {"verbose", 0, 0, 'v'},
|
|
|
+ {"totals", 0, &f_totals, 1},
|
|
|
+
|
|
|
+ {"read-full-blocks", 0, &f_reblock, 1},
|
|
|
+ {"starting-file", 1, 0, 'K'},
|
|
|
+ {"to-stdout", 0, &f_exstdout, 1},
|
|
|
+ {"ignore-zeros", 0, &f_ignorez, 1},
|
|
|
+ {"keep-old-files", 0, 0, 'k'},
|
|
|
+ {"uncompress", 0, &f_compress, 1},
|
|
|
+ {"same-permissions", 0, &f_use_protection, 1},
|
|
|
+ {"preserve-permissions",0, &f_use_protection, 1},
|
|
|
+ {"modification-time", 0, &f_modified, 1},
|
|
|
+ {"preserve", 0, 0, 10},
|
|
|
+ {"same-order", 0, &f_sorted_names, 1},
|
|
|
+ {"same-owner", 0, &f_do_chown, 1},
|
|
|
+ {"preserve-order", 0, &f_sorted_names, 1},
|
|
|
+
|
|
|
+ {"newer", 1, 0, 'N'},
|
|
|
+ {"after-date", 1, 0, 'N'},
|
|
|
+ {"newer-mtime", 1, 0, 13},
|
|
|
+ {"incremental", 0, 0, 'G'},
|
|
|
+ {"listed-incremental", 1, 0, 'g'},
|
|
|
+ {"multi-volume", 0, &f_multivol, 1},
|
|
|
+ {"info-script", 1, 0, 'F'},
|
|
|
+ {"absolute-paths", 0, &f_absolute_paths, 1},
|
|
|
+ {"interactive", 0, &f_confirm, 1},
|
|
|
+ {"confirmation", 0, &f_confirm, 1},
|
|
|
+
|
|
|
+ {"verify", 0, &f_verify, 1},
|
|
|
+ {"dereference", 0, &f_follow_links, 1},
|
|
|
+ {"one-file-system", 0, &f_local_filesys, 1},
|
|
|
+ {"old-archive", 0, 0, 'o'},
|
|
|
+ {"portability", 0, 0, 'o'},
|
|
|
+ {"compress", 0, &f_compress, 1},
|
|
|
+ {"compress-block", 0, &f_compress, 2},
|
|
|
+ {"sparse", 0, &f_sparse_files, 1},
|
|
|
+ {"tape-length", 1, 0, 'L'},
|
|
|
+
|
|
|
+ {0, 0, 0, 0}
|
|
|
+};
|
|
|
+
|
|
|
+/*
|
|
|
+ * Main routine for tar.
|
|
|
+ */
|
|
|
+void
|
|
|
+main(argc, argv)
|
|
|
+ int argc;
|
|
|
+ char **argv;
|
|
|
+{
|
|
|
+ extern char version_string[];
|
|
|
+
|
|
|
+ tar = argv[0]; /* JF: was "tar" Set program name */
|
|
|
+ filename_terminator = '\n';
|
|
|
+ errors = 0;
|
|
|
+
|
|
|
+ options(argc, argv);
|
|
|
+
|
|
|
+ if(!n_argv)
|
|
|
+ name_init(argc, argv);
|
|
|
+
|
|
|
+ switch(cmd_mode) {
|
|
|
+ case CMD_CAT:
|
|
|
+ case CMD_UPDATE:
|
|
|
+ case CMD_APPEND:
|
|
|
+ update_archive();
|
|
|
+ break;
|
|
|
+ case CMD_DELETE:
|
|
|
+ junk_archive();
|
|
|
+ break;
|
|
|
+ case CMD_CREATE:
|
|
|
+ create_archive();
|
|
|
+ if (f_totals)
|
|
|
+ fprintf (stderr, "Total bytes written: %d\n", tot_written);
|
|
|
+ break;
|
|
|
+ case CMD_EXTRACT:
|
|
|
+ if (f_volhdr) {
|
|
|
+ char *err;
|
|
|
+ label_pattern = (struct re_pattern_buffer *)
|
|
|
+ ck_malloc (sizeof *label_pattern);
|
|
|
+ err = re_compile_pattern (f_volhdr, strlen (f_volhdr),
|
|
|
+ label_pattern);
|
|
|
+ if (err) {
|
|
|
+ fprintf (stderr,"Bad regular expression: %s\n",
|
|
|
+ err);
|
|
|
+ errors++;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ extr_init();
|
|
|
+ read_and(extract_archive);
|
|
|
+ break;
|
|
|
+ case CMD_LIST:
|
|
|
+ if (f_volhdr) {
|
|
|
+ char *err;
|
|
|
+ label_pattern = (struct re_pattern_buffer *)
|
|
|
+ ck_malloc (sizeof *label_pattern);
|
|
|
+ err = re_compile_pattern (f_volhdr, strlen (f_volhdr),
|
|
|
+ label_pattern);
|
|
|
+ if (err) {
|
|
|
+ fprintf (stderr,"Bad regular expression: %s\n",
|
|
|
+ err);
|
|
|
+ errors++;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ read_and(list_archive);
|
|
|
+#if 0
|
|
|
+ if (!errors)
|
|
|
+ errors = different;
|
|
|
+#endif
|
|
|
+ break;
|
|
|
+ case CMD_DIFF:
|
|
|
+ diff_init();
|
|
|
+ read_and(diff_archive);
|
|
|
+ break;
|
|
|
+ case CMD_VERSION:
|
|
|
+ fprintf(stderr,"%s\n",version_string);
|
|
|
+ break;
|
|
|
+ case CMD_NONE:
|
|
|
+ msg("you must specify exactly one of the r, c, t, x, or d options\n");
|
|
|
+ fprintf(stderr,"For more information, type ``%s +help''.\n",tar);
|
|
|
+ exit(EX_ARGSBAD);
|
|
|
+ }
|
|
|
+ exit(errors);
|
|
|
+ /* NOTREACHED */
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * Parse the options for tar.
|
|
|
+ */
|
|
|
+void
|
|
|
+options(argc, argv)
|
|
|
+ int argc;
|
|
|
+ char **argv;
|
|
|
+{
|
|
|
+ register int c; /* Option letter */
|
|
|
+ int ind = -1;
|
|
|
+
|
|
|
+ /* Set default option values */
|
|
|
+ blocking = DEFBLOCKING; /* From Makefile */
|
|
|
+ ar_file = getenv("TAPE"); /* From environment, or */
|
|
|
+ if (ar_file == 0)
|
|
|
+ ar_file = DEF_AR_FILE; /* From Makefile */
|
|
|
+
|
|
|
+ /* Parse options */
|
|
|
+ while ((c = getoldopt(argc, argv,
|
|
|
+ "-01234567Ab:BcC:df:F:g:GhikK:lL:mMN:oOpPrRsStT:uvV:wWxX:zZ",
|
|
|
+ long_options, &ind)) != EOF) {
|
|
|
+ switch (c) {
|
|
|
+ case 0: /* long options that set a single flag */
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ /* File name or non-parsed option */
|
|
|
+ name_add(optarg);
|
|
|
+ break;
|
|
|
+ case 'C':
|
|
|
+ name_add("-C");
|
|
|
+ name_add(optarg);
|
|
|
+ break;
|
|
|
+ case 10: /* preserve */
|
|
|
+ f_use_protection = f_sorted_names = 1;
|
|
|
+ break;
|
|
|
+ case 11:
|
|
|
+ if(cmd_mode!=CMD_NONE)
|
|
|
+ goto badopt;
|
|
|
+ cmd_mode=CMD_VERSION;
|
|
|
+ break;
|
|
|
+ case 12: /* help */
|
|
|
+ printf("This is GNU tar, the tape archiving program.\n");
|
|
|
+ describe();
|
|
|
+ exit(1);
|
|
|
+ case 13:
|
|
|
+ f_new_files++;
|
|
|
+ goto get_newer;
|
|
|
+
|
|
|
+ case 14: /* Delete in the archive */
|
|
|
+ if(cmd_mode!=CMD_NONE)
|
|
|
+ goto badopt;
|
|
|
+ cmd_mode=CMD_DELETE;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 15:
|
|
|
+ f_exclude++;
|
|
|
+ add_exclude(optarg);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 16: /* -T reads null terminated filenames. */
|
|
|
+ filename_terminator = '\0';
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'g': /* We are making a GNU dump; save
|
|
|
+ directories at the beginning of
|
|
|
+ the archive, and include in each
|
|
|
+ directory its contents */
|
|
|
+ if(f_oldarch)
|
|
|
+ goto badopt;
|
|
|
+ f_gnudump++;
|
|
|
+ gnu_dumpfile=optarg;
|
|
|
+ break;
|
|
|
+
|
|
|
+
|
|
|
+ case '0':
|
|
|
+ case '1':
|
|
|
+ case '2':
|
|
|
+ case '3':
|
|
|
+ case '4':
|
|
|
+ case '5':
|
|
|
+ case '6':
|
|
|
+ case '7':
|
|
|
+ {
|
|
|
+ /* JF this'll have to be modified for other
|
|
|
+ systems, of course! */
|
|
|
+ int d,add;
|
|
|
+ static char buf[50];
|
|
|
+
|
|
|
+ d=getoldopt(argc,argv,"lmh");
|
|
|
+#ifdef MAYBEDEF
|
|
|
+ sprintf(buf,"/dev/rmt/%d%c",c,d);
|
|
|
+#else
|
|
|
+#ifndef LOW_NUM
|
|
|
+#define LOW_NUM 0
|
|
|
+#define MID_NUM 8
|
|
|
+#define HGH_NUM 16
|
|
|
+#endif
|
|
|
+ if(d=='l') add=LOW_NUM;
|
|
|
+ else if(d=='m') add=MID_NUM;
|
|
|
+ else if(d=='h') add=HGH_NUM;
|
|
|
+ else goto badopt;
|
|
|
+
|
|
|
+ sprintf(buf,"/dev/rmt%d",add+c-'0');
|
|
|
+#endif
|
|
|
+ ar_file=buf;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'A': /* Arguments are tar files,
|
|
|
+ just cat them onto the end
|
|
|
+ of the archive. */
|
|
|
+ if(cmd_mode!=CMD_NONE)
|
|
|
+ goto badopt;
|
|
|
+ cmd_mode=CMD_CAT;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'b': /* Set blocking factor */
|
|
|
+ blocking = intconv(optarg);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'B': /* Try to reblock input */
|
|
|
+ f_reblock++; /* For reading 4.2BSD pipes */
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'c': /* Create an archive */
|
|
|
+ if(cmd_mode!=CMD_NONE)
|
|
|
+ goto badopt;
|
|
|
+ cmd_mode=CMD_CREATE;
|
|
|
+ break;
|
|
|
+
|
|
|
+#if 0
|
|
|
+ case 'C':
|
|
|
+ if(chdir(optarg)<0)
|
|
|
+ msg_perror("Can't change directory to %d",optarg);
|
|
|
+ break;
|
|
|
+#endif
|
|
|
+
|
|
|
+ case 'd': /* Find difference tape/disk */
|
|
|
+ if(cmd_mode!=CMD_NONE)
|
|
|
+ goto badopt;
|
|
|
+ cmd_mode=CMD_DIFF;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'f': /* Use ar_file for the archive */
|
|
|
+ ar_file = optarg;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'F':
|
|
|
+ /* Since -F is only useful with -M , make it implied */
|
|
|
+ f_run_script_at_end++; /* run this script at the end */
|
|
|
+ info_script = optarg; /* of each tape */
|
|
|
+ f_multivol++;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'G': /* We are making a GNU dump; save
|
|
|
+ directories at the beginning of
|
|
|
+ the archive, and include in each
|
|
|
+ directory its contents */
|
|
|
+ if(f_oldarch)
|
|
|
+ goto badopt;
|
|
|
+ f_gnudump++;
|
|
|
+ gnu_dumpfile=0;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'h':
|
|
|
+ f_follow_links++; /* follow symbolic links */
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'i':
|
|
|
+ f_ignorez++; /* Ignore zero records (eofs) */
|
|
|
+ /*
|
|
|
+ * This can't be the default, because Unix tar
|
|
|
+ * writes two records of zeros, then pads out the
|
|
|
+ * block with garbage.
|
|
|
+ */
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'k': /* Don't overwrite files */
|
|
|
+#ifdef NO_OPEN3
|
|
|
+ msg("can't keep old files on this system");
|
|
|
+ exit(EX_ARGSBAD);
|
|
|
+#else
|
|
|
+ f_keep++;
|
|
|
+#endif
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'K':
|
|
|
+ f_startfile++;
|
|
|
+ addname(optarg);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'l': /* When dumping directories, don't
|
|
|
+ dump files/subdirectories that are
|
|
|
+ on other filesystems. */
|
|
|
+ f_local_filesys++;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'L':
|
|
|
+ tape_length = intconv (optarg);
|
|
|
+ f_multivol++;
|
|
|
+ break;
|
|
|
+ case 'm':
|
|
|
+ f_modified++;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'M': /* Make Multivolume archive:
|
|
|
+ When we can't write any more
|
|
|
+ into the archive, re-open it,
|
|
|
+ and continue writing */
|
|
|
+ f_multivol++;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'N': /* Only write files newer than X */
|
|
|
+ get_newer:
|
|
|
+ f_new_files++;
|
|
|
+ new_time=get_date(optarg, (PTR) 0);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'o': /* Generate old archive */
|
|
|
+ if(f_gnudump /* || f_dironly */)
|
|
|
+ goto badopt;
|
|
|
+ f_oldarch++;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'O':
|
|
|
+ f_exstdout++;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'p':
|
|
|
+ f_use_protection++;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'P':
|
|
|
+ f_absolute_paths++;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'r': /* Append files to the archive */
|
|
|
+ if(cmd_mode!=CMD_NONE)
|
|
|
+ goto badopt;
|
|
|
+ cmd_mode=CMD_APPEND;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'R':
|
|
|
+ f_sayblock++; /* Print block #s for debug */
|
|
|
+ break; /* of bad tar archives */
|
|
|
+
|
|
|
+ case 's':
|
|
|
+ f_sorted_names++; /* Names to extr are sorted */
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'S': /* deal with sparse files */
|
|
|
+ f_sparse_files++;
|
|
|
+ break;
|
|
|
+ case 't':
|
|
|
+ if(cmd_mode!=CMD_NONE)
|
|
|
+ goto badopt;
|
|
|
+ cmd_mode=CMD_LIST;
|
|
|
+ f_verbose++; /* "t" output == "cv" or "xv" */
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'T':
|
|
|
+ name_file = optarg;
|
|
|
+ f_namefile++;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'u': /* Append files to the archive that
|
|
|
+ aren't there, or are newer than the
|
|
|
+ copy in the archive */
|
|
|
+ if(cmd_mode!=CMD_NONE)
|
|
|
+ goto badopt;
|
|
|
+ cmd_mode=CMD_UPDATE;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'v':
|
|
|
+ f_verbose++;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'V':
|
|
|
+ f_volhdr=optarg;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'w':
|
|
|
+ f_confirm++;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'W':
|
|
|
+ f_verify++;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'x': /* Extract files from the archive */
|
|
|
+ if(cmd_mode!=CMD_NONE)
|
|
|
+ goto badopt;
|
|
|
+ cmd_mode=CMD_EXTRACT;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'X':
|
|
|
+ f_exclude++;
|
|
|
+ add_exclude_file(optarg);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'z': /* Easy to type */
|
|
|
+ case 'Z': /* Like the filename extension .Z */
|
|
|
+ f_compress++;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case '?':
|
|
|
+ badopt:
|
|
|
+ msg("Unknown option. Use '%s +help' for a complete list of options.", tar);
|
|
|
+ exit(EX_ARGSBAD);
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ blocksize = blocking * RECORDSIZE;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * Print as much help as the user's gonna get.
|
|
|
+ *
|
|
|
+ * We have to sprinkle in the KLUDGE lines because too many compilers
|
|
|
+ * cannot handle character strings longer than about 512 bytes. Yuk!
|
|
|
+ * In particular, MS-DOS and Xenix MSC and PDP-11 V7 Unix have this
|
|
|
+ * problem.
|
|
|
+ */
|
|
|
+void
|
|
|
+describe()
|
|
|
+{
|
|
|
+ puts("choose one of the following:");
|
|
|
+ fputs("\
|
|
|
+-A, +catenate,\n\
|
|
|
+ +concatenate append tar files to an archive\n\
|
|
|
+-c, +create create a new archive\n\
|
|
|
+-d, +diff,\n\
|
|
|
+ +compare find differences between archive and file system\n\
|
|
|
++delete delete from the archive (not for use on mag tapes!)\n\
|
|
|
+-r, +append append files to the end of an archive\n\
|
|
|
+-t, +list list the contents of an archive\n\
|
|
|
+-u, +update only append files that are newer than copy in archive\n\
|
|
|
+-x, +extract,\n\
|
|
|
+ +get extract files from an archive\n",stdout);
|
|
|
+
|
|
|
+ fprintf(stdout, "\
|
|
|
+Other options:\n\
|
|
|
+-b, +block-size N block size of Nx512 bytes (default N=%d)\n", DEFBLOCKING);
|
|
|
+ fputs ("\
|
|
|
+-B, +read-full-blocks reblock as we read (for reading 4.2BSD pipes)\n\
|
|
|
+-C, +directory DIR change to directory DIR\n\
|
|
|
+", stdout); /* KLUDGE */ fprintf(stdout, "\
|
|
|
+-f, +file [HOSTNAME:]F use archive file or device F (default %s)\n",
|
|
|
+ DEF_AR_FILE); fputs("\
|
|
|
+-F, +info-script F run script at end of each tape (implies -M)\n\
|
|
|
+-G, +incremental create/list/extract old GNU-format incremental backup\n\
|
|
|
+-g, +listed-incremental F create/list/extract new GNU-format incremental backup\n\
|
|
|
+-h, +dereference don't dump symlinks; dump the files they point to\n\
|
|
|
+-i, +ignore-zeros ignore blocks of zeros in archive (normally mean EOF)\n\
|
|
|
+-k, +keep-old-files keep existing files; don't overwrite them from archive\n\
|
|
|
+-K, +starting-file FILE begin at FILE in the archive\n\
|
|
|
+-l, +one-file-system stay in local file system when creating an archive\n\
|
|
|
+-L, +tape-length LENGTH change tapes after writing LENGTH\n\
|
|
|
+", stdout); /* KLUDGE */ fputs("\
|
|
|
+-m, +modification-time don't extract file modified time\n\
|
|
|
+-M, +multi-volume create/list/extract multi-volume archive\n\
|
|
|
+-N, +after-date DATE,\n\
|
|
|
+ +newer DATE only store files newer than DATE\n\
|
|
|
+-o, +old-archive,\n\
|
|
|
+ +portability write a V7 format archive, rather than ANSI format\n\
|
|
|
+-O, +to-stdout extract files to standard output\n\
|
|
|
+-p, +same-permissions,\n\
|
|
|
+ +preserve-permissions extract all protection information\n\
|
|
|
+-P, +absolute-paths don't strip leading `/'s from file names\n\
|
|
|
++preserve like -p -s\n\
|
|
|
+", stdout); /* KLUDGE */ fputs("\
|
|
|
+-R, +record-number show record number within archive with each message\n\
|
|
|
+-s, +same-order,\n\
|
|
|
+ +preserve-order list of names to extract is sorted to match archive\n\
|
|
|
++same-order create extracted files with the same ownership \n\
|
|
|
+-S, +sparse handle sparse files efficiently\n\
|
|
|
+-T, +files-from F get names to extract or create from file F\n\
|
|
|
++null -T reads null-terminated names, disable -C\n\
|
|
|
++totals print total bytes written with +create\n\
|
|
|
+-v, +verbose verbosely list files processed\n\
|
|
|
+-V, +label NAME create archive with volume name NAME\n\
|
|
|
++version print tar program version number\n\
|
|
|
+-w, +interactive,\n\
|
|
|
+ +confirmation ask for confirmation for every action\n\
|
|
|
+", stdout); /* KLUDGE */ fputs("\
|
|
|
+-W, +verify attempt to verify the archive after writing it\n\
|
|
|
++exclude FILE exclude file FILE\n\
|
|
|
+-X, +exclude-from FILE exclude files listed in FILE\n\
|
|
|
+-z, -Z, +compress,\n\
|
|
|
+ +uncompress filter the archive through compress\n\
|
|
|
+-[0-7][lmh] specify drive and density\n\
|
|
|
+", stdout);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+name_add(name)
|
|
|
+char *name;
|
|
|
+{
|
|
|
+ if(n_indalloc==n_indused) {
|
|
|
+ n_indalloc+=10;
|
|
|
+ n_ind=(char **)(n_indused ? ck_realloc(n_ind,n_indalloc*sizeof(char *)) : ck_malloc(n_indalloc*sizeof(char *)));
|
|
|
+ }
|
|
|
+ n_ind[n_indused++]=name;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Set up to gather file names for tar.
|
|
|
+ *
|
|
|
+ * They can either come from stdin or from argv.
|
|
|
+ */
|
|
|
+void
|
|
|
+name_init(argc, argv)
|
|
|
+ int argc;
|
|
|
+ char **argv;
|
|
|
+{
|
|
|
+
|
|
|
+ if (f_namefile) {
|
|
|
+ if (optind < argc) {
|
|
|
+ msg("too many args with -T option");
|
|
|
+ exit(EX_ARGSBAD);
|
|
|
+ }
|
|
|
+ if (!strcmp(name_file, "-")) {
|
|
|
+ namef = stdin;
|
|
|
+ } else {
|
|
|
+ namef = fopen(name_file, "r");
|
|
|
+ if (namef == NULL) {
|
|
|
+ msg_perror("can't open file %s",name_file);
|
|
|
+ exit(EX_BADFILE);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /* Get file names from argv, after options. */
|
|
|
+ n_argc = argc;
|
|
|
+ n_argv = argv;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* Read the next filename read from STREAM and null-terminate it.
|
|
|
+ Put it into BUFFER, reallocating and adjusting *PBUFFER_SIZE if necessary.
|
|
|
+ Return the new value for BUFFER, or NULL at end of file. */
|
|
|
+
|
|
|
+char *
|
|
|
+read_name_from_file (buffer, pbuffer_size, stream)
|
|
|
+ char *buffer;
|
|
|
+ size_t *pbuffer_size;
|
|
|
+ FILE *stream;
|
|
|
+{
|
|
|
+ register int c;
|
|
|
+ register int indx = 0;
|
|
|
+ register size_t buffer_size = *pbuffer_size;
|
|
|
+
|
|
|
+ while ((c = getc (stream)) != EOF && c != filename_terminator)
|
|
|
+ {
|
|
|
+ if (indx == buffer_size)
|
|
|
+ {
|
|
|
+ buffer_size += NAMSIZ;
|
|
|
+ buffer = ck_realloc (buffer, buffer_size + 2);
|
|
|
+ }
|
|
|
+ buffer[indx++] = c;
|
|
|
+ }
|
|
|
+ if (indx == 0 && c == EOF)
|
|
|
+ return NULL;
|
|
|
+ if (indx == buffer_size)
|
|
|
+ {
|
|
|
+ buffer_size += NAMSIZ;
|
|
|
+ buffer = ck_realloc (buffer, buffer_size + 2);
|
|
|
+ }
|
|
|
+ buffer[indx] = '\0';
|
|
|
+ *pbuffer_size = buffer_size;
|
|
|
+ return buffer;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Get the next name from argv or the name file.
|
|
|
+ *
|
|
|
+ * Result is in static storage and can't be relied upon across two calls.
|
|
|
+ *
|
|
|
+ * If CHANGE_DIRS is non-zero, treat a filename of the form "-C" as
|
|
|
+ * meaning that the next filename is the name of a directory to change to.
|
|
|
+ * If `filename_terminator' is '\0', CHANGE_DIRS is effectively always 0.
|
|
|
+ */
|
|
|
+
|
|
|
+char *
|
|
|
+name_next(change_dirs)
|
|
|
+ int change_dirs;
|
|
|
+{
|
|
|
+ static char *buffer; /* Holding pattern */
|
|
|
+ static int buffer_siz;
|
|
|
+ register char *p;
|
|
|
+ register char *q = 0;
|
|
|
+ register int next_name_is_dir = 0;
|
|
|
+ extern char *un_quote_string();
|
|
|
+
|
|
|
+ if(buffer_siz==0) {
|
|
|
+ buffer=ck_malloc(NAMSIZ+2);
|
|
|
+ buffer_siz=NAMSIZ;
|
|
|
+ }
|
|
|
+ if (filename_terminator == '\0')
|
|
|
+ change_dirs = 0;
|
|
|
+ tryagain:
|
|
|
+ if (namef == NULL) {
|
|
|
+ if(n_indscan<n_indused)
|
|
|
+ p=n_ind[n_indscan++];
|
|
|
+ else if (optind < n_argc)
|
|
|
+ /* Names come from argv, after options */
|
|
|
+ p=n_argv[optind++];
|
|
|
+ else {
|
|
|
+ if(q)
|
|
|
+ msg("Missing filename after -C");
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* JF trivial support for -C option. I don't know if
|
|
|
+ chdir'ing at this point is dangerous or not.
|
|
|
+ It seems to work, which is all I ask. */
|
|
|
+ if(change_dirs && !q && p[0]=='-' && p[1]=='C' && p[2]=='\0') {
|
|
|
+ q=p;
|
|
|
+ goto tryagain;
|
|
|
+ }
|
|
|
+ if(q) {
|
|
|
+ if(chdir(p)<0)
|
|
|
+ msg_perror("Can't chdir to %s",p);
|
|
|
+ q=0;
|
|
|
+ goto tryagain;
|
|
|
+ }
|
|
|
+ /* End of JF quick -C hack */
|
|
|
+
|
|
|
+ if(f_exclude && check_exclude(p))
|
|
|
+ goto tryagain;
|
|
|
+ return un_quote_string(p);
|
|
|
+ }
|
|
|
+ while (p = read_name_from_file (buffer, &buffer_siz, namef)) {
|
|
|
+ buffer = p;
|
|
|
+ if (*p == '\0')
|
|
|
+ continue; /* Ignore empty lines. */
|
|
|
+ q = p + strlen (p) - 1;
|
|
|
+ while (q > p && *q == '/') /* Zap trailing "/"s. */
|
|
|
+ *q-- = '\0';
|
|
|
+ if (change_dirs && next_name_is_dir == 0
|
|
|
+ && p[0] == '-' && p[1] == 'C' && p[2] == '\0') {
|
|
|
+ next_name_is_dir = 1;
|
|
|
+ goto tryagain;
|
|
|
+ }
|
|
|
+ if (next_name_is_dir) {
|
|
|
+ if (chdir (p) < 0)
|
|
|
+ msg_perror ("Can't change to directory %s", p);
|
|
|
+ next_name_is_dir = 0;
|
|
|
+ goto tryagain;
|
|
|
+ }
|
|
|
+ if(f_exclude && check_exclude(p))
|
|
|
+ goto tryagain;
|
|
|
+ return un_quote_string(p);
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * Close the name file, if any.
|
|
|
+ */
|
|
|
+void
|
|
|
+name_close()
|
|
|
+{
|
|
|
+
|
|
|
+ if (namef != NULL && namef != stdin) fclose(namef);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * Gather names in a list for scanning.
|
|
|
+ * Could hash them later if we really care.
|
|
|
+ *
|
|
|
+ * If the names are already sorted to match the archive, we just
|
|
|
+ * read them one by one. name_gather reads the first one, and it
|
|
|
+ * is called by name_match as appropriate to read the next ones.
|
|
|
+ * At EOF, the last name read is just left in the buffer.
|
|
|
+ * This option lets users of small machines extract an arbitrary
|
|
|
+ * number of files by doing "tar t" and editing down the list of files.
|
|
|
+ */
|
|
|
+void
|
|
|
+name_gather()
|
|
|
+{
|
|
|
+ register char *p;
|
|
|
+ static struct name *namebuf; /* One-name buffer */
|
|
|
+ static namelen;
|
|
|
+ static char *chdir_name;
|
|
|
+
|
|
|
+ if (f_sorted_names) {
|
|
|
+ if(!namelen) {
|
|
|
+ namelen=NAMSIZ;
|
|
|
+ namebuf=(struct name *)ck_malloc(sizeof(struct name)+NAMSIZ);
|
|
|
+ }
|
|
|
+ p = name_next(0);
|
|
|
+ if (p) {
|
|
|
+ if(*p=='-' && p[1]=='C' && p[2]=='\0') {
|
|
|
+ chdir_name=name_next(0);
|
|
|
+ p=name_next(0);
|
|
|
+ if(!p) {
|
|
|
+ msg("Missing file name after -C");
|
|
|
+ exit(EX_ARGSBAD);
|
|
|
+ }
|
|
|
+ namebuf->change_dir=chdir_name;
|
|
|
+ }
|
|
|
+ namebuf->length = strlen(p);
|
|
|
+ if (namebuf->length >= namelen) {
|
|
|
+ namebuf=(struct name *)ck_realloc(namebuf,sizeof(struct name)+namebuf->length);
|
|
|
+ namelen=namebuf->length;
|
|
|
+ }
|
|
|
+ strncpy(namebuf->name, p, namebuf->length);
|
|
|
+ namebuf->name[ namebuf->length ] = 0;
|
|
|
+ namebuf->next = (struct name *)NULL;
|
|
|
+ namebuf->found = 0;
|
|
|
+ namelist = namebuf;
|
|
|
+ namelast = namelist;
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Non sorted names -- read them all in */
|
|
|
+ while (p = name_next(0))
|
|
|
+ addname(p);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Add a name to the namelist.
|
|
|
+ */
|
|
|
+void
|
|
|
+addname(name)
|
|
|
+ char *name; /* pointer to name */
|
|
|
+{
|
|
|
+ register int i; /* Length of string */
|
|
|
+ register struct name *p; /* Current struct pointer */
|
|
|
+ static char *chdir_name;
|
|
|
+ char *new_name();
|
|
|
+
|
|
|
+ if(name[0]=='-' && name[1]=='C' && name[2]=='\0') {
|
|
|
+ chdir_name=name_next(0);
|
|
|
+ name=name_next(0);
|
|
|
+ if(!chdir_name) {
|
|
|
+ msg("Missing file name after -C");
|
|
|
+ exit(EX_ARGSBAD);
|
|
|
+ }
|
|
|
+ if(chdir_name[0]!='/') {
|
|
|
+ char *path = ck_malloc(PATH_MAX);
|
|
|
+#if defined(__MSDOS__) || defined(USG) || defined(_POSIX_VERSION)
|
|
|
+ if(!getcwd(path,PATH_MAX))
|
|
|
+ msg("Couldn't get current directory.");
|
|
|
+ exit(EX_SYSTEM);
|
|
|
+#else
|
|
|
+ char *getwd();
|
|
|
+
|
|
|
+ if(!getwd(path)) {
|
|
|
+ msg("Couldn't get current directory: %s",path);
|
|
|
+ exit(EX_SYSTEM);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ chdir_name=new_name(path,chdir_name);
|
|
|
+ free(path);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (name)
|
|
|
+ {
|
|
|
+ i = strlen(name);
|
|
|
+ /*NOSTRICT*/
|
|
|
+ p = (struct name *)malloc((unsigned)(sizeof(struct name) + i));
|
|
|
+ }
|
|
|
+ else
|
|
|
+ p = (struct name *)malloc ((unsigned)(sizeof (struct name)));
|
|
|
+ if (!p) {
|
|
|
+ if (name)
|
|
|
+ msg("cannot allocate mem for name '%s'.",name);
|
|
|
+ else
|
|
|
+ msg("cannot allocate mem for chdir record.");
|
|
|
+ exit(EX_SYSTEM);
|
|
|
+ }
|
|
|
+ p->next = (struct name *)NULL;
|
|
|
+ if (name)
|
|
|
+ {
|
|
|
+ p->fake = 0;
|
|
|
+ p->length = i;
|
|
|
+ strncpy(p->name, name, i);
|
|
|
+ p->name[i] = '\0'; /* Null term */
|
|
|
+ }
|
|
|
+ else
|
|
|
+ p->fake = 1;
|
|
|
+ p->found = 0;
|
|
|
+ p->regexp = 0; /* Assume not a regular expression */
|
|
|
+ p->firstch = 1; /* Assume first char is literal */
|
|
|
+ p->change_dir=chdir_name;
|
|
|
+ p->dir_contents = 0; /* JF */
|
|
|
+ if (name)
|
|
|
+ {
|
|
|
+ if (index(name, '*') || index(name, '[') || index(name, '?')) {
|
|
|
+ p->regexp = 1; /* No, it's a regexp */
|
|
|
+ if (name[0] == '*' || name[0] == '[' || name[0] == '?')
|
|
|
+ p->firstch = 0; /* Not even 1st char literal */
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (namelast) namelast->next = p;
|
|
|
+ namelast = p;
|
|
|
+ if (!namelist) namelist = p;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Return nonzero if name P (from an archive) matches any name from
|
|
|
+ * the namelist, zero if not.
|
|
|
+ */
|
|
|
+int
|
|
|
+name_match(p)
|
|
|
+ register char *p;
|
|
|
+{
|
|
|
+ register struct name *nlp;
|
|
|
+ register int len;
|
|
|
+
|
|
|
+again:
|
|
|
+ if (0 == (nlp = namelist)) /* Empty namelist is easy */
|
|
|
+ return 1;
|
|
|
+ if (nlp->fake)
|
|
|
+ {
|
|
|
+ if (nlp->change_dir && chdir (nlp->change_dir))
|
|
|
+ msg_perror ("Can't change to directory %d", nlp->change_dir);
|
|
|
+ namelist = 0;
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ len = strlen(p);
|
|
|
+ for (; nlp != 0; nlp = nlp->next) {
|
|
|
+ /* If first chars don't match, quick skip */
|
|
|
+ if (nlp->firstch && nlp->name[0] != p[0])
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* Regular expressions (shell globbing, actually). */
|
|
|
+ if (nlp->regexp) {
|
|
|
+ if (wildmat(p, nlp->name)) {
|
|
|
+ nlp->found = 1; /* Remember it matched */
|
|
|
+ if(f_startfile) {
|
|
|
+ free((void *)namelist);
|
|
|
+ namelist=0;
|
|
|
+ }
|
|
|
+ if(nlp->change_dir && chdir(nlp->change_dir))
|
|
|
+ msg_perror("Can't change to directory %s",nlp->change_dir);
|
|
|
+ return 1; /* We got a match */
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Plain Old Strings */
|
|
|
+ if (nlp->length <= len /* Archive len >= specified */
|
|
|
+ && (p[nlp->length] == '\0' || p[nlp->length] == '/')
|
|
|
+ /* Full match on file/dirname */
|
|
|
+ && strncmp(p, nlp->name, nlp->length) == 0) /* Name compare */
|
|
|
+ {
|
|
|
+ nlp->found = 1; /* Remember it matched */
|
|
|
+ if(f_startfile) {
|
|
|
+ free((void *)namelist);
|
|
|
+ namelist = 0;
|
|
|
+ }
|
|
|
+ if(nlp->change_dir && chdir(nlp->change_dir))
|
|
|
+ msg_perror("Can't change to directory %s",nlp->change_dir);
|
|
|
+ return 1; /* We got a match */
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Filename from archive not found in namelist.
|
|
|
+ * If we have the whole namelist here, just return 0.
|
|
|
+ * Otherwise, read the next name in and compare it.
|
|
|
+ * If this was the last name, namelist->found will remain on.
|
|
|
+ * If not, we loop to compare the newly read name.
|
|
|
+ */
|
|
|
+ if (f_sorted_names && namelist->found) {
|
|
|
+ name_gather(); /* Read one more */
|
|
|
+ if (!namelist->found) goto again;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * Print the names of things in the namelist that were not matched.
|
|
|
+ */
|
|
|
+void
|
|
|
+names_notfound()
|
|
|
+{
|
|
|
+ register struct name *nlp,*next;
|
|
|
+ register char *p;
|
|
|
+
|
|
|
+ for (nlp = namelist; nlp != 0; nlp = next) {
|
|
|
+ next=nlp->next;
|
|
|
+ if (!nlp->found)
|
|
|
+ msg("%s not found in archive",nlp->name);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We could free() the list, but the process is about
|
|
|
+ * to die anyway, so save some CPU time. Amigas and
|
|
|
+ * other similarly broken software will need to waste
|
|
|
+ * the time, though.
|
|
|
+ */
|
|
|
+#ifdef amiga
|
|
|
+ if (!f_sorted_names)
|
|
|
+ free(nlp);
|
|
|
+#endif
|
|
|
+ }
|
|
|
+ namelist = (struct name *)NULL;
|
|
|
+ namelast = (struct name *)NULL;
|
|
|
+
|
|
|
+ if (f_sorted_names) {
|
|
|
+ while (0 != (p = name_next(1)))
|
|
|
+ msg("%s not found in archive", p);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* These next routines were created by JF */
|
|
|
+
|
|
|
+void
|
|
|
+name_expand()
|
|
|
+{
|
|
|
+;
|
|
|
+}
|
|
|
+
|
|
|
+/* This is like name_match(), except that it returns a pointer to the name
|
|
|
+ it matched, and doesn't set ->found The caller will have to do that
|
|
|
+ if it wants to. Oh, and if the namelist is empty, it returns 0, unlike
|
|
|
+ name_match(), which returns TRUE */
|
|
|
+
|
|
|
+struct name *
|
|
|
+name_scan(p)
|
|
|
+register char *p;
|
|
|
+{
|
|
|
+ register struct name *nlp;
|
|
|
+ register int len;
|
|
|
+
|
|
|
+again:
|
|
|
+ if (0 == (nlp = namelist)) /* Empty namelist is easy */
|
|
|
+ return 0;
|
|
|
+ len = strlen(p);
|
|
|
+ for (; nlp != 0; nlp = nlp->next) {
|
|
|
+ /* If first chars don't match, quick skip */
|
|
|
+ if (nlp->firstch && nlp->name[0] != p[0])
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* Regular expressions */
|
|
|
+ if (nlp->regexp) {
|
|
|
+ if (wildmat(p, nlp->name))
|
|
|
+ return nlp; /* We got a match */
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Plain Old Strings */
|
|
|
+ if (nlp->length <= len /* Archive len >= specified */
|
|
|
+ && (p[nlp->length] == '\0' || p[nlp->length] == '/')
|
|
|
+ /* Full match on file/dirname */
|
|
|
+ && strncmp(p, nlp->name, nlp->length) == 0) /* Name compare */
|
|
|
+ return nlp; /* We got a match */
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Filename from archive not found in namelist.
|
|
|
+ * If we have the whole namelist here, just return 0.
|
|
|
+ * Otherwise, read the next name in and compare it.
|
|
|
+ * If this was the last name, namelist->found will remain on.
|
|
|
+ * If not, we loop to compare the newly read name.
|
|
|
+ */
|
|
|
+ if (f_sorted_names && namelist->found) {
|
|
|
+ name_gather(); /* Read one more */
|
|
|
+ if (!namelist->found) goto again;
|
|
|
+ }
|
|
|
+ return (struct name *) 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* This returns a name from the namelist which doesn't have ->found set.
|
|
|
+ It sets ->found before returning, so successive calls will find and return
|
|
|
+ all the non-found names in the namelist */
|
|
|
+
|
|
|
+struct name *gnu_list_name;
|
|
|
+
|
|
|
+char *
|
|
|
+name_from_list()
|
|
|
+{
|
|
|
+ if(!gnu_list_name)
|
|
|
+ gnu_list_name = namelist;
|
|
|
+ while(gnu_list_name && gnu_list_name->found)
|
|
|
+ gnu_list_name=gnu_list_name->next;
|
|
|
+ if(gnu_list_name) {
|
|
|
+ gnu_list_name->found++;
|
|
|
+ if(gnu_list_name->change_dir)
|
|
|
+ if(chdir(gnu_list_name->change_dir)<0)
|
|
|
+ msg_perror("can't chdir to %s",gnu_list_name->change_dir);
|
|
|
+ return gnu_list_name->name;
|
|
|
+ }
|
|
|
+ return (char *)0;
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+blank_name_list()
|
|
|
+{
|
|
|
+ struct name *n;
|
|
|
+
|
|
|
+ gnu_list_name = 0;
|
|
|
+ for(n=namelist;n;n=n->next)
|
|
|
+ n->found = 0;
|
|
|
+}
|
|
|
+
|
|
|
+char *
|
|
|
+new_name(path,name)
|
|
|
+char *path,*name;
|
|
|
+{
|
|
|
+ char *path_buf;
|
|
|
+
|
|
|
+ path_buf=(char *)malloc(strlen(path)+strlen(name)+2);
|
|
|
+ if(path_buf==0) {
|
|
|
+ msg("Can't allocate memory for name '%s/%s",path,name);
|
|
|
+ exit(EX_SYSTEM);
|
|
|
+ }
|
|
|
+ (void) sprintf(path_buf,"%s/%s",path,name);
|
|
|
+ return path_buf;
|
|
|
+}
|
|
|
+
|
|
|
+/* returns non-zero if the luser typed 'y' or 'Y', zero otherwise. */
|
|
|
+
|
|
|
+int
|
|
|
+confirm(action,file)
|
|
|
+char *action, *file;
|
|
|
+{
|
|
|
+ int c,nl;
|
|
|
+ static FILE *confirm_file = 0;
|
|
|
+ extern FILE *msg_file;
|
|
|
+ extern char TTY_NAME[];
|
|
|
+
|
|
|
+ fprintf(msg_file,"%s %s?", action, file);
|
|
|
+ fflush(msg_file);
|
|
|
+ if(!confirm_file) {
|
|
|
+ confirm_file = (archive == 0) ? fopen(TTY_NAME, "r") : stdin;
|
|
|
+ if(!confirm_file) {
|
|
|
+ msg("Can't read confirmation from user");
|
|
|
+ exit(EX_SYSTEM);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ c=getc(confirm_file);
|
|
|
+ for(nl = c; nl != '\n' && nl != EOF; nl = getc(confirm_file))
|
|
|
+ ;
|
|
|
+ return (c=='y' || c=='Y');
|
|
|
+}
|
|
|
+
|
|
|
+char *x_buffer = 0;
|
|
|
+int size_x_buffer;
|
|
|
+int free_x_buffer;
|
|
|
+
|
|
|
+char **exclude = 0;
|
|
|
+int size_exclude = 0;
|
|
|
+int free_exclude = 0;
|
|
|
+
|
|
|
+char **re_exclude = 0;
|
|
|
+int size_re_exclude = 0;
|
|
|
+int free_re_exclude = 0;
|
|
|
+
|
|
|
+void
|
|
|
+add_exclude(name)
|
|
|
+char *name;
|
|
|
+{
|
|
|
+/* char *rname;*/
|
|
|
+/* char **tmp_ptr;*/
|
|
|
+ int size_buf;
|
|
|
+
|
|
|
+ un_quote_string(name);
|
|
|
+ size_buf = strlen(name);
|
|
|
+
|
|
|
+ if(x_buffer==0) {
|
|
|
+ x_buffer = (char *)ck_malloc(size_buf+1024);
|
|
|
+ free_x_buffer=1024;
|
|
|
+ } else if(free_x_buffer<=size_buf) {
|
|
|
+ char *old_x_buffer;
|
|
|
+ char **tmp_ptr;
|
|
|
+
|
|
|
+ old_x_buffer = x_buffer;
|
|
|
+ x_buffer = (char *)ck_realloc(x_buffer,size_x_buffer+1024);
|
|
|
+ free_x_buffer = 1024;
|
|
|
+ for(tmp_ptr=exclude;tmp_ptr<exclude+size_exclude;tmp_ptr++)
|
|
|
+ *tmp_ptr= x_buffer + ((*tmp_ptr) - old_x_buffer);
|
|
|
+ for(tmp_ptr=re_exclude;tmp_ptr<re_exclude+size_re_exclude;tmp_ptr++)
|
|
|
+ *tmp_ptr= x_buffer + ((*tmp_ptr) - old_x_buffer);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(is_regex(name)) {
|
|
|
+ if(free_re_exclude==0) {
|
|
|
+ re_exclude= (char **)(re_exclude ? ck_realloc(re_exclude,(size_re_exclude+32)*sizeof(char *)) : ck_malloc(sizeof(char *)*32));
|
|
|
+ free_re_exclude+=32;
|
|
|
+ }
|
|
|
+ re_exclude[size_re_exclude]=x_buffer+size_x_buffer;
|
|
|
+ size_re_exclude++;
|
|
|
+ free_re_exclude--;
|
|
|
+ } else {
|
|
|
+ if(free_exclude==0) {
|
|
|
+ exclude=(char **)(exclude ? ck_realloc(exclude,(size_exclude+32)*sizeof(char *)) : ck_malloc(sizeof(char *)*32));
|
|
|
+ free_exclude+=32;
|
|
|
+ }
|
|
|
+ exclude[size_exclude]=x_buffer+size_x_buffer;
|
|
|
+ size_exclude++;
|
|
|
+ free_exclude--;
|
|
|
+ }
|
|
|
+ strcpy(x_buffer+size_x_buffer,name);
|
|
|
+ size_x_buffer+=size_buf+1;
|
|
|
+ free_x_buffer-=size_buf+1;
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+add_exclude_file(file)
|
|
|
+char *file;
|
|
|
+{
|
|
|
+ FILE *fp;
|
|
|
+ char buf[1024];
|
|
|
+ extern char *rindex();
|
|
|
+
|
|
|
+ if(strcmp(file, "-"))
|
|
|
+ fp=fopen(file,"r");
|
|
|
+ else
|
|
|
+ /* Let's hope the person knows what they're doing. */
|
|
|
+ /* Using -X - -T - -f - will get you *REALLY* strange
|
|
|
+ results. . . */
|
|
|
+ fp=stdin;
|
|
|
+
|
|
|
+ if(!fp) {
|
|
|
+ msg_perror("can't open %s",file);
|
|
|
+ exit(2);
|
|
|
+ }
|
|
|
+ while(fgets(buf,1024,fp)) {
|
|
|
+/* int size_buf;*/
|
|
|
+ char *end_str;
|
|
|
+
|
|
|
+ end_str=rindex(buf,'\n');
|
|
|
+ if(end_str)
|
|
|
+ *end_str='\0';
|
|
|
+ add_exclude(buf);
|
|
|
+
|
|
|
+ }
|
|
|
+ fclose(fp);
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+is_regex(str)
|
|
|
+char *str;
|
|
|
+{
|
|
|
+ return index(str,'*') || index(str,'[') || index(str,'?');
|
|
|
+}
|
|
|
+
|
|
|
+/* Returns non-zero if the file 'name' should not be added/extracted */
|
|
|
+int
|
|
|
+check_exclude(name)
|
|
|
+char *name;
|
|
|
+{
|
|
|
+ int n;
|
|
|
+ char *str;
|
|
|
+ extern char *strstr();
|
|
|
+
|
|
|
+ for(n=0;n<size_re_exclude;n++) {
|
|
|
+ if(wildmat(name,re_exclude[n]))
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ for(n=0;n<size_exclude;n++) {
|
|
|
+ /* Accept the output from strstr only if it is the last
|
|
|
+ part of the string. There is certainly a faster way to
|
|
|
+ do this. . . */
|
|
|
+ if( (str=strstr(name,exclude[n]))
|
|
|
+ && (str==name || str[-1]=='/')
|
|
|
+ && str[strlen(exclude[n])]=='\0')
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|