X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=ar.c;h=f75fba7abb8a4ea6fea8388c70c25e8081c759e5;hb=58e89747d5fb9c18c1bd1097ad53df9c13b00dc9;hp=fb0d988264559404ea44dce6e5c20083feb4ebb1;hpb=3208e6185faae5460a1e606a4830adb6a6a4c9c1;p=lha%2Folha.git diff --git a/ar.c b/ar.c index fb0d988..f75fba7 100644 --- a/ar.c +++ b/ar.c @@ -1,392 +1,1203 @@ -/*********************************************************** - ar.c -- main file -***********************************************************/ - -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 -#include -#include -#include "ar.h" - -#define FNAME_MAX (255 - 25) /* max strlen(filename) */ -#define namelen header[19] -#define filename ((char *)&header[20]) - -int unpackable; /* global, set in io.c */ -ulong compsize, origsize; /* global */ - -static uchar buffer[DICSIZ]; -static uchar header[255]; -static uchar headersize, headersum; -static uint file_crc; -static char *temp_name; - -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 void put_to_header(int i, int n, ulong x) -{ - while (--n >= 0) { - header[i++] = (uchar)((uint)x & 0xFF); x >>= 8; - } -} - -static ulong get_from_header(int i, int n) -{ - ulong s; - - s = 0; - while (--n >= 0) s = (s << 8) + header[i + n]; /* little endian */ - return s; -} - -static uint calc_headersum(void) -{ - int i; - uint s; - - s = 0; - for (i = 0; i < headersize; i++) s += header[i]; - return s & 0xFF; -} - -static int read_header(void) -{ - headersize = (uchar) fgetc(arcfile); - if (headersize == 0) return 0; /* end of archive */ - headersum = (uchar) fgetc(arcfile); - fread_crc(header, headersize, arcfile); /* CRC not used */ - if (calc_headersum() != headersum) error("Header sum error"); - compsize = get_from_header(5, 4); - origsize = get_from_header(9, 4); - file_crc = (uint)get_from_header(headersize - 5, 2); - filename[namelen] = '\0'; - return 1; /* success */ -} - -static void write_header(void) -{ - fputc(headersize, outfile); - /* We've destroyed file_crc by null-terminating filename. */ - put_to_header(headersize - 5, 2, (ulong)file_crc); - fputc(calc_headersum(), outfile); - fwrite_crc(header, headersize, outfile); /* CRC not used */ -} - -static void skip(void) -{ - fseek(arcfile, compsize, SEEK_CUR); -} - -static void copy(void) -{ - uint n; - - write_header(); - while (compsize != 0) { - n = (uint)((compsize > DICSIZ) ? DICSIZ : 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"); - compsize -= n; - } -} - -static void store(void) -{ - uint n; - - origsize = 0; - crc = INIT_CRC; - while ((n = fread((char *)buffer, 1, DICSIZ, infile)) != 0) { - fwrite_crc(buffer, n, outfile); origsize += n; - } - compsize = origsize; -} - -static int add(int replace_flag) -{ - long headerpos, arcpos; - uint r; - - if ((infile = fopen(filename, "rb")) == NULL) { - fprintf(stderr, "Can't open %s\n", filename); - return 0; /* failure */ - } - if (replace_flag) { - printf("Replacing %s ", filename); skip(); - } else - printf("Adding %s ", filename); - headerpos = ftell(outfile); - namelen = strlen(filename); - headersize = 25 + namelen; - memcpy(header, "-lh5-", 5); /* compress */ - write_header(); /* temporarily */ - arcpos = ftell(outfile); - origsize = compsize = 0; unpackable = 0; - crc = INIT_CRC; encode(); - if (unpackable) { - header[3] = '0'; /* store */ - rewind(infile); - fseek(outfile, arcpos, SEEK_SET); - store(); - } - file_crc = crc ^ INIT_CRC; - fclose(infile); - put_to_header(5, 4, compsize); - put_to_header(9, 4, origsize); - memcpy(header + 13, "\0\0\0\0\x20\x01", 6); - memcpy(header + headersize - 3, "\x20\0\0", 3); - fseek(outfile, headerpos, SEEK_SET); - write_header(); /* true header */ - fseek(outfile, 0L, SEEK_END); - r = ratio(compsize, origsize); - printf(" %d.%d%%\n", r / 10, r % 10); - return 1; /* success */ -} - -int get_line(char *s, int n) -{ - 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) -{ - int n, method; - uint ext_headersize; - - if (to_file) { - while ((outfile = fopen(filename, "wb")) == NULL) { - fprintf(stderr, "Can't open %s\nNew filename: ", filename); - if (get_line(filename, FNAME_MAX) == 0) { - fprintf(stderr, "Not extracted\n"); - skip(); return; - } - namelen = strlen(filename); - } - printf("Extracting %s ", filename); - } else { - outfile = stdout; - printf("===== %s =====\n", filename); - } - crc = INIT_CRC; - method = header[3]; header[3] = ' '; - if (! strchr("045", method) || memcmp("-lh -", header, 5)) { - fprintf(stderr, "Unknown method: %u\n", method); - skip(); - } else { - ext_headersize = (uint)get_from_header(headersize - 2, 2); - while (ext_headersize != 0) { - fprintf(stderr, "There's an extended header of size %u.\n", - ext_headersize); - compsize -= ext_headersize; - if (fseek(arcfile, ext_headersize - 2, SEEK_CUR)) - error("Can't read"); - ext_headersize = fgetc(arcfile); - ext_headersize += (uint)fgetc(arcfile) << 8; - } - crc = INIT_CRC; - if (method != '0') decode_start(); - while (origsize != 0) { - n = (uint)((origsize > DICSIZ) ? DICSIZ : origsize); - if (method != '0') decode(n, buffer); - else if (fread((char *)buffer, 1, n, arcfile) != n) - error("Can't read"); - fwrite_crc(buffer, n, outfile); - if (outfile != stdout) putc('.', stderr); - origsize -= n; - } - } - if (to_file) fclose(outfile); else outfile = NULL; - printf("\n"); - if ((crc ^ INIT_CRC) != file_crc) - fprintf(stderr, "CRC error\n"); -} - -static void list_start(void) -{ - printf("Filename Original Compressed Ratio CRC Method\n"); -} - -static void list(void) -{ - uint r; - - printf("%-14s", filename); - if (namelen > 14) printf("\n "); - r = ratio(compsize, origsize); - printf(" %10lu %10lu %u.%03u %04X %5.5s\n", - origsize, compsize, r / 1000, r % 1000, file_crc, header); -} - -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[]) -{ - int i; - - if (argc == 3) return 1; - for (i = 3; i < argc; i++) - if (match(filename, argv[i])) return 1; - return 0; -} - -static void exitfunc(void) -{ - fclose(outfile); remove(temp_name); -} - -int main(int argc, char *argv[]) -{ - int i, j, cmd, count, nfiles, found, done; - - /* Check command line arguments. */ - if (argc < 3 - || argv[1][1] != '\0' - || ! strchr("AXRDPL", cmd = toupper(argv[1][0])) - || (argc == 3 && strchr("AD", cmd))) - error(usage); - - /* Wildcards used? */ - for (i = 3; i < argc; i++) - if (strpbrk(argv[i], "*?")) break; - if (cmd == 'A' && i < argc) - error("Filenames may not contain '*' and '?'"); - if (i < argc) nfiles = -1; /* contains wildcards */ - else nfiles = argc - 3; /* number of files to process */ - - /* Open archive. */ - arcfile = fopen(argv[2], "rb"); - if (arcfile == NULL && cmd != 'A') - error("Can't open archive '%s'", argv[2]); - - /* Open temporary file. */ - if (strchr("ARD", cmd)) { - temp_name = tmpnam(NULL); - outfile = fopen(temp_name, "wb"); - if (outfile == NULL) - error("Can't open temporary file"); - atexit(exitfunc); - } else temp_name = NULL; - - make_crctable(); count = done = 0; - - if (cmd == 'A') { - for (i = 3; i < argc; i++) { - for (j = 3; j < i; j++) - if (strcmp(argv[j], argv[i]) == 0) break; - if (j == i) { - strcpy(filename, argv[i]); - if (add(0)) count++; else argv[i][0] = 0; - } else nfiles--; - } - if (count == 0 || arcfile == NULL) done = 1; - } - - while (! done && read_header()) { - found = search(argc, argv); - switch (cmd) { - case 'R': - if (found) { - if (add(1)) count++; else copy(); - } else copy(); - break; - case 'A': case 'D': - if (found) { - count += (cmd == 'D'); skip(); - } else copy(); - break; - case 'X': case 'P': - if (found) { - extract(cmd == 'X'); - if (++count == nfiles) done = 1; - } else skip(); - break; - case 'L': - if (found) { - if (count == 0) list_start(); - list(); - if (++count == nfiles) done = 1; - } - skip(); break; - } - } - - if (temp_name != NULL && count != 0) { - fputc(0, outfile); /* end of archive */ - if (ferror(outfile) || fclose(outfile) == EOF) - error("Can't write"); - remove(argv[2]); rename(temp_name, argv[2]); - } - - printf(" %d files\n", count); - return EXIT_SUCCESS; -} +/*********************************************************** + 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 +#include +#include +#include +#include +#include +#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) { + /* - - ... */ + argv++; + argc--; + } + else { + /* - => - */ + *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; +}