-/***********************************************************\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;
+}