xref: /plan9/sys/src/ape/cmd/pax/pax.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1 /* $Source: /u/mark/src/pax/RCS/pax.c,v $
2  *
3  * $Revision: 1.2 $
4  *
5  * DESCRIPTION
6  *
7  *	Pax is the archiver described in IEEE P1003.2.  It is an archiver
8  *	which understands both tar and cpio archives and has a new interface.
9  *
10  * SYNOPSIS
11  *
12  *	pax -[cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...]
13  *	pax -r [-cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...]
14  *	pax -w [-adimuvy] [-b blocking] [-f archive] [-s replstr]...]
15  *	       [-t device][-x format][pathname...]
16  *	pax -r -w [-ilmopuvy][-s replstr][pathname...] directory
17  *
18  * DESCRIPTION
19  *
20  * 	PAX - POSIX conforming tar and cpio archive handler.  This
21  *	program implements POSIX conformant versions of tar, cpio and pax
22  *	archive handlers for UNIX.  These handlers have defined befined
23  *	by the IEEE P1003.2 commitee.
24  *
25  * COMPILATION
26  *
27  *	A number of different compile time configuration options are
28  *	available, please see the Makefile and config.h for more details.
29  *
30  * AUTHOR
31  *
32  *     Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
33  *
34  *
35  * Sponsored by The USENIX Association for public distribution.
36  *
37  * Copyright (c) 1989 Mark H. Colburn.
38  * All rights reserved.
39  *
40  * Redistribution and use in source and binary forms are permitted
41  * provided that the above copyright notice is duplicated in all such
42  * forms and that any documentation, advertising materials, and other
43  * materials related to such distribution and use acknowledge that the
44  * software was developed * by Mark H. Colburn and sponsored by The
45  * USENIX Association.
46  *
47  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
48  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
49  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
50  *
51  * $Log:	pax.c,v $
52  * Revision 1.2  89/02/12  10:05:17  mark
53  * 1.2 release fixes
54  *
55  * Revision 1.1  88/12/23  18:02:23  mark
56  * Initial revision
57  *
58  */
59 
60 #ifndef lint
61 static char *ident = "$Id: pax.c,v 1.2 89/02/12 10:05:17 mark Exp $";
62 static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
63 #endif /* ! lint */
64 
65 
66 /* Headers */
67 
68 #define NO_EXTERN
69 #include "pax.h"
70 
71 
72 /* Globally Available Identifiers */
73 
74 char           *ar_file;		/* File containing name of archive */
75 char           *bufend;			/* End of data within archive buffer */
76 char           *bufstart;		/* Archive buffer */
77 char           *bufidx;			/* Archive buffer index */
78 char           *myname;			/* name of executable (argv[0]) */
79 char          **n_argv;			/* Argv used by name routines */
80 int             n_argc;			/* Argc used by name routines */
81 int             archivefd;		/* Archive file descriptor */
82 int             blocking;		/* Size of each block, in records */
83 int             gid;			/* Group ID */
84 int             head_standard;		/* true if archive is POSIX format */
85 int             ar_interface;		/* defines interface we are using */
86 int             ar_format;		/* defines current archve format */
87 int             mask;			/* File creation mask */
88 int             ttyf;			/* For interactive queries */
89 int             uid;			/* User ID */
90 int		names_from_stdin;	/* names for files are from stdin */
91 OFFSET          total;			/* Total number of bytes transferred */
92 short           f_access_time;		/* Reset access times of input files */
93 short           areof;			/* End of input volume reached */
94 short           f_dir_create;		/* Create missing directories */
95 short           f_append;		/* Add named files to end of archive */
96 short           f_create;		/* create a new archive */
97 short           f_extract;		/* Extract named files from archive */
98 short           f_follow_links;		/* follow symbolic links */
99 short           f_interactive;		/* Interactivly extract files */
100 short           f_linksleft;		/* Report on unresolved links */
101 short           f_list;			/* List files on the archive */
102 short           f_modified;		/* Don't restore modification times */
103 short           f_verbose;		/* Turn on verbose mode */
104 short		f_link;			/* link files where possible */
105 short		f_owner;		/* extract files as the user */
106 short		f_pass;			/* pass files between directories */
107 short           f_newer;		/* append files to archive if newer */
108 short		f_disposition;		/* ask for file disposition */
109 short           f_reverse_match;	/* Reverse sense of pattern match */
110 short           f_mtime;		/* Retain file modification time */
111 short           f_unconditional;	/* Copy unconditionally */
112 time_t          now = 0;		/* Current time */
113 uint            arvolume;		/* Volume number */
114 uint            blocksize = BLOCKSIZE;	/* Archive block size */
115 FILE	       *msgfile;		/* message outpu file stdout/stderr */
116 Replstr        *rplhead = (Replstr *)NULL;	/*  head of replstr list */
117 Replstr        *rpltail;		/* pointer to tail of replstr list */
118 
119 
120 /* Function Prototypes */
121 
122 #ifdef __STDC__
123 
124 static void 	usage(void);
125 static OFFSET   pax_optsize(char *);
126 
127 #else /* !__STDC__ */
128 
129 static void 	usage();
130 static OFFSET   pax_optsize();
131 
132 #endif /* __STDC__ */
133 
134 
135 /* main - main routine for handling all archive formats.
136  *
137  * DESCRIPTION
138  *
139  * 	Set up globals and call the proper interface as specified by the user.
140  *
141  * PARAMETERS
142  *
143  *	int argc	- count of user supplied arguments
144  *	char **argv	- user supplied arguments
145  *
146  * RETURNS
147  *
148  *	Returns an exit code of 0 to the parent process.
149  */
150 
151 #ifdef __STDC__
152 
main(int argc,char ** argv)153 int main(int argc, char **argv)
154 
155 #else
156 
157 int main(argc, argv)
158 int             argc;
159 char          **argv;
160 
161 #endif
162 {
163     /* strip the pathname off of the name of the executable */
164     if ((myname = strrchr(argv[0], '/')) != (char *)NULL) {
165 	myname++;
166     } else {
167 	myname = argv[0];
168     }
169 
170     /* set upt for collecting other command line arguments */
171     name_init(argc, argv);
172 
173     /* get all our necessary information */
174     mask = umask(0);
175     uid = getuid();
176     gid = getgid();
177     now = time((time_t *) 0);
178 
179     /* open terminal for interactive queries */
180     ttyf = open_tty();
181 
182     if (strcmp(myname, "tar")==0) {
183 	do_tar(argc, argv);
184     } else if (strcmp(myname, "cpio")==0) {
185 	do_cpio(argc, argv);
186     } else {
187 	do_pax(argc, argv);
188     }
189     exit(0);
190     /* NOTREACHED */
191 }
192 
193 
194 /* do_pax - provide a PAX conformant user interface for archive handling
195  *
196  * DESCRIPTION
197  *
198  *	Process the command line parameters given, doing some minimal sanity
199  *	checking, and then launch the specified archiving functions.
200  *
201  * PARAMETERS
202  *
203  *    int ac		- A count of arguments in av.  Should be passed argc
204  *			  from main
205  *    char **av		- A pointer to an argument list.  Should be passed
206  *			  argv from main
207  *
208  * RETURNS
209  *
210  *    Normally returns 0.  If an error occurs, -1 is returned
211  *    and state is set to reflect the error.
212  *
213  */
214 
215 #ifdef __STDC__
216 
do_pax(int ac,char ** av)217 int do_pax(int ac, char **av)
218 
219 #else
220 
221 int do_pax(ac, av)
222 int             ac;		/* argument counter */
223 char          **av;		/* arguments */
224 
225 #endif
226 {
227     int             c;
228     char	   *dirname;
229     Stat	    st;
230 
231     /* default input/output file for PAX is STDIN/STDOUT */
232     ar_file = "-";
233 
234     /*
235      * set up the flags to reflect the default pax inteface.  Unfortunately
236      * the pax interface has several options which are completely opposite
237      * of the tar and/or cpio interfaces...
238      */
239     f_unconditional = 1;
240     f_mtime = 1;
241     f_dir_create = 1;
242     f_list = 1;
243     blocksize = 0;
244     blocking = 0;
245     ar_interface = PAX;
246     ar_format = TAR;	/* default interface if none given for -w */
247     msgfile=stdout;
248 
249     while ((c = getopt(ac, av, "ab:cdf:ilmoprs:t:uvwx:y")) != EOF) {
250 	switch (c) {
251 	case 'a':
252 	    f_append = 1;
253 	    f_list = 0;
254 	    break;
255 	case 'b':
256 	    if ((blocksize = pax_optsize(optarg)) == 0) {
257 		fatal("Bad block size");
258 	    }
259 	    break;
260 	case 'c':
261 	    f_reverse_match = 1;
262 	    break;
263 	case 'd':
264 	    f_dir_create = 0;
265 	    break;
266 	case 'f':
267 	    if (blocksize == 0) {
268 		blocking = 1;
269 		blocksize = 1 * BLOCKSIZE;
270 	    }
271 	    ar_file = optarg;
272 	    break;
273 	case 'i':
274 	    f_interactive = 1;
275 	    break;
276 	case 'l':
277 	    f_link = 1;
278 	    break;
279 	case 'm':
280 	    f_mtime = 0;
281 	    break;
282 	case 'o':
283 	    f_owner = 1;
284 	    break;
285 	case 'p':
286 	    f_access_time = 1;
287 	    break;
288 	case 'r':
289 	    if (f_create) {
290 		f_create = 0;
291 		f_pass = 1;
292 	    } else {
293 		f_list = 0;
294 		f_extract = 1;
295 	    }
296 	    msgfile=stderr;
297 	    break;
298 	case 's':
299 	    add_replstr(optarg);
300 	    break;
301 	case 't':
302 	    if (blocksize == 0) {
303 		blocking = 1;
304 		blocksize = 10 * BLOCKSIZE;
305 	    }
306 	    ar_file = optarg;
307 	    break;
308 	case 'u':
309 	    f_unconditional = 1;
310 	    break;
311 	case 'v':
312 	    f_verbose = 1;
313 	    break;
314 	case 'w':
315 	    if (f_extract) {
316 		f_extract = 0;
317 		f_pass = 1;
318 	    } else {
319 		f_list = 0;
320 		f_create = 1;
321 	    }
322 	    msgfile=stderr;
323 	    break;
324 	case 'x':
325 	    if (strcmp(optarg, "ustar") == 0) {
326 		ar_format = TAR;
327 	    } else if (strcmp(optarg, "cpio") == 0) {
328 		ar_format = CPIO;
329 	    } else {
330 		usage();
331 	    }
332 	    break;
333 	case 'y':
334 	    f_disposition = 1;
335 	    break;
336 	default:
337 	    usage();
338 	}
339     }
340 
341     if (blocksize == 0) {
342 	blocking = 1;
343 	blocksize = blocking * BLOCKSIZE;
344     }
345     buf_allocate((OFFSET) blocksize);
346 
347     if (f_extract || f_list) {
348 	open_archive(AR_READ);
349 	get_archive_type();
350 	read_archive();
351     } else if (f_create) {
352 	if (optind >= n_argc) {
353 	    names_from_stdin++;		/* args from stdin */
354 	}
355 	open_archive(AR_WRITE);
356 	create_archive();
357     } else if (f_append) {
358 	open_archive(AR_APPEND);
359 	get_archive_type();
360 	append_archive();
361     } else if (f_pass && optind < n_argc) {
362 	dirname = n_argv[--n_argc];
363 	if (LSTAT(dirname, &st) < 0) {
364 	    fatal(strerror());
365 	}
366 	if ((st.sb_mode & S_IFMT) != S_IFDIR) {
367 	    fatal("Not a directory");
368 	}
369 	if (optind >= n_argc) {
370 	    names_from_stdin++;		/* args from stdin */
371 	}
372 	pass(dirname);
373     } else {
374 	usage();
375     }
376 
377     return (0);
378 }
379 
380 
381 /* get_archive_type - determine input archive type from archive header
382  *
383  * DESCRIPTION
384  *
385  * 	reads the first block of the archive and determines the archive
386  *	type from the data.  If the archive type cannot be determined,
387  *	processing stops, and a 1 is returned to the caller.  If verbose
388  *	mode is on, then the archive type will be printed on the standard
389  *	error device as it is determined.
390  *
391  * FIXME
392  *
393  *	be able to understand TAR and CPIO magic numbers
394  */
395 
396 #ifdef __STDC__
397 
get_archive_type(void)398 void get_archive_type(void)
399 
400 #else
401 
402 void get_archive_type()
403 
404 #endif
405 {
406     if (ar_read() != 0) {
407 	fatal("Unable to determine archive type.");
408     }
409     if (strncmp(bufstart, "070707", 6) == 0) {
410 	ar_format = CPIO;
411 	if (f_verbose) {
412 	    fputs("CPIO format archive\n", stderr);
413 	}
414     } else if (strncmp(&bufstart[257], "ustar", 5) == 0) {
415 	ar_format = TAR;
416 	if (f_verbose) {
417 	    fputs("USTAR format archive\n", stderr);
418 	}
419     } else {
420 	ar_format = TAR;
421     }
422 }
423 
424 
425 /* pax_optsize - interpret a size argument
426  *
427  * DESCRIPTION
428  *
429  * 	Recognizes suffixes for blocks (512-bytes), k-bytes and megabytes.
430  * 	Also handles simple expressions containing '+' for addition.
431  *
432  * PARAMETERS
433  *
434  *    char 	*str	- A pointer to the string to interpret
435  *
436  * RETURNS
437  *
438  *    Normally returns the value represented by the expression in the
439  *    the string.
440  *
441  * ERRORS
442  *
443  *	If the string cannot be interpretted, the program will fail, since
444  *	the buffering will be incorrect.
445  *
446  */
447 
448 #ifdef __STDC__
449 
pax_optsize(char * str)450 static OFFSET pax_optsize(char *str)
451 
452 #else
453 
454 static OFFSET pax_optsize(str)
455 char           *str;		/* pointer to string to interpret */
456 
457 #endif
458 {
459     char           *idx;
460     OFFSET          number;	/* temporary storage for current number */
461     OFFSET          result;	/* cumulative total to be returned to caller */
462 
463     result = 0;
464     idx = str;
465     for (;;) {
466 	number = 0;
467 	while (*idx >= '0' && *idx <= '9')
468 	    number = number * 10 + *idx++ - '0';
469 	switch (*idx++) {
470 	case 'b':
471 	    result += number * 512L;
472 	    continue;
473 	case 'k':
474 	    result += number * 1024L;
475 	    continue;
476 	case 'm':
477 	    result += number * 1024L * 1024L;
478 	    continue;
479 	case '+':
480 	    result += number;
481 	    continue;
482 	case '\0':
483 	    result += number;
484 	    break;
485 	default:
486 	    break;
487 	}
488 	break;
489     }
490     if (*--idx) {
491 	fatal("Unrecognizable value");
492     }
493     return (result);
494 }
495 
496 
497 /* usage - print a helpful message and exit
498  *
499  * DESCRIPTION
500  *
501  *	Usage prints out the usage message for the PAX interface and then
502  *	exits with a non-zero termination status.  This is used when a user
503  *	has provided non-existant or incompatible command line arguments.
504  *
505  * RETURNS
506  *
507  *	Returns an exit status of 1 to the parent process.
508  *
509  */
510 
511 #ifdef __STDC__
512 
usage(void)513 static void usage(void)
514 
515 #else
516 
517 static void usage()
518 
519 #endif
520 {
521     fprintf(stderr, "Usage: %s -[cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...]\n",
522 	myname);
523     fprintf(stderr, "       %s -r [-cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...]\n",
524 	myname);
525     fprintf(stderr, "       %s -w [-adimuvy] [-b blocking] [-f archive] [-s replstr]\n              [-t device] [-x format] [pathname...]\n",
526 	myname);
527     fprintf(stderr, "       %s -r -w [-ilmopuvy] [-s replstr] [pathname...] directory\n",
528 	myname);
529     exit(1);
530 }
531