Commit | Line | Data |
---|---|---|
68c14404 WJ |
1 | /* Tar -- a tape archiver. |
2 | ||
3 | Copyright (C) 1988 Free Software Foundation | |
4 | ||
5 | GNU tar is distributed in the hope that it will be useful, but WITHOUT ANY | |
6 | WARRANTY. No author or distributor accepts responsibility to anyone | |
7 | for the consequences of using it or for whether it serves any | |
8 | particular purpose or works at all, unless he says so in writing. | |
9 | Refer to the GNU tar General Public License for full details. | |
10 | ||
11 | Everyone is granted permission to copy, modify and redistribute GNU tar, | |
12 | but only under the conditions described in the GNU tar General Public | |
13 | License. A copy of this license is supposed to have been given to you | |
14 | along with GNU tar so you can know your rights and responsibilities. It | |
15 | should be in a file named COPYING. Among other things, the copyright | |
16 | notice and this notice must be preserved on all copies. | |
17 | ||
18 | In other words, go ahead and share GNU tar, but don't try to stop | |
19 | anyone 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 | ||
71 | extern char *malloc(); | |
72 | extern char *getenv(); | |
73 | extern char *strncpy(); | |
74 | extern char *index(); | |
75 | extern char *strcpy(); /* JF */ | |
76 | extern char *strcat(); /* JF */ | |
77 | ||
78 | extern char *optarg; /* Pointer to argument */ | |
79 | extern int optind; /* Global argv index from getopt */ | |
80 | ||
81 | extern char *ck_malloc(); | |
82 | extern 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 | |
95 | extern int getoldopt(); | |
96 | extern void read_and(); | |
97 | extern void list_archive(); | |
98 | extern void extract_archive(); | |
99 | extern void diff_archive(); | |
100 | extern void create_archive(); | |
101 | extern void update_archive(); | |
102 | extern void junk_archive(); | |
103 | ||
104 | /* JF */ | |
105 | extern time_t get_date(); | |
106 | ||
107 | time_t new_time; | |
108 | ||
109 | static FILE *namef; /* File to read names from */ | |
110 | static char **n_argv; /* Argv used by name routines */ | |
111 | static int n_argc; /* Argc used by name routines */ | |
112 | static char **n_ind; /* Store an array of names */ | |
113 | static int n_indalloc; /* How big is the array? */ | |
114 | static int n_indused; /* How many entries does it have? */ | |
115 | static int n_indscan; /* How many of the entries have we scanned? */ | |
116 | ||
117 | ||
118 | extern FILE *msg_file; | |
119 | ||
120 | void describe(); | |
121 | void 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 | ||
142 | struct 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 | */ | |
210 | main(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 | */ | |
296 | void | |
297 | options(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 | */ | |
612 | void | |
613 | describe() | |
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, "\ | |
630 | Other 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 | ||
682 | name_add(name) | |
683 | char *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 | */ | |
697 | name_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 */ | |
730 | char * | |
731 | name_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 | */ | |
812 | name_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 | */ | |
830 | name_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 | */ | |
876 | addname(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 | */ | |
958 | name_match(p) | |
959 | register char *p; | |
960 | { | |
961 | register struct name *nlp; | |
962 | register int len; | |
963 | ||
964 | again: | |
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 | */ | |
1030 | names_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 | ||
1062 | name_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 | ||
1072 | struct name * | |
1073 | name_scan(p) | |
1074 | register char *p; | |
1075 | { | |
1076 | register struct name *nlp; | |
1077 | register int len; | |
1078 | ||
1079 | again: | |
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 | ||
1121 | struct name *gnu_list_name; | |
1122 | ||
1123 | char * | |
1124 | name_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 | ||
1140 | blank_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 | ||
1149 | char * | |
1150 | new_name(path,name) | |
1151 | char *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 | ||
1166 | int | |
1167 | confirm(action,file) | |
1168 | char *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 | ||
1190 | char *x_buffer = 0; | |
1191 | int size_x_buffer; | |
1192 | int free_x_buffer; | |
1193 | ||
1194 | char **exclude = 0; | |
1195 | int size_exclude = 0; | |
1196 | int free_exclude = 0; | |
1197 | ||
1198 | char **re_exclude = 0; | |
1199 | int size_re_exclude = 0; | |
1200 | int free_re_exclude = 0; | |
1201 | ||
1202 | add_exclude(name) | |
1203 | char *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 | ||
1250 | add_exclude_file(file) | |
1251 | char *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 | ||
1282 | int | |
1283 | is_regex(str) | |
1284 | char *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 */ | |
1290 | int | |
1291 | check_exclude(name) | |
1292 | char *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 | } |