Bläddra i källkod

Initial revision

François Pinard 30 år sedan
förälder
incheckning
54694ee258
1 ändrade filer med 1352 tillägg och 0 borttagningar
  1. 1352 0
      src/tar.c

+ 1352 - 0
src/tar.c

@@ -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;
+}