OSDN Git Service

pass the lha-test3
[lha/olha.git] / ar.c
1 /***********************************************************
2         ar.c -- main file
3 ***********************************************************/
4
5 static char *version = "0.01";
6
7 static char *usage =
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"
12     "Commands:\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";
22
23 /***********************************************************
24
25 Structure of archive block (low order byte first):
26 -----preheader
27  1      basic header size
28                 = 25 + strlen(filename) (= 0 if end of archive)
29  1      basic header algebraic sum (mod 256)
30 -----basic header
31  5      method ("-lh0-" = stored, "-lh5-" = compressed)
32  4      compressed size (including extended headers)
33  4      original size
34  4      not used
35  1      0x20
36  1      0x01
37  1      filename length (x)
38  x      filename
39  2      original file's CRC
40  1      0x20
41  2      first extended header size (0 if none)
42 -----first extended header, etc.
43 -----compressed file
44
45 ***********************************************************/
46
47 #include <stdlib.h>
48 #include <string.h>
49 #include <ctype.h>
50 #include <errno.h>
51 #include <dirent.h>
52 #include <sys/stat.h>
53 #include "ar.h"
54
55 extern char *basename(const char *);
56
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 */
72 };
73
74 struct lha_opts opts;
75
76 struct lha_method *
77 which_method(char *id)
78 {
79     int i;
80
81     for (i = 0; i < sizeof(methods)/sizeof(methods[0]); i++) {
82         if (strncmp(id, methods[i].id, sizeof(methods[0].id)) == 0) {
83             return &methods[i];
84         }
85     }
86     return NULL;
87 }
88
89 init_opts()
90 {
91     opts.nocompress = 0;
92     opts.outdir = NULL;
93     opts.quiet = 0;
94     opts.header_level = 2;
95     opts.generic = 0;
96     opts.verbose = 0;
97
98     /* default is the -lh5- method */
99     opts.method   = &methods[5];
100 }
101
102 #define FNAME_MAX (255 - 25)    /* max strlen(filename) */
103
104 int unpackable;                 /* global, set in io.c */
105 ulong compsize, origsize;       /* global */
106
107 static char *temp_name;
108
109 static void
110 print_usage()
111 {
112     printf("%s", usage);
113     exit(0);
114 }
115
116 static void
117 print_version()
118 {
119     printf("version %s\n", version);
120     exit(0);
121 }
122
123 static uint
124 ratio(ulong a, ulong b)
125 {                               /* [(1000a + [b/2]) / b] */
126     int i;
127
128     for (i = 0; i < 3; i++)
129         if (a <= ULONG_MAX / 10)
130             a *= 10;
131         else
132             b /= 10;
133     if ((ulong) (a + (b >> 1)) < a) {
134         a >>= 1;
135         b >>= 1;
136     }
137     if (b == 0)
138         return 0;
139     return (uint) ((a + (b >> 1)) / b);
140 }
141
142 static char*
143 os_string(char id)
144 {
145     int i;
146     static struct os {
147         char id;
148         char *name;
149     } os_types[] = {
150         {'M',  "MS-DOS"},       /* Microsoft */
151         {'U',  "Unix"},         /* Unix or POSIX compliant OS */
152         {'J',  "Java"},         /* Sun Microsystems */
153         {'\0', "generic"},
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 */
167     };
168
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;
172     }
173
174     return "Unknown";
175 }
176
177 static void
178 put_to_header(char *buf, int i, int n, ulong x)
179 {
180     while (--n >= 0) {
181         buf[i++] = (uchar) ((uint) x & 0xFF);
182         x >>= 8;
183     }
184 }
185
186 static ulong
187 get_from_header(char *buf, int i, int n)
188 {
189     ulong s;
190
191     s = 0;
192     while (--n >= 0)
193         s = (s << 8) + buf[i + n];   /* little endian */
194     return s;
195 }
196
197 static uint
198 calc_headersum(char *buf, int size)
199 {
200     int i;
201     uint s;
202
203     s = 0;
204     for (i = 0; i < size; i++)
205         s += buf[i];
206     return s & 0xFF;
207 }
208
209 int
210 get_byte(char *buf)
211 {
212     return *(unsigned char*)buf;
213 }
214
215 uint16_t
216 get_word(char *buf)
217 {
218     return get_byte(buf) | (get_byte(buf+1) << 8);
219 }
220
221 uint32_t
222 get_dword(char *buf)
223 {
224     return get_byte(buf) |
225         (get_byte(buf+1) << 8) |
226         (get_byte(buf+2) << 16) |
227         (get_byte(buf+3) << 24);
228 }
229
230 void
231 get_char(char *buf, char *p, size_t size)
232 {
233     memcpy(p, buf, size);
234 }
235
236 void
237 put_byte(char *buf, int c)
238 {
239     *buf = (unsigned char)(c & 0xff);
240 }
241
242 void
243 put_word(char *buf, uint16_t c)
244 {
245     put_byte(buf,   c);
246     put_byte(buf+1, c>>8);
247 }
248
249 void
250 put_dword(char *buf, uint32_t c)
251 {
252     put_byte(buf, c);
253     put_byte(buf+1, c>>8);
254     put_byte(buf+2, c>>16);
255     put_byte(buf+3, c>>24);
256 }
257
258 void
259 put_char(char *buf, char *p, size_t size)
260 {
261     memcpy(buf, p, size);
262 }
263
264 static int
265 read_header_lv0(FILE *fp, char *buf, struct lzh_header *h)
266 {
267     int headersize;
268     int headersum;
269     int ext_headersize;
270
271     headersize = get_byte(&buf[0]);
272     headersum = get_byte(&buf[1]);
273
274     fread_crc(buf + 21, headersize - (21 - 2), fp);     /* CRC not used */
275
276     buf += 2;
277
278     if (calc_headersum(buf, headersize) != headersum)
279         error("Header sum error");
280
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 */
292
293     return 1;                   /* success */
294 }
295
296 static int
297 read_header_lv1(FILE *fp, char *buf, struct lzh_header *h)
298 {
299     int headersize;
300     int headersum;
301     int ext_headersize;
302
303     headersize = get_byte(&buf[0]);
304     headersum = get_byte(&buf[1]);
305
306     fread_crc(buf + 21, headersize - (21 - 2), fp);     /* CRC not used */
307
308     buf += 2;
309
310     if (calc_headersum(buf, headersize) != headersum)
311         error("Header sum error");
312
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]);
324
325     ext_headersize = get_word(&buf[20+h->namelen+3]);
326
327     while (ext_headersize != 0) {
328         fprintf(stderr, "There's an extended header of size %u.\n",
329                 ext_headersize);
330         h->compsize -= ext_headersize;
331
332         /* skip ext header */
333         if (fseek(arcfile, ext_headersize - 2, SEEK_CUR))
334             error("Can't read");
335         ext_headersize = fgetc(arcfile);
336         ext_headersize += (uint) fgetc(arcfile) << 8;
337     }
338
339     return 1;                   /* success */
340 }
341
342 static int
343 read_header_lv2(FILE *fp, char *buf, struct lzh_header *h)
344 {
345     int headersize;
346     int headersum;
347     int ext_headersize;
348     char extbuf[1024];
349     uchar ext_type;
350     int remainder;
351     char dirname[1024] = "";
352     int dirnamelen = 0;
353
354     headersize = get_word(&buf[0]);
355
356     fread_crc(buf + 21, 26 - 21, fp);     /* CRC not used */
357
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]);
366
367     ext_headersize = get_word(&buf[24]);
368
369     remainder = headersize - 26;
370
371     while (ext_headersize != 0) {
372         char *p = extbuf;
373
374         remainder -= ext_headersize;
375
376         fread_crc(p, ext_headersize, fp);
377         ext_type = get_byte(p++);
378         switch (ext_type) {
379         case 0:
380             /* header crc */
381             break;
382         case 1:
383             /* filename header */
384             h->namelen = ext_headersize - 3;
385             get_char(p, h->filename, h->namelen);
386             h->filename[h->namelen] = 0;
387             break;
388         case 2:
389             /* dirname header */
390             dirnamelen = ext_headersize - 3;
391             get_char(p, dirname, dirnamelen);
392             dirname[dirnamelen] = 0;
393             break;
394         }
395         ext_headersize = get_word(&extbuf[ext_headersize - 2]);
396     }
397     if (dirnamelen > 0 && dirname[dirnamelen-1] != '/') {
398         dirname[dirnamelen++] = '/';
399     }
400
401     strcat(dirname, h->filename);
402     h->namelen = strlen(dirname);
403     strcpy(h->filename, dirname);
404
405     while (remainder > 0) {
406         fgetc(fp); /* skip padding */
407         remainder--;
408     }
409
410     return 1;                   /* success */
411 }
412
413 static int
414 read_header(FILE *fp, struct lzh_header *h)
415 {
416     int headersize;
417     int headersum;
418     char buf[4096];
419     int ext_headersize;
420     int ret;
421
422     ret = fgetc(fp);
423     buf[0] = (uchar)ret;
424     if (buf[0] == 0 || ret == EOF)
425         return 0;               /* end of archive */
426     fread_crc(buf + 1, 21 - 1, fp);
427     switch (buf[20]) {
428     case 0:
429         return read_header_lv0(fp, buf, h);
430         break;
431     case 1:
432         return read_header_lv1(fp, buf, h);
433         break;
434     case 2:
435         return read_header_lv2(fp, buf, h);
436         break;
437     default:
438         error("unknown level (%d)\n", buf[20]);
439         break;
440     }
441
442     return 1;                   /* success */
443 }
444
445
446 void
447 write_header_lv0(FILE *fp, struct lzh_header *h)
448 {
449     char buf[4096], *p = buf;
450     int sum;
451     int headersize;
452
453     headersize = 22 + h->namelen;
454
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);
466
467     headersize += 0;            /* size of ext-header (old style) */
468     put_byte(&buf[0], headersize);
469
470     sum = calc_headersum(buf+2, headersize);
471     put_byte(&buf[1], sum);
472
473     fwrite_crc(buf, headersize+2, fp);
474 }
475
476 void
477 write_header_lv1(FILE *fp, struct lzh_header *h)
478 {
479     char buf[4096], *p = buf;
480     int sum;
481     int headersize;
482
483     headersize = 25 + h->namelen;
484
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);
496     if (opts.generic)
497         put_byte(&buf[22+h->namelen+2], '\0');
498     else
499         put_byte(&buf[22+h->namelen+2], 'U');
500     put_word(&buf[22+h->namelen+3], 0x0000); /* next header size */
501
502     sum = calc_headersum(buf+2, headersize);
503     put_byte(&buf[1], sum);
504
505     fwrite_crc(buf, headersize+2, fp);
506 }
507
508 void
509 write_header_lv2(FILE *fp, struct lzh_header *h)
510 {
511     char buf[4096], *p = buf, *crcptr;
512     int sum;
513     int headersize, next_headersize;
514     extern ushort crctable[];
515     char dirname[1024] = "", *fname;
516     int dirnamelen, len;
517
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);
526     if (opts.generic)
527         put_byte(&buf[23], '\0');
528     else
529         put_byte(&buf[23], 'U');
530
531     headersize = 24;
532
533     /* ext-header */
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 */
538     crcptr = p+3;
539     put_word(p+3, 0);             /* crc (dummy) */
540     headersize += next_headersize;
541
542     fname = basename(h->filename);
543     len = strlen(fname);
544
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;
551
552     {
553         char *ptr;
554
555         ptr = strrchr(h->filename, '/');
556         if (ptr) {
557             /* 0123 */
558             /* abc/ */
559             /* 3 - 0 = 3 */
560             dirnamelen = ptr - h->filename;
561             strncpy(dirname, h->filename, dirnamelen);
562             dirname[dirnamelen+ 1] = 0;
563         }
564     }
565
566     if (*dirname) {
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;
573     }
574
575     p = buf + headersize;
576     next_headersize = 0;
577     put_word(p, next_headersize); /* next header size */
578     headersize += next_headersize;
579     headersize += 2;
580
581     /* padding */
582     if (headersize % 256 == 0) {
583         put_byte(&buf[headersize], 0);
584         headersize++;
585     }
586
587     put_word(&buf[0], headersize);
588
589     crc = INIT_CRC;
590     for (p = buf; p - buf < headersize; p++)
591         UPDATE_CRC(*p);
592
593     put_word(crcptr, crc);
594
595     fwrite_crc(buf, headersize, fp);
596 }
597
598 void
599 write_header(FILE *fp, struct lzh_header *h)
600 {
601     switch (h->level) {
602     case 0:
603         write_header_lv0(fp, h);
604         break;
605     case 1:
606         write_header_lv1(fp, h);
607         break;
608     case 2:
609         write_header_lv2(fp, h);
610         break;
611     default:
612         error("unknown level (%d)", h->level);
613         break;
614     }
615 }
616
617 static void
618 skip(FILE *fp, struct lzh_header *h)
619 {
620     fseek(fp, h->compsize, SEEK_CUR);
621 }
622
623 static void
624 copy(FILE *arcfile, FILE *outfile, struct lzh_header *h)
625 {
626     uint n;
627     uchar buffer[MAXDICSIZ];
628
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)
633             error("Can't read");
634         if (fwrite((char *) buffer, 1, n, outfile) != n)
635             error("Can't write");
636         h->compsize -= n;
637     }
638 }
639
640 static void
641 store(void)
642 {
643     uint n;
644     uchar buffer[MAXDICSIZ];
645
646     origsize = 0;
647     crc = INIT_CRC;
648     while ((n = fread((char *) buffer, 1, sizeof(buffer), infile)) != 0) {
649         fwrite_crc(buffer, n, outfile);
650         origsize += n;
651     }
652     compsize = origsize;
653 }
654
655 static int
656 add_dir(int replace_flag, struct lzh_header *h)
657 {
658     long headerpos, arcpos;
659     uint r;
660
661     h->origsize = h->compsize = 0;
662     h->file_crc = INIT_CRC;
663
664     headerpos = ftell(outfile);
665     write_header(outfile, h);
666     arcpos = ftell(outfile);
667
668     if (opts.quiet < 2)
669         printf(" %d.%d%%\n", r / 10, r % 10);
670     return 1;                   /* success */
671 }
672
673 static int
674 add_1(int replace_flag, struct lzh_header *h)
675 {
676     long headerpos, arcpos;
677     uint r;
678
679     if ((infile = fopen(h->filename, "rb")) == NULL) {
680         fprintf(stderr, "Can't open %s\n", h->filename);
681         return 0;               /* failure */
682     }
683     if (replace_flag) {
684         if (opts.quiet < 2)
685             printf("Replacing %s ", h->filename);
686         skip(arcfile, h);
687     }
688     else {
689         if (opts.quiet < 2)
690             printf("Adding %s ", h->filename);
691     }
692
693     headerpos = ftell(outfile);
694     write_header(outfile, h);
695     arcpos = ftell(outfile);
696
697     origsize = compsize = 0;
698     crc = INIT_CRC;
699     if (opts.nocompress) {
700         unpackable = 1;
701     }
702     else {
703         unpackable = 0;
704         encode();
705     }
706
707     if (unpackable) {
708         memcpy(h->method, "-lh0-", sizeof(h->method));  /* store */
709         rewind(infile);
710         fseek(outfile, arcpos, SEEK_SET);
711         store();
712     }
713     h->file_crc = crc ^ INIT_CRC;
714     fclose(infile);
715
716     h->compsize = compsize;
717     h->origsize = origsize;
718
719     fseek(outfile, headerpos, SEEK_SET);
720     write_header(outfile, h);
721     fseek(outfile, 0L, SEEK_END);
722     r = ratio(compsize, origsize);
723     if (opts.quiet < 2)
724         printf(" %d.%d%%\n", r / 10, r % 10);
725     return 1;                   /* success */
726 }
727
728 static int
729 add(int replace_flag, char *filename, int namelen)
730 {
731     long headerpos, arcpos;
732     uint r;
733     struct lzh_header h;
734     struct stat st;
735
736     memset(&h, 0, sizeof(h));
737
738     h.level = opts.header_level;
739
740     strcpy(h.filename, filename);
741     h.namelen = namelen;
742
743     stat(h.filename, &st);
744
745     if (S_ISDIR(st.st_mode)) {
746         DIR *dir;
747         struct dirent *ent;
748
749         memcpy(h.method, "-lhd-", sizeof(h.method));  /* directory */
750         add_dir(replace_flag, &h);
751
752         dir = opendir(h.filename);
753         if (dir == NULL)
754             error("cannot open directory: \"%s\"", h.filename);
755
756         while ((ent = readdir(dir)) != 0) {
757             char filename[1024];
758
759             if (string_equal(ent->d_name, ".") ||
760                 string_equal(ent->d_name, ".."))
761                 continue;
762
763             h.namelen = path_addsep(h.filename, sizeof(h.filename));
764
765             string_cat(filename, sizeof(filename),
766                        h.filename, ent->d_name, NULL);
767
768             add(replace_flag, filename, strlen(filename));
769         }
770         closedir(dir);
771     }
772     else {
773         memcpy(h.method, opts.method->id, sizeof(h.method));  /* compress */
774         add_1(replace_flag, &h);
775     }
776 }
777
778 int
779 get_line(char *s, int n)
780 {
781     int i, c;
782
783     i = 0;
784     while ((c = getchar()) != EOF && c != '\n')
785         if (i < n)
786             s[i++] = (char) c;
787     s[i] = '\0';
788     return i;
789 }
790
791 static void
792 extract(int to_file, struct lzh_header *h)
793 {
794     int n;
795     uint ext_headersize;
796     uchar buffer[MAXDICSIZ];
797
798     outfile = NULL;
799
800     if (to_file) {
801         if (memcmp(h->method, "-lhd-", sizeof(h->method)) == 0) {
802             /* directory */
803             if (mkdir(h->filename, 0777) == -1) {
804                 if (errno != EEXIST)
805                     error("cannot make directory \"%s\"", opts.outdir);
806             }
807         }
808         else {
809             /* regular file */
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");
814                     skip(arcfile, h);
815                     return;
816                 }
817                 h->namelen = strlen(h->filename);
818             }
819         }
820         if (opts.quiet < 2)
821             printf("Extracting %s ", h->filename);
822     }
823     else {
824         outfile = stdout;
825         if (opts.quiet < 2)
826             printf("===== %s =====\n", h->filename);
827     }
828     crc = INIT_CRC;
829     opts.method = which_method(h->method);
830     if (opts.method == NULL) {
831         fprintf(stderr, "Unknown method: %.5s\n", h->method);
832         skip(arcfile, h);
833     }
834     else {
835         crc = INIT_CRC;
836         if (opts.method->dicbit != 0)
837             decode_start();
838         while (h->origsize != 0) {
839             n = (uint) ((h->origsize > MAXDICSIZ) ? MAXDICSIZ : h->origsize);
840             if (opts.method->dicbit != 0)
841                 decode(n, buffer);
842             else if (fread((char *) buffer, 1, n, arcfile) != n)
843                 error("Can't read");
844             fwrite_crc(buffer, n, outfile);
845             if (outfile != stdout && opts.quiet < 1) {
846                 putc('.', stdout);
847             }
848             h->origsize -= n;
849         }
850     }
851
852     if ((crc ^ INIT_CRC) != h->file_crc)
853         error("CRC error");
854
855     if (to_file) {
856         fprintf(stdout, "\n");
857         fclose(outfile);
858     }
859     else
860         outfile = NULL;
861 }
862
863 static void
864 list_start(void)
865 {
866     if (opts.quiet < 2)
867         printf("%-14.14s %-7.7s %10.10s %10.10s %-5.5s %-4.4s %-5.5s %-4.4s\n",
868                "Filename",
869                "OS",
870                "Original",
871                "Compressed",
872                "Ratio",
873                "CRC",
874                "Method",
875                "Lv");
876 }
877
878 static void
879 list(struct lzh_header *h)
880 {
881     uint r;
882
883     printf("%-14.*s", h->namelen, h->filename);
884     if (h->namelen > 14)
885         printf("\n              ");
886     r = ratio(h->compsize, h->origsize);
887     printf(" %-7s %10lu %10lu %u.%03u %04x %-6.6s [%d]\n",
888            os_string(h->os_id),
889            h->origsize,
890            h->compsize,
891            r / 1000,
892            r % 1000,
893            h->file_crc,
894            h->method,
895            h->level);
896 }
897
898 static int
899 match(char *s1, char *s2)
900 {
901     for (;;) {
902         while (*s2 == '*' || *s2 == '?') {
903             if (*s2++ == '*')
904                 while (*s1 && *s1 != *s2)
905                     s1++;
906             else if (*s1 == 0)
907                 return 0;
908             else
909                 s1++;
910         }
911         if (*s1 != *s2)
912             return 0;
913         if (*s1 == 0)
914             return 1;
915         s1++;
916         s2++;
917     }
918 }
919
920 static int
921 search(int argc, char *argv[], struct lzh_header *h)
922 {
923     int i;
924
925     if (argc == 0)
926         return 1;
927     for (i = 0; i < argc; i++)
928         if (match(h->filename, argv[i]))
929             return 1;
930     return 0;
931 }
932
933 static void
934 exitfunc(void)
935 {
936     fclose(outfile);
937     remove(temp_name);
938 }
939
940 #include "getopt_long.h"
941
942 int
943 parse_args(int argc, char **argv)
944 {
945     int c;
946
947     for (;;) {
948         int this_option_optind = optind ? optind : 1;
949         int option_index = 0;
950
951         enum {
952             LHA_OPT_HELP = 128,
953             LHA_OPT_VERSION,
954         };
955
956         static struct option long_options[] = {
957             /* name, has_arg, *flag, val */
958             /* has_arg:
959                no_argument (0)
960                required_argument (1)
961                optional_argument (2)
962                flag:
963                NULL: getopt_long() return val
964                non-NULL: getopt_long() return 0, and *flag set val.
965             */
966             {"help", no_argument, NULL, LHA_OPT_HELP},
967             {"version", no_argument, NULL, LHA_OPT_VERSION},
968             {0, 0, 0, 0}
969         };
970
971         c = getopt_long(argc, argv, "012go[567]q[012]vw:z",
972                         long_options, &option_index);
973
974         if (c == -1) break;     /* end of parsing options */
975
976         switch (c) {
977         case '?':
978             print_usage();
979             break;
980         case 0:
981             /* set value by long option */
982             break;
983         case '0': case '1': case '2':
984             /* header level */
985             opts.header_level = c - '0';
986             break;
987         case 'g':
988             opts.generic = 1;
989             break;
990         case 'o':
991             /* compress method */
992             {
993                 int idx = 1;    /* -o means -lh1- method */
994
995                 if (optarg)
996                     idx = *optarg - '0'; /* -lh[567]- method */
997
998                 opts.method   = &methods[idx];
999             }
1000             break;
1001         case 'q':
1002             /* quiet mode */
1003             opts.quiet = 2;     /* -q is equivalent to -q2 */
1004             if (optarg)
1005                 opts.quiet = *optarg - '0';
1006             break;
1007         case 'v':
1008             /* verbose mode */
1009             opts.verbose = 1;
1010             break;
1011
1012         case 'w':
1013             /* extract directory */
1014             if (!optarg)
1015                 error("extract directory does not specified for `-w'");
1016             if (*optarg == '=')
1017                 optarg++;
1018
1019             opts.outdir = optarg;
1020             break;
1021         case 'z':               /* no compress */
1022             opts.nocompress = 1;
1023             break;
1024         case LHA_OPT_HELP:
1025             print_usage();
1026             break;
1027         case LHA_OPT_VERSION:
1028             print_version();
1029             break;
1030         default:
1031             break;
1032         }
1033     }
1034 }
1035
1036 FILE *
1037 open_tempfile()
1038 {
1039     temp_name = tmpnam(NULL);
1040     outfile = fopen(temp_name, "wb");
1041     if (outfile == NULL)
1042         error("Can't open temporary file");
1043     atexit(exitfunc);
1044
1045     return outfile;
1046 }
1047
1048 int
1049 main(int argc, char *argv[])
1050 {
1051     int i, j, cmd, count, nfiles, found, done;
1052     char *archive_file;
1053     struct lzh_header h;
1054
1055     init_opts();
1056
1057     if (argv[1] == 0)
1058         print_usage();
1059
1060     /*take a command character */
1061     {
1062         char *arg1;
1063
1064         arg1 = argv[1];
1065         if (arg1[0] == '-')
1066             arg1++;
1067         if (arg1[0] == 0)
1068             print_usage();
1069
1070         cmd = *arg1;
1071         if (arg1[1] == 0) {
1072             /* -<cmd> -<opts> ... */
1073             argv++;
1074             argc--;
1075         }
1076         else {
1077             /* -<cmd><opts> => -<opts> */
1078             *arg1 = '-';
1079         }
1080     }
1081
1082     parse_args(argc, argv);
1083     argv += optind;
1084     argc -= optind;
1085
1086     archive_file = argv[0];
1087
1088     argv++;
1089     argc--;
1090
1091     temp_name = NULL;
1092
1093     make_crctable();
1094     count = done = nfiles = 0;
1095
1096     switch (cmd) {
1097     case 'a':
1098     case 'c':
1099         outfile = open_tempfile();
1100         if (*argv == 0)
1101             error("archived files are not specified.");
1102         for (i = 0; i < argc; i++) {
1103             add(0, argv[i], strlen(argv[i]));
1104         }
1105
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);
1112
1113         exit(0);
1114         break;
1115     case 'r':
1116     case 'd':
1117         outfile = open_tempfile();
1118     case 'x':
1119     case 'p':
1120     case 'l':
1121     case 'v':
1122         /* Open archive. */
1123         arcfile = fopen(archive_file, "rb");
1124         if (arcfile == NULL)
1125             error("Can't open archive '%s'", argv[2]);
1126
1127         break;
1128     default:
1129         print_usage();
1130         break;
1131     }
1132
1133     /* change directory to extract dir */
1134     if (cmd == 'x') {
1135         struct stat *stbuf;
1136
1137         if (opts.outdir) {
1138             if (mkdir(opts.outdir, 0777) == -1) {
1139                 if (errno != EEXIST)
1140                     error("cannot make directory \"%s\"", opts.outdir);
1141             }
1142
1143             if (chdir(opts.outdir) == -1)
1144                 error("cannot change directory \"%s\"", opts.outdir);
1145         }
1146     }
1147
1148     while (!done && read_header(arcfile, &h)) {
1149
1150         compsize = h.compsize;
1151         origsize = h.origsize;
1152
1153         found = search(argc, argv, &h);
1154         switch (cmd) {
1155         case 'r':
1156             if (found) {
1157                 if (add(1, *argv, strlen(*argv)))
1158                     count++;
1159                 else
1160                     copy(arcfile, outfile, &h);
1161             }
1162             else
1163                 copy(arcfile, outfile, &h);
1164             break;
1165         case 'a':
1166         case 'd':
1167             if (found) {
1168                 count += (cmd == 'D');
1169                 skip(arcfile, &h);
1170             }
1171             else
1172                 copy(arcfile, outfile, &h);
1173             break;
1174         case 'x':
1175         case 'p':
1176             if (found) {
1177                 extract(cmd == 'x', &h);
1178                 if (++count == nfiles)
1179                     done = 1;
1180             }
1181             else
1182                 skip(arcfile, &h);
1183             break;
1184         case 'l':
1185         case 'v':
1186             if (found) {
1187                 if (count == 0)
1188                     list_start();
1189                 list(&h);
1190                 if (++count == nfiles)
1191                     done = 1;
1192             }
1193             skip(arcfile, &h);
1194             break;
1195         }
1196     }
1197
1198     if (cmd != 'p') {
1199         if (opts.quiet < 2)
1200             printf("  %d files\n", count);
1201     }
1202     return EXIT_SUCCESS;
1203 }