386BSD 0.1 development
[unix-history] / usr / src / usr.bin / tar / tar.c
CommitLineData
68c14404
WJ
1/* Tar -- a tape archiver.
2
3 Copyright (C) 1988 Free Software Foundation
4
5GNU tar is distributed in the hope that it will be useful, but WITHOUT ANY
6WARRANTY. No author or distributor accepts responsibility to anyone
7for the consequences of using it or for whether it serves any
8particular purpose or works at all, unless he says so in writing.
9Refer to the GNU tar General Public License for full details.
10
11Everyone is granted permission to copy, modify and redistribute GNU tar,
12but only under the conditions described in the GNU tar General Public
13License. A copy of this license is supposed to have been given to you
14along with GNU tar so you can know your rights and responsibilities. It
15should be in a file named COPYING. Among other things, the copyright
16notice and this notice must be preserved on all copies.
17
18In other words, go ahead and share GNU tar, but don't try to stop
19anyone else from sharing it farther. Help stamp out software hoarding!
20*/
21
22/*
23 * A tar (tape archiver) program.
24 *
25 * Written by John Gilmore, ihnp4!hoptoad!gnu, starting 25 Aug 85.
26 *
27 * @(#)tar.c 1.34 11/6/87 - gnu
28 */
29
30#include <stdio.h>
31#include <sys/types.h> /* Needed for typedefs in tar.h */
32#include <sys/stat.h> /* JF */
33#include "getopt.h"
34#include "regex.h"
35
36#ifdef USG
37#define rindex strrchr
38#endif
39
40#ifdef BSD42
41#include <sys/dir.h>
42#else
43#ifdef __MSDOS__
44#include "msd_dir.h"
45#else
46#ifdef USG
47#ifdef NDIR
48#include <ndir.h>
49#else
50#include <dirent.h>
51#endif
52#ifndef DIRECT
53#define direct dirent
54#endif
55#define DP_NAMELEN(x) strlen((x)->d_name)
56#else
57/*
58 * FIXME: On other systems there is no standard place for the header file
59 * for the portable directory access routines. Change the #include line
60 * below to bring it in from wherever it is.
61 */
62#include "ndir.h"
63#endif
64#endif
65#endif
66
67#ifndef DP_NAMELEN
68#define DP_NAMELEN(x) (x)->d_namlen
69#endif
70
71extern char *malloc();
72extern char *getenv();
73extern char *strncpy();
74extern char *index();
75extern char *strcpy(); /* JF */
76extern char *strcat(); /* JF */
77
78extern char *optarg; /* Pointer to argument */
79extern int optind; /* Global argv index from getopt */
80
81extern char *ck_malloc();
82extern char *ck_realloc();
83/*
84 * The following causes "tar.h" to produce definitions of all the
85 * global variables, rather than just "extern" declarations of them.
86 */
87#define TAR_EXTERN /**/
88#include "tar.h"
89
90/*
91 * We should use a conversion routine that does reasonable error
92 * checking -- atoi doesn't. For now, punt. FIXME.
93 */
94#define intconv atoi
95extern int getoldopt();
96extern void read_and();
97extern void list_archive();
98extern void extract_archive();
99extern void diff_archive();
100extern void create_archive();
101extern void update_archive();
102extern void junk_archive();
103
104/* JF */
105extern time_t get_date();
106
107time_t new_time;
108
109static FILE *namef; /* File to read names from */
110static char **n_argv; /* Argv used by name routines */
111static int n_argc; /* Argc used by name routines */
112static char **n_ind; /* Store an array of names */
113static int n_indalloc; /* How big is the array? */
114static int n_indused; /* How many entries does it have? */
115static int n_indscan; /* How many of the entries have we scanned? */
116
117
118extern FILE *msg_file;
119
120void describe();
121void options();
122
123#ifndef S_IFLNK
124#define lstat stat
125#endif
126
127#ifndef DEFBLOCKING
128#define DEFBLOCKING 20
129#endif
130
131#ifndef DEF_AR_FILE
132#define DEF_AR_FILE "tar.out"
133#endif
134
135/* For long options that unconditionally set a single flag, we have getopt
136 do it. For the others, we share the code for the equivalent short
137 named option, the name of which is stored in the otherwise-unused `val'
138 field of the `struct option'; for long options that have no equivalent
139 short option, we use nongraphic characters as pseudo short option
140 characters, starting (for no particular reason) with character 10. */
141
142struct option long_options[] =
143{
144 {"create", 0, 0, 'c'},
145 {"append", 0, 0, 'r'},
146 {"extract", 0, 0, 'x'},
147 {"get", 0, 0, 'x'},
148 {"list", 0, 0, 't'},
149 {"update", 0, 0, 'u'},
150 {"catenate", 0, 0, 'A'},
151 {"concatenate", 0, 0, 'A'},
152 {"compare", 0, 0, 'd'},
153 {"diff", 0, 0, 'd'},
154 {"delete", 0, 0, 14},
155 {"help", 0, 0, 12},
156
157 {"directory", 1, 0, 'C'},
158 {"record-number", 0, &f_sayblock, 1},
159 {"files-from", 1, 0, 'T'},
160 {"label", 1, 0, 'V'},
161 {"exclude-from", 1, 0, 'X'},
162 {"exclude", 1, 0, 15},
163 {"file", 1, 0, 'f'},
164 {"block-size", 1, 0, 'b'},
165 {"version", 0, 0, 11},
166 {"verbose", 0, 0, 'v'},
167 {"totals", 0, &f_totals, 1},
168
169 {"read-full-blocks", 0, &f_reblock, 1},
170 {"starting-file", 1, 0, 'K'},
171 {"to-stdout", 0, &f_exstdout, 1},
172 {"ignore-zeros", 0, &f_ignorez, 1},
173 {"keep-old-files", 0, 0, 'k'},
174 {"uncompress", 0, &f_compress, 1},
175 {"same-permissions", 0, &f_use_protection, 1},
176 {"preserve-permissions",0, &f_use_protection, 1},
177 {"modification-time", 0, &f_modified, 1},
178 {"preserve", 0, 0, 10},
179 {"same-order", 0, &f_sorted_names, 1},
180 {"same-owner", 0, &f_do_chown, 1},
181 {"preserve-order", 0, &f_sorted_names, 1},
182
183 {"newer", 1, 0, 'N'},
184 {"after-date", 1, 0, 'N'},
185 {"newer-mtime", 1, 0, 13},
186 {"incremental", 0, 0, 'G'},
187 {"listed-incremental", 1, 0, 'g'},
188 {"multi-volume", 0, &f_multivol, 1},
189 {"info-script", 1, &f_run_script_at_end, 1},
190 {"absolute-paths", 0, &f_absolute_paths, 1},
191 {"interactive", 0, &f_confirm, 1},
192 {"confirmation", 0, &f_confirm, 1},
193
194 {"verify", 0, &f_verify, 1},
195 {"dereference", 0, &f_follow_links, 1},
196 {"one-file-system", 0, &f_local_filesys, 1},
197 {"old-archive", 0, 0, 'o'},
198 {"portability", 0, 0, 'o'},
199 {"compress", 0, &f_compress, 1},
200 {"compress-block", 0, &f_compress, 2},
201 {"sparse", 0, &f_sparse_files, 1},
202 {"tape-length", 1, 0, 'L'},
203
204 {0, 0, 0, 0}
205};
206
207/*
208 * Main routine for tar.
209 */
210main(argc, argv)
211 int argc;
212 char **argv;
213{
214 extern char version_string[];
215
216 tar = argv[0]; /* JF: was "tar" Set program name */
217 errors = 0;
218
219 options(argc, argv);
220
221 if(!n_argv)
222 name_init(argc, argv);
223
224 switch(cmd_mode) {
225 case CMD_CAT:
226 case CMD_UPDATE:
227 case CMD_APPEND:
228 update_archive();
229 break;
230 case CMD_DELETE:
231 junk_archive();
232 break;
233 case CMD_CREATE:
234 create_archive();
235 if (f_totals)
236 fprintf (stderr, "Total bytes written: %d\n", tot_written);
237 break;
238 case CMD_EXTRACT:
239 if (f_volhdr) {
240 char *err;
241 label_pattern = (struct re_pattern_buffer *)
242 ck_malloc (sizeof *label_pattern);
243 err = re_compile_pattern (f_volhdr, strlen (f_volhdr),
244 label_pattern);
245 if (err) {
246 fprintf (stderr,"Bad regular expression: %s\n",
247 err);
248 errors++;
249 break;
250 }
251
252 }
253 extr_init();
254 read_and(extract_archive);
255 break;
256 case CMD_LIST:
257 if (f_volhdr) {
258 char *err;
259 label_pattern = (struct re_pattern_buffer *)
260 ck_malloc (sizeof *label_pattern);
261 err = re_compile_pattern (f_volhdr, strlen (f_volhdr),
262 label_pattern);
263 if (err) {
264 fprintf (stderr,"Bad regular expression: %s\n",
265 err);
266 errors++;
267 break;
268 }
269 }
270 read_and(list_archive);
271#if 0
272 if (!errors)
273 errors = different;
274#endif
275 break;
276 case CMD_DIFF:
277 diff_init();
278 read_and(diff_archive);
279 break;
280 case CMD_VERSION:
281 fprintf(stderr,"%s\n",version_string);
282 break;
283 case CMD_NONE:
284 msg("you must specify exactly one of the r, c, t, x, or d options\n");
285 fprintf(stderr,"For more information, type ``%s +help''.\n",tar);
286 exit(EX_ARGSBAD);
287 }
288 exit(errors);
289 /* NOTREACHED */
290}
291
292
293/*
294 * Parse the options for tar.
295 */
296void
297options(argc, argv)
298 int argc;
299 char **argv;
300{
301 register int c; /* Option letter */
302 int ind = -1;
303
304 /* Set default option values */
305 blocking = DEFBLOCKING; /* From Makefile */
306 ar_file = getenv("TAPE"); /* From environment, or */
307 if (ar_file == 0)
308 ar_file = DEF_AR_FILE; /* From Makefile */
309
310 /* Parse options */
311 while ((c = getoldopt(argc, argv,
312 "-01234567Ab:BcC:df:F:g:GhikK:lL:mMN:oOpPrRsStT:uvV:wWxX:zZ",
313 long_options, &ind)) != EOF) {
314 switch (c) {
315 case 0: /* long options that set a single flag */
316 break;
317 case 1:
318 /* File name or non-parsed option */
319 name_add(optarg);
320 break;
321 case 'C':
322 name_add("-C");
323 name_add(optarg);
324 break;
325 case 10: /* preserve */
326 f_use_protection = f_sorted_names = 1;
327 break;
328 case 11:
329 if(cmd_mode!=CMD_NONE)
330 goto badopt;
331 cmd_mode=CMD_VERSION;
332 break;
333 case 12: /* help */
334 fprintf(stderr,"This is GNU tar, the tape archiving program.\n");
335 describe();
336 exit(1);
337 case 13:
338 f_new_files++;
339 goto get_newer;
340
341 case 14: /* Delete in the archive */
342 if(cmd_mode!=CMD_NONE)
343 goto badopt;
344 cmd_mode=CMD_DELETE;
345 break;
346
347 case 15:
348 f_exclude++;
349 add_exclude(optarg);
350 break;
351
352 case 'g': /* We are making a GNU dump; save
353 directories at the beginning of
354 the archive, and include in each
355 directory its contents */
356 if(f_oldarch)
357 goto badopt;
358 f_gnudump++;
359 gnu_dumpfile=optarg;
360 break;
361
362
363 case '0':
364 case '1':
365 case '2':
366 case '3':
367 case '4':
368 case '5':
369 case '6':
370 case '7':
371 {
372 /* JF this'll have to be modified for other
373 systems, of course! */
374 int d,add;
375 static char buf[50];
376
377 d=getoldopt(argc,argv,"lmh");
378#ifdef MAYBEDEF
379 sprintf(buf,"/dev/rmt/%d%c",c,d);
380#else
381#ifndef LOW_NUM
382#define LOW_NUM 0
383#define MID_NUM 8
384#define HGH_NUM 16
385#endif
386 if(d=='l') add=LOW_NUM;
387 else if(d=='m') add=MID_NUM;
388 else if(d=='h') add=HGH_NUM;
389 else goto badopt;
390
391 sprintf(buf,"/dev/rmt%d",add+c-'0');
392#endif
393 ar_file=buf;
394 }
395 break;
396
397 case 'A': /* Arguments are tar files,
398 just cat them onto the end
399 of the archive. */
400 if(cmd_mode!=CMD_NONE)
401 goto badopt;
402 cmd_mode=CMD_CAT;
403 break;
404
405 case 'b': /* Set blocking factor */
406 blocking = intconv(optarg);
407 break;
408
409 case 'B': /* Try to reblock input */
410 f_reblock++; /* For reading 4.2BSD pipes */
411 break;
412
413 case 'c': /* Create an archive */
414 if(cmd_mode!=CMD_NONE)
415 goto badopt;
416 cmd_mode=CMD_CREATE;
417 break;
418
419/* case 'C':
420 if(chdir(optarg)<0)
421 msg_perror("Can't change directory to %d",optarg);
422 break; */
423
424 case 'd': /* Find difference tape/disk */
425 if(cmd_mode!=CMD_NONE)
426 goto badopt;
427 cmd_mode=CMD_DIFF;
428 break;
429
430 case 'f': /* Use ar_file for the archive */
431 ar_file = optarg;
432 break;
433
434 case 'F':
435 /* Since -F is only useful with -M , make it implied */
436 f_run_script_at_end++; /* run this script at the end */
437 info_script = optarg; /* of each tape */
438 f_multivol++;
439 break;
440
441 case 'G': /* We are making a GNU dump; save
442 directories at the beginning of
443 the archive, and include in each
444 directory its contents */
445 if(f_oldarch)
446 goto badopt;
447 f_gnudump++;
448 gnu_dumpfile=0;
449 break;
450
451 case 'h':
452 f_follow_links++; /* follow symbolic links */
453 break;
454
455 case 'i':
456 f_ignorez++; /* Ignore zero records (eofs) */
457 /*
458 * This can't be the default, because Unix tar
459 * writes two records of zeros, then pads out the
460 * block with garbage.
461 */
462 break;
463
464 case 'k': /* Don't overwrite files */
465#ifdef NO_OPEN3
466 msg("can't do -k option on this system");
467 exit(EX_ARGSBAD);
468#else
469 f_keep++;
470#endif
471 break;
472
473 case 'K':
474 f_startfile++;
475 addname(optarg);
476 break;
477
478 case 'l': /* When dumping directories, don't
479 dump files/subdirectories that are
480 on other filesystems. */
481 f_local_filesys++;
482 break;
483
484 case 'L':
485 tape_length = intconv (optarg);
486 f_multivol++;
487 break;
488 case 'm':
489 f_modified++;
490 break;
491
492 case 'M': /* Make Multivolume archive:
493 When we can't write any more
494 into the archive, re-open it,
495 and continue writing */
496 f_multivol++;
497 break;
498
499 case 'N': /* Only write files newer than X */
500 get_newer:
501 f_new_files++;
502 new_time=get_date(optarg,(struct timeb *)0);
503 break;
504
505 case 'o': /* Generate old archive */
506 if(f_gnudump /* || f_dironly */)
507 goto badopt;
508 f_oldarch++;
509 break;
510
511 case 'O':
512 f_exstdout++;
513 break;
514
515 case 'p':
516 f_use_protection++;
517 break;
518
519 case 'P':
520 f_absolute_paths++;
521 break;
522
523 case 'r': /* Append files to the archive */
524 if(cmd_mode!=CMD_NONE)
525 goto badopt;
526 cmd_mode=CMD_APPEND;
527 break;
528
529 case 'R':
530 f_sayblock++; /* Print block #s for debug */
531 break; /* of bad tar archives */
532
533 case 's':
534 f_sorted_names++; /* Names to extr are sorted */
535 break;
536
537 case 'S': /* deal with sparse files */
538 f_sparse_files++;
539 break;
540 case 't':
541 if(cmd_mode!=CMD_NONE)
542 goto badopt;
543 cmd_mode=CMD_LIST;
544 f_verbose++; /* "t" output == "cv" or "xv" */
545 break;
546
547 case 'T':
548 name_file = optarg;
549 f_namefile++;
550 break;
551
552 case 'u': /* Append files to the archive that
553 aren't there, or are newer than the
554 copy in the archive */
555 if(cmd_mode!=CMD_NONE)
556 goto badopt;
557 cmd_mode=CMD_UPDATE;
558 break;
559
560 case 'v':
561 f_verbose++;
562 break;
563
564 case 'V':
565 f_volhdr=optarg;
566 break;
567
568 case 'w':
569 f_confirm++;
570 break;
571
572 case 'W':
573 f_verify++;
574 break;
575
576 case 'x': /* Extract files from the archive */
577 if(cmd_mode!=CMD_NONE)
578 goto badopt;
579 cmd_mode=CMD_EXTRACT;
580 break;
581
582 case 'X':
583 f_exclude++;
584 add_exclude_file(optarg);
585 break;
586
587 case 'z': /* Easy to type */
588 case 'Z': /* Like the filename extension .Z */
589 f_compress++;
590 break;
591
592 case '?':
593 badopt:
594 msg("Unknown option. Use '%s +help' for a complete list of options.", tar);
595 exit(EX_ARGSBAD);
596
597 }
598 }
599
600 blocksize = blocking * RECORDSIZE;
601}
602
603
604/*
605 * Print as much help as the user's gonna get.
606 *
607 * We have to sprinkle in the KLUDGE lines because too many compilers
608 * cannot handle character strings longer than about 512 bytes. Yuk!
609 * In particular, MSDOS and Xenix MSC and PDP-11 V7 Unix have this
610 * problem.
611 */
612void
613describe()
614{
615 msg("choose one of the following:");
616 fputs("\
617-A, +catenate,\n\
618 +concatenate append tar files to an archive\n\
619-c, +create create a new archive\n\
620-d, +diff,\n\
621 +compare find differences between archive and file system\n\
622+delete delete from the archive (not for use on mag tapes!)\n\
623-r, +append append files to the end of an archive\n\
624-t, +list list the contents of an archive\n\
625-u, +update only append files that are newer than copy in archive\n\
626-x, +extract,\n\
627 +get extract files from an archive\n",stderr);
628
629 fprintf(stderr, "\
630Other options:\n\
631-b, +block-size N block size of Nx512 bytes (default N=%d)\n", DEFBLOCKING);
632 fputs ("\
633-B, +read-full-blocks reblock as we read (for reading 4.2BSD pipes)\n\
634-C, +directory DIR change to directory DIR\n\
635", stderr); /* KLUDGE */ fprintf(stderr, "\
636-f, +file [HOSTNAME:]F use archive file or device F (default %s)\n",
637 DEF_AR_FILE); fputs("\
638-F, +info-script F run script at end of each tape (implies -M)\n\
639-G, +incremental create/list/extract old GNU-format incremental backup\n\
640-g, +listed-incremental F create/list/extract new GNU-format incremental backup\n\
641-h, +dereference don't dump symlinks; dump the files they point to\n\
642-i, +ignore-zeros ignore blocks of zeros in archive (normally mean EOF)\n\
643-k, +keep-old-files keep existing files; don't overwrite them from archive\n\
644-K, +starting-file FILE begin at FILE in the archive\n\
645-l, +one-file-system stay in local file system when creating an archive\n\
646-L, +tape-length LENGTH change tapes after writing LENGTH\n\
647", stderr); /* KLUDGE */ fputs("\
648-m, +modification-time don't extract file modified time\n\
649-M, +multi-volume create/list/extract multi-volume archive\n\
650-N, +after-date DATE,\n\
651 +newer DATE only store files newer than DATE\n\
652-o, +old-archive,\n\
653 +portability write a V7 format archive, rather than ANSI format\n\
654-O, +to-stdout extract files to standard output\n\
655-p, +same-permissions,\n\
656 +preserve-permissions extract all protection information\n\
657-P, +absolute-paths don't strip leading `/'s from file names\n\
658+preserve like -p -s\n\
659", stderr); /* KLUDGE */ fputs("\
660-R, +record-number show record number within archive with each message\n\
661-s, +same-order,\n\
662 +preserve-order list of names to extract is sorted to match archive\n\
663+same-order create extracted files with the same ownership \n\
664-S, +sparse handle sparse files efficiently\n\
665-T, +files-from F get names to extract or create from file F\n\
666+totals print total bytes written with +create\n\
667-v, +verbose verbosely list files processed\n\
668-V, +label NAME create archive with volume name NAME\n\
669+version print tar program version number\n\
670-w, +interactive,\n\
671 +confirmation ask for confirmation for every action\n\
672", stderr); /* KLUDGE */ fputs("\
673-W, +verify attempt to verify the archive after writing it\n\
674-X, +exclude FILE exclude file FILE\n\
675+exclude-from FILE exclude files listed in FILE\n\
676-z, -Z, +compress,\n\
677 +uncompress filter the archive through compress\n\
678-[0-7][lmh] specify drive and density\n\
679", stderr);
680}
681
682name_add(name)
683char *name;
684{
685 if(n_indalloc==n_indused) {
686 n_indalloc+=10;
687 n_ind=(char **)(n_indused ? ck_realloc(n_ind,n_indalloc*sizeof(char *)) : ck_malloc(n_indalloc*sizeof(char *)));
688 }
689 n_ind[n_indused++]=name;
690}
691
692/*
693 * Set up to gather file names for tar.
694 *
695 * They can either come from stdin or from argv.
696 */
697name_init(argc, argv)
698 int argc;
699 char **argv;
700{
701
702 if (f_namefile) {
703 if (optind < argc) {
704 msg("too many args with -T option");
705 exit(EX_ARGSBAD);
706 }
707 if (!strcmp(name_file, "-")) {
708 namef = stdin;
709 } else {
710 namef = fopen(name_file, "r");
711 if (namef == NULL) {
712 msg_perror("can't open file %s",name_file);
713 exit(EX_BADFILE);
714 }
715 }
716 } else {
717 /* Get file names from argv, after options. */
718 n_argc = argc;
719 n_argv = argv;
720 }
721}
722
723/*
724 * Get the next name from argv or the name file.
725 *
726 * Result is in static storage and can't be relied upon across two calls.
727 */
728
729/* C is non-zero if we should deal with -C */
730char *
731name_next(c)
732{
733 static char *buffer; /* Holding pattern */
734 static buffer_siz;
735 register char *p;
736 register char *q = 0;
737 register char *q2 = 0;
738 extern char *un_quote_string();
739
740 if(buffer_siz==0) {
741 buffer=ck_malloc(NAMSIZ+2);
742 buffer_siz=NAMSIZ;
743 }
744 tryagain:
745 if (namef == NULL) {
746 if(n_indscan<n_indused)
747 p=n_ind[n_indscan++];
748 else if (optind < n_argc)
749 /* Names come from argv, after options */
750 p=n_argv[optind++];
751 else {
752 if(q)
753 msg("Missing filename after -C");
754 return NULL;
755 }
756
757 /* JF trivial support for -C option. I don't know if
758 chdir'ing at this point is dangerous or not.
759 It seems to work, which is all I ask. */
760 if(c && !q && p[0]=='-' && p[1]=='C' && p[2]=='\0') {
761 q=p;
762 goto tryagain;
763 }
764 if(q) {
765 if(chdir(p)<0)
766 msg_perror("Can't chdir to %s",p);
767 q=0;
768 goto tryagain;
769 }
770 /* End of JF quick -C hack */
771
772 if(f_exclude && check_exclude(p))
773 goto tryagain;
774 return un_quote_string(p);
775 }
776 while(p = fgets(buffer, buffer_siz+1 /*nl*/, namef)) {
777 q = p+strlen(p)-1; /* Find the newline */
778 if (q <= p) /* Ignore empty lines */
779 continue;
780 while(q==p+buffer_siz && *q!='\n') {
781 buffer=ck_realloc(buffer,buffer_siz+NAMSIZ+2);
782 p=buffer;
783 q=buffer+buffer_siz;
784 buffer_siz+=NAMSIZ;
785 fgets(q+1,NAMSIZ,namef);
786 q=p+strlen(p)-1;
787 }
788 *q-- = '\0'; /* Zap the newline */
789 while (q > p && *q == '/') /* Zap trailing /s */
790 *q-- = '\0';
791 if (c && !q2 && p[0] == '-' && p[1] == 'C' && p[2] == '\0') {
792 q2 = p;
793 goto tryagain;
794 }
795 if (q2) {
796 if (chdir (p) < 0)
797 msg_perror ("Can't chdir to %s", p);
798 q2 = 0;
799 goto tryagain;
800 }
801 if(f_exclude && check_exclude(p))
802 goto tryagain;
803 return un_quote_string(p);
804 }
805 return NULL;
806}
807
808
809/*
810 * Close the name file, if any.
811 */
812name_close()
813{
814
815 if (namef != NULL && namef != stdin) fclose(namef);
816}
817
818
819/*
820 * Gather names in a list for scanning.
821 * Could hash them later if we really care.
822 *
823 * If the names are already sorted to match the archive, we just
824 * read them one by one. name_gather reads the first one, and it
825 * is called by name_match as appropriate to read the next ones.
826 * At EOF, the last name read is just left in the buffer.
827 * This option lets users of small machines extract an arbitrary
828 * number of files by doing "tar t" and editing down the list of files.
829 */
830name_gather()
831{
832 register char *p;
833 static struct name *namebuf; /* One-name buffer */
834 static namelen;
835 static char *chdir_name;
836
837 if (f_sorted_names) {
838 if(!namelen) {
839 namelen=NAMSIZ;
840 namebuf=(struct name *)ck_malloc(sizeof(struct name)+NAMSIZ);
841 }
842 p = name_next(0);
843 if (p) {
844 if(*p=='-' && p[1]=='C' && p[2]=='\0') {
845 chdir_name=name_next(0);
846 p=name_next(0);
847 if(!p) {
848 msg("Missing file name after -C");
849 exit(EX_ARGSBAD);
850 }
851 namebuf->change_dir=chdir_name;
852 }
853 namebuf->length = strlen(p);
854 if (namebuf->length >= namelen) {
855 namebuf=(struct name *)ck_realloc(namebuf,sizeof(struct name)+namebuf->length);
856 namelen=namebuf->length;
857 }
858 strncpy(namebuf->name, p, namebuf->length);
859 namebuf->name[ namebuf->length ] = 0;
860 namebuf->next = (struct name *)NULL;
861 namebuf->found = 0;
862 namelist = namebuf;
863 namelast = namelist;
864 }
865 return;
866 }
867
868 /* Non sorted names -- read them all in */
869 while (p = name_next(0))
870 addname(p);
871}
872
873/*
874 * Add a name to the namelist.
875 */
876addname(name)
877 char *name; /* pointer to name */
878{
879 register int i; /* Length of string */
880 register struct name *p; /* Current struct pointer */
881 static char *chdir_name;
882 char *new_name();
883#define MAXPATHLEN 1024
884
885 if(name[0]=='-' && name[1]=='C' && name[2]=='\0') {
886 chdir_name=name_next(0);
887 name=name_next(0);
888 if(!chdir_name) {
889 msg("Missing file name after -C");
890 exit(EX_ARGSBAD);
891 }
892 if(chdir_name[0]!='/') {
893 char path[MAXPATHLEN];
894#if defined(MSDOS) || defined(USG)
895 int getcwd();
896
897 if(!getcwd(path,MAXPATHLEN))
898 msg("Couldn't get current directory.");
899 exit(EX_SYSTEM);
900#else
901 char *getwd();
902
903 if(!getwd(path)) {
904 msg("Couldn't get current directory: %s",path);
905 exit(EX_SYSTEM);
906 }
907#endif
908 chdir_name=new_name(path,chdir_name);
909 }
910 }
911
912 if (name)
913 {
914 i = strlen(name);
915 /*NOSTRICT*/
916 p = (struct name *)malloc((unsigned)(sizeof(struct name) + i));
917 }
918 else
919 p = (struct name *)malloc ((unsigned)(sizeof (struct name)));
920 if (!p) {
921 if (name)
922 msg("cannot allocate mem for name '%s'.",name);
923 else
924 msg("cannot allocate mem for chdir record.");
925 exit(EX_SYSTEM);
926 }
927 p->next = (struct name *)NULL;
928 if (name)
929 {
930 p->fake = 0;
931 p->length = i;
932 strncpy(p->name, name, i);
933 p->name[i] = '\0'; /* Null term */
934 }
935 else
936 p->fake = 1;
937 p->found = 0;
938 p->regexp = 0; /* Assume not a regular expression */
939 p->firstch = 1; /* Assume first char is literal */
940 p->change_dir=chdir_name;
941 p->dir_contents = 0; /* JF */
942 if (name)
943 {
944 if (index(name, '*') || index(name, '[') || index(name, '?')) {
945 p->regexp = 1; /* No, it's a regexp */
946 if (name[0] == '*' || name[0] == '[' || name[0] == '?')
947 p->firstch = 0; /* Not even 1st char literal */
948 }
949 }
950
951 if (namelast) namelast->next = p;
952 namelast = p;
953 if (!namelist) namelist = p;
954}
955/*
956 * Match a name from an archive, p, with a name from the namelist.
957 */
958name_match(p)
959 register char *p;
960{
961 register struct name *nlp;
962 register int len;
963
964again:
965 if (0 == (nlp = namelist)) /* Empty namelist is easy */
966 return 1;
967 if (nlp->fake)
968 {
969 if (nlp->change_dir && chdir (nlp->change_dir))
970 msg_perror ("Can't change to directory %d", nlp->change_dir);
971 namelist = 0;
972 return 1;
973 }
974 len = strlen(p);
975 for (; nlp != 0; nlp = nlp->next) {
976 /* If first chars don't match, quick skip */
977 if (nlp->firstch && nlp->name[0] != p[0])
978 continue;
979
980 /* Regular expressions */
981 if (nlp->regexp) {
982 if (wildmat(p, nlp->name)) {
983 nlp->found = 1; /* Remember it matched */
984 if(f_startfile) {
985 free((void *)namelist);
986 namelist=0;
987 }
988 if(nlp->change_dir && chdir(nlp->change_dir))
989 msg_perror("Can't change to directory %s",nlp->change_dir);
990 return 1; /* We got a match */
991 }
992 continue;
993 }
994
995 /* Plain Old Strings */
996 if (nlp->length <= len /* Archive len >= specified */
997 && (p[nlp->length] == '\0' || p[nlp->length] == '/')
998 /* Full match on file/dirname */
999 && strncmp(p, nlp->name, nlp->length) == 0) /* Name compare */
1000 {
1001 nlp->found = 1; /* Remember it matched */
1002 if(f_startfile) {
1003 free((void *)namelist);
1004 namelist = 0;
1005 }
1006 if(nlp->change_dir && chdir(nlp->change_dir))
1007 msg_perror("Can't change to directory %s",nlp->change_dir);
1008 return 1; /* We got a match */
1009 }
1010 }
1011
1012 /*
1013 * Filename from archive not found in namelist.
1014 * If we have the whole namelist here, just return 0.
1015 * Otherwise, read the next name in and compare it.
1016 * If this was the last name, namelist->found will remain on.
1017 * If not, we loop to compare the newly read name.
1018 */
1019 if (f_sorted_names && namelist->found) {
1020 name_gather(); /* Read one more */
1021 if (!namelist->found) goto again;
1022 }
1023 return 0;
1024}
1025
1026
1027/*
1028 * Print the names of things in the namelist that were not matched.
1029 */
1030names_notfound()
1031{
1032 register struct name *nlp,*next;
1033 register char *p;
1034
1035 for (nlp = namelist; nlp != 0; nlp = next) {
1036 next=nlp->next;
1037 if (!nlp->found)
1038 msg("%s not found in archive",nlp->name);
1039
1040 /*
1041 * We could free() the list, but the process is about
1042 * to die anyway, so save some CPU time. Amigas and
1043 * other similarly broken software will need to waste
1044 * the time, though.
1045 */
1046#ifndef unix
1047 if (!f_sorted_names)
1048 free(nlp);
1049#endif
1050 }
1051 namelist = (struct name *)NULL;
1052 namelast = (struct name *)NULL;
1053
1054 if (f_sorted_names) {
1055 while (0 != (p = name_next(1)))
1056 msg("%s not found in archive", p);
1057 }
1058}
1059
1060/* These next routines were created by JF */
1061
1062name_expand()
1063{
1064;
1065}
1066
1067/* This is like name_match(), except that it returns a pointer to the name
1068 it matched, and doesn't set ->found The caller will have to do that
1069 if it wants to. Oh, and if the namelist is empty, it returns 0, unlike
1070 name_match(), which returns TRUE */
1071
1072struct name *
1073name_scan(p)
1074register char *p;
1075{
1076 register struct name *nlp;
1077 register int len;
1078
1079again:
1080 if (0 == (nlp = namelist)) /* Empty namelist is easy */
1081 return 0;
1082 len = strlen(p);
1083 for (; nlp != 0; nlp = nlp->next) {
1084 /* If first chars don't match, quick skip */
1085 if (nlp->firstch && nlp->name[0] != p[0])
1086 continue;
1087
1088 /* Regular expressions */
1089 if (nlp->regexp) {
1090 if (wildmat(p, nlp->name))
1091 return nlp; /* We got a match */
1092 continue;
1093 }
1094
1095 /* Plain Old Strings */
1096 if (nlp->length <= len /* Archive len >= specified */
1097 && (p[nlp->length] == '\0' || p[nlp->length] == '/')
1098 /* Full match on file/dirname */
1099 && strncmp(p, nlp->name, nlp->length) == 0) /* Name compare */
1100 return nlp; /* We got a match */
1101 }
1102
1103 /*
1104 * Filename from archive not found in namelist.
1105 * If we have the whole namelist here, just return 0.
1106 * Otherwise, read the next name in and compare it.
1107 * If this was the last name, namelist->found will remain on.
1108 * If not, we loop to compare the newly read name.
1109 */
1110 if (f_sorted_names && namelist->found) {
1111 name_gather(); /* Read one more */
1112 if (!namelist->found) goto again;
1113 }
1114 return (struct name *) 0;
1115}
1116
1117/* This returns a name from the namelist which doesn't have ->found set.
1118 It sets ->found before returning, so successive calls will find and return
1119 all the non-found names in the namelist */
1120
1121struct name *gnu_list_name;
1122
1123char *
1124name_from_list()
1125{
1126 if(!gnu_list_name)
1127 gnu_list_name = namelist;
1128 while(gnu_list_name && gnu_list_name->found)
1129 gnu_list_name=gnu_list_name->next;
1130 if(gnu_list_name) {
1131 gnu_list_name->found++;
1132 if(gnu_list_name->change_dir)
1133 if(chdir(gnu_list_name->change_dir)<0)
1134 msg_perror("can't chdir to %s",gnu_list_name->change_dir);
1135 return gnu_list_name->name;
1136 }
1137 return (char *)0;
1138}
1139
1140blank_name_list()
1141{
1142 struct name *n;
1143
1144 gnu_list_name = 0;
1145 for(n=namelist;n;n=n->next)
1146 n->found = 0;
1147}
1148
1149char *
1150new_name(path,name)
1151char *path,*name;
1152{
1153 char *path_buf;
1154
1155 path_buf=(char *)malloc(strlen(path)+strlen(name)+2);
1156 if(path_buf==0) {
1157 msg("Can't allocate memory for name '%s/%s",path,name);
1158 exit(EX_SYSTEM);
1159 }
1160 (void) sprintf(path_buf,"%s/%s",path,name);
1161 return path_buf;
1162}
1163
1164/* returns non-zero if the luser typed 'y' or 'Y', zero otherwise. */
1165
1166int
1167confirm(action,file)
1168char *action, *file;
1169{
1170 int c,nl;
1171 static FILE *confirm_file = 0;
1172 extern FILE *msg_file;
1173 extern char TTY_NAME[];
1174
1175 fprintf(msg_file,"%s %s?", action, file);
1176 fflush(msg_file);
1177 if(!confirm_file) {
1178 confirm_file = (archive == 0) ? fopen(TTY_NAME, "r") : stdin;
1179 if(!confirm_file) {
1180 msg("Can't read confirmation from user");
1181 exit(EX_SYSTEM);
1182 }
1183 }
1184 c=getc(confirm_file);
1185 for(nl = c; nl != '\n' && nl != EOF; nl = getc(confirm_file))
1186 ;
1187 return (c=='y' || c=='Y');
1188}
1189
1190char *x_buffer = 0;
1191int size_x_buffer;
1192int free_x_buffer;
1193
1194char **exclude = 0;
1195int size_exclude = 0;
1196int free_exclude = 0;
1197
1198char **re_exclude = 0;
1199int size_re_exclude = 0;
1200int free_re_exclude = 0;
1201
1202add_exclude(name)
1203char *name;
1204{
1205 char *rname;
1206 char **tmp_ptr;
1207 int size_buf;
1208
1209 un_quote_string(name);
1210 size_buf = strlen(name);
1211
1212 if(x_buffer==0) {
1213 x_buffer = (char *)ck_malloc(size_buf+1024);
1214 free_x_buffer=1024;
1215 } else if(free_x_buffer<=size_buf) {
1216 char *old_x_buffer;
1217 char **tmp_ptr;
1218
1219 old_x_buffer = x_buffer;
1220 x_buffer = (char *)ck_realloc(x_buffer,size_x_buffer+1024);
1221 free_x_buffer = 1024;
1222 for(tmp_ptr=exclude;tmp_ptr<exclude+size_exclude;tmp_ptr++)
1223 *tmp_ptr= x_buffer + ((*tmp_ptr) - old_x_buffer);
1224 for(tmp_ptr=re_exclude;tmp_ptr<re_exclude+size_re_exclude;tmp_ptr++)
1225 *tmp_ptr= x_buffer + ((*tmp_ptr) - old_x_buffer);
1226 }
1227
1228 if(is_regex(name)) {
1229 if(free_re_exclude==0) {
1230 re_exclude= (char **)(re_exclude ? ck_realloc(re_exclude,(size_re_exclude+32)*sizeof(char *)) : ck_malloc(sizeof(char *)*32));
1231 free_re_exclude+=32;
1232 }
1233 re_exclude[size_re_exclude]=x_buffer+size_x_buffer;
1234 size_re_exclude++;
1235 free_re_exclude--;
1236 } else {
1237 if(free_exclude==0) {
1238 exclude=(char **)(exclude ? ck_realloc(exclude,(size_exclude+32)*sizeof(char *)) : ck_malloc(sizeof(char *)*32));
1239 free_exclude+=32;
1240 }
1241 exclude[size_exclude]=x_buffer+size_x_buffer;
1242 size_exclude++;
1243 free_exclude--;
1244 }
1245 strcpy(x_buffer+size_x_buffer,name);
1246 size_x_buffer+=size_buf+1;
1247 free_x_buffer-=size_buf+1;
1248}
1249
1250add_exclude_file(file)
1251char *file;
1252{
1253 FILE *fp;
1254 char buf[1024];
1255 extern char *rindex();
1256
1257 if(strcmp(file, "-"))
1258 fp=fopen(file,"r");
1259 else
1260 /* Let's hope the person knows what they're doing. */
1261 /* Using -X - -T - -f - will get you *REALLY* strange
1262 results. . . */
1263 fp=stdin;
1264
1265 if(!fp) {
1266 msg_perror("can't open %s",file);
1267 exit(2);
1268 }
1269 while(fgets(buf,1024,fp)) {
1270 int size_buf;
1271 char *end_str;
1272
1273 end_str=rindex(buf,'\n');
1274 if(end_str)
1275 *end_str='\0';
1276 add_exclude(buf);
1277
1278 }
1279 fclose(fp);
1280}
1281
1282int
1283is_regex(str)
1284char *str;
1285{
1286 return index(str,'*') || index(str,'[') || index(str,'?');
1287}
1288
1289/* Returns non-zero if the file 'name' should not be added/extracted */
1290int
1291check_exclude(name)
1292char *name;
1293{
1294 int n;
1295 char *str;
1296 extern char *strstr();
1297
1298 for(n=0;n<size_re_exclude;n++) {
1299 if(wildmat(name,re_exclude[n]))
1300 return 1;
1301 }
1302 for(n=0;n<size_exclude;n++) {
1303 /* Accept the output from strstr only if it is the last
1304 part of the string. There is certainly a faster way to
1305 do this. . . */
1306 if( (str=strstr(name,exclude[n]))
1307 && (str==name || str[-1]=='/')
1308 && str[strlen(exclude[n])]=='\0')
1309 return 1;
1310 }
1311 return 0;
1312}