Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/* $Source: /u/mark/src/pax/RCS/list.c,v $
 *
 * $Revision: 1.2 $
 *
 * list.c - List all files on an archive
 *
 * DESCRIPTION
 *
 *      These function are needed to support archive table of contents and
 *      verbose mode during extraction and creation of achives.
 *
 * AUTHOR
 *
 *      Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
 *
 * Sponsored by The USENIX Association for public distribution. 
 *
 * Copyright (c) 1989 Mark H. Colburn.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice is duplicated in all such 
 * forms and that any documentation, advertising materials, and other 
 * materials related to such distribution and use acknowledge that the 
 * software was developed * by Mark H. Colburn and sponsored by The 
 * USENIX Association. 
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * $Log:        list.c,v $
 * Revision 1.2  89/02/12  10:04:43  mark
 * 1.2 release fixes
 * 
 * Revision 1.1  88/12/23  18:02:14  mark
 * Initial revision
 * 
 */

#ifndef lint
static char *ident = "$Id: list.c,v 1.2 89/02/12 10:04:43 mark Exp $";
static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
#endif /* ! lint */


/* Headers */

#include "pax.h"


/* Defines */

/*
 * isodigit returns non zero iff argument is an octal digit, zero otherwise
 */
#define ISODIGIT(c)     (((c) >= '0') && ((c) <= '7'))


/* Function Prototypes */

#ifdef __STDC__

static void cpio_entry(char *, Stat *);
static void tar_entry(char *, Stat *);
static void pax_entry(char *, Stat *);
static void print_mode(ushort);
static long from_oct(int digs, char *where);

#else /* !__STDC__ */

static void cpio_entry();
static void tar_entry();
static void pax_entry();
static void print_mode();
static long from_oct();

#endif /* __STDC__ */


/* Internal Identifiers */

static char       *monnames[] = {
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};


/* read_header - read a header record
 *
 * DESCRIPTION
 *
 *      Read a record that's supposed to be a header record. Return its 
 *      address in "head", and if it is good, the file's size in 
 *      asb->sb_size.  Decode things from a file header record into a "Stat". 
 *      Also set "head_standard" to !=0 or ==0 depending whether header record 
 *      is "Unix Standard" tar format or regular old tar format. 
 *
 * PARAMETERS
 *
 *      char   *name            - pointer which will contain name of file
 *      Stat   *asb             - pointer which will contain stat info
 *
 * RETURNS
 *
 *      Return 1 for success, 0 if the checksum is bad, EOF on eof, 2 for a 
 *      record full of zeros (EOF marker). 
 */

#ifdef __STDC__

int read_header(char *name, Stat *asb)

#else
    
int read_header(name, asb)
char           *name;
Stat           *asb;

#endif
{
    int             i;
    long            sum;
    long            recsum;
    Link           *link;
    char           *p;
    char            hdrbuf[BLOCKSIZE];

    memset((char *)asb, 0, sizeof(Stat));
    /* read the header from the buffer */
    if (buf_read(hdrbuf, BLOCKSIZE) != 0) {
        return (EOF);
    }

    strcpy(name, hdrbuf);

    recsum = from_oct(8, &hdrbuf[148]);
    sum = 0;
    p = hdrbuf;
    for (i = 0 ; i < 500; i++) {

        /*
         * We can't use unsigned char here because of old compilers, e.g. V7. 
         */
        sum += 0xFF & *p++;
    }

    /* Adjust checksum to count the "chksum" field as blanks. */
    for (i = 0; i < 8; i++) {
        sum -= 0xFF & hdrbuf[148 + i];
    }
    sum += ' ' * 8;

    if (sum == 8 * ' ') {

        /*
         * This is a zeroed record...whole record is 0's except for the 8
         * blanks we faked for the checksum field. 
         */
        return (2);
    }
    if (sum == recsum) {
        /*
         * Good record.  Decode file size and return. 
         */
        if (hdrbuf[156] != LNKTYPE) {
            asb->sb_size = from_oct(1 + 12, &hdrbuf[124]);
        }
        asb->sb_mtime = from_oct(1 + 12, &hdrbuf[136]);
        asb->sb_mode = from_oct(8, &hdrbuf[100]);

        if (strcmp(&hdrbuf[257], TMAGIC) == 0) {
            /* Unix Standard tar archive */
            head_standard = 1;
#ifdef NONAMES
            asb->sb_uid = from_oct(8, &hdrbuf[108]);
            asb->sb_gid = from_oct(8, &hdrbuf[116]);
#else
            asb->sb_uid = finduid(&hdrbuf[265]);
            asb->sb_gid = findgid(&hdrbuf[297]);
#endif
            switch (hdrbuf[156]) {
            case BLKTYPE:
            case CHRTYPE:
#ifndef _POSIX_SOURCE
                asb->sb_rdev = makedev(from_oct(8, &hdrbuf[329]),
                                      from_oct(8, &hdrbuf[337]));
#endif
                break;
            default:
                /* do nothing... */
                break;
            }
        } else {
            /* Old fashioned tar archive */
            head_standard = 0;
            asb->sb_uid = from_oct(8, &hdrbuf[108]);
            asb->sb_gid = from_oct(8, &hdrbuf[116]);
        }

        switch (hdrbuf[156]) {
        case REGTYPE:
        case AREGTYPE:
            /*
             * Berkeley tar stores directories as regular files with a
             * trailing /
             */
            if (name[strlen(name) - 1] == '/') {
                name[strlen(name) - 1] = '\0';
                asb->sb_mode |= S_IFDIR;
            } else {
                asb->sb_mode |= S_IFREG;
            }
            break;
        case LNKTYPE:
            asb->sb_nlink = 2;
            linkto(&hdrbuf[157], asb);
            linkto(name, asb);
            asb->sb_mode |= S_IFREG;
            break;
        case BLKTYPE:
            asb->sb_mode |= S_IFBLK;
            break;
        case CHRTYPE:
            asb->sb_mode |= S_IFCHR;
            break;
        case DIRTYPE:
            asb->sb_mode |= S_IFDIR;
            break;
#ifdef S_IFLNK
        case SYMTYPE:
            asb->sb_mode |= S_IFLNK;
            strcpy(asb->sb_link, &hdrbuf[157]);
            break;
#endif
#ifdef S_IFIFO
        case FIFOTYPE:
            asb->sb_mode |= S_IFIFO;
            break;
#endif
#ifdef S_IFCTG
        case CONTTYPE:
            asb->sb_mode |= S_IFCTG;
            break;
#endif
        }
        return (1);
    }
    return (0);
}


/* print_entry - print a single table-of-contents entry
 *
 * DESCRIPTION
 * 
 *      Print_entry prints a single line of file information.  The format
 *      of the line is the same as that used by the LS command.  For some
 *      archive formats, various fields may not make any sense, such as
 *      the link count on tar archives.  No error checking is done for bad
 *      or invalid data.
 *
 * PARAMETERS
 *
 *      char   *name            - pointer to name to print an entry for
 *      Stat   *asb             - pointer to the stat structure for the file
 */

#ifdef __STDC__

void print_entry(char *name, Stat *asb)

#else
    
void print_entry(name, asb)
char            *name;
Stat            *asb;

#endif
{
    switch (ar_interface) {
    case TAR:
        tar_entry(name, asb);
        break;
    case CPIO:
        cpio_entry(name, asb);
        break;
    case PAX: pax_entry(name, asb);
        break;
    }
}


/* cpio_entry - print a verbose cpio-style entry
 *
 * DESCRIPTION
 *
 *      Print_entry prints a single line of file information.  The format
 *      of the line is the same as that used by the traditional cpio 
 *      command.  No error checking is done for bad or invalid data.
 *
 * PARAMETERS
 *
 *      char   *name            - pointer to name to print an entry for
 *      Stat   *asb             - pointer to the stat structure for the file
 */

#ifdef __STDC__

static void cpio_entry(char *name, Stat *asb)

#else
    
static void cpio_entry(name, asb)
char           *name;
Stat           *asb;

#endif
{
    struct tm          *atm;
    Link               *from;
    struct passwd      *pwp;
    struct group       *grp;

    if (f_list && f_verbose) {
        fprintf(msgfile, "%-7o", asb->sb_mode);
        atm = localtime(&asb->sb_mtime);
        if (pwp = getpwuid((int) USH(asb->sb_uid))) {
            fprintf(msgfile, "%-6s", pwp->pw_name);
        } else {
            fprintf(msgfile, "%-6u", USH(asb->sb_uid));
        }
        fprintf(msgfile,"%7ld  %3s %2d %02d:%02d:%02d %4d  ",
                       asb->sb_size, monnames[atm->tm_mon], 
                       atm->tm_mday, atm->tm_hour, atm->tm_min, 
                       atm->tm_sec, atm->tm_year + 1900);
    }
    fprintf(msgfile, "%s", name);
    if ((asb->sb_nlink > 1) && (from = islink(name, asb))) {
        fprintf(msgfile, " linked to %s", from->l_name);
    }
#ifdef  S_IFLNK
    if ((asb->sb_mode & S_IFMT) == S_IFLNK) {
        fprintf(msgfile, " symbolic link to %s", asb->sb_link);
    }
#endif  /* S_IFLNK */
    putc('\n', msgfile);
}


/* tar_entry - print a tar verbose mode entry
 *
 * DESCRIPTION
 *
 *      Print_entry prints a single line of tar file information.  The format
 *      of the line is the same as that produced by the traditional tar 
 *      command.  No error checking is done for bad or invalid data.
 *
 * PARAMETERS
 *
 *      char   *name            - pointer to name to print an entry for
 *      Stat   *asb             - pointer to the stat structure for the file
 */

#ifdef __STDC__

static void tar_entry(char *name, Stat *asb)

#else
    
static void tar_entry(name, asb)
char            *name;
Stat            *asb;

#endif
{
    struct tm          *atm;
    int                 i;
    int                 mode;
    char               *symnam = "NULL";
    Link               *link;

    if ((mode = asb->sb_mode & S_IFMT) == S_IFDIR) {
        return;                 /* don't print directories */
    }
    if (f_extract) {
        switch (mode) {
#ifdef S_IFLNK
        case S_IFLNK:   /* This file is a symbolic link */
            i = readlink(name, symnam, PATH_MAX - 1);
            if (i < 0) {                /* Could not find symbolic link */
                warn("can't read symbolic link", strerror());
            } else {            /* Found symbolic link filename */
                symnam[i] = '\0';
                fprintf(msgfile, "x %s symbolic link to %s\n", name, symnam);
            }
            break;
#endif
        case S_IFREG:   /* It is a link or a file */
            if ((asb->sb_nlink > 1) && (link = islink(name, asb))) {
                fprintf(msgfile, "%s linked to %s\n", name, link->l_name); 
            } else {
                fprintf(msgfile, "x %s, %ld bytes, %d tape blocks\n", 
                        name, asb->sb_size, ROUNDUP(asb->sb_size, 
                        BLOCKSIZE) / BLOCKSIZE);
            }
        }
    } else if (f_append || f_create) {
        switch (mode) {
#ifdef S_IFLNK
        case S_IFLNK:   /* This file is a symbolic link */
            i = readlink(name, symnam, PATH_MAX - 1);
            if (i < 0) {                /* Could not find symbolic link */
                warn("can't read symbolic link", strerror());
            } else {            /* Found symbolic link filename */
                symnam[i] = '\0';
                fprintf(msgfile, "a %s symbolic link to %s\n", name, symnam);
            }
            break;
#endif
        case S_IFREG:   /* It is a link or a file */
            fprintf(msgfile, "a %s ", name);
            if ((asb->sb_nlink > 1) && (link = islink(name, asb))) {
                fprintf(msgfile, "link to %s\n", link->l_name); 
            } else {
                fprintf(msgfile, "%ld Blocks\n", 
                        ROUNDUP(asb->sb_size, BLOCKSIZE) / BLOCKSIZE);
            }
            break;
        }
    } else if (f_list) {
        if (f_verbose) {
            atm = localtime(&asb->sb_mtime);
            print_mode(asb->sb_mode);
            fprintf(msgfile," %d/%d %6d %3s %2d %02d:%02d %4d %s",
                    asb->sb_uid, asb->sb_gid, asb->sb_size,
                    monnames[atm->tm_mon], atm->tm_mday, atm->tm_hour, 
                    atm->tm_min, atm->tm_year + 1900, name);
        } else {
            fprintf(msgfile, "%s", name);
        }
        switch (mode) {
#ifdef S_IFLNK
        case S_IFLNK:   /* This file is a symbolic link */
            i = readlink(name, symnam, PATH_MAX - 1);
            if (i < 0) {                /* Could not find symbolic link */
                warn("can't read symbolic link", strerror());
            } else {            /* Found symbolic link filename */
                symnam[i] = '\0';
                fprintf(msgfile, " symbolic link to %s", symnam);
            }
            break;
#endif
        case S_IFREG:   /* It is a link or a file */
            if ((asb->sb_nlink > 1) && (link = islink(name, asb))) {
                fprintf(msgfile, " linked to %s", link->l_name);
            }
            break;              /* Do not print out directories */
        }
        fputc('\n', msgfile);
    } else {
        fprintf(msgfile, "? %s %ld blocks\n", name,
                ROUNDUP(asb->sb_size, BLOCKSIZE) / BLOCKSIZE);
    }
}


/* pax_entry - print a verbose cpio-style entry
 *
 * DESCRIPTION
 *
 *      Print_entry prints a single line of file information.  The format
 *      of the line is the same as that used by the LS command.  
 *      No error checking is done for bad or invalid data.
 *
 * PARAMETERS
 *
 *      char   *name            - pointer to name to print an entry for
 *      Stat   *asb             - pointer to the stat structure for the file
 */

#ifdef __STDC__

static void pax_entry(char *name, Stat *asb)

#else
    
static void pax_entry(name, asb)
char           *name;
Stat           *asb;

#endif
{
    struct tm          *atm;
    Link               *from;
    struct passwd      *pwp;
    struct group       *grp;

    if (f_list && f_verbose) {
        print_mode(asb->sb_mode);
        fprintf(msgfile, " %2d", asb->sb_nlink);
        atm = localtime(&asb->sb_mtime);
        if (pwp = getpwuid((int) USH(asb->sb_uid))) {
            fprintf(msgfile, " %-8s", pwp->pw_name);
        } else {
            fprintf(msgfile, " %-8u", USH(asb->sb_uid));
        }
        if (grp = getgrgid((int) USH(asb->sb_gid))) {
            fprintf(msgfile, " %-8s", grp->gr_name);
        } else {
            fprintf(msgfile, " %-8u", USH(asb->sb_gid));
        }
        switch (asb->sb_mode & S_IFMT) {
        case S_IFBLK:
        case S_IFCHR:
            fprintf(msgfile, "\t%3d, %3d",
                           major(asb->sb_rdev), minor(asb->sb_rdev));
            break;
        case S_IFREG:
            fprintf(msgfile, "\t%8ld", asb->sb_size);
            break;
        default:
            fprintf(msgfile, "\t        ");
        }
        fprintf(msgfile," %3s %2d %02d:%02d ",
                monnames[atm->tm_mon], atm->tm_mday, 
                atm->tm_hour, atm->tm_min);
    }
    fprintf(msgfile, "%s", name);
    if ((asb->sb_nlink > 1) && (from = islink(name, asb))) {
        fprintf(msgfile, " == %s", from->l_name);
    }
#ifdef  S_IFLNK
    if ((asb->sb_mode & S_IFMT) == S_IFLNK) {
        fprintf(msgfile, " -> %s", asb->sb_link);
    }
#endif  /* S_IFLNK */
    putc('\n', msgfile);
}


/* print_mode - fancy file mode display
 *
 * DESCRIPTION
 *
 *      Print_mode displays a numeric file mode in the standard unix
 *      representation, ala ls (-rwxrwxrwx).  No error checking is done
 *      for bad mode combinations.  FIFOS, sybmbolic links, sticky bits,
 *      block- and character-special devices are supported if supported
 *      by the hosting implementation.
 *
 * PARAMETERS
 *
 *      ushort  mode    - The integer representation of the mode to print.
 */

#ifdef __STDC__

static void print_mode(ushort mode)

#else
    
static void print_mode(mode)
ushort  mode;

#endif
{
    /* Tar does not print the leading identifier... */
    if (ar_interface != TAR) {
        switch (mode & S_IFMT) {
        case S_IFDIR: 
            putc('d', msgfile); 
            break;
#ifdef  S_IFLNK
        case S_IFLNK: 
            putc('l', msgfile); 
            break;
#endif  /* S_IFLNK */
        case S_IFBLK: 
            putc('b', msgfile); 
            break;
        case S_IFCHR: 
            putc('c', msgfile); 
            break;
#ifdef  S_IFIFO
        case S_IFIFO: 
            putc('p', msgfile); 
            break; 
#endif  /* S_IFIFO */ 
        case S_IFREG: 
        default:
            putc('-', msgfile); 
            break;
        }
    }
    putc(mode & 0400 ? 'r' : '-', msgfile);
    putc(mode & 0200 ? 'w' : '-', msgfile);
    putc(mode & 0100
         ? mode & 04000 ? 's' : 'x'
         : mode & 04000 ? 'S' : '-', msgfile);
    putc(mode & 0040 ? 'r' : '-', msgfile);
    putc(mode & 0020 ? 'w' : '-', msgfile);
    putc(mode & 0010
         ? mode & 02000 ? 's' : 'x'
         : mode & 02000 ? 'S' : '-', msgfile);
    putc(mode & 0004 ? 'r' : '-', msgfile);
    putc(mode & 0002 ? 'w' : '-', msgfile);
    putc(mode & 0001
         ? mode & 01000 ? 't' : 'x'
         : mode & 01000 ? 'T' : '-', msgfile);
}


/* from_oct - quick and dirty octal conversion
 *
 * DESCRIPTION
 *
 *      From_oct will convert an ASCII representation of an octal number
 *      to the numeric representation.  The number of characters to convert
 *      is given by the parameter "digs".  If there are less numbers than
 *      specified by "digs", then the routine returns -1.
 *
 * PARAMETERS
 *
 *      int digs        - Number to of digits to convert 
 *      char *where     - Character representation of octal number
 *
 * RETURNS
 *
 *      The value of the octal number represented by the first digs
 *      characters of the string where.  Result is -1 if the field 
 *      is invalid (all blank, or nonoctal). 
 *
 * ERRORS
 *
 *      If the field is all blank, then the value returned is -1.
 *
 */

#ifdef __STDC__

static long from_oct(int digs, char *where)

#else

static long from_oct(digs, where)
int             digs;           /* number of characters to convert */
char           *where;          /* character representation of octal number */

#endif
{
    long            value;

    while (isspace(*where)) {   /* Skip spaces */
        where++;
        if (--digs <= 0) {
            return(-1);         /* All blank field */
        }
    }
    value = 0;
    while (digs > 0 && ISODIGIT(*where)) {      /* Scan til nonoctal */
        value = (value << 3) | (*where++ - '0');
        --digs;
    }

    if (digs > 0 && *where && !isspace(*where)) {
        return(-1);             /* Ended on non-space/nul */
    }
    return(value);
}