]> git.lizzy.rs Git - plan9front.git/blob - sys/src/ape/cmd/pax/list.c
ape: bring strtod() in line with plan9's libc version
[plan9front.git] / sys / src / ape / cmd / pax / list.c
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
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
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
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
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
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
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
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 }