OSDN Git Service

In encode.c, cause SEGV when the USE_LH6_METHOD and PERCOLATE are defined.
[lha/olha.git] / ar.c
diff --git a/ar.c b/ar.c
index fb0d988..f75fba7 100644 (file)
--- a/ar.c
+++ b/ar.c
-/***********************************************************\r
-       ar.c -- main file\r
-***********************************************************/\r
-\r
-static char *usage =\r
-       "ar -- compression archiver -- written by Haruhiko Okumura\n"\r
-       "  PC-VAN:SCIENCE        CompuServe:74050,1022\n"\r
-       "  NIFTY-Serve:PAF01022  INTERNET:74050.1022@compuserve.com\n"\r
-       "Usage: ar command archive [file ...]\n"\r
-       "Commands:\n"\r
-       "   a: Add files to archive (replace if present)\n"\r
-       "   x: Extract files from archive\n"\r
-       "   r: Replace files in archive\n"\r
-       "   d: Delete files from archive\n"\r
-       "   p: Print files on standard output\n"\r
-       "   l: List contents of archive\n"\r
-       "If no files are named, all files in archive are processed,\n"\r
-       "   except for commands 'a' and 'd'.\n"\r
-       "You may copy, distribute, and rewrite this program freely.\n";\r
-\r
-/***********************************************************\r
-\r
-Structure of archive block (low order byte first):\r
------preheader\r
- 1     basic header size\r
-               = 25 + strlen(filename) (= 0 if end of archive)\r
- 1     basic header algebraic sum (mod 256)\r
------basic header\r
- 5     method ("-lh0-" = stored, "-lh5-" = compressed)\r
- 4     compressed size (including extended headers)\r
- 4     original size\r
- 4     not used\r
- 1     0x20\r
- 1     0x01\r
- 1     filename length (x)\r
- x     filename\r
- 2     original file's CRC\r
- 1     0x20\r
- 2     first extended header size (0 if none)\r
------first extended header, etc.\r
------compressed file\r
-\r
-***********************************************************/\r
-\r
-#include <stdlib.h>\r
-#include <string.h>\r
-#include <ctype.h>\r
-#include "ar.h"\r
-\r
-#define FNAME_MAX (255 - 25) /* max strlen(filename) */\r
-#define namelen  header[19]\r
-#define filename ((char *)&header[20])\r
-\r
-int unpackable;            /* global, set in io.c */\r
-ulong compsize, origsize;  /* global */\r
-\r
-static uchar buffer[DICSIZ];\r
-static uchar header[255];\r
-static uchar headersize, headersum;\r
-static uint  file_crc;\r
-static char  *temp_name;\r
-\r
-static uint ratio(ulong a, ulong b)  /* [(1000a + [b/2]) / b] */\r
-{\r
-       int i;\r
-\r
-       for (i = 0; i < 3; i++)\r
-               if (a <= ULONG_MAX / 10) a *= 10;  else b /= 10;\r
-       if ((ulong)(a + (b >> 1)) < a) {  a >>= 1;  b >>= 1;  }\r
-       if (b == 0) return 0;\r
-       return (uint)((a + (b >> 1)) / b);\r
-}\r
-\r
-static void put_to_header(int i, int n, ulong x)\r
-{\r
-       while (--n >= 0) {\r
-               header[i++] = (uchar)((uint)x & 0xFF);  x >>= 8;\r
-       }\r
-}\r
-\r
-static ulong get_from_header(int i, int n)\r
-{\r
-       ulong s;\r
-\r
-       s = 0;\r
-       while (--n >= 0) s = (s << 8) + header[i + n];  /* little endian */\r
-       return s;\r
-}\r
-\r
-static uint calc_headersum(void)\r
-{\r
-       int i;\r
-       uint s;\r
-\r
-       s = 0;\r
-       for (i = 0; i < headersize; i++) s += header[i];\r
-       return s & 0xFF;\r
-}\r
-\r
-static int read_header(void)\r
-{\r
-       headersize = (uchar) fgetc(arcfile);\r
-       if (headersize == 0) return 0;  /* end of archive */\r
-       headersum  = (uchar) fgetc(arcfile);\r
-       fread_crc(header, headersize, arcfile);  /* CRC not used */\r
-       if (calc_headersum() != headersum) error("Header sum error");\r
-       compsize = get_from_header(5, 4);\r
-       origsize = get_from_header(9, 4);\r
-       file_crc = (uint)get_from_header(headersize - 5, 2);\r
-       filename[namelen] = '\0';\r
-       return 1;  /* success */\r
-}\r
-\r
-static void write_header(void)\r
-{\r
-       fputc(headersize, outfile);\r
-       /* We've destroyed file_crc by null-terminating filename. */\r
-       put_to_header(headersize - 5, 2, (ulong)file_crc);\r
-       fputc(calc_headersum(), outfile);\r
-       fwrite_crc(header, headersize, outfile);  /* CRC not used */\r
-}\r
-\r
-static void skip(void)\r
-{\r
-       fseek(arcfile, compsize, SEEK_CUR);\r
-}\r
-\r
-static void copy(void)\r
-{\r
-       uint n;\r
-\r
-       write_header();\r
-       while (compsize != 0) {\r
-               n = (uint)((compsize > DICSIZ) ? DICSIZ : compsize);\r
-               if (fread ((char *)buffer, 1, n, arcfile) != n)\r
-                       error("Can't read");\r
-               if (fwrite((char *)buffer, 1, n, outfile) != n)\r
-                       error("Can't write");\r
-               compsize -= n;\r
-       }\r
-}\r
-\r
-static void store(void)\r
-{\r
-       uint n;\r
-\r
-       origsize = 0;\r
-       crc = INIT_CRC;\r
-       while ((n = fread((char *)buffer, 1, DICSIZ, infile)) != 0) {\r
-               fwrite_crc(buffer, n, outfile);  origsize += n;\r
-       }\r
-       compsize = origsize;\r
-}\r
-\r
-static int add(int replace_flag)\r
-{\r
-       long headerpos, arcpos;\r
-       uint r;\r
-\r
-       if ((infile = fopen(filename, "rb")) == NULL) {\r
-               fprintf(stderr, "Can't open %s\n", filename);\r
-               return 0;  /* failure */\r
-       }\r
-       if (replace_flag) {\r
-               printf("Replacing %s ", filename);  skip();\r
-       } else\r
-               printf("Adding %s ", filename);\r
-       headerpos = ftell(outfile);\r
-       namelen = strlen(filename);\r
-       headersize = 25 + namelen;\r
-       memcpy(header, "-lh5-", 5);  /* compress */\r
-       write_header();  /* temporarily */\r
-       arcpos = ftell(outfile);\r
-       origsize = compsize = 0;  unpackable = 0;\r
-       crc = INIT_CRC;  encode();\r
-       if (unpackable) {\r
-               header[3] = '0';  /* store */\r
-               rewind(infile);\r
-               fseek(outfile, arcpos, SEEK_SET);\r
-               store();\r
-       }\r
-       file_crc = crc ^ INIT_CRC;\r
-       fclose(infile);\r
-       put_to_header(5, 4, compsize);\r
-       put_to_header(9, 4, origsize);\r
-       memcpy(header + 13, "\0\0\0\0\x20\x01", 6);\r
-       memcpy(header + headersize - 3, "\x20\0\0", 3);\r
-       fseek(outfile, headerpos, SEEK_SET);\r
-       write_header();  /* true header */\r
-       fseek(outfile, 0L, SEEK_END);\r
-       r = ratio(compsize, origsize);\r
-       printf(" %d.%d%%\n", r / 10, r % 10);\r
-       return 1;  /* success */\r
-}\r
-\r
-int get_line(char *s, int n)\r
-{\r
-       int i, c;\r
-\r
-       i = 0;\r
-       while ((c = getchar()) != EOF && c != '\n')\r
-               if (i < n) s[i++] = (char)c;\r
-       s[i] = '\0';\r
-       return i;\r
-}\r
-\r
-static void extract(int to_file)\r
-{\r
-       int n, method;\r
-       uint ext_headersize;\r
-\r
-       if (to_file) {\r
-               while ((outfile = fopen(filename, "wb")) == NULL) {\r
-                       fprintf(stderr, "Can't open %s\nNew filename: ", filename);\r
-                       if (get_line(filename, FNAME_MAX) == 0) {\r
-                               fprintf(stderr, "Not extracted\n");\r
-                               skip();  return;\r
-                       }\r
-                       namelen = strlen(filename);\r
-               }\r
-               printf("Extracting %s ", filename);\r
-       } else {\r
-               outfile = stdout;\r
-               printf("===== %s =====\n", filename);\r
-       }\r
-       crc = INIT_CRC;\r
-       method = header[3];  header[3] = ' ';\r
-       if (! strchr("045", method) || memcmp("-lh -", header, 5)) {\r
-               fprintf(stderr, "Unknown method: %u\n", method);\r
-               skip();\r
-       } else {\r
-               ext_headersize = (uint)get_from_header(headersize - 2, 2);\r
-               while (ext_headersize != 0) {\r
-                       fprintf(stderr, "There's an extended header of size %u.\n",\r
-                               ext_headersize);\r
-                       compsize -= ext_headersize;\r
-                       if (fseek(arcfile, ext_headersize - 2, SEEK_CUR))\r
-                               error("Can't read");\r
-                       ext_headersize = fgetc(arcfile);\r
-                       ext_headersize += (uint)fgetc(arcfile) << 8;\r
-               }\r
-               crc = INIT_CRC;\r
-               if (method != '0') decode_start();\r
-               while (origsize != 0) {\r
-                       n = (uint)((origsize > DICSIZ) ? DICSIZ : origsize);\r
-                       if (method != '0') decode(n, buffer);\r
-                       else if (fread((char *)buffer, 1, n, arcfile) != n)\r
-                               error("Can't read");\r
-                       fwrite_crc(buffer, n, outfile);\r
-                       if (outfile != stdout) putc('.', stderr);\r
-                       origsize -= n;\r
-               }\r
-       }\r
-       if (to_file) fclose(outfile);  else outfile = NULL;\r
-       printf("\n");\r
-       if ((crc ^ INIT_CRC) != file_crc)\r
-               fprintf(stderr, "CRC error\n");\r
-}\r
-\r
-static void list_start(void)\r
-{\r
-       printf("Filename         Original Compressed Ratio CRC Method\n");\r
-}\r
-\r
-static void list(void)\r
-{\r
-       uint r;\r
-\r
-       printf("%-14s", filename);\r
-       if (namelen > 14) printf("\n              ");\r
-       r = ratio(compsize, origsize);\r
-       printf(" %10lu %10lu %u.%03u %04X %5.5s\n",\r
-               origsize, compsize, r / 1000, r % 1000, file_crc, header);\r
-}\r
-\r
-static int match(char *s1, char *s2)\r
-{\r
-       for ( ; ; ) {\r
-               while (*s2 == '*' || *s2 == '?') {\r
-                       if (*s2++ == '*')\r
-                               while (*s1 && *s1 != *s2) s1++;\r
-                       else if (*s1 == 0)\r
-                               return 0;\r
-                       else s1++;\r
-               }\r
-               if (*s1 != *s2) return 0;\r
-               if (*s1 == 0  ) return 1;\r
-               s1++;  s2++;\r
-       }\r
-}\r
-\r
-static int search(int argc, char *argv[])\r
-{\r
-       int i;\r
-\r
-       if (argc == 3) return 1;\r
-       for (i = 3; i < argc; i++)\r
-               if (match(filename, argv[i])) return 1;\r
-       return 0;\r
-}\r
-\r
-static void exitfunc(void)\r
-{\r
-       fclose(outfile);  remove(temp_name);\r
-}\r
-\r
-int main(int argc, char *argv[])\r
-{\r
-       int i, j, cmd, count, nfiles, found, done;\r
-\r
-       /* Check command line arguments. */\r
-       if (argc < 3\r
-        || argv[1][1] != '\0'\r
-        || ! strchr("AXRDPL", cmd = toupper(argv[1][0]))\r
-        || (argc == 3 && strchr("AD", cmd)))\r
-               error(usage);\r
-\r
-       /* Wildcards used? */\r
-       for (i = 3; i < argc; i++)\r
-               if (strpbrk(argv[i], "*?")) break;\r
-       if (cmd == 'A' && i < argc)\r
-               error("Filenames may not contain '*' and '?'");\r
-       if (i < argc) nfiles = -1;  /* contains wildcards */\r
-       else nfiles = argc - 3;     /* number of files to process */\r
-\r
-       /* Open archive. */\r
-       arcfile = fopen(argv[2], "rb");\r
-       if (arcfile == NULL && cmd != 'A')\r
-               error("Can't open archive '%s'", argv[2]);\r
-\r
-       /* Open temporary file. */\r
-       if (strchr("ARD", cmd)) {\r
-               temp_name = tmpnam(NULL);\r
-               outfile = fopen(temp_name, "wb");\r
-               if (outfile == NULL)\r
-                       error("Can't open temporary file");\r
-               atexit(exitfunc);\r
-       } else temp_name = NULL;\r
-\r
-       make_crctable();  count = done = 0;\r
-\r
-       if (cmd == 'A') {\r
-               for (i = 3; i < argc; i++) {\r
-                       for (j = 3; j < i; j++)\r
-                               if (strcmp(argv[j], argv[i]) == 0) break;\r
-                       if (j == i) {\r
-                               strcpy(filename, argv[i]);\r
-                               if (add(0)) count++;  else argv[i][0] = 0;\r
-                       } else nfiles--;\r
-               }\r
-               if (count == 0 || arcfile == NULL) done = 1;\r
-       }\r
-\r
-       while (! done && read_header()) {\r
-               found = search(argc, argv);\r
-               switch (cmd) {\r
-               case 'R':\r
-                       if (found) {\r
-                               if (add(1)) count++;  else copy();\r
-                       } else copy();\r
-                       break;\r
-               case 'A':  case 'D':\r
-                       if (found) {\r
-                               count += (cmd == 'D');  skip();\r
-                       } else copy();\r
-                       break;\r
-               case 'X':  case 'P':\r
-                       if (found) {\r
-                               extract(cmd == 'X');\r
-                               if (++count == nfiles) done = 1;\r
-                       } else skip();\r
-                       break;\r
-               case 'L':\r
-                       if (found) {\r
-                               if (count == 0) list_start();\r
-                               list();\r
-                               if (++count == nfiles) done = 1;\r
-                       }\r
-                       skip();  break;\r
-               }\r
-       }\r
-\r
-       if (temp_name != NULL && count != 0) {\r
-               fputc(0, outfile);  /* end of archive */\r
-               if (ferror(outfile) || fclose(outfile) == EOF)\r
-                       error("Can't write");\r
-               remove(argv[2]);  rename(temp_name, argv[2]);\r
-       }\r
-\r
-       printf("  %d files\n", count);\r
-       return EXIT_SUCCESS;\r
-}\r
+/***********************************************************
+       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"
+    "  NIFTY-Serve:PAF01022  INTERNET:74050.1022@compuserve.com\n"
+    "Usage: ar command archive [file ...]\n"
+    "Commands:\n"
+    "   a: Add files to archive (replace if present)\n"
+    "   x: Extract files from archive\n"
+    "   r: Replace files in archive\n"
+    "   d: Delete files from archive\n"
+    "   p: Print files on standard output\n"
+    "   l: List contents of archive\n"
+    "If no files are named, all files in archive are processed,\n"
+    "   except for commands 'a' and 'd'.\n"
+    "You may copy, distribute, and rewrite this program freely.\n";
+
+/***********************************************************
+
+Structure of archive block (low order byte first):
+-----preheader
+ 1     basic header size
+               = 25 + strlen(filename) (= 0 if end of archive)
+ 1     basic header algebraic sum (mod 256)
+-----basic header
+ 5     method ("-lh0-" = stored, "-lh5-" = compressed)
+ 4     compressed size (including extended headers)
+ 4     original size
+ 4     not used
+ 1     0x20
+ 1     0x01
+ 1     filename length (x)
+ x     filename
+ 2     original file's CRC
+ 1     0x20
+ 2     first extended header size (0 if none)
+-----first extended header, etc.
+-----compressed file
+
+***********************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include "ar.h"
+
+extern char *basename(const char *);
+
+struct lha_method methods[] = {
+    /* id, dicbit, pbit, maxmatch */
+    /* note: dicbit == 0 means no compress */
+    {"-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},  /* 1: no compress (LArc) */
+    {"-lhd-", 0,  0,  0},  /* 1: directory */
+};
+
+struct lha_opts opts;
+
+struct lha_method *
+which_method(char *id)
+{
+    int i;
+
+    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;
+}
+
+init_opts()
+{
+    opts.nocompress = 0;
+    opts.outdir = NULL;
+    opts.quiet = 0;
+    opts.header_level = 2;
+    opts.generic = 0;
+    opts.verbose = 0;
+
+    /* default is the -lh5- method */
+    opts.method   = &methods[5];
+}
+
+#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()
+{
+    printf("%s", usage);
+    exit(0);
+}
+
+static void
+print_version()
+{
+    printf("version %s\n", version);
+    exit(0);
+}
+
+static uint
+ratio(ulong a, ulong b)
+{                               /* [(1000a + [b/2]) / b] */
+    int i;
+
+    for (i = 0; i < 3; i++)
+        if (a <= ULONG_MAX / 10)
+            a *= 10;
+        else
+            b /= 10;
+    if ((ulong) (a + (b >> 1)) < a) {
+        a >>= 1;
+        b >>= 1;
+    }
+    if (b == 0)
+        return 0;
+    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_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_lv0(FILE *fp, char *buf, struct lzh_header *h)
+{
+    int headersize;
+    int headersum;
+    int ext_headersize;
+
+    headersize = get_byte(&buf[0]);
+    headersum = get_byte(&buf[1]);
+
+    fread_crc(buf + 21, headersize - (21 - 2), fp);     /* CRC not used */
+
+    buf += 2;
+
+    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]); */
+    h->level    = get_byte(&buf[18]); /* header level */
+    h->namelen = get_byte(&buf[19]);
+    get_char(&buf[20], h->filename, h->namelen);
+    h->filename[h->namelen] = 0;
+    h->file_crc = get_word(&buf[20+h->namelen]);
+    h->os_id    = 0;            /* generic */
+
+    return 1;                   /* success */
+}
+
+static int
+read_header_lv1(FILE *fp, char *buf, struct lzh_header *h)
+{
+    int headersize;
+    int headersum;
+    int ext_headersize;
+
+    headersize = get_byte(&buf[0]);
+    headersum = get_byte(&buf[1]);
+
+    fread_crc(buf + 21, headersize - (21 - 2), fp);     /* CRC not used */
+
+    buf += 2;
+
+    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]); */
+    h->level    = get_byte(&buf[18]); /* header level */
+    h->namelen = get_byte(&buf[19]);
+    get_char(&buf[20], h->filename, h->namelen);
+    h->filename[h->namelen] = 0;
+    h->file_crc = get_word(&buf[20+h->namelen]);
+    h->os_id    = get_byte(&buf[20+h->namelen+2]);
+
+    ext_headersize = get_word(&buf[20+h->namelen+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 */
+}
+
+static int
+read_header_lv2(FILE *fp, char *buf, struct lzh_header *h)
+{
+    int headersize;
+    int headersum;
+    int ext_headersize;
+    char extbuf[1024];
+    uchar ext_type;
+    int remainder;
+    char dirname[1024] = "";
+    int dirnamelen = 0;
+
+    headersize = get_word(&buf[0]);
+
+    fread_crc(buf + 21, 26 - 21, fp);     /* CRC not used */
+
+    get_char(&buf[2], h->method, 5);
+    h->compsize = get_dword(&buf[7]);
+    h->origsize = get_dword(&buf[11]);
+    h->ftime    = get_dword(&buf[15]);
+    /* attrib   = get_byte(&buf[19]); */
+    h->level    = get_byte(&buf[20]); /* header level */
+    h->file_crc = get_word(&buf[21]);
+    h->os_id    = get_byte(&buf[23]);
+
+    ext_headersize = get_word(&buf[24]);
+
+    remainder = headersize - 26;
+
+    while (ext_headersize != 0) {
+        char *p = extbuf;
+
+        remainder -= ext_headersize;
+
+        fread_crc(p, ext_headersize, fp);
+        ext_type = get_byte(p++);
+        switch (ext_type) {
+        case 0:
+            /* header crc */
+            break;
+        case 1:
+            /* filename header */
+            h->namelen = ext_headersize - 3;
+            get_char(p, h->filename, h->namelen);
+            h->filename[h->namelen] = 0;
+            break;
+        case 2:
+            /* dirname header */
+            dirnamelen = ext_headersize - 3;
+            get_char(p, dirname, dirnamelen);
+            dirname[dirnamelen] = 0;
+            break;
+        }
+        ext_headersize = get_word(&extbuf[ext_headersize - 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)
+{
+    int headersize;
+    int headersum;
+    char buf[4096];
+    int ext_headersize;
+    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], *p = buf;
+    int sum;
+    int headersize;
+
+    headersize = 22 + h->namelen;
+
+    put_byte(&buf[0], 0);       /* dummy */
+    put_byte(&buf[1], 0);       /* dummy */
+    put_char(&buf[2], h->method, 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], 0);              /* level */
+    put_byte(&buf[21], h->namelen);     /* length of pathname */
+    put_char(&buf[22], h->filename, h->namelen);
+    put_word(&buf[22+h->namelen], h->file_crc);
+
+    headersize += 0;            /* size of ext-header (old style) */
+    put_byte(&buf[0], headersize);
+
+    sum = calc_headersum(buf+2, headersize);
+    put_byte(&buf[1], sum);
+
+    fwrite_crc(buf, headersize+2, fp);
+}
+
+void
+write_header_lv1(FILE *fp, struct lzh_header *h)
+{
+    char buf[4096], *p = buf;
+    int sum;
+    int headersize;
+
+    headersize = 25 + h->namelen;
+
+    put_byte(&buf[0], headersize);
+    put_byte(&buf[1], 0);       /* dummy */
+    put_char(&buf[2], h->method, 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->namelen);     /* length of pathname */
+    put_char(&buf[22], h->filename, h->namelen);
+    put_word(&buf[22+h->namelen], h->file_crc);
+    if (opts.generic)
+        put_byte(&buf[22+h->namelen+2], '\0');
+    else
+        put_byte(&buf[22+h->namelen+2], 'U');
+    put_word(&buf[22+h->namelen+3], 0x0000); /* next header size */
+
+    sum = calc_headersum(buf+2, headersize);
+    put_byte(&buf[1], sum);
+
+    fwrite_crc(buf, headersize+2, fp);
+}
+
+void
+write_header_lv2(FILE *fp, struct lzh_header *h)
+{
+    char buf[4096], *p = buf, *crcptr;
+    int sum;
+    int headersize, next_headersize;
+    extern ushort crctable[];
+    char dirname[1024] = "", *fname;
+    int dirnamelen, len;
+
+    put_word(&buf[0], 0);       /* dummy */
+    put_char(&buf[2], h->method, 5);
+    put_dword(&buf[7], h->compsize);    /* packed size */
+    put_dword(&buf[11], h->origsize);   /* original size */
+    put_dword(&buf[15], h->ftime);      /* time_t */
+    put_byte(&buf[19], 0x20);           /* DOS attribute (0x20 fixed) */
+    put_byte(&buf[20], 2);              /* level */
+    put_word(&buf[21], h->file_crc);
+    if (opts.generic)
+        put_byte(&buf[23], '\0');
+    else
+        put_byte(&buf[23], 'U');
+
+    headersize = 24;
+
+    /* ext-header */
+    p = buf + headersize;
+    next_headersize = 3 + 2;
+    put_word(p, next_headersize); /* next header size */
+    put_byte(p+2, 0);             /* 0x00: header crc */
+    crcptr = p+3;
+    put_word(p+3, 0);             /* crc (dummy) */
+    headersize += next_headersize;
+
+    fname = basename(h->filename);
+    len = strlen(fname);
+
+    p = buf + headersize;
+    next_headersize = 3 + len;
+    put_word(p, next_headersize);      /* next header size */
+    put_byte(p+2, 1);                  /* 0x01: filename header */
+    put_char(p+3, fname, len); /* filename */
+    headersize += next_headersize;
+
+    {
+        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) {
+        p = buf + headersize;
+        next_headersize = 3 + dirnamelen;
+        put_word(p, next_headersize); /* next header size */
+        put_byte(p+2, 2);             /* 0x02: dirname header */
+        put_char(p+3, dirname, dirnamelen);        /* dirname */
+        headersize += next_headersize;
+    }
+
+    p = buf + headersize;
+    next_headersize = 0;
+    put_word(p, next_headersize); /* next header size */
+    headersize += next_headersize;
+    headersize += 2;
+
+    /* padding */
+    if (headersize % 256 == 0) {
+        put_byte(&buf[headersize], 0);
+        headersize++;
+    }
+
+    put_word(&buf[0], headersize);
+
+    crc = INIT_CRC;
+    for (p = buf; p - buf < headersize; p++)
+        UPDATE_CRC(*p);
+
+    put_word(crcptr, 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)
+{
+    fseek(fp, h->compsize, SEEK_CUR);
+}
+
+static void
+copy(FILE *arcfile, FILE *outfile, struct lzh_header *h)
+{
+    uint n;
+    uchar buffer[MAXDICSIZ];
+
+    write_header(outfile, h);
+    while (h->compsize != 0) {
+        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)
+            error("Can't write");
+        h->compsize -= n;
+    }
+}
+
+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);
+        skip(arcfile, h);
+    }
+    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)
+{
+    long headerpos, arcpos;
+    uint r;
+    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);
+
+    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)
+{
+    int i, c;
+
+    i = 0;
+    while ((c = getchar()) != EOF && c != '\n')
+        if (i < n)
+            s[i++] = (char) c;
+    s[i] = '\0';
+    return i;
+}
+
+static void
+extract(int to_file, struct lzh_header *h)
+{
+    int n;
+    uint ext_headersize;
+    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 */
+            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)
+            fclose(outfile);
+    }
+    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)
+{
+    for (;;) {
+        while (*s2 == '*' || *s2 == '?') {
+            if (*s2++ == '*')
+                while (*s1 && *s1 != *s2)
+                    s1++;
+            else if (*s1 == 0)
+                return 0;
+            else
+                s1++;
+        }
+        if (*s1 != *s2)
+            return 0;
+        if (*s1 == 0)
+            return 1;
+        s1++;
+        s2++;
+    }
+}
+
+static int
+search(int argc, char *argv[], struct lzh_header *h)
+{
+    int i;
+
+    if (argc == 0)
+        return 1;
+    for (i = 0; i < argc; i++)
+        if (match(h->filename, argv[i]))
+            return 1;
+    return 0;
+}
+
+static void
+exitfunc(void)
+{
+    fclose(outfile);
+    remove(temp_name);
+}
+
+#include "getopt_long.h"
+
+int
+parse_args(int argc, char **argv)
+{
+    int c;
+
+    for (;;) {
+        int this_option_optind = optind ? optind : 1;
+        int option_index = 0;
+
+        enum {
+            LHA_OPT_HELP = 128,
+            LHA_OPT_VERSION,
+        };
+
+        static struct option long_options[] = {
+            /* name, has_arg, *flag, val */
+            /* has_arg:
+               no_argument (0)
+               required_argument (1)
+               optional_argument (2)
+               flag:
+               NULL: getopt_long() return val
+               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, "012go[567]q[012]vw:z",
+                        long_options, &option_index);
+
+        if (c == -1) break;     /* end of parsing options */
+
+        switch (c) {
+        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 'g':
+            opts.generic = 1;
+            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()
+{
+    temp_name = tmpnam(NULL);
+    outfile = fopen(temp_name, "wb");
+    if (outfile == NULL)
+        error("Can't open temporary file");
+    atexit(exitfunc);
+
+    return outfile;
+}
+
+int
+main(int argc, char *argv[])
+{
+    int i, j, cmd, count, nfiles, found, done;
+    char *archive_file;
+    struct lzh_header h;
+
+    init_opts();
+
+    if (argv[1] == 0)
+        print_usage();
+
+    /*take a command character */
+    {
+        char *arg1;
+
+        arg1 = argv[1];
+        if (arg1[0] == '-')
+            arg1++;
+        if (arg1[0] == 0)
+            print_usage();
+
+        cmd = *arg1;
+        if (arg1[1] == 0) {
+            /* -<cmd> -<opts> ... */
+            argv++;
+            argc--;
+        }
+        else {
+            /* -<cmd><opts> => -<opts> */
+            *arg1 = '-';
+        }
+    }
+
+    parse_args(argc, argv);
+    argv += optind;
+    argc -= optind;
+
+    archive_file = argv[0];
+
+    argv++;
+    argc--;
+
+    temp_name = NULL;
+
+    make_crctable();
+    count = done = nfiles = 0;
+
+    switch (cmd) {
+    case 'a':
+    case 'c':
+        outfile = open_tempfile();
+        if (*argv == 0)
+            error("archived files are not specified.");
+        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");
+        remove(archive_file);
+        if (xrename(temp_name, archive_file) == -1)
+            error("fail to rename(): %s -> %s",temp_name,archive_file);
+
+        exit(0);
+        break;
+    case 'r':
+    case 'd':
+        outfile = open_tempfile();
+    case 'x':
+    case 'p':
+    case 'l':
+    case 'v':
+        /* Open archive. */
+        arcfile = fopen(archive_file, "rb");
+        if (arcfile == NULL)
+            error("Can't open archive '%s'", argv[2]);
+
+        break;
+    default:
+        print_usage();
+        break;
+    }
+
+    /* change directory to extract dir */
+    if (cmd == 'x') {
+        struct stat *stbuf;
+
+        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)) {
+
+        compsize = h.compsize;
+        origsize = h.origsize;
+
+        found = search(argc, argv, &h);
+        switch (cmd) {
+        case 'r':
+            if (found) {
+                if (add(1, *argv, strlen(*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':
+        case 'v':
+            if (found) {
+                if (count == 0)
+                    list_start();
+                list(&h);
+                if (++count == nfiles)
+                    done = 1;
+            }
+            skip(arcfile, &h);
+            break;
+        }
+    }
+
+    if (cmd != 'p') {
+        if (opts.quiet < 2)
+            printf("  %d files\n", count);
+    }
+    return EXIT_SUCCESS;
+}