OSDN Git Service

ar.c was refined.
[lha/olha.git] / ar.c
diff --git a/ar.c b/ar.c
index e7ad621..7e42b8e 100644 (file)
--- a/ar.c
+++ b/ar.c
@@ -48,17 +48,16 @@ Structure of archive block (low order byte first):
 #include <string.h>
 #include <ctype.h>
 #include <errno.h>
-#include <dirent.h>
 #include <sys/stat.h>
 #include <time.h>
 #include <utime.h>
+#include <unistd.h>
 #include "ar.h"
 
-extern char *basename(const char *);
-
 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 */
@@ -69,8 +68,8 @@ struct lha_method methods[] = {
     {"-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},  /* 1: no compress (LArc) */
-    {"-lhd-", 0,  0,  0},  /* 1: directory */
+    {"-lz4-", 0,  0,  0},  /*10: no compress (LArc) */
+    {"-lhd-", 0,  0,  0},  /*11: directory */
 };
 
 struct lha_opts opts;
@@ -88,13 +87,6 @@ which_method(char *id)
     return NULL;
 }
 
-#define FNAME_MAX (255 - 25)    /* max strlen(filename) */
-
-int unpackable;                 /* global, set in io.c */
-ulong compsize, origsize;       /* global */
-
-static char *temp_name;
-
 static void
 print_usage()
 {
@@ -109,7 +101,7 @@ print_version()
     exit(0);
 }
 
-static uint
+uint
 ratio(ulong a, ulong b)
 {                               /* [(1000a + [b/2]) / b] */
     int i;
@@ -128,716 +120,7 @@ ratio(ulong a, ulong b)
     return (uint) ((a + (b >> 1)) / b);
 }
 
-static char*
-os_string(char id)
-{
-    int i;
-    static struct os {
-        char id;
-        char *name;
-    } os_types[] = {
-        {'M',  "MS-DOS"},       /* Microsoft */
-        {'U',  "Unix"},         /* Unix or POSIX compliant OS */
-        {'J',  "Java"},         /* Sun Microsystems */
-        {'\0', "generic"},
-        {'w',  "Win9x"},        /* reserved by UNLHA32.DLL */
-        {'W',  "WinNT"},        /* reserved by UNLHA32.DLL */
-        {'2',  "OS/2"},         /* IBM OS/2 */
-        {'9',  "OS9"},          /* unknown */
-        {'K',  "OS/68K"},       /* unknown */
-        {'3',  "OS/386"},       /* unknown */
-        {'H',  "Human"},        /* SHARP Human68K */
-        {'C',  "CP/M"},         /* Digital Research */
-        {'F',  "FLEX"},         /* unknown */
-        {'m',  "Mac"},          /* Apple */
-        {'R',  "Runser"},       /* unknown */
-        {'T',  "TownsOS"},      /* Fujitsu FM-TOWNS */
-        {'X',  "XOSK"},         /* unknown */
-    };
-
-    for (i = 0; i < sizeof(os_types)/sizeof(os_types[0]); i++) {
-        if (id == os_types[i].id)
-            return os_types[i].name;
-    }
-
-    return "Unknown";
-}
-
-static void
-put_header(char *buf, int *i, int n, uint32_t x)
-{
-    while (--n >= 0) {
-        buf[(*i)++] = (uchar) (x & 0xFF);
-        x >>= 8;
-    }
-}
-
-static void
-put_header_tmp(char *buf, int i, int n, uint32_t x)
-{
-    put_header(buf, &i, n, x);
-}
-
-void
-put_string(char *buf, int *n, size_t size, char *p)
-{
-    memcpy(buf + *n, p, size);
-    *n += size;
-}
-
-static uint32_t
-get_header(char *buf, int *i, int size)
-{
-    uint32_t s;
-    int n;
-
-    s = 0;
-    for (n = size-1; n >= 0; n--) {
-        s = (s << 8) + buf[*i + n];   /* little endian */
-    }
-    *i += size;
-    return s;
-}
-
-void
-get_string(char *buf, int *i, size_t size, char *p)
-{
-    memcpy(p, buf + *i, size);
-    *i += size;
-}
-
-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;
-}
-
-#if 0
-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);
-}
-
-#endif
-time_t
-ftime_to_time_t(uint32_t ftime)
-{
-    struct tm tm;
-    /* ftime is time structure on MS-DOS
-
-    32              24              16               8
-     0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
-      |-------------|-------|---------|---------|-----------|---------|
-       year(-1980)   month   day       hour      minute      second/2
-       (1-127)       (1-12)  (1-31)    (0-23)    (0-59)      (0-29)
-    */
-
-    memset(&tm, 0, sizeof(tm));
-    tm.tm_year = (ftime >> 25) + 1980 - 1900;
-    tm.tm_mon  = ((ftime >> 21) & 0x0f) - 1;
-    tm.tm_mday = (ftime >> 16) & 0x1f;
-    tm.tm_hour = (ftime >> 11) & 0x1f;
-    tm.tm_min  = (ftime >>  5) & 0x3f;
-    tm.tm_sec  = (ftime & 0x1f) * 2;
-
-    return mktime(&tm);
-}
-
-uint32_t
-time_t_to_ftime(time_t t)
-{
-    struct tm tm;
-    /* ftime is time structure on MS-DOS
-
-    32              24              16               8
-     0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
-      |-------------|-------|---------|---------|-----------|---------|
-       year(-1980)   month   day       hour      minute      second/2
-       (1-127)       (1-12)  (1-31)    (0-23)    (0-59)      (0-29)
-    */
-
-#if HAVE_LOCALTIME_R
-    localtime_r(&t, &tm);
-#else
-    tm = *localtime(&t);
-#endif
-
-    return (uint32_t)(tm.tm_year + 1900 - 1980) << 25
-        | (uint32_t)(tm.tm_mon + 1) << 21
-        | (uint32_t)(tm.tm_mday)    << 16
-        | (uint32_t)(tm.tm_hour)    << 11
-        | (uint32_t)(tm.tm_min)     <<  5
-        | (uint32_t)(tm.tm_sec / 2);
-}
-
-
-/*
- * level 0 header
- *
- *
- * offset  size  field name
- * ----------------------------------
- *     0      1  header size    [*1]
- *     1      1  header sum
- *            ---------------------------------------
- *     2      5  method ID                         ^
- *     7      4  packed size    [*2]               |
- *    11      4  original size                     |
- *    15      2  time                              |
- *    17      2  date                              |
- *    19      1  attribute                         | [*1] header size (X+Y+22)
- *    20      1  level (0x00 fixed)                |
- *    21      1  name length                       |
- *    22      X  pathname                          |
- * X +22      2  file crc (CRC-16)                 |
- * X +24      Y  ext-header(old style)             v
- * -------------------------------------------------
- * X+Y+24        data                              ^
- *                 :                               | [*2] packed size
- *                 :                               v
- * -------------------------------------------------
- *
- * ext-header(old style)
- *     0      1  ext-type ('U')
- *     1      1  minor version
- *     2      4  UNIX time
- *     6      2  mode
- *     8      2  uid
- *    10      2  gid
- *
- * attribute (MS-DOS)
- *    bit1  read only
- *    bit2  hidden
- *    bit3  system
- *    bit4  volume label
- *    bit5  directory
- *    bit6  archive bit (need to backup)
- *
- */
-static int
-read_header_lv0(FILE *fp, char *buf, struct lzh_header *h)
-{
-    int headersize;
-    int headersum;
-    int ext_headersize;
-    int pos = 0;
-    int ext_size;
-
-    headersize = get_header(buf, &pos, 1);
-    headersum = get_header(buf, &pos, 1);
-
-    fread_crc(buf + 21, headersize - (21 - pos), fp);     /* CRC not used */
-
-    if (calc_headersum(buf+pos, headersize) != headersum)
-        error("Header sum error");
-
-    get_string(buf, &pos, 5, h->method);
-    h->compsize = get_header(buf, &pos, 4);
-    h->origsize = get_header(buf, &pos, 4);
-    h->mtime    = ftime_to_time_t(get_header(buf, &pos, 4));
-    /* attrib   = */ get_header(buf, &pos, 1);
-    h->level    = get_header(buf, &pos, 1); /* header level */
-    h->namelen  = get_header(buf, &pos, 1);
-    get_string(buf, &pos, h->namelen, h->filename);
-    h->filename[h->namelen] = 0;
-    h->file_crc = get_header(buf, &pos, 2);
-    h->os_id    = 0;            /* generic */
-
-    ext_size = headersize - pos;
-
-    if (ext_size > 0) {
-        if (ext_size > 1) {
-            h->os_id = get_header(buf, &pos, 1);
-            ext_size--;
-        }
-        if (ext_size > 1) {
-            get_header(buf, &pos, 1); /* minor version */
-            ext_size--;
-        }
-        if (ext_size > 4) {
-            h->mtime = get_header(buf, &pos, 4);
-            ext_size -= 4;
-        }
-        if (ext_size > 2) {
-            get_header(buf, &pos, 2);         /* mode */
-            ext_size -= 2;
-        }
-        if (ext_size > 2) {
-            get_header(buf, &pos, 2);         /* uid */
-            ext_size -= 2;
-        }
-        if (ext_size > 2) {
-            get_header(buf, &pos, 2);         /* gid */
-            ext_size -= 2;
-        }
-    }
-
-    return 1;                   /* success */
-}
-
-/*
- * level 1 header
- *
- *
- * offset   size  field name
- * -----------------------------------
- *     0       1  header size   [*1]
- *     1       1  header sum
- *             -------------------------------------
- *     2       5  method ID                        ^
- *     7       4  skip size     [*2]               |
- *    11       4  original size                    |
- *    15       2  time                             |
- *    17       2  date                             |
- *    19       1  attribute (0x20 fixed)           | [*1] header size (X+Y+25)
- *    20       1  level (0x01 fixed)               |
- *    21       1  name length                      |
- *    22       X  filename                         |
- * X+ 22       2  file crc (CRC-16)                |
- * X+ 24       1  OS ID                            |
- * X +25       Y  ???                              |
- * X+Y+25      2  next-header size                 v
- * -------------------------------------------------
- * X+Y+27      Z  ext-header                       ^
- *                 :                               |
- * -----------------------------------             | [*2] skip size
- * X+Y+Z+27       data                             |
- *                 :                               v
- * -------------------------------------------------
- *
- */
-static int
-read_header_lv1(FILE *fp, char *buf, struct lzh_header *h)
-{
-    int headersize;
-    int headersum;
-    int ext_headersize;
-    int pos = 0;
-
-    headersize = get_header(buf, &pos, 1);
-    headersum = get_header(buf, &pos, 1);
-
-    fread_crc(buf + 21, headersize - (21 - 2), fp);     /* CRC not used */
-
-    if (calc_headersum(&buf[pos], headersize) != headersum)
-        error("Header sum error");
-
-    get_string(buf, &pos, 5, h->method);
-    h->compsize = get_header(buf, &pos, 4);
-    h->origsize = get_header(buf, &pos, 4);
-    h->mtime    = ftime_to_time_t(get_header(buf, &pos, 4));
-    get_header(buf, &pos, 1);   /* attribute */
-    h->level = get_header(buf, &pos, 1); /* header level */
-    h->namelen = get_header(buf, &pos, 1);
-    get_string(buf, &pos, h->namelen, h->filename);
-    h->filename[h->namelen] = 0;
-    h->file_crc = get_header(buf, &pos, 2);
-    h->os_id = get_header(buf, &pos, 1);
-
-    ext_headersize = get_header(buf, &pos, 2);
-
-    while (ext_headersize != 0) {
-        char extbuf[4096];
-       uchar ext_type;
-        int extpos = 0;
-
-        h->compsize -= ext_headersize;
-
-        if (fread(extbuf, ext_headersize, 1, fp) != 1) {
-            error("can't read ext header");
-        }
-
-        ext_type = get_header(extbuf, &extpos, 1);
-        switch (ext_type) {
-        case 0x54:
-            h->mtime = get_header(extbuf, &extpos, 4);
-            break;
-        default:
-           break;
-        }
-        extpos = ext_headersize - 2;
-        ext_headersize = get_header(extbuf, &extpos, 2);
-    }
-
-    return 1;                   /* success */
-}
-
-/*
- * level 2 header
- *
- *
- * offset   size  field name
- * --------------------------------------------------
- *     0       2  total header size [*1]           ^
- *             -----------------------             |
- *     2       5  method ID                        |
- *     7       4  packed size       [*2]           |
- *    11       4  original size                    |
- *    15       4  time                             |
- *    19       1  RESERVED (0x20 fixed)            | [*1] total header size
- *    20       1  level (0x02 fixed)               |      (X+26+(1))
- *    21       2  file crc (CRC-16)                |
- *    23       1  OS ID                            |
- *    24       2  next-header size                 |
- * -----------------------------------             |
- *    26       X  ext-header                       |
- *                 :                               |
- * -----------------------------------             |
- * X +26      (1) padding                          v
- * -------------------------------------------------
- * X +26+(1)      data                             ^
- *                 :                               | [*2] packed size
- *                 :                               v
- * -------------------------------------------------
- *
- */
-static int
-read_header_lv2(FILE *fp, char *buf, struct lzh_header *h)
-{
-    int headersize;
-    int headersum;
-    int ext_headersize;
-    int remainder;
-    char dirname[1024] = "";
-    int dirnamelen = 0;
-    int pos = 0;
-
-    headersize = get_header(buf, &pos, 2);
-
-    fread_crc(buf + 21, 26 - 21, fp);     /* CRC not used */
-
-    get_string(buf, &pos, 5, h->method);
-    h->compsize = get_header(buf, &pos, 4);
-    h->origsize = get_header(buf, &pos, 4);
-    h->mtime    = get_header(buf, &pos, 4);
-    get_header(buf, &pos, 1);         /* attrib */
-    h->level    = get_header(buf, &pos, 1); /* header level */
-    h->file_crc = get_header(buf, &pos, 2);
-    h->os_id    = get_header(buf, &pos, 1);
-
-    ext_headersize = get_header(buf, &pos, 2);
-
-    remainder = headersize - pos;
-
-    while (ext_headersize != 0) {
-        char extbuf[4096];
-        uchar ext_type;
-        int extpos = 0;
-
-        remainder -= ext_headersize;
-
-        if (fread(extbuf, ext_headersize, 1, fp) != 1) {
-            error("can't read ext header");
-        }
-        ext_type = get_header(extbuf, &extpos, 1);
-        switch (ext_type) {
-        case 0:
-            /* header crc */
-            break;
-        case 1:
-            /* filename header */
-            h->namelen = ext_headersize - 3;
-            get_string(extbuf, &extpos, h->namelen, h->filename);
-            h->filename[h->namelen] = 0;
-            break;
-        case 2:
-            /* dirname header */
-            dirnamelen = ext_headersize - 3;
-            get_string(extbuf, &extpos, dirnamelen, dirname);
-            dirname[dirnamelen] = 0;
-            break;
-        default:
-            break;
-        }
-        extpos = ext_headersize - 2;
-        ext_headersize = get_header(extbuf, &extpos, 2);
-    }
-
-    if (dirnamelen > 0 && dirname[dirnamelen-1] != '/') {
-        dirname[dirnamelen++] = '/';
-    }
-
-    strcat(dirname, h->filename);
-    h->namelen = strlen(dirname);
-    strcpy(h->filename, dirname);
-
-    while (remainder > 0) {
-        fgetc(fp); /* skip padding */
-        remainder--;
-    }
-
-    return 1;                   /* success */
-}
-
-static int
-read_header(FILE *fp, struct lzh_header *h)
-{
-    char buf[4096];
-    int ret;
-
-    ret = fgetc(fp);
-    buf[0] = (uchar)ret;
-    if (buf[0] == 0 || ret == EOF)
-        return 0;               /* end of archive */
-    fread_crc(buf + 1, 21 - 1, fp);
-    switch (buf[20]) {
-    case 0:
-        return read_header_lv0(fp, buf, h);
-        break;
-    case 1:
-        return read_header_lv1(fp, buf, h);
-        break;
-    case 2:
-        return read_header_lv2(fp, buf, h);
-        break;
-    default:
-        error("unknown level (%d)\n", buf[20]);
-        break;
-    }
-
-    return 1;                   /* success */
-}
-
-
-void
-write_header_lv0(FILE *fp, struct lzh_header *h)
-{
-    char buf[4096];
-    int sum;
-    int headersize;
-    int pos = 0;
-
-    headersize = 22 + h->namelen;
-
-    put_header(buf, &pos, 1, 0); /* dummy */
-    put_header(buf, &pos, 1, 0); /* dummy */
-
-    put_string(buf, &pos, 5, h->method);
-    put_header(buf, &pos, 4, h->compsize); /* packed size */
-    put_header(buf, &pos, 4, h->origsize); /* original size */
-    put_header(buf, &pos, 4, time_t_to_ftime(h->mtime)); /* ftime */
-    put_header(buf, &pos, 1, 0x20);     /* attribute */
-    put_header(buf, &pos, 1, 0);        /* level */
-    put_header(buf, &pos, 1, h->namelen); /* length of pathname */
-    put_string(buf, &pos, h->namelen, h->filename);
-    put_header(buf, &pos, 2, h->file_crc);
-
-    if (!opts.generic) {
-        /* extended header for Unix */
-        put_header(buf, &pos, 1, 'U');  /* OS type */
-        put_header(buf, &pos, 1, '\0'); /* minor version */
-        put_header(buf, &pos, 4, h->mtime); /* time_t */
-        put_header(buf, &pos, 2, 0100000);  /* mode */
-        put_header(buf, &pos, 2, 0);  /* uid */
-        put_header(buf, &pos, 2, 0);  /* gid */
-        headersize += pos;            /* size of ext-header (old style) */
-    }
-
-    put_header_tmp(buf, 0, 1, headersize);
-
-    sum = calc_headersum(buf+2, headersize);
-    put_header_tmp(buf, 1, 1, sum);
-
-    fwrite_crc(buf, headersize+2, fp);
-}
-
 void
-write_header_lv1(FILE *fp, struct lzh_header *h)
-{
-    char buf[4096];
-    int sum;
-    int headersize;
-    int extsize = 0;
-    int pos = 0;
-    int extpos;
-
-    headersize = 25 + h->namelen;
-
-    put_header(buf, &pos, 1, headersize);
-    put_header(buf, &pos, 1, 0); /* dummy */
-    put_string(buf, &pos, 5, h->method);
-    put_header(buf, &pos, 4, h->compsize); /* packed size */
-    put_header(buf, &pos, 4, h->origsize); /* original size */
-    put_header(buf, &pos, 4, time_t_to_ftime(h->mtime)); /* ftime */
-    put_header(buf, &pos, 1, 0x20);     /* attribute */
-    put_header(buf, &pos, 1, 1);        /* level */
-    put_header(buf, &pos, 1, h->namelen); /* length of pathname */
-    put_string(buf, &pos, h->namelen, h->filename);
-    put_header(buf, &pos, 2, h->file_crc);
-    if (opts.generic)
-        put_header(buf, &pos, 1, '\0');
-    else
-        put_header(buf, &pos, 1, 'U');
-
-    extpos = pos;
-    put_header(buf, &pos, 2, 7); /* next header size */
-    put_header(buf, &pos, 1, 0x54); /* time stamp */
-    put_header(buf, &pos, 4, h->mtime); /* time_t */
-
-    extsize = pos - extpos;
-    put_header(buf, &pos, 2, 0); /* next header size (end of header) */
-
-    put_header_tmp(buf, 7, 4, h->compsize+extsize);    /* packed size */
-
-    sum = calc_headersum(buf+2, headersize);
-    put_header_tmp(buf, 1, 1, sum);
-
-    fwrite_crc(buf, headersize+2+extsize, fp);
-}
-
-void
-write_header_lv2(FILE *fp, struct lzh_header *h)
-{
-    char buf[4096], *crcptr;
-    int headersize;
-    extern ushort crctable[];
-    char dirname[1024] = "", *fname;
-    int dirnamelen, len;
-    int pos = 0;
-
-    put_header(buf, &pos, 2, 0); /* dummy */
-    put_string(buf, &pos, 5, h->method);
-    put_header(buf, &pos, 4, h->compsize); /* packed size */
-    put_header(buf, &pos, 4, h->origsize); /* original size */
-    put_header(buf, &pos, 4, h->mtime);    /* time_t */
-    put_header(buf, &pos, 1, 0x20);        /* DOS attribute (0x20 fixed) */
-    put_header(buf, &pos, 1, 2);           /* level */
-    put_header(buf, &pos, 2, h->file_crc);
-    if (opts.generic)
-        put_header(buf, &pos, 1, '\0');
-    else
-        put_header(buf, &pos, 1, 'U');
-
-    put_header(buf, &pos, 2, 5);
-    put_header(buf, &pos, 1, 0); /* 0x00: header crc */
-    crcptr = &buf[pos];
-    put_header(buf, &pos, 2, 0); /* crc (dummy) */
-
-    fname = basename(h->filename);
-    len = strlen(fname);
-
-    put_header(buf, &pos, 2, 3 + len);
-    put_header(buf, &pos, 1, 1); /* 0x01: filename header */
-    put_string(buf, &pos, len, fname); /* filename */
-
-    {
-        char *ptr;
-
-        ptr = strrchr(h->filename, '/');
-        if (ptr) {
-            /* 0123 */
-            /* abc/ */
-            /* 3 - 0 = 3 */
-            dirnamelen = ptr - h->filename;
-            strncpy(dirname, h->filename, dirnamelen);
-            dirname[dirnamelen+ 1] = 0;
-        }
-    }
-
-    if (*dirname) {
-        put_header(buf, &pos, 2, 3 + dirnamelen);
-        put_header(buf, &pos, 1, 2); /* 0x02: dirname header */
-        put_string(buf, &pos, dirnamelen, dirname); /* dirname */
-    }
-
-    put_header(buf, &pos, 2, 0); /* next header size (end of header) */
-
-    /* padding */
-    if (pos % 256 == 0) {
-        put_header(buf, &pos, 1, 0);
-    }
-    headersize = pos;
-
-    put_header_tmp(buf, 0, 2, headersize);
-
-    {
-        int i;
-
-        crc = INIT_CRC;
-        for (i = 0; i < headersize; i++)
-            UPDATE_CRC(buf[i]);
-        put_header_tmp(crcptr, 0, 2, crc);
-    }
-
-    fwrite_crc(buf, headersize, fp);
-}
-
-void
-write_header(FILE *fp, struct lzh_header *h)
-{
-    switch (h->level) {
-    case 0:
-        write_header_lv0(fp, h);
-        break;
-    case 1:
-        write_header_lv1(fp, h);
-        break;
-    case 2:
-        write_header_lv2(fp, h);
-        break;
-    default:
-        error("unknown level (%d)", h->level);
-        break;
-    }
-}
-
-static void
 skip(FILE *fp, struct lzh_header *h)
 {
     int i;
@@ -865,142 +148,6 @@ copy(FILE *arcfile, FILE *outfile, struct lzh_header *h)
     }
 }
 
-static void
-store(void)
-{
-    uint n;
-    uchar buffer[MAXDICSIZ];
-
-    origsize = 0;
-    crc = INIT_CRC;
-    while ((n = fread((char *) buffer, 1, sizeof(buffer), infile)) != 0) {
-        fwrite_crc(buffer, n, outfile);
-        origsize += n;
-    }
-    compsize = origsize;
-}
-
-static int
-add_dir(int replace_flag, struct lzh_header *h)
-{
-    long headerpos, arcpos;
-    uint r;
-
-    h->origsize = h->compsize = 0;
-    h->file_crc = INIT_CRC;
-
-    headerpos = ftell(outfile);
-    write_header(outfile, h);
-    arcpos = ftell(outfile);
-
-    if (opts.quiet < 2)
-        printf(" %d.%d%%\n", r / 10, r % 10);
-    return 1;                   /* success */
-}
-
-static int
-add_1(int replace_flag, struct lzh_header *h)
-{
-    long headerpos, arcpos;
-    uint r;
-
-    if ((infile = fopen(h->filename, "rb")) == NULL) {
-        fprintf(stderr, "Can't open %s\n", h->filename);
-        return 0;               /* failure */
-    }
-    if (replace_flag) {
-        if (opts.quiet < 2)
-            printf("Replacing %s ", h->filename);
-    }
-    else {
-        if (opts.quiet < 2)
-            printf("Adding %s ", h->filename);
-    }
-
-    headerpos = ftell(outfile);
-    write_header(outfile, h);
-    arcpos = ftell(outfile);
-
-    origsize = compsize = 0;
-    crc = INIT_CRC;
-    if (opts.nocompress) {
-        unpackable = 1;
-    }
-    else {
-        unpackable = 0;
-        encode();
-    }
-
-    if (unpackable) {
-        memcpy(h->method, "-lh0-", sizeof(h->method));  /* 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, h);
-    fseek(outfile, 0L, SEEK_END);
-    r = ratio(compsize, origsize);
-    if (opts.quiet < 2)
-        printf(" %d.%d%%\n", r / 10, r % 10);
-    return 1;                   /* success */
-}
-
-static int
-add(int replace_flag, char *filename, int namelen)
-{
-    struct lzh_header h;
-    struct stat st;
-
-    memset(&h, 0, sizeof(h));
-
-    h.level = opts.header_level;
-
-    strcpy(h.filename, filename);
-    h.namelen = namelen;
-
-    stat(h.filename, &st);
-
-    h.mtime = st.st_mtime;
-    if (S_ISDIR(st.st_mode)) {
-        DIR *dir;
-        struct dirent *ent;
-
-        memcpy(h.method, "-lhd-", sizeof(h.method));  /* directory */
-        add_dir(replace_flag, &h);
-
-        dir = opendir(h.filename);
-        if (dir == NULL)
-            error("cannot open directory: \"%s\"", h.filename);
-
-        while ((ent = readdir(dir)) != 0) {
-            char filename[1024];
-
-            if (string_equal(ent->d_name, ".") ||
-                string_equal(ent->d_name, ".."))
-                continue;
-
-            h.namelen = path_addsep(h.filename, sizeof(h.filename));
-
-            string_cat(filename, sizeof(filename),
-                       h.filename, ent->d_name, NULL);
-
-            add(replace_flag, filename, strlen(filename));
-        }
-        closedir(dir);
-    }
-    else {
-        memcpy(h.method, opts.method->id, sizeof(h.method));  /* compress */
-        add_1(replace_flag, &h);
-    }
-}
-
 int
 get_line(char *s, int n)
 {
@@ -1014,125 +161,6 @@ get_line(char *s, int n)
     return i;
 }
 
-static void
-extract(int to_file, struct lzh_header *h)
-{
-    int n;
-    uchar buffer[MAXDICSIZ];
-
-    outfile = NULL;
-
-    if (to_file) {
-        if (memcmp(h->method, "-lhd-", sizeof(h->method)) == 0) {
-            /* directory */
-            if (mkdir(h->filename, 0777) == -1) {
-                if (errno != EEXIST)
-                    error("cannot make directory \"%s\"", opts.outdir);
-            }
-        }
-        else {
-            /* regular file */
-            if (file_exists(h->filename)) {
-                if (!opts.force_extract) {
-                    message("'%s' has been already exist. skip", h->filename);
-                    skip(arcfile, h);
-                    return;
-                }
-            }
-            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->namelen = strlen(h->filename);
-            }
-        }
-        if (opts.quiet < 2)
-            printf("Extracting %s ", h->filename);
-    }
-    else {
-        outfile = stdout;
-        if (opts.quiet < 2)
-            printf("===== %s =====\n", h->filename);
-    }
-    crc = INIT_CRC;
-    opts.method = which_method(h->method);
-    if (opts.method == NULL) {
-        fprintf(stderr, "Unknown method: %.5s\n", h->method);
-        skip(arcfile, h);
-    }
-    else {
-        crc = INIT_CRC;
-        if (opts.method->dicbit != 0)
-            decode_start();
-        while (h->origsize != 0) {
-            n = (uint) ((h->origsize > MAXDICSIZ) ? MAXDICSIZ : h->origsize);
-            if (opts.method->dicbit != 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 && opts.quiet < 1) {
-                putc('.', stdout);
-            }
-            h->origsize -= n;
-        }
-    }
-
-    if ((crc ^ INIT_CRC) != h->file_crc)
-        error("CRC error");
-
-    if (to_file) {
-        fprintf(stdout, "\n");
-        if (outfile) {
-            struct utimbuf ut;
-
-            fclose(outfile);
-
-            ut.actime = ut.modtime = h->mtime;
-            utime(h->filename, &ut);
-        }
-    }
-    outfile = NULL;
-}
-
-static void
-list_start(void)
-{
-    if (opts.quiet < 2)
-        printf("%-14.14s %-7.7s %10.10s %10.10s %-5.5s %-4.4s %-5.5s %-4.4s\n",
-               "Filename",
-               "OS",
-               "Original",
-               "Compressed",
-               "Ratio",
-               "CRC",
-               "Method",
-               "Lv");
-}
-
-static void
-list(struct lzh_header *h)
-{
-    uint r;
-
-    printf("%-14.*s", h->namelen, h->filename);
-    if (h->namelen > 14)
-        printf("\n              ");
-    r = ratio(h->compsize, h->origsize);
-    printf(" %-7s %10lu %10lu %u.%03u %04x %-6.6s [%d]\n",
-           os_string(h->os_id),
-           h->origsize,
-           h->compsize,
-           r / 1000,
-           r % 1000,
-           h->file_crc,
-           h->method,
-           h->level);
-}
-
 static int
 match(char *s1, char *s2)
 {
@@ -1168,15 +196,9 @@ search(int argc, char *argv[], struct lzh_header *h)
     return 0;
 }
 
-static void
-exitfunc(void)
-{
-    remove(temp_name);
-}
-
 #include "getopt_long.h"
 
-int
+void
 parse_args(int argc, char **argv)
 {
     int c;
@@ -1226,6 +248,7 @@ parse_args(int argc, char **argv)
             break;
         case 'g':
             opts.generic = 1;
+            opts.header_level = 0;
             break;
         case 'o':
             /* compress method */
@@ -1276,22 +299,292 @@ parse_args(int argc, char **argv)
 FILE *
 open_tempfile()
 {
-    temp_name = tmpnam(NULL);
-    outfile = fopen(temp_name, "wb");
+    FILE *outfile;
+
+    outfile = tmpfile();
     if (outfile == NULL)
         error("Can't open temporary file");
-    atexit(exitfunc);
 
     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, cmd, count, nfiles, found, done;
+    int cmd;
     char *archive_file;
-    struct lzh_header h;
-    int arc_count;
 
     INITIALIZE_OPTS(opts);
 
@@ -1328,193 +621,46 @@ main(int argc, char *argv[])
 
     if (strcmp(archive_file, "-") == 0)
         opts.archive_to_stdio = 1;
+    if (opts.archive_to_stdio)
+        opts.quiet = 2;
 
     argv++;
     argc--;
 
-    temp_name = NULL;
-
     make_crctable();
-    count = done = nfiles = 0;
 
     switch (cmd) {
     case 'a':
     case 'u':
-    case 'c':
-        if (opts.archive_to_stdio)
-            opts.quiet = 2;
-
-        outfile = open_tempfile();
-        if (*argv == 0)
-            error("archived files are not specified.");
-
-        if (!opts.archive_to_stdio && (cmd == 'a' || cmd == 'u')) {
-            if (file_exists(archive_file)) {
-                arcfile = fopen(archive_file, "rb");
-                if (arcfile == NULL)
-                    error("Can't open archive '%s'", archive_file);
-
-                break;
-            }
-        }
-        for (i = 0; i < argc; i++) {
-            add(0, argv[i], strlen(argv[i]));
-        }
-
-        fputc(0, outfile);      /* end of archive */
-        if (ferror(outfile))
-            error("Can't write");
-        fclose(outfile);
-        if (opts.archive_to_stdio) {
-            if (move_file_to_stream(temp_name, stdout) == -1)
-                error("fail to move_file_to_stream(): %s -> %s",temp_name,"stdout");
+        if (opts.archive_to_stdio || !file_exists(archive_file)) {
+            op_create(cmd, archive_file, argc, argv);
         }
         else {
-            unlink(archive_file);
-            if (xrename(temp_name, archive_file) == -1)
-                error("fail to rename(): %s -> %s",temp_name,archive_file);
+            op_add(cmd, archive_file, argc, argv);
         }
-        exit(0);
         break;
-    case 'r':
+
+    case 'c':
+        op_create(cmd, archive_file, argc, argv);
+        break;
+
     case 'd':
-        if (argc == 0) {
-            message("No files given in argument, do nothing.");
-            exit(0);
-        }
-        outfile = open_tempfile();
+        op_delete(cmd, archive_file, argc, argv);
+        break;
+
     case 'x':
     case 'p':
+        op_extract(cmd, archive_file, argc, argv);
+        break;
+
     case 'l':
     case 'v':
-        /* 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);
-
+        op_list(cmd, archive_file, argc, argv);
         break;
+
     default:
         print_usage();
         break;
     }
-
-    /* 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);
-        }
-    }
-
-    arc_count = 0;
-
-    while (!done && read_header(arcfile, &h)) {
-
-        arc_count++;
-
-        compsize = h.compsize;
-        origsize = h.origsize;
-
-        found = search(argc, argv, &h);
-        switch (cmd) {
-        case 'a':
-        case 'u':
-            if (found>0) {
-                argv[found-1] = 0;
-
-                if (cmd == 'u' && h.mtime > file_mtime(h.filename)) {
-                    copy(arcfile, outfile, &h);
-                    break;
-                }
-
-                if (add(1, h.filename, h.namelen)) {
-                    skip(arcfile, &h);
-                    count++;
-                }
-                else
-                    copy(arcfile, outfile, &h);
-            }
-            else
-                copy(arcfile, outfile, &h);
-            break;
-        case 'd':
-            if (found) {
-                count++;
-                message("'%s' deleted", h.filename);
-                skip(arcfile, &h);
-            }
-            else
-                copy(arcfile, outfile, &h);
-            break;
-        case 'x':
-        case 'p':
-            if (found != 0) {
-                extract(cmd == 'x', &h);
-                if (++count == nfiles)
-                    done = 1;
-            }
-            else
-                skip(arcfile, &h);
-            break;
-        case 'l':
-        case 'v':
-            if (found != 0) {
-                if (count == 0)
-                    list_start();
-                list(&h);
-                if (++count == nfiles)
-                    done = 1;
-            }
-            skip(arcfile, &h);
-            break;
-        }
-    }
-
-    if (cmd == 'a' || cmd == 'u') {
-        for (i = 0; i < argc; i++) {
-            if (argv[i]) {
-                count++;
-                add(0, argv[i], strlen(argv[i]));
-            }
-        }
-    }
-
-    if (cmd != 'p') {
-        if (opts.quiet < 2)
-            printf("  %d files\n", count);
-    }
-
-    if (count > 0 && (cmd == 'd' || cmd == 'a' || cmd == 'u')) {
-        fputc(0, outfile);      /* end of archive */
-        if (ferror(outfile))
-            error("Can't write");
-        if (!opts.archive_to_stdio)
-            unlink(archive_file);
-        fclose(outfile);
-        if (cmd == 'd') {
-            if (arc_count > count) {
-                if (xrename(temp_name, archive_file) == -1)
-                    error("fail to rename(): %s -> %s",temp_name,archive_file);
-            }
-            else {
-                message("The archive file \"%s\" was removed because it would be empty.", archive_file);
-            }
-        }
-        else {
-            if (xrename(temp_name, archive_file) == -1)
-                error("fail to rename(): %s -> %s",temp_name,archive_file);
-        }
-        exit(0);
-    }
     return EXIT_SUCCESS;
 }