1 /***********************************************************
3 ***********************************************************/
5 static char *version = "0.01";
8 "ar -- compression archiver -- written by Haruhiko Okumura\n"
9 " PC-VAN:SCIENCE CompuServe:74050,1022\n"
10 " NIFTY-Serve:PAF01022 INTERNET:74050.1022@compuserve.com\n"
11 "Usage: ar command archive [file ...]\n"
13 " a: Add files to archive (replace if present)\n"
14 " x: Extract files from archive\n"
15 " r: Replace files in archive\n"
16 " d: Delete files from archive\n"
17 " p: Print files on standard output\n"
18 " l: List contents of archive\n"
19 "If no files are named, all files in archive are processed,\n"
20 " except for commands 'a' and 'd'.\n"
21 "You may copy, distribute, and rewrite this program freely.\n";
23 /***********************************************************
25 Structure of archive block (low order byte first):
28 = 25 + strlen(filename) (= 0 if end of archive)
29 1 basic header algebraic sum (mod 256)
31 5 method ("-lh0-" = stored, "-lh5-" = compressed)
32 4 compressed size (including extended headers)
41 2 first extended header size (0 if none)
42 -----first extended header, etc.
45 ***********************************************************/
55 extern char *basename(const char *);
57 struct lha_method methods[] = {
58 /* id, dicbit, pbit, maxmatch */
59 /* note: dicbit == 0 means no compress */
60 {"-lh0-", 0, 0, 0}, /* 0: no compress */
61 {"-lh1-", 12, 0, 60}, /* 1: 2^12 = 4KB dynamic huffman (LHarc) */
62 {"-lh2-", 13, 0,256}, /* 2: 2^13 = 8KB dynamic huffman */
63 {"-lh3-", 13, 0,256}, /* 3: 2^13 = 8KB static huffman */
64 {"-lh4-", 12, 4,256}, /* 4: 2^12 = 4KB static huffman (pos and len)*/
65 {"-lh5-", 13, 4,256}, /* 5: 2^13 = 8KB static huffman (pos and len)*/
66 {"-lh6-", 15, 5,256}, /* 6: 2^15 = 32KB static huffman (pos and len)*/
67 {"-lh7-", 16, 5,256}, /* 7: 2^16 = 64KB static huffman (pos and len)*/
68 {"-lzs-", 11, 0, 17}, /* 8: 2^11 = 2KB (LArc) */
69 {"-lz5-", 12, 0, 17}, /* 9: 2^12 = 4KB (LArc) */
70 {"-lz4-", 0, 0, 0}, /* 1: no compress (LArc) */
71 {"-lhd-", 0, 0, 0}, /* 1: directory */
77 which_method(char *id)
81 for (i = 0; i < sizeof(methods)/sizeof(methods[0]); i++) {
82 if (strncmp(id, methods[i].id, sizeof(methods[0].id)) == 0) {
94 opts.header_level = 2;
98 /* default is the -lh5- method */
99 opts.method = &methods[5];
102 #define FNAME_MAX (255 - 25) /* max strlen(filename) */
104 int unpackable; /* global, set in io.c */
105 ulong compsize, origsize; /* global */
107 static char *temp_name;
119 printf("version %s\n", version);
124 ratio(ulong a, ulong b)
125 { /* [(1000a + [b/2]) / b] */
128 for (i = 0; i < 3; i++)
129 if (a <= ULONG_MAX / 10)
133 if ((ulong) (a + (b >> 1)) < a) {
139 return (uint) ((a + (b >> 1)) / b);
150 {'M', "MS-DOS"}, /* Microsoft */
151 {'U', "Unix"}, /* Unix or POSIX compliant OS */
152 {'J', "Java"}, /* Sun Microsystems */
154 {'w', "Win9x"}, /* reserved by UNLHA32.DLL */
155 {'W', "WinNT"}, /* reserved by UNLHA32.DLL */
156 {'2', "OS/2"}, /* IBM OS/2 */
157 {'9', "OS9"}, /* unknown */
158 {'K', "OS/68K"}, /* unknown */
159 {'3', "OS/386"}, /* unknown */
160 {'H', "Human"}, /* SHARP Human68K */
161 {'C', "CP/M"}, /* Digital Research */
162 {'F', "FLEX"}, /* unknown */
163 {'m', "Mac"}, /* Apple */
164 {'R', "Runser"}, /* unknown */
165 {'T', "TownsOS"}, /* Fujitsu FM-TOWNS */
166 {'X', "XOSK"}, /* unknown */
169 for (i = 0; i < sizeof(os_types)/sizeof(os_types[0]); i++) {
170 if (id == os_types[i].id)
171 return os_types[i].name;
178 put_to_header(char *buf, int i, int n, ulong x)
181 buf[i++] = (uchar) ((uint) x & 0xFF);
187 get_from_header(char *buf, int i, int n)
193 s = (s << 8) + buf[i + n]; /* little endian */
198 calc_headersum(char *buf, int size)
204 for (i = 0; i < size; i++)
212 return *(unsigned char*)buf;
218 return get_byte(buf) | (get_byte(buf+1) << 8);
224 return get_byte(buf) |
225 (get_byte(buf+1) << 8) |
226 (get_byte(buf+2) << 16) |
227 (get_byte(buf+3) << 24);
231 get_char(char *buf, char *p, size_t size)
233 memcpy(p, buf, size);
237 put_byte(char *buf, int c)
239 *buf = (unsigned char)(c & 0xff);
243 put_word(char *buf, uint16_t c)
246 put_byte(buf+1, c>>8);
250 put_dword(char *buf, uint32_t c)
253 put_byte(buf+1, c>>8);
254 put_byte(buf+2, c>>16);
255 put_byte(buf+3, c>>24);
259 put_char(char *buf, char *p, size_t size)
261 memcpy(buf, p, size);
265 read_header_lv0(FILE *fp, char *buf, struct lzh_header *h)
271 headersize = get_byte(&buf[0]);
272 headersum = get_byte(&buf[1]);
274 fread_crc(buf + 21, headersize - (21 - 2), fp); /* CRC not used */
278 if (calc_headersum(buf, headersize) != headersum)
279 error("Header sum error");
281 get_char(&buf[0], h->method, 5);
282 h->compsize = get_dword(&buf[5]);
283 h->origsize = get_dword(&buf[9]);
284 h->ftime = get_dword(&buf[13]);
285 /* attrib = get_byte(&buf[17]); */
286 h->level = get_byte(&buf[18]); /* header level */
287 h->namelen = get_byte(&buf[19]);
288 get_char(&buf[20], h->filename, h->namelen);
289 h->filename[h->namelen] = 0;
290 h->file_crc = get_word(&buf[20+h->namelen]);
291 h->os_id = 0; /* generic */
293 return 1; /* success */
297 read_header_lv1(FILE *fp, char *buf, struct lzh_header *h)
303 headersize = get_byte(&buf[0]);
304 headersum = get_byte(&buf[1]);
306 fread_crc(buf + 21, headersize - (21 - 2), fp); /* CRC not used */
310 if (calc_headersum(buf, headersize) != headersum)
311 error("Header sum error");
313 get_char(&buf[0], h->method, 5);
314 h->compsize = get_dword(&buf[5]);
315 h->origsize = get_dword(&buf[9]);
316 h->ftime = get_dword(&buf[13]);
317 /* attrib = get_byte(&buf[17]); */
318 h->level = get_byte(&buf[18]); /* header level */
319 h->namelen = get_byte(&buf[19]);
320 get_char(&buf[20], h->filename, h->namelen);
321 h->filename[h->namelen] = 0;
322 h->file_crc = get_word(&buf[20+h->namelen]);
323 h->os_id = get_byte(&buf[20+h->namelen+2]);
325 ext_headersize = get_word(&buf[20+h->namelen+3]);
327 while (ext_headersize != 0) {
328 fprintf(stderr, "There's an extended header of size %u.\n",
330 h->compsize -= ext_headersize;
332 /* skip ext header */
333 if (fseek(arcfile, ext_headersize - 2, SEEK_CUR))
335 ext_headersize = fgetc(arcfile);
336 ext_headersize += (uint) fgetc(arcfile) << 8;
339 return 1; /* success */
343 read_header_lv2(FILE *fp, char *buf, struct lzh_header *h)
351 char dirname[1024] = "";
354 headersize = get_word(&buf[0]);
356 fread_crc(buf + 21, 26 - 21, fp); /* CRC not used */
358 get_char(&buf[2], h->method, 5);
359 h->compsize = get_dword(&buf[7]);
360 h->origsize = get_dword(&buf[11]);
361 h->ftime = get_dword(&buf[15]);
362 /* attrib = get_byte(&buf[19]); */
363 h->level = get_byte(&buf[20]); /* header level */
364 h->file_crc = get_word(&buf[21]);
365 h->os_id = get_byte(&buf[23]);
367 ext_headersize = get_word(&buf[24]);
369 remainder = headersize - 26;
371 while (ext_headersize != 0) {
374 remainder -= ext_headersize;
376 fread_crc(p, ext_headersize, fp);
377 ext_type = get_byte(p++);
383 /* filename header */
384 h->namelen = ext_headersize - 3;
385 get_char(p, h->filename, h->namelen);
386 h->filename[h->namelen] = 0;
390 dirnamelen = ext_headersize - 3;
391 get_char(p, dirname, dirnamelen);
392 dirname[dirnamelen] = 0;
395 ext_headersize = get_word(&extbuf[ext_headersize - 2]);
397 if (dirnamelen > 0 && dirname[dirnamelen-1] != '/') {
398 dirname[dirnamelen++] = '/';
401 strcat(dirname, h->filename);
402 h->namelen = strlen(dirname);
403 strcpy(h->filename, dirname);
405 while (remainder > 0) {
406 fgetc(fp); /* skip padding */
410 return 1; /* success */
414 read_header(FILE *fp, struct lzh_header *h)
424 if (buf[0] == 0 || ret == EOF)
425 return 0; /* end of archive */
426 fread_crc(buf + 1, 21 - 1, fp);
429 return read_header_lv0(fp, buf, h);
432 return read_header_lv1(fp, buf, h);
435 return read_header_lv2(fp, buf, h);
438 error("unknown level (%d)\n", buf[20]);
442 return 1; /* success */
447 write_header_lv0(FILE *fp, struct lzh_header *h)
449 char buf[4096], *p = buf;
453 headersize = 22 + h->namelen;
455 put_byte(&buf[0], 0); /* dummy */
456 put_byte(&buf[1], 0); /* dummy */
457 put_char(&buf[2], h->method, 5);
458 put_dword(&buf[7], h->compsize); /* packed size */
459 put_dword(&buf[11], h->origsize); /* original size */
460 put_dword(&buf[15], h->ftime); /* ftime */
461 put_byte(&buf[19], 0x20); /* attribute */
462 put_byte(&buf[20], 0); /* level */
463 put_byte(&buf[21], h->namelen); /* length of pathname */
464 put_char(&buf[22], h->filename, h->namelen);
465 put_word(&buf[22+h->namelen], h->file_crc);
467 headersize += 0; /* size of ext-header (old style) */
468 put_byte(&buf[0], headersize);
470 sum = calc_headersum(buf+2, headersize);
471 put_byte(&buf[1], sum);
473 fwrite_crc(buf, headersize+2, fp);
477 write_header_lv1(FILE *fp, struct lzh_header *h)
479 char buf[4096], *p = buf;
483 headersize = 25 + h->namelen;
485 put_byte(&buf[0], headersize);
486 put_byte(&buf[1], 0); /* dummy */
487 put_char(&buf[2], h->method, 5);
488 put_dword(&buf[7], h->compsize); /* packed size */
489 put_dword(&buf[11], h->origsize); /* original size */
490 put_dword(&buf[15], h->ftime); /* ftime */
491 put_byte(&buf[19], 0x20); /* attribute */
492 put_byte(&buf[20], 1); /* level */
493 put_byte(&buf[21], h->namelen); /* length of pathname */
494 put_char(&buf[22], h->filename, h->namelen);
495 put_word(&buf[22+h->namelen], h->file_crc);
497 put_byte(&buf[22+h->namelen+2], '\0');
499 put_byte(&buf[22+h->namelen+2], 'U');
500 put_word(&buf[22+h->namelen+3], 0x0000); /* next header size */
502 sum = calc_headersum(buf+2, headersize);
503 put_byte(&buf[1], sum);
505 fwrite_crc(buf, headersize+2, fp);
509 write_header_lv2(FILE *fp, struct lzh_header *h)
511 char buf[4096], *p = buf, *crcptr;
513 int headersize, next_headersize;
514 extern ushort crctable[];
515 char dirname[1024] = "", *fname;
518 put_word(&buf[0], 0); /* dummy */
519 put_char(&buf[2], h->method, 5);
520 put_dword(&buf[7], h->compsize); /* packed size */
521 put_dword(&buf[11], h->origsize); /* original size */
522 put_dword(&buf[15], h->ftime); /* time_t */
523 put_byte(&buf[19], 0x20); /* DOS attribute (0x20 fixed) */
524 put_byte(&buf[20], 2); /* level */
525 put_word(&buf[21], h->file_crc);
527 put_byte(&buf[23], '\0');
529 put_byte(&buf[23], 'U');
534 p = buf + headersize;
535 next_headersize = 3 + 2;
536 put_word(p, next_headersize); /* next header size */
537 put_byte(p+2, 0); /* 0x00: header crc */
539 put_word(p+3, 0); /* crc (dummy) */
540 headersize += next_headersize;
542 fname = basename(h->filename);
545 p = buf + headersize;
546 next_headersize = 3 + len;
547 put_word(p, next_headersize); /* next header size */
548 put_byte(p+2, 1); /* 0x01: filename header */
549 put_char(p+3, fname, len); /* filename */
550 headersize += next_headersize;
555 ptr = strrchr(h->filename, '/');
560 dirnamelen = ptr - h->filename;
561 strncpy(dirname, h->filename, dirnamelen);
562 dirname[dirnamelen+ 1] = 0;
567 p = buf + headersize;
568 next_headersize = 3 + dirnamelen;
569 put_word(p, next_headersize); /* next header size */
570 put_byte(p+2, 2); /* 0x02: dirname header */
571 put_char(p+3, dirname, dirnamelen); /* dirname */
572 headersize += next_headersize;
575 p = buf + headersize;
577 put_word(p, next_headersize); /* next header size */
578 headersize += next_headersize;
582 if (headersize % 256 == 0) {
583 put_byte(&buf[headersize], 0);
587 put_word(&buf[0], headersize);
590 for (p = buf; p - buf < headersize; p++)
593 put_word(crcptr, crc);
595 fwrite_crc(buf, headersize, fp);
599 write_header(FILE *fp, struct lzh_header *h)
603 write_header_lv0(fp, h);
606 write_header_lv1(fp, h);
609 write_header_lv2(fp, h);
612 error("unknown level (%d)", h->level);
618 skip(FILE *fp, struct lzh_header *h)
620 fseek(fp, h->compsize, SEEK_CUR);
624 copy(FILE *arcfile, FILE *outfile, struct lzh_header *h)
627 uchar buffer[MAXDICSIZ];
629 write_header(outfile, h);
630 while (h->compsize != 0) {
631 n = (uint) ((h->compsize > sizeof(buffer)) ? sizeof(buffer) : h->compsize);
632 if (fread((char *) buffer, 1, n, arcfile) != n)
634 if (fwrite((char *) buffer, 1, n, outfile) != n)
635 error("Can't write");
644 uchar buffer[MAXDICSIZ];
648 while ((n = fread((char *) buffer, 1, sizeof(buffer), infile)) != 0) {
649 fwrite_crc(buffer, n, outfile);
656 add_dir(int replace_flag, struct lzh_header *h)
658 long headerpos, arcpos;
661 h->origsize = h->compsize = 0;
662 h->file_crc = INIT_CRC;
664 headerpos = ftell(outfile);
665 write_header(outfile, h);
666 arcpos = ftell(outfile);
669 printf(" %d.%d%%\n", r / 10, r % 10);
670 return 1; /* success */
674 add_1(int replace_flag, struct lzh_header *h)
676 long headerpos, arcpos;
679 if ((infile = fopen(h->filename, "rb")) == NULL) {
680 fprintf(stderr, "Can't open %s\n", h->filename);
681 return 0; /* failure */
685 printf("Replacing %s ", h->filename);
690 printf("Adding %s ", h->filename);
693 headerpos = ftell(outfile);
694 write_header(outfile, h);
695 arcpos = ftell(outfile);
697 origsize = compsize = 0;
699 if (opts.nocompress) {
708 memcpy(h->method, "-lh0-", sizeof(h->method)); /* store */
710 fseek(outfile, arcpos, SEEK_SET);
713 h->file_crc = crc ^ INIT_CRC;
716 h->compsize = compsize;
717 h->origsize = origsize;
719 fseek(outfile, headerpos, SEEK_SET);
720 write_header(outfile, h);
721 fseek(outfile, 0L, SEEK_END);
722 r = ratio(compsize, origsize);
724 printf(" %d.%d%%\n", r / 10, r % 10);
725 return 1; /* success */
729 add(int replace_flag, char *filename, int namelen)
731 long headerpos, arcpos;
736 memset(&h, 0, sizeof(h));
738 h.level = opts.header_level;
740 strcpy(h.filename, filename);
743 stat(h.filename, &st);
745 if (S_ISDIR(st.st_mode)) {
749 memcpy(h.method, "-lhd-", sizeof(h.method)); /* directory */
750 add_dir(replace_flag, &h);
752 dir = opendir(h.filename);
754 error("cannot open directory: \"%s\"", h.filename);
756 while ((ent = readdir(dir)) != 0) {
759 if (string_equal(ent->d_name, ".") ||
760 string_equal(ent->d_name, ".."))
763 h.namelen = path_addsep(h.filename, sizeof(h.filename));
765 string_cat(filename, sizeof(filename),
766 h.filename, ent->d_name, NULL);
768 add(replace_flag, filename, strlen(filename));
773 memcpy(h.method, opts.method->id, sizeof(h.method)); /* compress */
774 add_1(replace_flag, &h);
779 get_line(char *s, int n)
784 while ((c = getchar()) != EOF && c != '\n')
792 extract(int to_file, struct lzh_header *h)
796 uchar buffer[MAXDICSIZ];
801 if (memcmp(h->method, "-lhd-", sizeof(h->method)) == 0) {
803 if (mkdir(h->filename, 0777) == -1) {
805 error("cannot make directory \"%s\"", opts.outdir);
810 while ((outfile = fopen(h->filename, "wb")) == NULL) {
811 fprintf(stderr, "Can't open %s\nNew filename: ", h->filename);
812 if (get_line(h->filename, FNAME_MAX) == 0) {
813 fprintf(stderr, "Not extracted\n");
817 h->namelen = strlen(h->filename);
821 printf("Extracting %s ", h->filename);
826 printf("===== %s =====\n", h->filename);
829 opts.method = which_method(h->method);
830 if (opts.method == NULL) {
831 fprintf(stderr, "Unknown method: %.5s\n", h->method);
836 if (opts.method->dicbit != 0)
838 while (h->origsize != 0) {
839 n = (uint) ((h->origsize > MAXDICSIZ) ? MAXDICSIZ : h->origsize);
840 if (opts.method->dicbit != 0)
842 else if (fread((char *) buffer, 1, n, arcfile) != n)
844 fwrite_crc(buffer, n, outfile);
845 if (outfile != stdout && opts.quiet < 1) {
852 if ((crc ^ INIT_CRC) != h->file_crc)
856 fprintf(stdout, "\n");
867 printf("%-14.14s %-7.7s %10.10s %10.10s %-5.5s %-4.4s %-5.5s %-4.4s\n",
879 list(struct lzh_header *h)
883 printf("%-14.*s", h->namelen, h->filename);
886 r = ratio(h->compsize, h->origsize);
887 printf(" %-7s %10lu %10lu %u.%03u %04x %-6.6s [%d]\n",
899 match(char *s1, char *s2)
902 while (*s2 == '*' || *s2 == '?') {
904 while (*s1 && *s1 != *s2)
921 search(int argc, char *argv[], struct lzh_header *h)
927 for (i = 0; i < argc; i++)
928 if (match(h->filename, argv[i]))
940 #include "getopt_long.h"
943 parse_args(int argc, char **argv)
948 int this_option_optind = optind ? optind : 1;
949 int option_index = 0;
956 static struct option long_options[] = {
957 /* name, has_arg, *flag, val */
960 required_argument (1)
961 optional_argument (2)
963 NULL: getopt_long() return val
964 non-NULL: getopt_long() return 0, and *flag set val.
966 {"help", no_argument, NULL, LHA_OPT_HELP},
967 {"version", no_argument, NULL, LHA_OPT_VERSION},
971 c = getopt_long(argc, argv, "012go[567]q[012]vw:z",
972 long_options, &option_index);
974 if (c == -1) break; /* end of parsing options */
981 /* set value by long option */
983 case '0': case '1': case '2':
985 opts.header_level = c - '0';
991 /* compress method */
993 int idx = 1; /* -o means -lh1- method */
996 idx = *optarg - '0'; /* -lh[567]- method */
998 opts.method = &methods[idx];
1003 opts.quiet = 2; /* -q is equivalent to -q2 */
1005 opts.quiet = *optarg - '0';
1013 /* extract directory */
1015 error("extract directory does not specified for `-w'");
1019 opts.outdir = optarg;
1021 case 'z': /* no compress */
1022 opts.nocompress = 1;
1027 case LHA_OPT_VERSION:
1039 temp_name = tmpnam(NULL);
1040 outfile = fopen(temp_name, "wb");
1041 if (outfile == NULL)
1042 error("Can't open temporary file");
1049 main(int argc, char *argv[])
1051 int i, j, cmd, count, nfiles, found, done;
1053 struct lzh_header h;
1060 /*take a command character */
1072 /* -<cmd> -<opts> ... */
1077 /* -<cmd><opts> => -<opts> */
1082 parse_args(argc, argv);
1086 archive_file = argv[0];
1094 count = done = nfiles = 0;
1099 outfile = open_tempfile();
1101 error("archived files are not specified.");
1102 for (i = 0; i < argc; i++) {
1103 add(0, argv[i], strlen(argv[i]));
1106 fputc(0, outfile); /* end of archive */
1107 if (ferror(outfile))
1108 error("Can't write");
1109 remove(archive_file);
1110 if (xrename(temp_name, archive_file) == -1)
1111 error("fail to rename(): %s -> %s",temp_name,archive_file);
1117 outfile = open_tempfile();
1123 arcfile = fopen(archive_file, "rb");
1124 if (arcfile == NULL)
1125 error("Can't open archive '%s'", argv[2]);
1133 /* change directory to extract dir */
1138 if (mkdir(opts.outdir, 0777) == -1) {
1139 if (errno != EEXIST)
1140 error("cannot make directory \"%s\"", opts.outdir);
1143 if (chdir(opts.outdir) == -1)
1144 error("cannot change directory \"%s\"", opts.outdir);
1148 while (!done && read_header(arcfile, &h)) {
1150 compsize = h.compsize;
1151 origsize = h.origsize;
1153 found = search(argc, argv, &h);
1157 if (add(1, *argv, strlen(*argv)))
1160 copy(arcfile, outfile, &h);
1163 copy(arcfile, outfile, &h);
1168 count += (cmd == 'D');
1172 copy(arcfile, outfile, &h);
1177 extract(cmd == 'x', &h);
1178 if (++count == nfiles)
1190 if (++count == nfiles)
1200 printf(" %d files\n", count);
1202 return EXIT_SUCCESS;