xref: /plan9/sys/src/ape/cmd/pax/list.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
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