OSDN Git Service

Should not use the basename() function.
[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 <time.h>
54 #include <utime.h>
55 #include "ar.h"
56
57 extern char *basename(const char *);
58
59 struct lha_method methods[] = {
60     /* id, dicbit, pbit, maxmatch */
61     /* note: dicbit == 0 means no compress */
62     {"-lh0-", 0,  0,  0},  /* 0: no compress */
63     {"-lh1-", 12, 0, 60},  /* 1: 2^12 =  4KB dynamic huffman (LHarc) */
64     {"-lh2-", 13, 0,256},  /* 2: 2^13 =  8KB dynamic huffman */
65     {"-lh3-", 13, 0,256},  /* 3: 2^13 =  8KB static huffman */
66     {"-lh4-", 12, 4,256},  /* 4: 2^12 =  4KB static huffman (pos and len)*/
67     {"-lh5-", 13, 4,256},  /* 5: 2^13 =  8KB static huffman (pos and len)*/
68     {"-lh6-", 15, 5,256},  /* 6: 2^15 = 32KB static huffman (pos and len)*/
69     {"-lh7-", 16, 5,256},  /* 7: 2^16 = 64KB static huffman (pos and len)*/
70     {"-lzs-", 11, 0, 17},  /* 8: 2^11 =  2KB (LArc) */
71     {"-lz5-", 12, 0, 17},  /* 9: 2^12 =  4KB (LArc) */
72     {"-lz4-", 0,  0,  0},  /* 1: no compress (LArc) */
73     {"-lhd-", 0,  0,  0},  /* 1: directory */
74 };
75
76 struct lha_opts opts;
77
78 struct lha_method *
79 which_method(char *id)
80 {
81     int i;
82
83     for (i = 0; i < sizeof(methods)/sizeof(methods[0]); i++) {
84         if (strncmp(id, methods[i].id, sizeof(methods[0].id)) == 0) {
85             return &methods[i];
86         }
87     }
88     return NULL;
89 }
90
91 #define FNAME_MAX (255 - 25)    /* max strlen(filename) */
92
93 int unpackable;                 /* global, set in io.c */
94 ulong compsize, origsize;       /* global */
95
96 static char *temp_name;
97
98 static void
99 print_usage()
100 {
101     printf("%s", usage);
102     exit(0);
103 }
104
105 static void
106 print_version()
107 {
108     printf("version %s\n", version);
109     exit(0);
110 }
111
112 static uint
113 ratio(ulong a, ulong b)
114 {                               /* [(1000a + [b/2]) / b] */
115     int i;
116
117     for (i = 0; i < 3; i++)
118         if (a <= ULONG_MAX / 10)
119             a *= 10;
120         else
121             b /= 10;
122     if ((ulong) (a + (b >> 1)) < a) {
123         a >>= 1;
124         b >>= 1;
125     }
126     if (b == 0)
127         return 0;
128     return (uint) ((a + (b >> 1)) / b);
129 }
130
131 static char*
132 os_string(char id)
133 {
134     int i;
135     static struct os {
136         char id;
137         char *name;
138     } os_types[] = {
139         {'M',  "MS-DOS"},       /* Microsoft */
140         {'U',  "Unix"},         /* Unix or POSIX compliant OS */
141         {'J',  "Java"},         /* Sun Microsystems */
142         {'\0', "generic"},
143         {'w',  "Win9x"},        /* reserved by UNLHA32.DLL */
144         {'W',  "WinNT"},        /* reserved by UNLHA32.DLL */
145         {'2',  "OS/2"},         /* IBM OS/2 */
146         {'9',  "OS9"},          /* unknown */
147         {'K',  "OS/68K"},       /* unknown */
148         {'3',  "OS/386"},       /* unknown */
149         {'H',  "Human"},        /* SHARP Human68K */
150         {'C',  "CP/M"},         /* Digital Research */
151         {'F',  "FLEX"},         /* unknown */
152         {'m',  "Mac"},          /* Apple */
153         {'R',  "Runser"},       /* unknown */
154         {'T',  "TownsOS"},      /* Fujitsu FM-TOWNS */
155         {'X',  "XOSK"},         /* unknown */
156     };
157
158     for (i = 0; i < sizeof(os_types)/sizeof(os_types[0]); i++) {
159         if (id == os_types[i].id)
160             return os_types[i].name;
161     }
162
163     return "Unknown";
164 }
165
166 static void
167 put_header(char *buf, int *i, int n, uint32_t x)
168 {
169     while (--n >= 0) {
170         buf[(*i)++] = (uchar) (x & 0xFF);
171         x >>= 8;
172     }
173 }
174
175 static void
176 put_header_tmp(char *buf, int i, int n, uint32_t x)
177 {
178     put_header(buf, &i, n, x);
179 }
180
181 void
182 put_string(char *buf, int *n, size_t size, char *p)
183 {
184     memcpy(buf + *n, p, size);
185     *n += size;
186 }
187
188 static uint32_t
189 get_header(char *buf, int *i, int size)
190 {
191     uint32_t s;
192     int n;
193
194     s = 0;
195     for (n = size-1; n >= 0; n--) {
196         s = (s << 8) + (unsigned char)buf[*i + n];   /* little endian */
197     }
198     *i += size;
199     return s;
200 }
201
202 void
203 get_string(char *buf, int *i, size_t size, char *p)
204 {
205     memcpy(p, buf + *i, size);
206     *i += size;
207 }
208
209 static uint
210 calc_headersum(char *buf, int size)
211 {
212     int i;
213     uint s;
214
215     s = 0;
216     for (i = 0; i < size; i++)
217         s += buf[i];
218     return s & 0xFF;
219 }
220
221 #if 0
222 int
223 get_byte(char *buf)
224 {
225     return *(unsigned char*)buf;
226 }
227
228 uint16_t
229 get_word(char *buf)
230 {
231     return get_byte(buf) | (get_byte(buf+1) << 8);
232 }
233
234 uint32_t
235 get_dword(char *buf)
236 {
237     return get_byte(buf) |
238         (get_byte(buf+1) << 8) |
239         (get_byte(buf+2) << 16) |
240         (get_byte(buf+3) << 24);
241 }
242
243 void
244 get_char(char *buf, char *p, size_t size)
245 {
246     memcpy(p, buf, size);
247 }
248
249 void
250 put_byte(char *buf, int c)
251 {
252     *buf = (unsigned char)(c & 0xff);
253 }
254
255 void
256 put_word(char *buf, uint16_t c)
257 {
258     put_byte(buf,   c);
259     put_byte(buf+1, c>>8);
260 }
261
262 void
263 put_dword(char *buf, uint32_t c)
264 {
265     put_byte(buf, c);
266     put_byte(buf+1, c>>8);
267     put_byte(buf+2, c>>16);
268     put_byte(buf+3, c>>24);
269 }
270
271 void
272 put_char(char *buf, char *p, size_t size)
273 {
274     memcpy(buf, p, size);
275 }
276
277 #endif
278 time_t
279 ftime_to_time_t(uint32_t ftime)
280 {
281     struct tm tm;
282     /* ftime is time structure on MS-DOS
283
284     32              24              16               8
285      0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
286       |-------------|-------|---------|---------|-----------|---------|
287        year(-1980)   month   day       hour      minute      second/2
288        (1-127)       (1-12)  (1-31)    (0-23)    (0-59)      (0-29)
289     */
290
291     memset(&tm, 0, sizeof(tm));
292     tm.tm_year = (ftime >> 25) + 1980 - 1900;
293     tm.tm_mon  = ((ftime >> 21) & 0x0f) - 1;
294     tm.tm_mday = (ftime >> 16) & 0x1f;
295     tm.tm_hour = (ftime >> 11) & 0x1f;
296     tm.tm_min  = (ftime >>  5) & 0x3f;
297     tm.tm_sec  = (ftime & 0x1f) * 2;
298
299     return mktime(&tm);
300 }
301
302 uint32_t
303 time_t_to_ftime(time_t t)
304 {
305     struct tm tm;
306     /* ftime is time structure on MS-DOS
307
308     32              24              16               8
309      0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
310       |-------------|-------|---------|---------|-----------|---------|
311        year(-1980)   month   day       hour      minute      second/2
312        (1-127)       (1-12)  (1-31)    (0-23)    (0-59)      (0-29)
313     */
314
315 #if HAVE_LOCALTIME_R
316     localtime_r(&t, &tm);
317 #else
318     tm = *localtime(&t);
319 #endif
320
321     return (uint32_t)(tm.tm_year + 1900 - 1980) << 25
322         | (uint32_t)(tm.tm_mon + 1) << 21
323         | (uint32_t)(tm.tm_mday)    << 16
324         | (uint32_t)(tm.tm_hour)    << 11
325         | (uint32_t)(tm.tm_min)     <<  5
326         | (uint32_t)(tm.tm_sec / 2);
327 }
328
329
330 /*
331  * level 0 header
332  *
333  *
334  * offset  size  field name
335  * ----------------------------------
336  *     0      1  header size    [*1]
337  *     1      1  header sum
338  *            ---------------------------------------
339  *     2      5  method ID                         ^
340  *     7      4  packed size    [*2]               |
341  *    11      4  original size                     |
342  *    15      2  time                              |
343  *    17      2  date                              |
344  *    19      1  attribute                         | [*1] header size (X+Y+22)
345  *    20      1  level (0x00 fixed)                |
346  *    21      1  name length                       |
347  *    22      X  pathname                          |
348  * X +22      2  file crc (CRC-16)                 |
349  * X +24      Y  ext-header(old style)             v
350  * -------------------------------------------------
351  * X+Y+24        data                              ^
352  *                 :                               | [*2] packed size
353  *                 :                               v
354  * -------------------------------------------------
355  *
356  * ext-header(old style)
357  *     0      1  ext-type ('U')
358  *     1      1  minor version
359  *     2      4  UNIX time
360  *     6      2  mode
361  *     8      2  uid
362  *    10      2  gid
363  *
364  * attribute (MS-DOS)
365  *    bit1  read only
366  *    bit2  hidden
367  *    bit3  system
368  *    bit4  volume label
369  *    bit5  directory
370  *    bit6  archive bit (need to backup)
371  *
372  */
373 static int
374 read_header_lv0(FILE *fp, char *buf, struct lzh_header *h)
375 {
376     int headersize;
377     int headersum;
378     int ext_headersize;
379     int pos = 0;
380     int ext_size;
381
382     headersize = get_header(buf, &pos, 1);
383     headersum = get_header(buf, &pos, 1);
384
385     fread_crc(buf + 21, headersize - (21 - pos), fp);     /* CRC not used */
386
387     if (calc_headersum(buf+pos, headersize) != headersum)
388         error("Header sum error");
389
390     get_string(buf, &pos, 5, h->method);
391     h->compsize = get_header(buf, &pos, 4);
392     h->origsize = get_header(buf, &pos, 4);
393     h->mtime    = ftime_to_time_t(get_header(buf, &pos, 4));
394     /* attrib   = */ get_header(buf, &pos, 1);
395     h->level    = get_header(buf, &pos, 1); /* header level */
396     h->namelen  = get_header(buf, &pos, 1);
397     if (pos + h->namelen > headersize+2) {
398         warn("path name is too long");
399         h->namelen = headersize+2-pos;
400     }
401     get_string(buf, &pos, h->namelen, h->filename);
402     h->filename[h->namelen] = 0;
403     h->file_crc = get_header(buf, &pos, 2);
404     h->os_id    = 0;            /* generic */
405
406     ext_size = headersize - pos;
407
408     if (ext_size > 0) {
409         if (ext_size > 1) {
410             h->os_id = get_header(buf, &pos, 1);
411             ext_size--;
412         }
413         if (ext_size > 1) {
414             get_header(buf, &pos, 1); /* minor version */
415             ext_size--;
416         }
417         if (ext_size > 4) {
418             h->mtime = get_header(buf, &pos, 4);
419             ext_size -= 4;
420         }
421         if (ext_size > 2) {
422             get_header(buf, &pos, 2);         /* mode */
423             ext_size -= 2;
424         }
425         if (ext_size > 2) {
426             get_header(buf, &pos, 2);         /* uid */
427             ext_size -= 2;
428         }
429         if (ext_size > 2) {
430             get_header(buf, &pos, 2);         /* gid */
431             ext_size -= 2;
432         }
433     }
434
435     return 1;                   /* success */
436 }
437
438 /*
439  * level 1 header
440  *
441  *
442  * offset   size  field name
443  * -----------------------------------
444  *     0       1  header size   [*1]
445  *     1       1  header sum
446  *             -------------------------------------
447  *     2       5  method ID                        ^
448  *     7       4  skip size     [*2]               |
449  *    11       4  original size                    |
450  *    15       2  time                             |
451  *    17       2  date                             |
452  *    19       1  attribute (0x20 fixed)           | [*1] header size (X+Y+25)
453  *    20       1  level (0x01 fixed)               |
454  *    21       1  name length                      |
455  *    22       X  filename                         |
456  * X+ 22       2  file crc (CRC-16)                |
457  * X+ 24       1  OS ID                            |
458  * X +25       Y  ???                              |
459  * X+Y+25      2  next-header size                 v
460  * -------------------------------------------------
461  * X+Y+27      Z  ext-header                       ^
462  *                 :                               |
463  * -----------------------------------             | [*2] skip size
464  * X+Y+Z+27       data                             |
465  *                 :                               v
466  * -------------------------------------------------
467  *
468  */
469 static int
470 read_header_lv1(FILE *fp, char *buf, struct lzh_header *h)
471 {
472     int headersize;
473     int headersum;
474     int ext_headersize;
475     char dirname[1024] = "";
476     int dirnamelen = 0;
477     int pos = 0;
478
479     headersize = get_header(buf, &pos, 1);
480     headersum = get_header(buf, &pos, 1);
481
482     fread_crc(buf + 21, headersize - (21 - 2), fp);     /* CRC not used */
483
484     if (calc_headersum(&buf[pos], headersize) != headersum)
485         error("Header sum error");
486
487     get_string(buf, &pos, 5, h->method);
488     h->compsize = get_header(buf, &pos, 4);
489     h->origsize = get_header(buf, &pos, 4);
490     h->mtime    = ftime_to_time_t(get_header(buf, &pos, 4));
491     get_header(buf, &pos, 1);   /* attribute */
492     h->level = get_header(buf, &pos, 1); /* header level */
493     h->namelen = get_header(buf, &pos, 1);
494     get_string(buf, &pos, h->namelen, h->filename);
495     h->filename[h->namelen] = 0;
496     h->file_crc = get_header(buf, &pos, 2);
497     h->os_id = get_header(buf, &pos, 1);
498
499     ext_headersize = get_header(buf, &pos, 2);
500
501     while (ext_headersize != 0) {
502         char extbuf[4096];
503         uchar ext_type;
504         int extpos = 0;
505
506         h->compsize -= ext_headersize;
507
508         if (fread(extbuf, ext_headersize, 1, fp) != 1) {
509             error("can't read ext header");
510         }
511
512         ext_type = get_header(extbuf, &extpos, 1);
513         switch (ext_type) {
514         case 1:
515             /* filename header */
516             h->namelen = ext_headersize - 3;
517             get_string(extbuf, &extpos, h->namelen, h->filename);
518             h->filename[h->namelen] = 0;
519             break;
520         case 2:
521             /* dirname header */
522             dirnamelen = ext_headersize - 3;
523             get_string(extbuf, &extpos, dirnamelen, dirname);
524             dirname[dirnamelen] = 0;
525             break;
526         case 0x54:
527             h->mtime = get_header(extbuf, &extpos, 4);
528             break;
529         default:
530             break;
531         }
532         extpos = ext_headersize - 2;
533         ext_headersize = get_header(extbuf, &extpos, 2);
534     }
535
536     if (dirnamelen > 0 && dirname[dirnamelen-1] != '/') {
537         dirname[dirnamelen++] = '/';
538     }
539
540     strcat(dirname, h->filename);
541     h->namelen = strlen(dirname);
542     strcpy(h->filename, dirname);
543
544     return 1;                   /* success */
545 }
546
547 /*
548  * level 2 header
549  *
550  *
551  * offset   size  field name
552  * --------------------------------------------------
553  *     0       2  total header size [*1]           ^
554  *             -----------------------             |
555  *     2       5  method ID                        |
556  *     7       4  packed size       [*2]           |
557  *    11       4  original size                    |
558  *    15       4  time                             |
559  *    19       1  RESERVED (0x20 fixed)            | [*1] total header size
560  *    20       1  level (0x02 fixed)               |      (X+26+(1))
561  *    21       2  file crc (CRC-16)                |
562  *    23       1  OS ID                            |
563  *    24       2  next-header size                 |
564  * -----------------------------------             |
565  *    26       X  ext-header                       |
566  *                 :                               |
567  * -----------------------------------             |
568  * X +26      (1) padding                          v
569  * -------------------------------------------------
570  * X +26+(1)      data                             ^
571  *                 :                               | [*2] packed size
572  *                 :                               v
573  * -------------------------------------------------
574  *
575  */
576 static int
577 read_header_lv2(FILE *fp, char *buf, struct lzh_header *h)
578 {
579     int headersize;
580     int headersum;
581     int ext_headersize;
582     int remainder;
583     char dirname[1024] = "";
584     int dirnamelen = 0;
585     int pos = 0;
586
587     headersize = get_header(buf, &pos, 2);
588
589     fread_crc(buf + 21, 26 - 21, fp);     /* CRC not used */
590
591     get_string(buf, &pos, 5, h->method);
592     h->compsize = get_header(buf, &pos, 4);
593     h->origsize = get_header(buf, &pos, 4);
594     h->mtime    = get_header(buf, &pos, 4);
595     get_header(buf, &pos, 1);         /* attrib */
596     h->level    = get_header(buf, &pos, 1); /* header level */
597     h->file_crc = get_header(buf, &pos, 2);
598     h->os_id    = get_header(buf, &pos, 1);
599
600     ext_headersize = get_header(buf, &pos, 2);
601
602     remainder = headersize - pos;
603
604     while (ext_headersize != 0) {
605         char extbuf[4096];
606         uchar ext_type;
607         int extpos = 0;
608
609         remainder -= ext_headersize;
610
611         if (fread(extbuf, ext_headersize, 1, fp) != 1) {
612             error("can't read ext header");
613         }
614         ext_type = get_header(extbuf, &extpos, 1);
615         switch (ext_type) {
616         case 0:
617             /* header crc */
618             break;
619         case 1:
620             /* filename header */
621             h->namelen = ext_headersize - 3;
622             get_string(extbuf, &extpos, h->namelen, h->filename);
623             h->filename[h->namelen] = 0;
624             break;
625         case 2:
626             /* dirname header */
627             dirnamelen = ext_headersize - 3;
628             get_string(extbuf, &extpos, dirnamelen, dirname);
629             dirname[dirnamelen] = 0;
630             break;
631         default:
632             break;
633         }
634         extpos = ext_headersize - 2;
635         ext_headersize = get_header(extbuf, &extpos, 2);
636     }
637
638     if (dirnamelen > 0 && dirname[dirnamelen-1] != '/') {
639         dirname[dirnamelen++] = '/';
640     }
641
642     strcat(dirname, h->filename);
643     h->namelen = strlen(dirname);
644     strcpy(h->filename, dirname);
645
646     while (remainder > 0) {
647         fgetc(fp); /* skip padding */
648         remainder--;
649     }
650
651     return 1;                   /* success */
652 }
653
654 static int
655 read_header(FILE *fp, struct lzh_header *h)
656 {
657     char buf[4096];
658     int ret;
659
660     ret = fgetc(fp);
661     buf[0] = (uchar)ret;
662     if (buf[0] == 0 || ret == EOF)
663         return 0;               /* end of archive */
664     fread_crc(buf + 1, 21 - 1, fp);
665     switch (buf[20]) {
666     case 0:
667         return read_header_lv0(fp, buf, h);
668         break;
669     case 1:
670         return read_header_lv1(fp, buf, h);
671         break;
672     case 2:
673         return read_header_lv2(fp, buf, h);
674         break;
675     default:
676         error("unknown level (%d)\n", buf[20]);
677         break;
678     }
679
680     return 1;                   /* success */
681 }
682
683
684 void
685 write_header_lv0(FILE *fp, struct lzh_header *h)
686 {
687     char buf[4096];
688     int sum;
689     int headersize;
690     int pos = 0;
691
692     headersize = 22;
693     if (!opts.generic)
694         headersize += 12;       /* extended header size */
695
696     if (headersize + h->namelen > 255) {
697         warn("path name is too long");
698         h->namelen = 255 - headersize;
699         headersize = 255;
700     }
701     else {
702         headersize += h->namelen;
703     }
704
705     put_header(buf, &pos, 1, headersize);
706     put_header(buf, &pos, 1, 0); /* dummy */
707
708     put_string(buf, &pos, 5, h->method);
709     put_header(buf, &pos, 4, h->compsize); /* packed size */
710     put_header(buf, &pos, 4, h->origsize); /* original size */
711     put_header(buf, &pos, 4, time_t_to_ftime(h->mtime)); /* ftime */
712     put_header(buf, &pos, 1, 0x20);     /* attribute */
713     put_header(buf, &pos, 1, 0);        /* level */
714     put_header(buf, &pos, 1, h->namelen); /* length of pathname */
715     put_string(buf, &pos, h->namelen, h->filename);
716     put_header(buf, &pos, 2, h->file_crc);
717
718     if (!opts.generic) {
719         /* extended header for Unix */
720         put_header(buf, &pos, 1, 'U');  /* OS type */
721         put_header(buf, &pos, 1, '\0'); /* minor version */
722         put_header(buf, &pos, 4, h->mtime); /* time_t */
723         put_header(buf, &pos, 2, 0100000);  /* mode */
724         put_header(buf, &pos, 2, 0);  /* uid */
725         put_header(buf, &pos, 2, 0);  /* gid */
726     }
727
728     sum = calc_headersum(buf+2, headersize);
729     put_header_tmp(buf, 1, 1, sum);
730
731     fwrite_crc(buf, headersize+2, fp);
732 }
733
734 void
735 write_header_lv1(FILE *fp, struct lzh_header *h)
736 {
737     char buf[4096];
738     int sum;
739     int headersize;
740     int extsize = 0;
741     int pos = 0;
742     int extpos;
743     char *dirname, *fname;
744     int dirnamelen;
745
746     fname  = xbasename(h->filename);
747     dirname   = h->filename;
748     dirnamelen = fname - dirname;
749     h->namelen = strlen(fname);
750
751     headersize = 25;
752
753     put_header(buf, &pos, 1, 0); /* dummy */
754     put_header(buf, &pos, 1, 0); /* dummy */
755     put_string(buf, &pos, 5, h->method);
756     put_header(buf, &pos, 4, h->compsize); /* packed size */
757     put_header(buf, &pos, 4, h->origsize); /* original size */
758     put_header(buf, &pos, 4, time_t_to_ftime(h->mtime)); /* ftime */
759     put_header(buf, &pos, 1, 0x20);     /* attribute */
760     put_header(buf, &pos, 1, 1);        /* level */
761     if (headersize + h->namelen > 255)
762         put_header(buf, &pos, 1, 0);            /* length of pathname */
763     else {
764         put_header(buf, &pos, 1, h->namelen);   /* length of pathname */
765         put_string(buf, &pos, h->namelen, fname);
766         headersize += h->namelen;
767     }
768     put_header_tmp(buf, 0, 1, headersize); /* header size */
769     put_header(buf, &pos, 2, h->file_crc);
770     if (opts.generic)
771         put_header(buf, &pos, 1, '\0');
772     else
773         put_header(buf, &pos, 1, 'U');
774
775     extpos = pos;
776     put_header(buf, &pos, 2, 7); /* next header size */
777     put_header(buf, &pos, 1, 0x54); /* time stamp */
778     put_header(buf, &pos, 4, h->mtime); /* time_t */
779
780     if (h->namelen > 0) {
781         put_header(buf, &pos, 2, 3 + h->namelen);
782         put_header(buf, &pos, 1, 1); /* 0x01: filename header */
783         put_string(buf, &pos, h->namelen, fname); /* filename */
784     }
785
786     if (dirnamelen > 0) {
787         put_header(buf, &pos, 2, 3 + dirnamelen);
788         put_header(buf, &pos, 1, 2); /* 0x02: dirname header */
789         put_string(buf, &pos, dirnamelen, dirname); /* dirname */
790     }
791
792     extsize = pos - extpos;
793     put_header(buf, &pos, 2, 0); /* next header size (end of header) */
794
795     put_header_tmp(buf, 7, 4, h->compsize+extsize);    /* packed size */
796
797     sum = calc_headersum(buf+2, headersize);
798     put_header_tmp(buf, 1, 1, sum);
799
800     fwrite_crc(buf, headersize+2+extsize, fp);
801 }
802
803 void
804 write_header_lv2(FILE *fp, struct lzh_header *h)
805 {
806     char buf[4096], *crcptr;
807     int headersize;
808     extern ushort crctable[];
809     char dirname[1024] = "", *fname;
810     int dirnamelen, len;
811     int pos = 0;
812
813     put_header(buf, &pos, 2, 0); /* dummy */
814     put_string(buf, &pos, 5, h->method);
815     put_header(buf, &pos, 4, h->compsize); /* packed size */
816     put_header(buf, &pos, 4, h->origsize); /* original size */
817     put_header(buf, &pos, 4, h->mtime);    /* time_t */
818     put_header(buf, &pos, 1, 0x20);        /* DOS attribute (0x20 fixed) */
819     put_header(buf, &pos, 1, 2);           /* level */
820     put_header(buf, &pos, 2, h->file_crc);
821     if (opts.generic)
822         put_header(buf, &pos, 1, '\0');
823     else
824         put_header(buf, &pos, 1, 'U');
825
826     put_header(buf, &pos, 2, 5);
827     put_header(buf, &pos, 1, 0); /* 0x00: header crc */
828     crcptr = &buf[pos];
829     put_header(buf, &pos, 2, 0); /* crc (dummy) */
830
831     fname = xbasename(h->filename);
832     len = strlen(fname);
833
834     put_header(buf, &pos, 2, 3 + len);
835     put_header(buf, &pos, 1, 1); /* 0x01: filename header */
836     put_string(buf, &pos, len, fname); /* filename */
837
838     {
839         char *ptr;
840
841         ptr = strrchr(h->filename, '/');
842         if (ptr) {
843             dirnamelen = ptr - h->filename;
844             strncpy(dirname, h->filename, dirnamelen);
845             dirname[dirnamelen+ 1] = 0;
846         }
847     }
848
849     if (*dirname) {
850         put_header(buf, &pos, 2, 3 + dirnamelen);
851         put_header(buf, &pos, 1, 2); /* 0x02: dirname header */
852         put_string(buf, &pos, dirnamelen, dirname); /* dirname */
853     }
854
855     put_header(buf, &pos, 2, 0); /* next header size (end of header) */
856
857     /* padding */
858     if (pos % 256 == 0) {
859         put_header(buf, &pos, 1, 0);
860     }
861     headersize = pos;
862
863     put_header_tmp(buf, 0, 2, headersize);
864
865     {
866         int i;
867
868         crc = INIT_CRC;
869         for (i = 0; i < headersize; i++)
870             UPDATE_CRC(buf[i]);
871         put_header_tmp(crcptr, 0, 2, crc);
872     }
873
874     fwrite_crc(buf, headersize, fp);
875 }
876
877 void
878 write_header(FILE *fp, struct lzh_header *h)
879 {
880     switch (h->level) {
881     case 0:
882         write_header_lv0(fp, h);
883         break;
884     case 1:
885         write_header_lv1(fp, h);
886         break;
887     case 2:
888         write_header_lv2(fp, h);
889         break;
890     default:
891         error("unknown level (%d)", h->level);
892         break;
893     }
894 }
895
896 static void
897 skip(FILE *fp, struct lzh_header *h)
898 {
899     int i;
900     if (opts.archive_to_stdio)
901         for (i = 0; i < h->compsize; i++)
902             fgetc(fp);
903     else
904         fseek(fp, h->compsize, SEEK_CUR);
905 }
906
907 static void
908 copy(FILE *arcfile, FILE *outfile, struct lzh_header *h)
909 {
910     uint n;
911     uchar buffer[MAXDICSIZ];
912
913     write_header(outfile, h);
914     while (h->compsize != 0) {
915         n = (uint) ((h->compsize > sizeof(buffer)) ? sizeof(buffer) : h->compsize);
916         if (fread((char *) buffer, 1, n, arcfile) != n)
917             error("Can't read");
918         if (fwrite((char *) buffer, 1, n, outfile) != n)
919             error("Can't write");
920         h->compsize -= n;
921     }
922 }
923
924 static void
925 store(void)
926 {
927     uint n;
928     uchar buffer[MAXDICSIZ];
929
930     origsize = 0;
931     crc = INIT_CRC;
932     while ((n = fread((char *) buffer, 1, sizeof(buffer), infile)) != 0) {
933         fwrite_crc(buffer, n, outfile);
934         origsize += n;
935     }
936     compsize = origsize;
937 }
938
939 static int
940 add_dir(int replace_flag, struct lzh_header *h)
941 {
942     long headerpos, arcpos;
943     uint r;
944
945     h->origsize = h->compsize = 0;
946     h->file_crc = INIT_CRC;
947
948     headerpos = ftell(outfile);
949     write_header(outfile, h);
950     arcpos = ftell(outfile);
951
952     if (opts.quiet < 2)
953         printf(" %d.%d%%\n", r / 10, r % 10);
954     return 1;                   /* success */
955 }
956
957 static int
958 add_1(int replace_flag, struct lzh_header *h)
959 {
960     long headerpos, arcpos;
961     uint r;
962
963     if ((infile = fopen(h->filename, "rb")) == NULL) {
964         fprintf(stderr, "Can't open %s\n", h->filename);
965         return 0;               /* failure */
966     }
967     if (replace_flag) {
968         if (opts.quiet < 2)
969             printf("Replacing %s ", h->filename);
970     }
971     else {
972         if (opts.quiet < 2)
973             printf("Adding %s ", h->filename);
974     }
975
976     headerpos = ftell(outfile);
977     write_header(outfile, h);
978     arcpos = ftell(outfile);
979
980     origsize = compsize = 0;
981     crc = INIT_CRC;
982     if (opts.nocompress) {
983         unpackable = 1;
984     }
985     else {
986         unpackable = 0;
987         encode();
988     }
989
990     if (unpackable) {
991         memcpy(h->method, "-lh0-", sizeof(h->method));  /* store */
992         rewind(infile);
993         fseek(outfile, arcpos, SEEK_SET);
994         store();
995     }
996     h->file_crc = crc ^ INIT_CRC;
997     fclose(infile);
998
999     h->compsize = compsize;
1000     h->origsize = origsize;
1001
1002     fseek(outfile, headerpos, SEEK_SET);
1003     write_header(outfile, h);
1004     fseek(outfile, 0L, SEEK_END);
1005     r = ratio(compsize, origsize);
1006     if (opts.quiet < 2)
1007         printf(" %d.%d%%\n", r / 10, r % 10);
1008     return 1;                   /* success */
1009 }
1010
1011 static int
1012 add(int replace_flag, char *filename, int namelen)
1013 {
1014     struct lzh_header h;
1015     struct stat st;
1016
1017     memset(&h, 0, sizeof(h));
1018
1019     h.level = opts.header_level;
1020
1021     strcpy(h.filename, filename);
1022     h.namelen = namelen;
1023
1024     stat(h.filename, &st);
1025
1026     h.mtime = st.st_mtime;
1027     if (S_ISDIR(st.st_mode)) {
1028         DIR *dir;
1029         struct dirent *ent;
1030
1031         memcpy(h.method, "-lhd-", sizeof(h.method));  /* directory */
1032         add_dir(replace_flag, &h);
1033
1034         dir = opendir(h.filename);
1035         if (dir == NULL)
1036             error("cannot open directory: \"%s\"", h.filename);
1037
1038         while ((ent = readdir(dir)) != 0) {
1039             char filename[1024];
1040
1041             if (string_equal(ent->d_name, ".") ||
1042                 string_equal(ent->d_name, ".."))
1043                 continue;
1044
1045             h.namelen = path_addsep(h.filename, sizeof(h.filename));
1046
1047             string_cat(filename, sizeof(filename),
1048                        h.filename, ent->d_name, NULL);
1049
1050             add(replace_flag, filename, strlen(filename));
1051         }
1052         closedir(dir);
1053     }
1054     else {
1055         memcpy(h.method, opts.method->id, sizeof(h.method));  /* compress */
1056         add_1(replace_flag, &h);
1057     }
1058 }
1059
1060 int
1061 get_line(char *s, int n)
1062 {
1063     int i, c;
1064
1065     i = 0;
1066     while ((c = getchar()) != EOF && c != '\n')
1067         if (i < n)
1068             s[i++] = (char) c;
1069     s[i] = '\0';
1070     return i;
1071 }
1072
1073 static void
1074 extract(int to_file, struct lzh_header *h)
1075 {
1076     int n;
1077     uchar buffer[MAXDICSIZ];
1078
1079     outfile = NULL;
1080
1081     if (to_file) {
1082         if (memcmp(h->method, "-lhd-", sizeof(h->method)) == 0) {
1083             /* directory */
1084             if (mkdir(h->filename, 0777) == -1) {
1085                 if (errno != EEXIST)
1086                     error("cannot make directory \"%s\"", opts.outdir);
1087             }
1088         }
1089         else {
1090             /* regular file */
1091             if (file_exists(h->filename)) {
1092                 if (!opts.force_extract) {
1093                     message("'%s' has been already exist. skip", h->filename);
1094                     skip(arcfile, h);
1095                     return;
1096                 }
1097             }
1098             while ((outfile = fopen(h->filename, "wb")) == NULL) {
1099                 fprintf(stderr, "Can't open %s\nNew filename: ", h->filename);
1100                 if (get_line(h->filename, FNAME_MAX) == 0) {
1101                     fprintf(stderr, "Not extracted\n");
1102                     skip(arcfile, h);
1103                     return;
1104                 }
1105                 h->namelen = strlen(h->filename);
1106             }
1107         }
1108         if (opts.quiet < 2)
1109             printf("Extracting %s ", h->filename);
1110     }
1111     else {
1112         outfile = stdout;
1113         if (opts.quiet < 2)
1114             printf("===== %s =====\n", h->filename);
1115     }
1116     crc = INIT_CRC;
1117     opts.method = which_method(h->method);
1118     if (opts.method == NULL) {
1119         fprintf(stderr, "Unknown method: %.5s\n", h->method);
1120         skip(arcfile, h);
1121     }
1122     else {
1123         crc = INIT_CRC;
1124         if (opts.method->dicbit != 0)
1125             decode_start();
1126         while (h->origsize != 0) {
1127             n = (uint) ((h->origsize > MAXDICSIZ) ? MAXDICSIZ : h->origsize);
1128             if (opts.method->dicbit != 0)
1129                 decode(n, buffer);
1130             else if (fread((char *) buffer, 1, n, arcfile) != n)
1131                 error("Can't read");
1132             fwrite_crc(buffer, n, outfile);
1133             if (outfile != stdout && opts.quiet < 1) {
1134                 putc('.', stdout);
1135             }
1136             h->origsize -= n;
1137         }
1138     }
1139
1140     if ((crc ^ INIT_CRC) != h->file_crc)
1141         error("CRC error");
1142
1143     if (to_file) {
1144         fprintf(stdout, "\n");
1145         if (outfile) {
1146             struct utimbuf ut;
1147
1148             fclose(outfile);
1149
1150             ut.actime = ut.modtime = h->mtime;
1151             utime(h->filename, &ut);
1152         }
1153     }
1154     outfile = NULL;
1155 }
1156
1157 static void
1158 list_start(void)
1159 {
1160     if (opts.quiet < 2)
1161         printf("%-14.14s %-7.7s %10.10s %10.10s %-5.5s %-4.4s %-5.5s %-4.4s\n",
1162                "Filename",
1163                "OS",
1164                "Original",
1165                "Compressed",
1166                "Ratio",
1167                "CRC",
1168                "Method",
1169                "Lv");
1170 }
1171
1172 static void
1173 list(struct lzh_header *h)
1174 {
1175     uint r;
1176
1177     printf("%-14.*s", h->namelen, h->filename);
1178     if (h->namelen > 14)
1179         printf("\n              ");
1180     r = ratio(h->compsize, h->origsize);
1181     printf(" %-7s %10lu %10lu %u.%03u %04x %-6.6s [%d]\n",
1182            os_string(h->os_id),
1183            h->origsize,
1184            h->compsize,
1185            r / 1000,
1186            r % 1000,
1187            h->file_crc,
1188            h->method,
1189            h->level);
1190 }
1191
1192 static int
1193 match(char *s1, char *s2)
1194 {
1195     for (;;) {
1196         while (*s2 == '*' || *s2 == '?') {
1197             if (*s2++ == '*')
1198                 while (*s1 && *s1 != *s2)
1199                     s1++;
1200             else if (*s1 == 0)
1201                 return 0;
1202             else
1203                 s1++;
1204         }
1205         if (*s1 != *s2)
1206             return 0;
1207         if (*s1 == 0)
1208             return 1;
1209         s1++;
1210         s2++;
1211     }
1212 }
1213
1214 static int
1215 search(int argc, char *argv[], struct lzh_header *h)
1216 {
1217     int i;
1218
1219     if (argc == 0)
1220         return -1;
1221     for (i = 0; i < argc; i++)
1222         if (argv[i] && match(h->filename, argv[i]))
1223             return i+1;
1224     return 0;
1225 }
1226
1227 static void
1228 exitfunc(void)
1229 {
1230     remove(temp_name);
1231 }
1232
1233 #include "getopt_long.h"
1234
1235 int
1236 parse_args(int argc, char **argv)
1237 {
1238     int c;
1239
1240     for (;;) {
1241         /* int this_option_optind = optind ? optind : 1; */
1242         int option_index = 0;
1243
1244         enum {
1245             LHA_OPT_HELP = 128,
1246             LHA_OPT_VERSION,
1247         };
1248
1249         static struct option long_options[] = {
1250             /* name, has_arg, *flag, val */
1251             /* has_arg:
1252                no_argument (0)
1253                required_argument (1)
1254                optional_argument (2)
1255                flag:
1256                NULL: getopt_long() return val
1257                non-NULL: getopt_long() return 0, and *flag set val.
1258             */
1259             {"help", no_argument, NULL, LHA_OPT_HELP},
1260             {"version", no_argument, NULL, LHA_OPT_VERSION},
1261             {0, 0, 0, 0}
1262         };
1263
1264         c = getopt_long(argc, argv, "012fgo[567]q[012]vw:z",
1265                         long_options, &option_index);
1266
1267         if (c == -1) break;     /* end of parsing options */
1268
1269         switch (c) {
1270         case '?':
1271             print_usage();
1272             break;
1273         case 0:
1274             /* set value by long option */
1275             break;
1276         case '0': case '1': case '2':
1277             /* header level */
1278             opts.header_level = c - '0';
1279             break;
1280         case 'f':
1281             opts.force_extract = 1;
1282             break;
1283         case 'g':
1284             opts.generic = 1;
1285             opts.header_level = 0;
1286             break;
1287         case 'o':
1288             /* compress method */
1289             {
1290                 int idx = 1;    /* -o means -lh1- method */
1291
1292                 if (optarg)
1293                     idx = *optarg - '0'; /* -lh[567]- method */
1294
1295                 opts.method   = &methods[idx];
1296             }
1297             break;
1298         case 'q':
1299             /* quiet mode */
1300             opts.quiet = 2;     /* -q is equivalent to -q2 */
1301             if (optarg)
1302                 opts.quiet = *optarg - '0';
1303             break;
1304         case 'v':
1305             /* verbose mode */
1306             opts.verbose = 1;
1307             break;
1308
1309         case 'w':
1310             /* extract directory */
1311             if (!optarg)
1312                 error("extract directory does not specified for `-w'");
1313             if (*optarg == '=')
1314                 optarg++;
1315
1316             opts.outdir = optarg;
1317             break;
1318         case 'z':               /* no compress */
1319             opts.nocompress = 1;
1320             break;
1321         case LHA_OPT_HELP:
1322             print_usage();
1323             break;
1324         case LHA_OPT_VERSION:
1325             print_version();
1326             break;
1327         default:
1328             break;
1329         }
1330     }
1331 }
1332
1333 FILE *
1334 open_tempfile()
1335 {
1336     temp_name = tmpnam(NULL);
1337     outfile = fopen(temp_name, "wb");
1338     if (outfile == NULL)
1339         error("Can't open temporary file");
1340     atexit(exitfunc);
1341
1342     return outfile;
1343 }
1344
1345 int
1346 main(int argc, char *argv[])
1347 {
1348     int i, cmd, count, nfiles, found, done;
1349     char *archive_file;
1350     struct lzh_header h;
1351     int arc_count;
1352
1353     INITIALIZE_OPTS(opts);
1354
1355     if (argv[1] == 0)
1356         print_usage();
1357
1358     /*take a command character */
1359     {
1360         char *arg1;
1361
1362         arg1 = argv[1];
1363         if (arg1[0] == '-')
1364             arg1++;
1365         if (arg1[0] == 0)
1366             print_usage();
1367
1368         cmd = *arg1;
1369         if (arg1[1] == 0) {
1370             /* -<cmd> -<opts> ... */
1371             argv++;
1372             argc--;
1373         }
1374         else {
1375             /* -<cmd><opts> => -<opts> */
1376             *arg1 = '-';
1377         }
1378     }
1379
1380     parse_args(argc, argv);
1381     argv += optind;
1382     argc -= optind;
1383
1384     archive_file = argv[0];
1385
1386     if (strcmp(archive_file, "-") == 0)
1387         opts.archive_to_stdio = 1;
1388
1389     argv++;
1390     argc--;
1391
1392     temp_name = NULL;
1393
1394     make_crctable();
1395     count = done = nfiles = 0;
1396
1397     switch (cmd) {
1398     case 'a':
1399     case 'u':
1400     case 'c':
1401         if (opts.archive_to_stdio)
1402             opts.quiet = 2;
1403
1404         outfile = open_tempfile();
1405         if (*argv == 0)
1406             error("archived files are not specified.");
1407
1408         if (!opts.archive_to_stdio && (cmd == 'a' || cmd == 'u')) {
1409             if (file_exists(archive_file)) {
1410                 arcfile = fopen(archive_file, "rb");
1411                 if (arcfile == NULL)
1412                     error("Can't open archive '%s'", archive_file);
1413
1414                 break;
1415             }
1416         }
1417         for (i = 0; i < argc; i++) {
1418             add(0, argv[i], strlen(argv[i]));
1419         }
1420
1421         fputc(0, outfile);      /* end of archive */
1422         if (ferror(outfile))
1423             error("Can't write");
1424         fclose(outfile);
1425         if (opts.archive_to_stdio) {
1426             if (move_file_to_stream(temp_name, stdout) == -1)
1427                 error("fail to move_file_to_stream(): %s -> %s",temp_name,"stdout");
1428         }
1429         else {
1430             unlink(archive_file);
1431             if (xrename(temp_name, archive_file) == -1)
1432                 error("fail to rename(): %s -> %s",temp_name,archive_file);
1433         }
1434         exit(0);
1435         break;
1436     case 'r':
1437     case 'd':
1438         if (argc == 0) {
1439             message("No files given in argument, do nothing.");
1440             exit(0);
1441         }
1442         outfile = open_tempfile();
1443     case 'x':
1444     case 'p':
1445     case 'l':
1446     case 'v':
1447         /* Open archive. */
1448         if (opts.archive_to_stdio) {
1449             arcfile = stdin;
1450         }
1451         else {
1452             arcfile = fopen(archive_file, "rb");
1453         }
1454         if (arcfile == NULL)
1455             error("Can't open archive '%s'", archive_file);
1456
1457         break;
1458     default:
1459         print_usage();
1460         break;
1461     }
1462
1463     /* change directory to extract dir */
1464     if (cmd == 'x') {
1465         if (opts.outdir) {
1466             if (mkdir(opts.outdir, 0777) == -1) {
1467                 if (errno != EEXIST)
1468                     error("cannot make directory \"%s\"", opts.outdir);
1469             }
1470
1471             if (chdir(opts.outdir) == -1)
1472                 error("cannot change directory \"%s\"", opts.outdir);
1473         }
1474     }
1475
1476     arc_count = 0;
1477
1478     while (!done && read_header(arcfile, &h)) {
1479
1480         arc_count++;
1481
1482         compsize = h.compsize;
1483         origsize = h.origsize;
1484
1485         found = search(argc, argv, &h);
1486         switch (cmd) {
1487         case 'a':
1488         case 'u':
1489             if (found>0) {
1490                 argv[found-1] = 0;
1491
1492                 if (cmd == 'u' && h.mtime > file_mtime(h.filename)) {
1493                     copy(arcfile, outfile, &h);
1494                     break;
1495                 }
1496
1497                 if (add(1, h.filename, h.namelen)) {
1498                     skip(arcfile, &h);
1499                     count++;
1500                 }
1501                 else
1502                     copy(arcfile, outfile, &h);
1503             }
1504             else
1505                 copy(arcfile, outfile, &h);
1506             break;
1507         case 'd':
1508             if (found) {
1509                 count++;
1510                 message("'%s' deleted", h.filename);
1511                 skip(arcfile, &h);
1512             }
1513             else
1514                 copy(arcfile, outfile, &h);
1515             break;
1516         case 'x':
1517         case 'p':
1518             if (found != 0) {
1519                 extract(cmd == 'x', &h);
1520                 if (++count == nfiles)
1521                     done = 1;
1522             }
1523             else
1524                 skip(arcfile, &h);
1525             break;
1526         case 'l':
1527         case 'v':
1528             if (found != 0) {
1529                 if (count == 0)
1530                     list_start();
1531                 list(&h);
1532                 if (++count == nfiles)
1533                     done = 1;
1534             }
1535             skip(arcfile, &h);
1536             break;
1537         }
1538     }
1539
1540     if (cmd == 'a' || cmd == 'u') {
1541         for (i = 0; i < argc; i++) {
1542             if (argv[i]) {
1543                 count++;
1544                 add(0, argv[i], strlen(argv[i]));
1545             }
1546         }
1547     }
1548
1549     if (cmd != 'p') {
1550         if (opts.quiet < 2)
1551             printf("  %d files\n", count);
1552     }
1553
1554     if (count > 0 && (cmd == 'd' || cmd == 'a' || cmd == 'u')) {
1555         fputc(0, outfile);      /* end of archive */
1556         if (ferror(outfile))
1557             error("Can't write");
1558         if (!opts.archive_to_stdio)
1559             unlink(archive_file);
1560         fclose(outfile);
1561         fclose(arcfile);
1562         if (cmd == 'd') {
1563             if (arc_count > count) {
1564                 if (xrename(temp_name, archive_file) == -1)
1565                     error("fail to rename(): %s -> %s",temp_name,archive_file);
1566             }
1567             else {
1568                 message("The archive file \"%s\" was removed because it would be empty.", archive_file);
1569             }
1570         }
1571         else {
1572             if (xrename(temp_name, archive_file) == -1)
1573                 error("fail to rename(): %s -> %s",temp_name,archive_file);
1574         }
1575         exit(0);
1576     }
1577     return EXIT_SUCCESS;
1578 }