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