Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/* $Source: /u/mark/src/pax/RCS/pax.c,v $
 *
 * $Revision: 1.2 $
 *
 * DESCRIPTION
 *
 *      Pax is the archiver described in IEEE P1003.2.  It is an archiver
 *      which understands both tar and cpio archives and has a new interface.
 *
 * SYNOPSIS
 *
 *      pax -[cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...]
 *      pax -r [-cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...]
 *      pax -w [-adimuvy] [-b blocking] [-f archive] [-s replstr]...]
 *             [-t device][-x format][pathname...]
 *      pax -r -w [-ilmopuvy][-s replstr][pathname...] directory
 *
 * DESCRIPTION
 *
 *      PAX - POSIX conforming tar and cpio archive handler.  This
 *      program implements POSIX conformant versions of tar, cpio and pax
 *      archive handlers for UNIX.  These handlers have defined befined
 *      by the IEEE P1003.2 commitee.
 *
 * COMPILATION
 *
 *      A number of different compile time configuration options are
 *      available, please see the Makefile and config.h for more details.
 *
 * 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:        pax.c,v $
 * Revision 1.2  89/02/12  10:05:17  mark
 * 1.2 release fixes
 * 
 * Revision 1.1  88/12/23  18:02:23  mark
 * Initial revision
 * 
 */

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


/* Headers */

#define NO_EXTERN
#include "pax.h"


/* Globally Available Identifiers */

char           *ar_file;                /* File containing name of archive */
char           *bufend;                 /* End of data within archive buffer */
char           *bufstart;               /* Archive buffer */
char           *bufidx;                 /* Archive buffer index */
char           *myname;                 /* name of executable (argv[0]) */
char          **n_argv;                 /* Argv used by name routines */
int             n_argc;                 /* Argc used by name routines */
int             archivefd;              /* Archive file descriptor */
int             blocking;               /* Size of each block, in records */
int             gid;                    /* Group ID */
int             head_standard;          /* true if archive is POSIX format */
int             ar_interface;           /* defines interface we are using */
int             ar_format;              /* defines current archve format */
int             mask;                   /* File creation mask */
int             ttyf;                   /* For interactive queries */
int             uid;                    /* User ID */
int             names_from_stdin;       /* names for files are from stdin */
OFFSET          total;                  /* Total number of bytes transferred */
short           f_access_time;          /* Reset access times of input files */
short           areof;                  /* End of input volume reached */
short           f_dir_create;           /* Create missing directories */
short           f_append;               /* Add named files to end of archive */
short           f_create;               /* create a new archive */
short           f_extract;              /* Extract named files from archive */
short           f_follow_links;         /* follow symbolic links */
short           f_interactive;          /* Interactivly extract files */
short           f_linksleft;            /* Report on unresolved links */
short           f_list;                 /* List files on the archive */
short           f_modified;             /* Don't restore modification times */
short           f_verbose;              /* Turn on verbose mode */
short           f_link;                 /* link files where possible */
short           f_owner;                /* extract files as the user */
short           f_pass;                 /* pass files between directories */
short           f_newer;                /* append files to archive if newer */
short           f_disposition;          /* ask for file disposition */
short           f_reverse_match;        /* Reverse sense of pattern match */
short           f_mtime;                /* Retain file modification time */
short           f_unconditional;        /* Copy unconditionally */
time_t          now = 0;                /* Current time */
uint            arvolume;               /* Volume number */
uint            blocksize = BLOCKSIZE;  /* Archive block size */
FILE           *msgfile;                /* message outpu file stdout/stderr */
Replstr        *rplhead = (Replstr *)NULL;      /*  head of replstr list */
Replstr        *rpltail;                /* pointer to tail of replstr list */


/* Function Prototypes */

#ifdef __STDC__

static void     usage(void);
static OFFSET   pax_optsize(char *);

#else /* !__STDC__ */

static void     usage();
static OFFSET   pax_optsize();

#endif /* __STDC__ */


/* main - main routine for handling all archive formats.
 *
 * DESCRIPTION
 *
 *      Set up globals and call the proper interface as specified by the user.
 *
 * PARAMETERS
 *
 *      int argc        - count of user supplied arguments
 *      char **argv     - user supplied arguments 
 *
 * RETURNS
 *
 *      Returns an exit code of 0 to the parent process.
 */

#ifdef __STDC__

int main(int argc, char **argv)

#else

int main(argc, argv)
int             argc;
char          **argv;

#endif
{
    /* strip the pathname off of the name of the executable */
    if ((myname = strrchr(argv[0], '/')) != (char *)NULL) {
        myname++;
    } else {
        myname = argv[0];
    }

    /* set upt for collecting other command line arguments */
    name_init(argc, argv);

    /* get all our necessary information */
    mask = umask(0);
    uid = getuid();
    gid = getgid();
    now = time((time_t *) 0);

    /* open terminal for interactive queries */
    ttyf = open_tty();

    if (strcmp(myname, "tar")==0) {
        do_tar(argc, argv);
    } else if (strcmp(myname, "cpio")==0) {
        do_cpio(argc, argv);
    } else {
        do_pax(argc, argv);
    }
    exit(0);
    /* NOTREACHED */
}


/* do_pax - provide a PAX conformant user interface for archive handling
 *
 * DESCRIPTION
 *
 *      Process the command line parameters given, doing some minimal sanity
 *      checking, and then launch the specified archiving functions.
 *
 * PARAMETERS
 *
 *    int ac            - A count of arguments in av.  Should be passed argc 
 *                        from main
 *    char **av         - A pointer to an argument list.  Should be passed 
 *                        argv from main
 *
 * RETURNS
 *
 *    Normally returns 0.  If an error occurs, -1 is returned 
 *    and state is set to reflect the error.
 *
 */

#ifdef __STDC__

int do_pax(int ac, char **av)

#else

int do_pax(ac, av)
int             ac;             /* argument counter */
char          **av;             /* arguments */

#endif
{
    int             c;
    char           *dirname;
    Stat            st;

    /* default input/output file for PAX is STDIN/STDOUT */
    ar_file = "-";

    /*
     * set up the flags to reflect the default pax inteface.  Unfortunately
     * the pax interface has several options which are completely opposite
     * of the tar and/or cpio interfaces...
     */
    f_unconditional = 1;
    f_mtime = 1;
    f_dir_create = 1;
    f_list = 1;
    blocksize = 0;
    blocking = 0;
    ar_interface = PAX;
    ar_format = TAR;    /* default interface if none given for -w */
    msgfile=stdout;

    while ((c = getopt(ac, av, "ab:cdf:ilmoprs:t:uvwx:y")) != EOF) {
        switch (c) {
        case 'a':
            f_append = 1;
            f_list = 0;
            break;
        case 'b':
            if ((blocksize = pax_optsize(optarg)) == 0) {
                fatal("Bad block size");
            }
            break;
        case 'c':
            f_reverse_match = 1;
            break;
        case 'd':
            f_dir_create = 0;
            break;
        case 'f':
            if (blocksize == 0) {
                blocking = 1;
                blocksize = 1 * BLOCKSIZE;
            }
            ar_file = optarg;
            break;
        case 'i':
            f_interactive = 1;
            break;
        case 'l':
            f_link = 1;
            break;
        case 'm':
            f_mtime = 0;
            break;
        case 'o':
            f_owner = 1;
            break;
        case 'p':
            f_access_time = 1;
            break;
        case 'r':
            if (f_create) {
                f_create = 0;
                f_pass = 1;
            } else {
                f_list = 0;
                f_extract = 1;
            } 
            msgfile=stderr;
            break;
        case 's':
            add_replstr(optarg);
            break;
        case 't':
            if (blocksize == 0) {
                blocking = 1;
                blocksize = 10 * BLOCKSIZE;
            }
            ar_file = optarg;
            break;
        case 'u':
            f_unconditional = 1;
            break;
        case 'v':
            f_verbose = 1;
            break;
        case 'w':
            if (f_extract) {
                f_extract = 0;
                f_pass = 1;
            } else {
                f_list = 0;
                f_create = 1;
            } 
            msgfile=stderr;
            break;
        case 'x':
            if (strcmp(optarg, "ustar") == 0) {
                ar_format = TAR;
            } else if (strcmp(optarg, "cpio") == 0) {
                ar_format = CPIO;
            } else {
                usage();
            }
            break;
        case 'y':
            f_disposition = 1;
            break;
        default:
            usage();
        }
    }

    if (blocksize == 0) {
        blocking = 1;
        blocksize = blocking * BLOCKSIZE;
    }
    buf_allocate((OFFSET) blocksize);

    if (f_extract || f_list) {
        open_archive(AR_READ);
        get_archive_type();
        read_archive();
    } else if (f_create) {
        if (optind >= n_argc) {
            names_from_stdin++;         /* args from stdin */
        }
        open_archive(AR_WRITE);
        create_archive();
    } else if (f_append) {
        open_archive(AR_APPEND);
        get_archive_type();
        append_archive();
    } else if (f_pass && optind < n_argc) {
        dirname = n_argv[--n_argc];
        if (LSTAT(dirname, &st) < 0) {
            fatal(strerror());
        }
        if ((st.sb_mode & S_IFMT) != S_IFDIR) {
            fatal("Not a directory");
        }
        if (optind >= n_argc) {
            names_from_stdin++;         /* args from stdin */
        }
        pass(dirname);
    } else {
        usage();
    }

    return (0);
}


/* get_archive_type - determine input archive type from archive header
 *
 * DESCRIPTION
 *
 *      reads the first block of the archive and determines the archive 
 *      type from the data.  If the archive type cannot be determined, 
 *      processing stops, and a 1 is returned to the caller.  If verbose
 *      mode is on, then the archive type will be printed on the standard
 *      error device as it is determined.
 *
 * FIXME 
 *
 *      be able to understand TAR and CPIO magic numbers
 */

#ifdef __STDC__

void get_archive_type(void)

#else

void get_archive_type()

#endif
{
    if (ar_read() != 0) {
        fatal("Unable to determine archive type.");
    }
    if (strncmp(bufstart, "070707", 6) == 0) {
        ar_format = CPIO;
        if (f_verbose) {
            fputs("CPIO format archive\n", stderr);
        }
    } else if (strncmp(&bufstart[257], "ustar", 5) == 0) {
        ar_format = TAR;
        if (f_verbose) {
            fputs("USTAR format archive\n", stderr);
        }
    } else {
        ar_format = TAR;
    }
}


/* pax_optsize - interpret a size argument
 *
 * DESCRIPTION
 *
 *      Recognizes suffixes for blocks (512-bytes), k-bytes and megabytes.  
 *      Also handles simple expressions containing '+' for addition.
 *
 * PARAMETERS
 *
 *    char      *str    - A pointer to the string to interpret
 *
 * RETURNS
 *
 *    Normally returns the value represented by the expression in the 
 *    the string.
 *
 * ERRORS
 *
 *      If the string cannot be interpretted, the program will fail, since
 *      the buffering will be incorrect.
 *
 */

#ifdef __STDC__

static OFFSET pax_optsize(char *str)

#else

static OFFSET pax_optsize(str)
char           *str;            /* pointer to string to interpret */

#endif
{
    char           *idx;
    OFFSET          number;     /* temporary storage for current number */
    OFFSET          result;     /* cumulative total to be returned to caller */

    result = 0;
    idx = str;
    for (;;) {
        number = 0;
        while (*idx >= '0' && *idx <= '9')
            number = number * 10 + *idx++ - '0';
        switch (*idx++) {
        case 'b':
            result += number * 512L;
            continue;
        case 'k':
            result += number * 1024L;
            continue;
        case 'm':
            result += number * 1024L * 1024L;
            continue;
        case '+':
            result += number;
            continue;
        case '\0':
            result += number;
            break;
        default:
            break;
        }
        break;
    }
    if (*--idx) {
        fatal("Unrecognizable value");
    }
    return (result);
}


/* usage - print a helpful message and exit
 *
 * DESCRIPTION
 *
 *      Usage prints out the usage message for the PAX interface and then
 *      exits with a non-zero termination status.  This is used when a user
 *      has provided non-existant or incompatible command line arguments.
 *
 * RETURNS
 *
 *      Returns an exit status of 1 to the parent process.
 *
 */

#ifdef __STDC__

static void usage(void)

#else

static void usage()

#endif
{
    fprintf(stderr, "Usage: %s -[cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...]\n",
        myname);
    fprintf(stderr, "       %s -r [-cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...]\n",
        myname);
    fprintf(stderr, "       %s -w [-adimuvy] [-b blocking] [-f archive] [-s replstr]\n              [-t device] [-x format] [pathname...]\n",
        myname);
    fprintf(stderr, "       %s -r -w [-ilmopuvy] [-s replstr] [pathname...] directory\n",
        myname);
    exit(1);
}