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 ***********************************************************/
65 struct lha_method methods[] = {
66 /* id, dicbit, pbit, maxmatch */
67 /* note: dicbit == 0 means no compress */
68 /*0*/ {"-lh0-", 0, 0, 0}, /* no compress */
69 /*1*/ {"-lh1-", 12, 0, 60}, /* 2^12 = 4KB dynamic huffman (LHarc) */
70 /*2*/ {"-lh2-", 13, 0,256}, /* 2^13 = 8KB dynamic huffman */
71 /*3*/ {"-lh3-", 13, 0,256}, /* 2^13 = 8KB static huffman */
72 /*4*/ {"-lh4-", 12, 4,256}, /* 2^12 = 4KB static huffman (pos and len)*/
73 /*5*/ {"-lh5-", 13, 4,256}, /* 2^13 = 8KB static huffman (pos and len)*/
74 /*6*/ {"-lh6-", 15, 5,256}, /* 2^15 = 32KB static huffman (pos and len)*/
75 /*7*/ {"-lh7-", 16, 5,256}, /* 2^16 = 64KB static huffman (pos and len)*/
76 /*8*/ {"-lzs-", 11, 0, 17}, /* 2^11 = 2KB (LArc) */
77 /*9*/ {"-lz5-", 12, 0, 17}, /* 2^12 = 4KB (LArc) */
78 /*10*/{"-lz4-", 0, 0, 0}, /* no compress (LArc) */
79 /*11*/{"-lzd-", 0, 0, 0}, /* directory */
85 which_method(char *id)
89 for (i = 0; i < sizeof(methods)/sizeof(methods[0]); i++) {
90 if (strncmp(id, methods[i].id, sizeof(methods[0].id)) == 0) {
102 opts.header_level = 2;
104 /* default is the -lh5- method */
105 opts.method = &methods[5];
108 #define FNAME_MAX (255 - 25) /* max strlen(filename) */
110 int unpackable; /* global, set in io.c */
111 ulong compsize, origsize; /* global */
113 static char *temp_name;
125 printf("version %s\n", version);
130 ratio(ulong a, ulong b)
131 { /* [(1000a + [b/2]) / b] */
134 for (i = 0; i < 3; i++)
135 if (a <= ULONG_MAX / 10)
139 if ((ulong) (a + (b >> 1)) < a) {
145 return (uint) ((a + (b >> 1)) / b);
149 put_to_header(char *buf, int i, int n, ulong x)
152 buf[i++] = (uchar) ((uint) x & 0xFF);
158 get_from_header(char *buf, int i, int n)
164 s = (s << 8) + buf[i + n]; /* little endian */
169 calc_headersum(char *buf, int size)
175 for (i = 0; i < size; i++)
183 return *(unsigned char*)buf;
189 return get_byte(buf) | (get_byte(buf+1) << 8);
195 return get_byte(buf) |
196 (get_byte(buf+1) << 8) |
197 (get_byte(buf+2) << 16) |
198 (get_byte(buf+3) << 24);
202 get_char(char *buf, char *p, size_t size)
204 memcpy(p, buf, size);
208 put_byte(char *buf, int c)
210 *buf = (unsigned char)(c & 0xff);
214 put_word(char *buf, uint16_t c)
217 put_byte(buf+1, c>>8);
221 put_dword(char *buf, uint32_t c)
224 put_byte(buf+1, c>>8);
225 put_byte(buf+2, c>>16);
226 put_byte(buf+3, c>>24);
230 put_char(char *buf, char *p, size_t size)
232 memcpy(buf, p, size);
236 read_header_lv1(FILE *fp, char *buf, struct lzh_header *h)
242 headersize = get_byte(&buf[0]);
243 headersum = get_byte(&buf[1]);
245 fread_crc(buf + 21, headersize - (21 - 2), fp); /* CRC not used */
249 if (calc_headersum(buf, headersize) != headersum)
250 error("Header sum error");
252 get_char(&buf[0], h->method, 5);
253 h->compsize = get_dword(&buf[5]);
254 h->origsize = get_dword(&buf[9]);
255 h->ftime = get_dword(&buf[13]);
256 /* attrib = get_byte(&buf[17]); */
257 h->level = get_byte(&buf[18]); /* header level */
258 h->namelen = get_byte(&buf[19]);
259 get_char(&buf[20], h->filename, h->namelen);
260 h->filename[h->namelen] = 0;
261 h->file_crc = get_word(&buf[20+h->namelen]);
262 h->os_id = get_byte(&buf[20+h->namelen+2]);
264 ext_headersize = get_word(&buf[20+h->namelen+3]);
266 while (ext_headersize != 0) {
267 fprintf(stderr, "There's an extended header of size %u.\n",
269 h->compsize -= ext_headersize;
271 /* skip ext header */
272 if (fseek(arcfile, ext_headersize - 2, SEEK_CUR))
274 ext_headersize = fgetc(arcfile);
275 ext_headersize += (uint) fgetc(arcfile) << 8;
278 return 1; /* success */
282 read_header_lv2(FILE *fp, char *buf, struct lzh_header *h)
291 headersize = get_word(&buf[0]);
293 fread_crc(buf + 21, 26 - 21, fp); /* CRC not used */
295 get_char(&buf[2], h->method, 5);
296 h->compsize = get_dword(&buf[7]);
297 h->origsize = get_dword(&buf[11]);
298 h->ftime = get_dword(&buf[15]);
299 /* attrib = get_byte(&buf[19]); */
300 h->level = get_byte(&buf[20]); /* header level */
301 h->file_crc = get_word(&buf[21]);
302 h->os_id = get_byte(&buf[23]);
304 ext_headersize = get_word(&buf[24]);
306 remainder = headersize - 26;
308 while (ext_headersize != 0) {
311 remainder -= ext_headersize;
313 fread_crc(p, ext_headersize, fp);
314 ext_type = get_byte(p++);
320 /* filename header */
321 h->namelen = ext_headersize - 3;
322 get_char(p, h->filename, h->namelen);
323 h->filename[h->namelen] = 0;
329 ext_headersize = get_word(&extbuf[ext_headersize - 2]);
332 while (remainder > 0) {
333 fgetc(fp); /* skip padding */
337 return 1; /* success */
341 read_header(FILE *fp, struct lzh_header *h)
351 if (buf[0] == 0 || ret == EOF)
352 return 0; /* end of archive */
353 fread_crc(buf + 1, 21 - 1, fp);
358 return read_header_lv1(fp, buf, h);
361 return read_header_lv2(fp, buf, h);
364 error("unknown level (%d)\n", buf[20]);
368 return 1; /* success */
373 write_header_lv0(FILE *fp, struct lzh_header *h)
378 write_header_lv1(FILE *fp, struct lzh_header *h)
380 char buf[4096], *p = buf;
384 headersize = 25 + h->namelen;
386 put_byte(&buf[0], headersize);
387 put_byte(&buf[1], 0); /* dummy */
388 put_char(&buf[2], h->method, 5);
389 put_dword(&buf[7], h->compsize); /* packed size */
390 put_dword(&buf[11], h->origsize); /* original size */
391 put_dword(&buf[15], h->ftime); /* ftime */
392 put_byte(&buf[19], 0x20); /* attribute */
393 put_byte(&buf[20], 1); /* level */
394 put_byte(&buf[21], h->namelen); /* length of pathname */
395 put_char(&buf[22], h->filename, h->namelen);
396 put_word(&buf[22+h->namelen], h->file_crc);
397 put_byte(&buf[22+h->namelen+2], 'M');
398 put_word(&buf[22+h->namelen+3], 0x0000); /* next header size */
400 sum = calc_headersum(buf+2, headersize);
401 put_byte(&buf[1], sum);
403 fwrite_crc(buf, headersize+2, fp);
407 write_header_lv2(FILE *fp, struct lzh_header *h)
409 char buf[4096], *p = buf, *crcptr;
411 int headersize, next_headersize;
412 extern ushort crctable[];
414 put_word(&buf[0], 0); /* dummy */
415 put_char(&buf[2], h->method, 5);
416 put_dword(&buf[7], h->compsize); /* packed size */
417 put_dword(&buf[11], h->origsize); /* original size */
418 put_dword(&buf[15], h->ftime); /* time_t */
419 put_byte(&buf[19], 0x20); /* DOS attribute (0x20 fixed) */
420 put_byte(&buf[20], 2); /* level */
421 put_word(&buf[21], h->file_crc);
422 put_byte(&buf[23], 'M');
427 p = buf + headersize;
428 next_headersize = 3 + 2;
429 put_word(p, next_headersize); /* next header size */
430 put_byte(p+2, 0); /* 0x00: header crc */
432 put_word(p+3, 0); /* crc (dummy) */
433 headersize += next_headersize;
435 p = buf + headersize;
436 next_headersize = 3 + h->namelen;
437 put_word(p, next_headersize); /* next header size */
438 put_byte(p+2, 1); /* 0x01: filename header */
439 put_char(p+3, h->filename, h->namelen); /* filename */
440 headersize += next_headersize;
443 p = buf + headersize;
444 next_headersize = 3 + h->namelen;
445 put_word(p, next_headersize); /* next header size */
446 put_byte(p+2, 2); /* 0x02: dirname header */
447 put_char(p+3, x, len); /* dirname */
448 headersize += next_headersize;
451 p = buf + headersize;
453 put_word(p, next_headersize); /* next header size */
454 headersize += next_headersize;
458 if (headersize % 256 == 0) {
459 put_byte(&buf[headersize], 0);
463 put_word(&buf[0], headersize);
466 for (p = buf; p - buf < headersize; p++)
469 put_word(crcptr, crc);
471 fwrite_crc(buf, headersize, fp);
475 write_header(FILE *fp, struct lzh_header *h)
479 write_header_lv0(fp, h);
482 write_header_lv1(fp, h);
485 write_header_lv2(fp, h);
488 error("unknown level (%d)", h->level);
494 skip(FILE *fp, struct lzh_header *h)
496 fseek(fp, h->compsize, SEEK_CUR);
500 copy(FILE *arcfile, FILE *outfile, struct lzh_header *h)
503 uchar buffer[MAXDICSIZ];
505 write_header(outfile, h);
506 while (h->compsize != 0) {
507 n = (uint) ((h->compsize > sizeof(buffer)) ? sizeof(buffer) : h->compsize);
508 if (fread((char *) buffer, 1, n, arcfile) != n)
510 if (fwrite((char *) buffer, 1, n, outfile) != n)
511 error("Can't write");
520 uchar buffer[MAXDICSIZ];
524 while ((n = fread((char *) buffer, 1, sizeof(buffer), infile)) != 0) {
525 fwrite_crc(buffer, n, outfile);
532 add(int replace_flag, char *filename)
534 long headerpos, arcpos;
538 h.level = opts.header_level;
540 if ((infile = fopen(filename, "rb")) == NULL) {
541 fprintf(stderr, "Can't open %s\n", filename);
542 return 0; /* failure */
546 printf("Replacing %s ", filename);
551 printf("Adding %s ", filename);
554 strcpy(h.filename, filename);
555 h.namelen = strlen(filename);
557 memcpy(h.method, opts.method->id, sizeof(h.method)); /* compress */
559 headerpos = ftell(outfile);
560 write_header(outfile, &h);
561 arcpos = ftell(outfile);
563 origsize = compsize = 0;
565 if (opts.nocompress) {
574 h.method[3] = '0'; /* store */
576 fseek(outfile, arcpos, SEEK_SET);
579 h.file_crc = crc ^ INIT_CRC;
582 h.compsize = compsize;
583 h.origsize = origsize;
585 fseek(outfile, headerpos, SEEK_SET);
586 write_header(outfile, &h);
587 fseek(outfile, 0L, SEEK_END);
588 r = ratio(compsize, origsize);
590 printf(" %d.%d%%\n", r / 10, r % 10);
591 return 1; /* success */
595 get_line(char *s, int n)
600 while ((c = getchar()) != EOF && c != '\n')
608 extract(int to_file, struct lzh_header *h)
612 uchar buffer[MAXDICSIZ];
615 while ((outfile = fopen(h->filename, "wb")) == NULL) {
616 fprintf(stderr, "Can't open %s\nNew filename: ", h->filename);
617 if (get_line(h->filename, FNAME_MAX) == 0) {
618 fprintf(stderr, "Not extracted\n");
622 h->namelen = strlen(h->filename);
625 printf("Extracting %s ", h->filename);
630 printf("===== %s =====\n", h->filename);
633 opts.method = which_method(h->method);
634 if (opts.method == NULL) {
635 fprintf(stderr, "Unknown method: %.5s\n", h->method);
640 if (opts.method->dicbit != 0)
642 while (h->origsize != 0) {
643 n = (uint) ((h->origsize > MAXDICSIZ) ? MAXDICSIZ : h->origsize);
644 if (opts.method->dicbit != 0)
646 else if (fread((char *) buffer, 1, n, arcfile) != n)
648 fwrite_crc(buffer, n, outfile);
649 if (outfile != stdout && opts.quiet < 1) {
659 if ((crc ^ INIT_CRC) != h->file_crc)
660 fprintf(stderr, "CRC error\n");
667 printf("Filename Original Compressed Ratio CRC Method\n");
671 list(struct lzh_header *h)
675 printf("%-14.*s", h->namelen, h->filename);
678 r = ratio(h->compsize, h->origsize);
679 printf(" %10lu %10lu %u.%03u %04X %5.5s\n",
680 h->origsize, h->compsize, r / 1000, r % 1000, h->file_crc, h->method);
684 match(char *s1, char *s2)
687 while (*s2 == '*' || *s2 == '?') {
689 while (*s1 && *s1 != *s2)
706 search(int argc, char *argv[], struct lzh_header *h)
712 for (i = 0; i < argc; i++)
713 if (match(h->filename, argv[i]))
725 #include "getopt_long.h"
728 parse_args(int argc, char **argv)
733 int this_option_optind = optind ? optind : 1;
734 int option_index = 0;
741 static struct option long_options[] = {
742 /* name, has_arg, *flag, val */
745 required_argument (1)
746 optional_argument (2)
748 NULL: getopt_long() return val
749 non-NULL: getopt_long() return 0, and *flag set val.
751 {"help", no_argument, NULL, LHA_OPT_HELP},
752 {"version", no_argument, NULL, LHA_OPT_VERSION},
756 c = getopt_long(argc, argv, "h[012]o[567]q[012]w:z",
757 long_options, &option_index);
759 if (c == -1) break; /* end of parsing options */
763 /* set value by long option */
767 opts.header_level = 2; /* -h is equivalent to -h2 */
769 opts.header_level = *optarg - '0';
772 /* compress method */
774 int idx = 1; /* -o means -lh1- method */
777 idx = *optarg - '0'; /* -lh[567]- method */
779 opts.method = &methods[idx];
784 opts.quiet = 2; /* -q is equivalent to -q2 */
786 opts.quiet = *optarg - '0';
789 /* extract directory */
791 error("extract directory does not specified for `-w'");
795 opts.outdir = optarg;
797 case 'z': /* no compress */
803 case LHA_OPT_VERSION:
815 temp_name = tmpnam(NULL);
816 outfile = fopen(temp_name, "wb");
818 error("Can't open temporary file");
825 main(int argc, char *argv[])
827 int i, j, cmd, count, nfiles, found, done;
836 /*take a command character */
848 /* -<cmd> -<opts> ... */
853 /* -<cmd><opts> => -<opts> */
858 parse_args(argc, argv);
862 archive_file = argv[0];
870 count = done = nfiles = 0;
875 outfile = open_tempfile();
877 error("archived files are not specified.");
878 for (i = 0; i < argc; i++) {
882 fputc(0, outfile); /* end of archive */
884 error("Can't write");
885 remove(archive_file);
886 if (rename(temp_name, archive_file) == -1)
887 error("fail to rename()");
893 outfile = open_tempfile();
899 arcfile = fopen(archive_file, "rb");
901 error("Can't open archive '%s'", argv[2]);
909 /* change directory to extract dir */
914 if (mkdir(opts.outdir, 0777) == -1) {
916 error("cannot make directory \"%s\"", opts.outdir);
919 if (chdir(opts.outdir) == -1)
920 error("cannot change directory \"%s\"", opts.outdir);
924 while (!done && read_header(arcfile, &h)) {
926 compsize = h.compsize;
927 origsize = h.origsize;
929 found = search(argc, argv, &h);
936 copy(arcfile, outfile, &h);
939 copy(arcfile, outfile, &h);
944 count += (cmd == 'D');
948 copy(arcfile, outfile, &h);
953 extract(cmd == 'x', &h);
954 if (++count == nfiles)
966 if (++count == nfiles)
976 printf(" %d files\n", count);