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