1 /* $Source: /u/mark/src/pax/RCS/list.c,v $
2 *
3 * $Revision: 1.2 $
4 *
5 * list.c - List all files on an archive
6 *
7 * DESCRIPTION
8 *
9 * These function are needed to support archive table of contents and
10 * verbose mode during extraction and creation of achives.
11 *
12 * AUTHOR
13 *
14 * Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
15 *
16 * Sponsored by The USENIX Association for public distribution.
17 *
18 * Copyright (c) 1989 Mark H. Colburn.
19 * All rights reserved.
20 *
21 * Redistribution and use in source and binary forms are permitted
22 * provided that the above copyright notice is duplicated in all such
23 * forms and that any documentation, advertising materials, and other
24 * materials related to such distribution and use acknowledge that the
25 * software was developed * by Mark H. Colburn and sponsored by The
26 * USENIX Association.
27 *
28 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
29 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
30 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
31 *
32 * $Log: list.c,v $
33 * Revision 1.2 89/02/12 10:04:43 mark
34 * 1.2 release fixes
35 *
36 * Revision 1.1 88/12/23 18:02:14 mark
37 * Initial revision
38 *
39 */
40
41 #ifndef lint
42 static char *ident = "$Id: list.c,v 1.2 89/02/12 10:04:43 mark Exp $";
43 static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
44 #endif /* ! lint */
45
46
47 /* Headers */
48
49 #include "pax.h"
50
51
52 /* Defines */
53
54 /*
55 * isodigit returns non zero iff argument is an octal digit, zero otherwise
56 */
57 #define ISODIGIT(c) (((c) >= '0') && ((c) <= '7'))
58
59
60 /* Function Prototypes */
61
62 #ifdef __STDC__
63
64 static void cpio_entry(char *, Stat *);
65 static void tar_entry(char *, Stat *);
66 static void pax_entry(char *, Stat *);
67 static void print_mode(ushort);
68 static long from_oct(int digs, char *where);
69
70 #else /* !__STDC__ */
71
72 static void cpio_entry();
73 static void tar_entry();
74 static void pax_entry();
75 static void print_mode();
76 static long from_oct();
77
78 #endif /* __STDC__ */
79
80
81 /* Internal Identifiers */
82
83 static char *monnames[] = {
84 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
85 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
86 };
87
88
89 /* read_header - read a header record
90 *
91 * DESCRIPTION
92 *
93 * Read a record that's supposed to be a header record. Return its
94 * address in "head", and if it is good, the file's size in
95 * asb->sb_size. Decode things from a file header record into a "Stat".
96 * Also set "head_standard" to !=0 or ==0 depending whether header record
97 * is "Unix Standard" tar format or regular old tar format.
98 *
99 * PARAMETERS
100 *
101 * char *name - pointer which will contain name of file
102 * Stat *asb - pointer which will contain stat info
103 *
104 * RETURNS
105 *
106 * Return 1 for success, 0 if the checksum is bad, EOF on eof, 2 for a
107 * record full of zeros (EOF marker).
108 */
109
110 #ifdef __STDC__
111
read_header(char * name,Stat * asb)112 int read_header(char *name, Stat *asb)
113
114 #else
115
116 int read_header(name, asb)
117 char *name;
118 Stat *asb;
119
120 #endif
121 {
122 int i;
123 long sum;
124 long recsum;
125 Link *link;
126 char *p;
127 char hdrbuf[BLOCKSIZE];
128
129 memset((char *)asb, 0, sizeof(Stat));
130 /* read the header from the buffer */
131 if (buf_read(hdrbuf, BLOCKSIZE) != 0) {
132 return (EOF);
133 }
134
135 strcpy(name, hdrbuf);
136
137 recsum = from_oct(8, &hdrbuf[148]);
138 sum = 0;
139 p = hdrbuf;
140 for (i = 0 ; i < 500; i++) {
141
142 /*
143 * We can't use unsigned char here because of old compilers, e.g. V7.
144 */
145 sum += 0xFF & *p++;
146 }
147
148 /* Adjust checksum to count the "chksum" field as blanks. */
149 for (i = 0; i < 8; i++) {
150 sum -= 0xFF & hdrbuf[148 + i];
151 }
152 sum += ' ' * 8;
153
154 if (sum == 8 * ' ') {
155
156 /*
157 * This is a zeroed record...whole record is 0's except for the 8
158 * blanks we faked for the checksum field.
159 */
160 return (2);
161 }
162 if (sum == recsum) {
163 /*
164 * Good record. Decode file size and return.
165 */
166 if (hdrbuf[156] != LNKTYPE) {
167 asb->sb_size = from_oct(1 + 12, &hdrbuf[124]);
168 }
169 asb->sb_mtime = from_oct(1 + 12, &hdrbuf[136]);
170 asb->sb_mode = from_oct(8, &hdrbuf[100]);
171
172 if (strcmp(&hdrbuf[257], TMAGIC) == 0) {
173 /* Unix Standard tar archive */
174 head_standard = 1;
175 #ifdef NONAMES
176 asb->sb_uid = from_oct(8, &hdrbuf[108]);
177 asb->sb_gid = from_oct(8, &hdrbuf[116]);
178 #else
179 asb->sb_uid = finduid(&hdrbuf[265]);
180 asb->sb_gid = findgid(&hdrbuf[297]);
181 #endif
182 switch (hdrbuf[156]) {
183 case BLKTYPE:
184 case CHRTYPE:
185 #ifndef _POSIX_SOURCE
186 asb->sb_rdev = makedev(from_oct(8, &hdrbuf[329]),
187 from_oct(8, &hdrbuf[337]));
188 #endif
189 break;
190 default:
191 /* do nothing... */
192 break;
193 }
194 } else {
195 /* Old fashioned tar archive */
196 head_standard = 0;
197 asb->sb_uid = from_oct(8, &hdrbuf[108]);
198 asb->sb_gid = from_oct(8, &hdrbuf[116]);
199 }
200
201 switch (hdrbuf[156]) {
202 case REGTYPE:
203 case AREGTYPE:
204 /*
205 * Berkeley tar stores directories as regular files with a
206 * trailing /
207 */
208 if (name[strlen(name) - 1] == '/') {
209 name[strlen(name) - 1] = '\0';
210 asb->sb_mode |= S_IFDIR;
211 } else {
212 asb->sb_mode |= S_IFREG;
213 }
214 break;
215 case LNKTYPE:
216 asb->sb_nlink = 2;
217 linkto(&hdrbuf[157], asb);
218 linkto(name, asb);
219 asb->sb_mode |= S_IFREG;
220 break;
221 case BLKTYPE:
222 asb->sb_mode |= S_IFBLK;
223 break;
224 case CHRTYPE:
225 asb->sb_mode |= S_IFCHR;
226 break;
227 case DIRTYPE:
228 asb->sb_mode |= S_IFDIR;
229 break;
230 #ifdef S_IFLNK
231 case SYMTYPE:
232 asb->sb_mode |= S_IFLNK;
233 strcpy(asb->sb_link, &hdrbuf[157]);
234 break;
235 #endif
236 #ifdef S_IFIFO
237 case FIFOTYPE:
238 asb->sb_mode |= S_IFIFO;
239 break;
240 #endif
241 #ifdef S_IFCTG
242 case CONTTYPE:
243 asb->sb_mode |= S_IFCTG;
244 break;
245 #endif
246 }
247 return (1);
248 }
249 return (0);
250 }
251
252
253 /* print_entry - print a single table-of-contents entry
254 *
255 * DESCRIPTION
256 *
257 * Print_entry prints a single line of file information. The format
258 * of the line is the same as that used by the LS command. For some
259 * archive formats, various fields may not make any sense, such as
260 * the link count on tar archives. No error checking is done for bad
261 * or invalid data.
262 *
263 * PARAMETERS
264 *
265 * char *name - pointer to name to print an entry for
266 * Stat *asb - pointer to the stat structure for the file
267 */
268
269 #ifdef __STDC__
270
print_entry(char * name,Stat * asb)271 void print_entry(char *name, Stat *asb)
272
273 #else
274
275 void print_entry(name, asb)
276 char *name;
277 Stat *asb;
278
279 #endif
280 {
281 switch (ar_interface) {
282 case TAR:
283 tar_entry(name, asb);
284 break;
285 case CPIO:
286 cpio_entry(name, asb);
287 break;
288 case PAX: pax_entry(name, asb);
289 break;
290 }
291 }
292
293
294 /* cpio_entry - print a verbose cpio-style entry
295 *
296 * DESCRIPTION
297 *
298 * Print_entry prints a single line of file information. The format
299 * of the line is the same as that used by the traditional cpio
300 * command. No error checking is done for bad or invalid data.
301 *
302 * PARAMETERS
303 *
304 * char *name - pointer to name to print an entry for
305 * Stat *asb - pointer to the stat structure for the file
306 */
307
308 #ifdef __STDC__
309
cpio_entry(char * name,Stat * asb)310 static void cpio_entry(char *name, Stat *asb)
311
312 #else
313
314 static void cpio_entry(name, asb)
315 char *name;
316 Stat *asb;
317
318 #endif
319 {
320 struct tm *atm;
321 Link *from;
322 struct passwd *pwp;
323 struct group *grp;
324
325 if (f_list && f_verbose) {
326 fprintf(msgfile, "%-7o", asb->sb_mode);
327 atm = localtime(&asb->sb_mtime);
328 if (pwp = getpwuid((int) USH(asb->sb_uid))) {
329 fprintf(msgfile, "%-6s", pwp->pw_name);
330 } else {
331 fprintf(msgfile, "%-6u", USH(asb->sb_uid));
332 }
333 fprintf(msgfile,"%7ld %3s %2d %02d:%02d:%02d %4d ",
334 asb->sb_size, monnames[atm->tm_mon],
335 atm->tm_mday, atm->tm_hour, atm->tm_min,
336 atm->tm_sec, atm->tm_year + 1900);
337 }
338 fprintf(msgfile, "%s", name);
339 if ((asb->sb_nlink > 1) && (from = islink(name, asb))) {
340 fprintf(msgfile, " linked to %s", from->l_name);
341 }
342 #ifdef S_IFLNK
343 if ((asb->sb_mode & S_IFMT) == S_IFLNK) {
344 fprintf(msgfile, " symbolic link to %s", asb->sb_link);
345 }
346 #endif /* S_IFLNK */
347 putc('\n', msgfile);
348 }
349
350
351 /* tar_entry - print a tar verbose mode entry
352 *
353 * DESCRIPTION
354 *
355 * Print_entry prints a single line of tar file information. The format
356 * of the line is the same as that produced by the traditional tar
357 * command. No error checking is done for bad or invalid data.
358 *
359 * PARAMETERS
360 *
361 * char *name - pointer to name to print an entry for
362 * Stat *asb - pointer to the stat structure for the file
363 */
364
365 #ifdef __STDC__
366
tar_entry(char * name,Stat * asb)367 static void tar_entry(char *name, Stat *asb)
368
369 #else
370
371 static void tar_entry(name, asb)
372 char *name;
373 Stat *asb;
374
375 #endif
376 {
377 struct tm *atm;
378 int i;
379 int mode;
380 char *symnam = "NULL";
381 Link *link;
382
383 if ((mode = asb->sb_mode & S_IFMT) == S_IFDIR) {
384 return; /* don't print directories */
385 }
386 if (f_extract) {
387 switch (mode) {
388 #ifdef S_IFLNK
389 case S_IFLNK: /* This file is a symbolic link */
390 i = readlink(name, symnam, PATH_MAX - 1);
391 if (i < 0) { /* Could not find symbolic link */
392 warn("can't read symbolic link", strerror());
393 } else { /* Found symbolic link filename */
394 symnam[i] = '\0';
395 fprintf(msgfile, "x %s symbolic link to %s\n", name, symnam);
396 }
397 break;
398 #endif
399 case S_IFREG: /* It is a link or a file */
400 if ((asb->sb_nlink > 1) && (link = islink(name, asb))) {
401 fprintf(msgfile, "%s linked to %s\n", name, link->l_name);
402 } else {
403 fprintf(msgfile, "x %s, %ld bytes, %d tape blocks\n",
404 name, asb->sb_size, ROUNDUP(asb->sb_size,
405 BLOCKSIZE) / BLOCKSIZE);
406 }
407 }
408 } else if (f_append || f_create) {
409 switch (mode) {
410 #ifdef S_IFLNK
411 case S_IFLNK: /* This file is a symbolic link */
412 i = readlink(name, symnam, PATH_MAX - 1);
413 if (i < 0) { /* Could not find symbolic link */
414 warn("can't read symbolic link", strerror());
415 } else { /* Found symbolic link filename */
416 symnam[i] = '\0';
417 fprintf(msgfile, "a %s symbolic link to %s\n", name, symnam);
418 }
419 break;
420 #endif
421 case S_IFREG: /* It is a link or a file */
422 fprintf(msgfile, "a %s ", name);
423 if ((asb->sb_nlink > 1) && (link = islink(name, asb))) {
424 fprintf(msgfile, "link to %s\n", link->l_name);
425 } else {
426 fprintf(msgfile, "%ld Blocks\n",
427 ROUNDUP(asb->sb_size, BLOCKSIZE) / BLOCKSIZE);
428 }
429 break;
430 }
431 } else if (f_list) {
432 if (f_verbose) {
433 atm = localtime(&asb->sb_mtime);
434 print_mode(asb->sb_mode);
435 fprintf(msgfile," %d/%d %6d %3s %2d %02d:%02d %4d %s",
436 asb->sb_uid, asb->sb_gid, asb->sb_size,
437 monnames[atm->tm_mon], atm->tm_mday, atm->tm_hour,
438 atm->tm_min, atm->tm_year + 1900, name);
439 } else {
440 fprintf(msgfile, "%s", name);
441 }
442 switch (mode) {
443 #ifdef S_IFLNK
444 case S_IFLNK: /* This file is a symbolic link */
445 i = readlink(name, symnam, PATH_MAX - 1);
446 if (i < 0) { /* Could not find symbolic link */
447 warn("can't read symbolic link", strerror());
448 } else { /* Found symbolic link filename */
449 symnam[i] = '\0';
450 fprintf(msgfile, " symbolic link to %s", symnam);
451 }
452 break;
453 #endif
454 case S_IFREG: /* It is a link or a file */
455 if ((asb->sb_nlink > 1) && (link = islink(name, asb))) {
456 fprintf(msgfile, " linked to %s", link->l_name);
457 }
458 break; /* Do not print out directories */
459 }
460 fputc('\n', msgfile);
461 } else {
462 fprintf(msgfile, "? %s %ld blocks\n", name,
463 ROUNDUP(asb->sb_size, BLOCKSIZE) / BLOCKSIZE);
464 }
465 }
466
467
468 /* pax_entry - print a verbose cpio-style entry
469 *
470 * DESCRIPTION
471 *
472 * Print_entry prints a single line of file information. The format
473 * of the line is the same as that used by the LS command.
474 * No error checking is done for bad or invalid data.
475 *
476 * PARAMETERS
477 *
478 * char *name - pointer to name to print an entry for
479 * Stat *asb - pointer to the stat structure for the file
480 */
481
482 #ifdef __STDC__
483
pax_entry(char * name,Stat * asb)484 static void pax_entry(char *name, Stat *asb)
485
486 #else
487
488 static void pax_entry(name, asb)
489 char *name;
490 Stat *asb;
491
492 #endif
493 {
494 struct tm *atm;
495 Link *from;
496 struct passwd *pwp;
497 struct group *grp;
498
499 if (f_list && f_verbose) {
500 print_mode(asb->sb_mode);
501 fprintf(msgfile, " %2d", asb->sb_nlink);
502 atm = localtime(&asb->sb_mtime);
503 if (pwp = getpwuid((int) USH(asb->sb_uid))) {
504 fprintf(msgfile, " %-8s", pwp->pw_name);
505 } else {
506 fprintf(msgfile, " %-8u", USH(asb->sb_uid));
507 }
508 if (grp = getgrgid((int) USH(asb->sb_gid))) {
509 fprintf(msgfile, " %-8s", grp->gr_name);
510 } else {
511 fprintf(msgfile, " %-8u", USH(asb->sb_gid));
512 }
513 switch (asb->sb_mode & S_IFMT) {
514 case S_IFBLK:
515 case S_IFCHR:
516 fprintf(msgfile, "\t%3d, %3d",
517 major(asb->sb_rdev), minor(asb->sb_rdev));
518 break;
519 case S_IFREG:
520 fprintf(msgfile, "\t%8ld", asb->sb_size);
521 break;
522 default:
523 fprintf(msgfile, "\t ");
524 }
525 fprintf(msgfile," %3s %2d %02d:%02d ",
526 monnames[atm->tm_mon], atm->tm_mday,
527 atm->tm_hour, atm->tm_min);
528 }
529 fprintf(msgfile, "%s", name);
530 if ((asb->sb_nlink > 1) && (from = islink(name, asb))) {
531 fprintf(msgfile, " == %s", from->l_name);
532 }
533 #ifdef S_IFLNK
534 if ((asb->sb_mode & S_IFMT) == S_IFLNK) {
535 fprintf(msgfile, " -> %s", asb->sb_link);
536 }
537 #endif /* S_IFLNK */
538 putc('\n', msgfile);
539 }
540
541
542 /* print_mode - fancy file mode display
543 *
544 * DESCRIPTION
545 *
546 * Print_mode displays a numeric file mode in the standard unix
547 * representation, ala ls (-rwxrwxrwx). No error checking is done
548 * for bad mode combinations. FIFOS, sybmbolic links, sticky bits,
549 * block- and character-special devices are supported if supported
550 * by the hosting implementation.
551 *
552 * PARAMETERS
553 *
554 * ushort mode - The integer representation of the mode to print.
555 */
556
557 #ifdef __STDC__
558
print_mode(ushort mode)559 static void print_mode(ushort mode)
560
561 #else
562
563 static void print_mode(mode)
564 ushort mode;
565
566 #endif
567 {
568 /* Tar does not print the leading identifier... */
569 if (ar_interface != TAR) {
570 switch (mode & S_IFMT) {
571 case S_IFDIR:
572 putc('d', msgfile);
573 break;
574 #ifdef S_IFLNK
575 case S_IFLNK:
576 putc('l', msgfile);
577 break;
578 #endif /* S_IFLNK */
579 case S_IFBLK:
580 putc('b', msgfile);
581 break;
582 case S_IFCHR:
583 putc('c', msgfile);
584 break;
585 #ifdef S_IFIFO
586 case S_IFIFO:
587 putc('p', msgfile);
588 break;
589 #endif /* S_IFIFO */
590 case S_IFREG:
591 default:
592 putc('-', msgfile);
593 break;
594 }
595 }
596 putc(mode & 0400 ? 'r' : '-', msgfile);
597 putc(mode & 0200 ? 'w' : '-', msgfile);
598 putc(mode & 0100
599 ? mode & 04000 ? 's' : 'x'
600 : mode & 04000 ? 'S' : '-', msgfile);
601 putc(mode & 0040 ? 'r' : '-', msgfile);
602 putc(mode & 0020 ? 'w' : '-', msgfile);
603 putc(mode & 0010
604 ? mode & 02000 ? 's' : 'x'
605 : mode & 02000 ? 'S' : '-', msgfile);
606 putc(mode & 0004 ? 'r' : '-', msgfile);
607 putc(mode & 0002 ? 'w' : '-', msgfile);
608 putc(mode & 0001
609 ? mode & 01000 ? 't' : 'x'
610 : mode & 01000 ? 'T' : '-', msgfile);
611 }
612
613
614 /* from_oct - quick and dirty octal conversion
615 *
616 * DESCRIPTION
617 *
618 * From_oct will convert an ASCII representation of an octal number
619 * to the numeric representation. The number of characters to convert
620 * is given by the parameter "digs". If there are less numbers than
621 * specified by "digs", then the routine returns -1.
622 *
623 * PARAMETERS
624 *
625 * int digs - Number to of digits to convert
626 * char *where - Character representation of octal number
627 *
628 * RETURNS
629 *
630 * The value of the octal number represented by the first digs
631 * characters of the string where. Result is -1 if the field
632 * is invalid (all blank, or nonoctal).
633 *
634 * ERRORS
635 *
636 * If the field is all blank, then the value returned is -1.
637 *
638 */
639
640 #ifdef __STDC__
641
from_oct(int digs,char * where)642 static long from_oct(int digs, char *where)
643
644 #else
645
646 static long from_oct(digs, where)
647 int digs; /* number of characters to convert */
648 char *where; /* character representation of octal number */
649
650 #endif
651 {
652 long value;
653
654 while (isspace(*where)) { /* Skip spaces */
655 where++;
656 if (--digs <= 0) {
657 return(-1); /* All blank field */
658 }
659 }
660 value = 0;
661 while (digs > 0 && ISODIGIT(*where)) { /* Scan til nonoctal */
662 value = (value << 3) | (*where++ - '0');
663 --digs;
664 }
665
666 if (digs > 0 && *where && !isspace(*where)) {
667 return(-1); /* Ended on non-space/nul */
668 }
669 return(value);
670 }
671