OSDN Git Service

ar.c was refined.
[lha/olha.git] / ar.c
diff --git a/ar.c b/ar.c
index 1490b28..7e42b8e 100644 (file)
--- a/ar.c
+++ b/ar.c
@@ -2,6 +2,8 @@
        ar.c -- main file
 ***********************************************************/
 
+static char *version = "0.01";
+
 static char *usage =
     "ar -- compression archiver -- written by Haruhiko Okumura\n"
     "  PC-VAN:SCIENCE        CompuServe:74050,1022\n"
@@ -45,35 +47,61 @@ Structure of archive block (low order byte first):
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <utime.h>
+#include <unistd.h>
 #include "ar.h"
 
-struct lzh_header {
-    char filename[1024];
-    int  xnamelen;
-    char method[5];
-    int compsize;
-    int origsize;
-    int ftime;
-    int file_crc;
-    char os_id;
+struct lha_method methods[] = {
+    /* id, dicbit, pbit, maxmatch */
+    /* note: dicbit == 0 means no compress */
+    /*       (1U << pbit) > np(dicbit+1) */
+    {"-lh0-", 0,  0,  0},  /* 0: no compress */
+    {"-lh1-", 12, 0, 60},  /* 1: 2^12 =  4KB dynamic huffman (LHarc) */
+    {"-lh2-", 13, 0,256},  /* 2: 2^13 =  8KB dynamic huffman */
+    {"-lh3-", 13, 0,256},  /* 3: 2^13 =  8KB static huffman */
+    {"-lh4-", 12, 4,256},  /* 4: 2^12 =  4KB static huffman (pos and len)*/
+    {"-lh5-", 13, 4,256},  /* 5: 2^13 =  8KB static huffman (pos and len)*/
+    {"-lh6-", 15, 5,256},  /* 6: 2^15 = 32KB static huffman (pos and len)*/
+    {"-lh7-", 16, 5,256},  /* 7: 2^16 = 64KB static huffman (pos and len)*/
+    {"-lzs-", 11, 0, 17},  /* 8: 2^11 =  2KB (LArc) */
+    {"-lz5-", 12, 0, 17},  /* 9: 2^12 =  4KB (LArc) */
+    {"-lz4-", 0,  0,  0},  /*10: no compress (LArc) */
+    {"-lhd-", 0,  0,  0},  /*11: directory */
 };
 
-#define FNAME_MAX (255 - 25)    /* max strlen(filename) */
+struct lha_opts opts;
 
-int unpackable;                 /* global, set in io.c */
-ulong compsize, origsize;       /* global */
+struct lha_method *
+which_method(char *id)
+{
+    int i;
 
-static uchar headersize;
-static char *temp_name;
+    for (i = 0; i < sizeof(methods)/sizeof(methods[0]); i++) {
+        if (strncmp(id, methods[i].id, sizeof(methods[0].id)) == 0) {
+            return &methods[i];
+        }
+    }
+    return NULL;
+}
 
 static void
 print_usage()
 {
-    puts("usage: ...");
+    printf("%s", usage);
+    exit(0);
+}
+
+static void
+print_version()
+{
+    printf("version %s\n", version);
     exit(0);
 }
 
-static uint
+uint
 ratio(ulong a, ulong b)
 {                               /* [(1000a + [b/2]) / b] */
     int i;
@@ -92,178 +120,26 @@ ratio(ulong a, ulong b)
     return (uint) ((a + (b >> 1)) / b);
 }
 
-static void
-put_to_header(char *buf, int i, int n, ulong x)
-{
-    while (--n >= 0) {
-        buf[i++] = (uchar) ((uint) x & 0xFF);
-        x >>= 8;
-    }
-}
-
-static ulong
-get_from_header(char *buf, int i, int n)
-{
-    ulong s;
-
-    s = 0;
-    while (--n >= 0)
-        s = (s << 8) + buf[i + n];   /* little endian */
-    return s;
-}
-
-static uint
-calc_headersum(char *buf, int size)
-{
-    int i;
-    uint s;
-
-    s = 0;
-    for (i = 0; i < size; i++)
-        s += buf[i];
-    return s & 0xFF;
-}
-
-int
-get_byte(char *buf)
-{
-    return *(unsigned char*)buf;
-}
-
-uint16_t
-get_word(char *buf)
-{
-    return get_byte(buf) | (get_byte(buf+1) << 8);
-}
-
-uint32_t
-get_dword(char *buf)
-{
-    return get_byte(buf) |
-        (get_byte(buf+1) << 8) |
-        (get_byte(buf+2) << 16) |
-        (get_byte(buf+3) << 24);
-}
-
 void
-get_char(char *buf, char *p, size_t size)
-{
-    memcpy(p, buf, size);
-}
-
-void
-put_byte(char *buf, int c)
-{
-    *buf = (unsigned char)(c & 0xff);
-}
-
-void
-put_word(char *buf, uint16_t c)
-{
-    put_byte(buf,   c);
-    put_byte(buf+1, c>>8);
-}
-
-void
-put_dword(char *buf, uint32_t c)
-{
-    put_byte(buf, c);
-    put_byte(buf+1, c>>8);
-    put_byte(buf+2, c>>16);
-    put_byte(buf+3, c>>24);
-}
-
-void
-put_char(char *buf, char *p, size_t size)
-{
-    memcpy(buf, p, size);
-}
-
-static int
-read_header(FILE *fp, struct lzh_header *h, uchar *headersize)
-{
-    int headersum;
-    char buf[4096];
-    int ext_headersize;
-
-    *headersize = (uchar) fgetc(fp);
-    if (*headersize == 0)
-        return 0;               /* end of archive */
-    headersum = (uchar) fgetc(fp);
-    fread_crc(buf, *headersize, fp);     /* CRC not used */
-    if (calc_headersum(buf, *headersize) != headersum)
-        error("Header sum error");
-
-    get_char(&buf[0], h->method, 5);
-    h->compsize = get_dword(&buf[5]);
-    h->origsize = get_dword(&buf[9]);
-    h->ftime    = get_dword(&buf[13]);
-    /* attrib   = get_byte(&buf[17]); */
-    /* level    = get_byte(&buf[18]); */         /* level */
-    h->xnamelen = get_byte(&buf[19]);
-    get_char(&buf[20], h->filename, h->xnamelen);
-    h->file_crc = get_word(&buf[20+h->xnamelen]);
-    h->os_id    = get_byte(&buf[20+h->xnamelen+2]);
-
-    ext_headersize = get_word(&buf[20+h->xnamelen+3]);
-
-    while (ext_headersize != 0) {
-        fprintf(stderr, "There's an extended header of size %u.\n",
-                ext_headersize);
-        h->compsize -= ext_headersize;
-
-        /* skip ext header */
-        if (fseek(arcfile, ext_headersize - 2, SEEK_CUR))
-            error("Can't read");
-        ext_headersize = fgetc(arcfile);
-        ext_headersize += (uint) fgetc(arcfile) << 8;
-    }
-
-    return 1;                   /* success */
-}
-
-
-void
-write_header(FILE *fp, int headersize, struct lzh_header *h)
-{
-    char buf[4096], *p = buf;
-    int sum;
-
-    put_byte(&buf[0], headersize);
-    put_byte(&buf[1], 0); /* dummy */
-    put_char(&buf[2], "-lh5-", 5);
-    put_dword(&buf[7], h->compsize);   /* packed size */
-    put_dword(&buf[11], h->origsize);   /* original size */
-    put_dword(&buf[15], h->ftime);   /* ftime */
-    put_byte(&buf[19], 0x20);   /* attribute */
-    put_byte(&buf[20], 1);  /* level */
-    put_byte(&buf[21], h->xnamelen); /* length of pathname */
-    put_char(&buf[22], h->filename, h->xnamelen);
-    put_word(&buf[22+h->xnamelen], h->file_crc);
-    put_byte(&buf[22+h->xnamelen+2], 'M');
-    put_word(&buf[22+h->xnamelen+3], 0x0000); /* next header size */
-
-    sum = calc_headersum(buf+2, headersize);
-    put_byte(&buf[1], sum);
-
-    fwrite_crc(buf, headersize+2, fp);
-}
-
-static void
 skip(FILE *fp, struct lzh_header *h)
 {
-    fseek(fp, h->compsize, SEEK_CUR);
+    int i;
+    if (opts.archive_to_stdio)
+        for (i = 0; i < h->compsize; i++)
+            fgetc(fp);
+    else
+        fseek(fp, h->compsize, SEEK_CUR);
 }
 
 static void
 copy(FILE *arcfile, FILE *outfile, struct lzh_header *h)
 {
     uint n;
-    uchar buffer[DICSIZ];
+    uchar buffer[MAXDICSIZ];
 
-    write_header(outfile, headersize, h);
+    write_header(outfile, h);
     while (h->compsize != 0) {
-        n = (uint) ((h->compsize > DICSIZ) ? DICSIZ : h->compsize);
+        n = (uint) ((h->compsize > sizeof(buffer)) ? sizeof(buffer) : h->compsize);
         if (fread((char *) buffer, 1, n, arcfile) != n)
             error("Can't read");
         if (fwrite((char *) buffer, 1, n, outfile) != n)
@@ -272,73 +148,6 @@ copy(FILE *arcfile, FILE *outfile, struct lzh_header *h)
     }
 }
 
-static void
-store(void)
-{
-    uint n;
-    uchar buffer[DICSIZ];
-
-    origsize = 0;
-    crc = INIT_CRC;
-    while ((n = fread((char *) buffer, 1, DICSIZ, infile)) != 0) {
-        fwrite_crc(buffer, n, outfile);
-        origsize += n;
-    }
-    compsize = origsize;
-}
-
-static int
-add(int replace_flag, char *filename)
-{
-    long headerpos, arcpos;
-    uint r;
-    struct lzh_header h;
-
-    if ((infile = fopen(filename, "rb")) == NULL) {
-        fprintf(stderr, "Can't open %s\n", filename);
-        return 0;               /* failure */
-    }
-    if (replace_flag) {
-        printf("Replacing %s ", filename);
-        skip(arcfile, &h);
-    }
-    else
-        printf("Adding %s ", filename);
-
-    strcpy(h.filename, filename);
-    h.xnamelen = strlen(filename);
-
-    headersize = 25 + h.xnamelen;
-    memcpy(h.method, "-lh5-", 5);  /* compress */
-
-    headerpos = ftell(outfile);
-    write_header(outfile, headersize, &h);
-    arcpos = ftell(outfile);
-
-    origsize = compsize = 0;
-    unpackable = 0;
-    crc = INIT_CRC;
-    encode();
-    if (unpackable) {
-        h.method[3] = '0';        /* store */
-        rewind(infile);
-        fseek(outfile, arcpos, SEEK_SET);
-        store();
-    }
-    h.file_crc = crc ^ INIT_CRC;
-    fclose(infile);
-
-    h.compsize = compsize;
-    h.origsize = origsize;
-
-    fseek(outfile, headerpos, SEEK_SET);
-    write_header(outfile, headersize, &h);
-    fseek(outfile, 0L, SEEK_END);
-    r = ratio(compsize, origsize);
-    printf(" %d.%d%%\n", r / 10, r % 10);
-    return 1;                   /* success */
-}
-
 int
 get_line(char *s, int n)
 {
@@ -352,80 +161,6 @@ get_line(char *s, int n)
     return i;
 }
 
-static void
-extract(int to_file, struct lzh_header *h)
-{
-    int n, method;
-    uint ext_headersize;
-    uchar buffer[DICSIZ];
-
-    if (to_file) {
-        while ((outfile = fopen(h->filename, "wb")) == NULL) {
-            fprintf(stderr, "Can't open %s\nNew filename: ", h->filename);
-            if (get_line(h->filename, FNAME_MAX) == 0) {
-                fprintf(stderr, "Not extracted\n");
-                skip(arcfile, h);
-                return;
-            }
-            h->xnamelen = strlen(h->filename);
-        }
-        printf("Extracting %s ", h->filename);
-    }
-    else {
-        outfile = stdout;
-        printf("===== %s =====\n", h->filename);
-    }
-    crc = INIT_CRC;
-    method = h->method[3];      /* -lh*5*- */
-    h->method[3] = ' ';
-    if (!strchr("045", method) || memcmp("-lh -", h->method, 5)) {
-        fprintf(stderr, "Unknown method: %u\n", method);
-        skip(arcfile, h);
-    }
-    else {
-        crc = INIT_CRC;
-        if (method != '0')
-            decode_start();
-        while (h->origsize != 0) {
-            n = (uint) ((h->origsize > DICSIZ) ? DICSIZ : h->origsize);
-            if (method != '0')
-                decode(n, buffer);
-            else if (fread((char *) buffer, 1, n, arcfile) != n)
-                error("Can't read");
-            fwrite_crc(buffer, n, outfile);
-            if (outfile != stdout)
-                putc('.', stderr);
-            h->origsize -= n;
-        }
-    }
-    if (to_file)
-        fclose(outfile);
-    else
-        outfile = NULL;
-    printf("\n");
-    if ((crc ^ INIT_CRC) != h->file_crc)
-        fprintf(stderr, "CRC error\n");
-}
-
-static void
-list_start(void)
-{
-    printf("Filename         Original Compressed Ratio CRC Method\n");
-}
-
-static void
-list(struct lzh_header *h)
-{
-    uint r;
-
-    printf("%-14s", h->filename);
-    if (h->xnamelen > 14)
-        printf("\n              ");
-    r = ratio(h->compsize, h->origsize);
-    printf(" %10lu %10lu %u.%03u %04X %5.5s\n",
-           h->origsize, h->compsize, r / 1000, r % 1000, h->file_crc, h->method);
-}
-
 static int
 match(char *s1, char *s2)
 {
@@ -454,33 +189,27 @@ search(int argc, char *argv[], struct lzh_header *h)
     int i;
 
     if (argc == 0)
-        return 1;
+        return -1;
     for (i = 0; i < argc; i++)
-        if (match(h->filename, argv[i]))
-            return 1;
+        if (argv[i] && match(h->filename, argv[i]))
+            return i+1;
     return 0;
 }
 
-static void
-exitfunc(void)
-{
-    fclose(outfile);
-    remove(temp_name);
-}
-
 #include "getopt_long.h"
 
-int
+void
 parse_args(int argc, char **argv)
 {
     int c;
 
     for (;;) {
-        int this_option_optind = optind ? optind : 1;
+        /* int this_option_optind = optind ? optind : 1; */
         int option_index = 0;
 
         enum {
             LHA_OPT_HELP = 128,
+            LHA_OPT_VERSION,
         };
 
         static struct option long_options[] = {
@@ -494,52 +223,393 @@ parse_args(int argc, char **argv)
                non-NULL: getopt_long() return 0, and *flag set val.
             */
             {"help", no_argument, NULL, LHA_OPT_HELP},
+            {"version", no_argument, NULL, LHA_OPT_VERSION},
             {0, 0, 0, 0}
         };
 
-        c = getopt_long(argc, argv, "a", long_options, &option_index);
+        c = getopt_long(argc, argv, "012fgo[567]q[012]vw:z",
+                        long_options, &option_index);
 
-        if (c == -1) break;
+        if (c == -1) break;     /* end of parsing options */
 
         switch (c) {
-        case 0:                 /* set vallue by long option */
+        case '?':
+            print_usage();
+            break;
+        case 0:
+            /* set value by long option */
+            break;
+        case '0': case '1': case '2':
+            /* header level */
+            opts.header_level = c - '0';
+            break;
+        case 'f':
+            opts.force_extract = 1;
+            break;
+        case 'g':
+            opts.generic = 1;
+            opts.header_level = 0;
+            break;
+        case 'o':
+            /* compress method */
+            {
+                int idx = 1;    /* -o means -lh1- method */
+
+                if (optarg)
+                    idx = *optarg - '0'; /* -lh[567]- method */
+
+                opts.method   = &methods[idx];
+            }
+            break;
+        case 'q':
+            /* quiet mode */
+            opts.quiet = 2;     /* -q is equivalent to -q2 */
+            if (optarg)
+                opts.quiet = *optarg - '0';
+            break;
+        case 'v':
+            /* verbose mode */
+            opts.verbose = 1;
+            break;
+
+        case 'w':
+            /* extract directory */
+            if (!optarg)
+                error("extract directory does not specified for `-w'");
+            if (*optarg == '=')
+                optarg++;
+
+            opts.outdir = optarg;
+            break;
+        case 'z':               /* no compress */
+            opts.nocompress = 1;
             break;
         case LHA_OPT_HELP:
             print_usage();
             break;
+        case LHA_OPT_VERSION:
+            print_version();
+            break;
         default:
             break;
         }
     }
 }
 
+FILE *
+open_tempfile()
+{
+    FILE *outfile;
+
+    outfile = tmpfile();
+    if (outfile == NULL)
+        error("Can't open temporary file");
+
+    return outfile;
+}
+
+static void
+op_add(int cmd, char *archive_file, int argc, char **argv)
+{
+    int i, count, found, done;
+    struct lzh_header h;
+    struct lzh_ostream w, *wp;
+    FILE *arcfile = NULL;
+    FILE *outfile = NULL;
+
+    wp = &w;
+
+    count = done = 0;
+
+    outfile = open_tempfile();
+    wp->fp = outfile;
+    wp->buf = 0;
+    if (*argv == 0)
+        error("archived files are not specified.");
+
+    arcfile = fopen(archive_file, "rb");
+    if (arcfile == NULL)
+        error("Can't open archive '%s'", archive_file);
+
+    while (!done && read_header(arcfile, &h)) {
+
+        found = search(argc, argv, &h);
+        if (found>0) {
+            argv[found-1] = 0;
+
+            if (cmd == 'u') {
+                time_t mtime;
+
+                if (file_mtime(h.filename, &mtime) == -1 || h.mtime > mtime) {
+                    copy(arcfile, outfile, &h);
+                    continue;
+                }
+            }
+
+            if (add(wp, 1, h.filename, h.namelen)) {
+                skip(arcfile, &h);
+                count++;
+            }
+            else
+                copy(arcfile, outfile, &h);
+        }
+        else
+            copy(arcfile, outfile, &h);
+    }
+
+    for (i = 0; i < argc; i++) {
+        if (argv[i]) {
+            count++;
+            add(wp, 0, argv[i], strlen(argv[i]));
+        }
+    }
+
+    if (opts.quiet < 2)
+        printf("  %d files\n", count);
+
+    if (count > 0) {
+        fputc(0, outfile);      /* end of archive */
+        if (ferror(outfile))
+            error("Can't write");
+        if (!opts.archive_to_stdio)
+            unlink(archive_file);
+
+        fclose(arcfile);
+        rewind(outfile);
+        if (copy_stream_to_file(outfile, archive_file) == -1)
+            error("fail to copy_stream_to_file(): temp -> %s",archive_file);
+    }
+}
+
+static void
+op_create(int cmd, char *archive_file, int argc, char **argv)
+{
+    int i;
+    struct lzh_ostream w, *wp;
+    FILE *outfile = NULL;
+
+    wp = &w;
+
+    outfile = open_tempfile();
+    wp->fp = outfile;
+    wp->buf = 0;
+    if (*argv == 0)
+        error("archived files are not specified.");
+
+    for (i = 0; i < argc; i++) {
+        add(wp, 0, argv[i], strlen(argv[i]));
+    }
+
+    fputc(0, outfile);      /* end of archive */
+    if (ferror(outfile))
+        error("Can't write");
+    rewind(outfile);
+    if (opts.archive_to_stdio) {
+        if (copy_stream(outfile, stdout) == -1)
+            error("fail to copy_stream_to_file(): temp -> %s","stdout");
+    }
+    else {
+        if (copy_stream_to_file(outfile, archive_file) == -1)
+            error("fail to copy_stream_to_file(): temp -> %s",archive_file);
+    }
+
+    return;
+}
+
+static void
+op_delete(int cmd, char *archive_file, int argc, char **argv)
+{
+    int count, found, done;
+    struct lzh_header h;
+    int arc_count;
+    struct lzh_ostream w, *wp;
+    FILE *arcfile = NULL;
+    FILE *outfile = NULL;
+
+    wp = &w;
+
+    count = done = 0;
+
+    if (argc == 0) {
+        message("No files given in argument, do nothing.");
+        return;
+    }
+    outfile = open_tempfile();
+
+    /* Open archive. */
+    if (opts.archive_to_stdio) {
+        arcfile = stdin;
+    }
+    else {
+        arcfile = fopen(archive_file, "rb");
+    }
+    if (arcfile == NULL)
+        error("Can't open archive '%s'", archive_file);
+
+    arc_count = 0;
+
+    while (!done && read_header(arcfile, &h)) {
+
+        arc_count++;
+
+        found = search(argc, argv, &h);
+        if (found) {
+            count++;
+            message("'%s' deleted", h.filename);
+            skip(arcfile, &h);
+        }
+        else
+            copy(arcfile, outfile, &h);
+    }
+
+    if (opts.quiet < 2)
+        printf("  %d files\n", count);
+
+    if (count > 0) {
+        fputc(0, outfile);      /* end of archive */
+        if (ferror(outfile))
+            error("Can't write");
+        if (!opts.archive_to_stdio)
+            unlink(archive_file);
+
+        fclose(arcfile);
+        rewind(outfile);
+
+        if (arc_count > count) {
+            if (copy_stream_to_file(outfile, archive_file) == -1)
+                error("fail to copy_stream_to_file(): temp -> %s",archive_file);
+        }
+        else {
+            message("The archive file \"%s\" was removed because it would be empty.", archive_file);
+        }
+    }
+}
+
+static void
+op_extract(int cmd, char *archive_file, int argc, char **argv)
+{
+    int count, nfiles, found, done;
+    struct lzh_header h;
+    struct lzh_istream r, *rp;
+    FILE *arcfile = NULL;
+
+    rp = &r;
+
+    count = done = nfiles = 0;
+
+    /* Open archive. */
+    if (opts.archive_to_stdio) {
+        arcfile = stdin;
+    }
+    else {
+        arcfile = fopen(archive_file, "rb");
+    }
+    if (arcfile == NULL)
+        error("Can't open archive '%s'", archive_file);
+
+    /* change directory to extract dir */
+    if (cmd == 'x') {
+        if (opts.outdir) {
+            if (mkdir(opts.outdir, 0777) == -1) {
+                if (errno != EEXIST)
+                    error("cannot make directory \"%s\"", opts.outdir);
+            }
+
+            if (chdir(opts.outdir) == -1)
+                error("cannot change directory \"%s\"", opts.outdir);
+        }
+    }
+
+    while (!done && read_header(arcfile, &h)) {
+
+        found = search(argc, argv, &h);
+        if (found != 0) {
+            rp->fp = arcfile;
+            rp->compsize = h.compsize;
+            extract(rp, cmd == 'x', &h);
+            if (++count == nfiles)
+                done = 1;
+        }
+        else
+            skip(arcfile, &h);
+    }
+
+    if (cmd != 'p') {
+        if (opts.quiet < 2)
+            printf("  %d files\n", count);
+    }
+}
+
+static void
+op_list(int cmd, char *archive_file, int argc, char **argv)
+{
+    int count, nfiles, found, done;
+    struct lzh_header h;
+    struct lzh_istream r, *rp;
+    FILE *arcfile = NULL;
+
+    rp = &r;
+
+    count = done = nfiles = 0;
+
+    /* Open archive. */
+    if (opts.archive_to_stdio) {
+        arcfile = stdin;
+    }
+    else {
+        arcfile = fopen(archive_file, "rb");
+    }
+    if (arcfile == NULL)
+        error("Can't open archive '%s'", archive_file);
+
+    while (!done && read_header(arcfile, &h)) {
+
+        found = search(argc, argv, &h);
+
+        if (found != 0) {
+            if (count == 0)
+                list_start();
+            list(&h);
+            if (++count == nfiles)
+                done = 1;
+        }
+        skip(arcfile, &h);
+    }
+
+    if (opts.quiet < 2)
+        printf("  %d files\n", count);
+}
+
 int
 main(int argc, char *argv[])
 {
-    int i, j, cmd, count, nfiles, found, done;
+    int cmd;
     char *archive_file;
-    struct lzh_header h;
+
+    INITIALIZE_OPTS(opts);
 
     if (argv[1] == 0)
         print_usage();
 
+    /*take a command character */
     {
-        char *str;
+        char *arg1;
 
-        str = argv[1];
-        if (str[0] == '-')
-            str++;
-        if (str[0] == 0)
+        arg1 = argv[1];
+        if (arg1[0] == '-')
+            arg1++;
+        if (arg1[0] == 0)
             print_usage();
 
-        cmd = *str;
-        if (str[1] == 0) {
+        cmd = *arg1;
+        if (arg1[1] == 0) {
+            /* -<cmd> -<opts> ... */
             argv++;
             argc--;
         }
         else {
             /* -<cmd><opts> => -<opts> */
-            *str = '-';
+            *arg1 = '-';
         }
     }
 
@@ -549,110 +619,48 @@ main(int argc, char *argv[])
 
     archive_file = argv[0];
 
-    /* Open archive. */
-    arcfile = fopen(archive_file, "rb");
-
-    if (arcfile == NULL && cmd != 'a')
-        error("Can't open archive '%s'", argv[2]);
+    if (strcmp(archive_file, "-") == 0)
+        opts.archive_to_stdio = 1;
+    if (opts.archive_to_stdio)
+        opts.quiet = 2;
 
     argv++;
     argc--;
 
-    /* Open temporary file. */
-    if (strchr("ard", cmd)) {
-        temp_name = tmpnam(NULL);
-        outfile = fopen(temp_name, "wb");
-        if (outfile == NULL)
-            error("Can't open temporary file");
-        atexit(exitfunc);
-    }
-    else
-        temp_name = NULL;
-
     make_crctable();
-    count = done = 0;
 
     switch (cmd) {
     case 'a':
-        if (*argv == 0)
-            error("archived files are not specified.");
-        for (i = 0; i < argc; i++) {
-            add(0, argv[i]);
+    case 'u':
+        if (opts.archive_to_stdio || !file_exists(archive_file)) {
+            op_create(cmd, archive_file, argc, argv);
         }
+        else {
+            op_add(cmd, archive_file, argc, argv);
+        }
+        break;
 
-        fputc(0, outfile);      /* end of archive */
-        if (ferror(outfile) || fclose(outfile) == EOF)
-            error("Can't write");
-        remove(archive_file);
-        fclose(arcfile);
-        if (rename(temp_name, archive_file) == -1)
-            error("fail to rename()");
+    case 'c':
+        op_create(cmd, archive_file, argc, argv);
+        break;
 
-        exit(0);
+    case 'd':
+        op_delete(cmd, archive_file, argc, argv);
         break;
+
     case 'x':
-    case 'r':
-    case 'd':
     case 'p':
+        op_extract(cmd, archive_file, argc, argv);
+        break;
+
     case 'l':
+    case 'v':
+        op_list(cmd, archive_file, argc, argv);
         break;
+
     default:
         print_usage();
         break;
     }
-
-    while (!done && read_header(arcfile, &h, &headersize)) {
-
-        compsize = h.compsize;
-        origsize = h.origsize;
-
-        found = search(argc, argv, &h);
-        switch (cmd) {
-        case 'r':
-            if (found) {
-                if (add(1, *argv))
-                    count++;
-                else
-                    copy(arcfile, outfile, &h);
-            }
-            else
-                copy(arcfile, outfile, &h);
-            break;
-        case 'a':
-        case 'd':
-            if (found) {
-                count += (cmd == 'D');
-                skip(arcfile, &h);
-            }
-            else
-                copy(arcfile, outfile, &h);
-            break;
-        case 'x':
-        case 'p':
-            if (found) {
-                extract(cmd == 'x', &h);
-                if (++count == nfiles)
-                    done = 1;
-            }
-            else
-                skip(arcfile, &h);
-            break;
-        case 'l':
-            if (found) {
-                if (count == 0)
-                    list_start();
-                list(&h);
-                if (++count == nfiles)
-                    done = 1;
-            }
-            skip(arcfile, &h);
-            break;
-        }
-    }
-
-    if (temp_name != NULL && count != 0) {
-    }
-
-    printf("  %d files\n", count);
     return EXIT_SUCCESS;
 }