OSDN Git Service

00readme.autoconf: Added description for the -b switch which extract MacBinaries.
[lha/lha.git] / src / header.c
1 /* ------------------------------------------------------------------------ */
2 /* LHa for UNIX                                                             */
3 /*              header.c -- header manipulate functions                     */
4 /*                                                                          */
5 /*      Modified                Nobutaka Watazaki                           */
6 /*                                                                          */
7 /*  Original                                                Y.Tagawa        */
8 /*  modified                                    1991.12.16  M.Oki           */
9 /*  Ver. 1.10  Symbolic Link added              1993.10.01  N.Watazaki      */
10 /*  Ver. 1.13b Symbolic Link Bug Fix            1994.08.22  N.Watazaki      */
11 /*  Ver. 1.14  Source All chagned               1995.01.14  N.Watazaki      */
12 /*  Ver. 1.14i bug fixed                        2000.10.06  t.okamoto       */
13 /*  Ver. 1.14i Contributed UTF-8 convertion for Mac OS X                    */
14 /*                                              2002.06.29  Hiroto Sakai    */
15 /*  Ver. 1.14i autoconfiscated & rewritten      2003.02.23  Koji Arai       */
16 /* ------------------------------------------------------------------------ */
17 #include "lha.h"
18
19 #define DUMP_HEADER 1           /* for debugging */
20
21 #if !STRCHR_8BIT_CLEAN
22 /* should use 8 bit clean version */
23 #undef strchr
24 #undef strrchr
25 #define strchr  xstrchr
26 #define strrchr  xstrrchr
27 #endif
28
29 static char    *get_ptr;
30 #define GET_BYTE()      (*get_ptr++ & 0xff)
31
32 #if DUMP_HEADER
33 static char    *start_ptr;
34 #define setup_get(PTR)  (start_ptr = get_ptr = (PTR))
35 #define get_byte()      dump_get_byte()
36 #define skip_bytes(len) dump_skip_bytes(len)
37 #else
38 #define setup_get(PTR)  (get_ptr = (PTR))
39 #define get_byte()      GET_BYTE()
40 #define skip_bytes(len) (get_ptr += (len))
41 #endif
42 #define put_ptr         get_ptr
43 #define setup_put(PTR)  (put_ptr = (PTR))
44 #define put_byte(c)     (*put_ptr++ = (char)(c))
45
46 int optional_archive_kanji_code = NONE;
47 int optional_system_kanji_code = NONE;
48 char *optional_archive_delim = NULL;
49 char *optional_system_delim = NULL;
50 int optional_filename_case = NONE;
51
52 #ifdef MULTIBYTE_FILENAME
53 int default_system_kanji_code = MULTIBYTE_FILENAME;
54 #else
55 int default_system_kanji_code = NONE;
56 #endif
57
58 int
59 calc_sum(p, len)
60     void *p;
61     int len;
62 {
63     int sum = 0;
64
65     while (len--) sum += *(unsigned char*)p++;
66
67     return sum & 0xff;
68 }
69
70 #if DUMP_HEADER
71 static int
72 dump_get_byte()
73 {
74     int c;
75
76     if (verbose_listing && verbose > 1)
77         printf("%02d %2d: ", get_ptr - start_ptr, 1);
78     c = GET_BYTE();
79     if (verbose_listing && verbose > 1) {
80         if (isprint(c))
81             printf("%d(0x%02x) '%c'\n", c, c, c);
82         else
83             printf("%d(0x%02x)\n", c, c);
84     }
85     return c;
86 }
87
88 static void
89 dump_skip_bytes(len)
90     int len;
91 {
92     if (len == 0) return;
93     if (verbose_listing && verbose > 1) {
94         printf("%02d %2d: ", get_ptr - start_ptr, len);
95         while (len--)
96             printf("0x%02x ", GET_BYTE());
97         printf("... ignored\n");
98     }
99     else
100         get_ptr += len;
101 }
102 #endif
103
104 static int
105 get_word()
106 {
107     int b0, b1;
108     int w;
109
110 #if DUMP_HEADER
111     if (verbose_listing && verbose > 1)
112         printf("%02d %2d: ", get_ptr - start_ptr, 2);
113 #endif
114     b0 = GET_BYTE();
115     b1 = GET_BYTE();
116     w = (b1 << 8) + b0;
117 #if DUMP_HEADER
118     if (verbose_listing && verbose > 1)
119         printf("%d(0x%04x)\n", w, w);
120 #endif
121     return w;
122 }
123
124 static void
125 put_word(v)
126     unsigned int    v;
127 {
128     put_byte(v);
129     put_byte(v >> 8);
130 }
131
132 static long
133 get_longword()
134 {
135     long b0, b1, b2, b3;
136     long l;
137
138 #if DUMP_HEADER
139     if (verbose_listing && verbose > 1)
140         printf("%02d %2d: ", get_ptr - start_ptr, 4);
141 #endif
142     b0 = GET_BYTE();
143     b1 = GET_BYTE();
144     b2 = GET_BYTE();
145     b3 = GET_BYTE();
146     l = (b3 << 24) + (b2 << 16) + (b1 << 8) + b0;
147 #if DUMP_HEADER
148     if (verbose_listing && verbose > 1)
149         printf("%ld(0x%08lx)\n", l, l);
150 #endif
151     return l;
152 }
153
154 static void
155 put_longword(v)
156     long v;
157 {
158     put_byte(v);
159     put_byte(v >> 8);
160     put_byte(v >> 16);
161     put_byte(v >> 24);
162 }
163
164 #ifdef HAVE_UINT64_T
165 static uint64_t
166 get_longlongword()
167 {
168     uint64_t b0, b1, b2, b3, b4, b5, b6, b7;
169     uint64_t l;
170
171 #if DUMP_HEADER
172     if (verbose_listing && verbose > 1)
173         printf("%02d %2d: ", get_ptr - start_ptr, 4);
174 #endif
175     b0 = GET_BYTE();
176     b1 = GET_BYTE();
177     b2 = GET_BYTE();
178     b3 = GET_BYTE();
179     b4 = GET_BYTE();
180     b5 = GET_BYTE();
181     b6 = GET_BYTE();
182     b7 = GET_BYTE();
183
184     l = (b7 << 24) + (b6 << 16) + (b5 << 8) + b4;
185     l <<= 32;
186     l |= (b3 << 24) + (b2 << 16) + (b1 << 8) + b0;
187 #if DUMP_HEADER
188     if (verbose_listing && verbose > 1)
189         printf("%lld(%#016llx)\n", l, l);
190 #endif
191     return l;
192 }
193
194 static void
195 put_longlongword(uint64_t v)
196 {
197     put_byte(v);
198     put_byte(v >> 8);
199     put_byte(v >> 16);
200     put_byte(v >> 24);
201     put_byte(v >> 32);
202     put_byte(v >> 40);
203     put_byte(v >> 48);
204     put_byte(v >> 56);
205 }
206 #endif
207
208 static int
209 get_bytes(buf, len, size)
210     char *buf;
211     int len, size;
212 {
213     int i;
214
215 #if DUMP_HEADER
216     if (verbose_listing && verbose > 1)
217         printf("%02d %2d: \"", get_ptr - start_ptr, len);
218
219     for (i = 0; i < len; i++) {
220         if (i < size) buf[i] = get_ptr[i];
221
222         if (verbose_listing && verbose > 1) {
223             if (isprint(buf[i]))
224                 printf("%c", buf[i]);
225             else
226                 printf("\\x%02x", (unsigned char)buf[i]);
227         }
228     }
229
230     if (verbose_listing && verbose > 1)
231         printf("\"\n");
232 #else
233     for (i = 0; i < len && i < size; i++)
234         buf[i] = get_ptr[i];
235 #endif
236
237     get_ptr += len;
238     return i;
239 }
240
241 static void
242 put_bytes(buf, len)
243     char *buf;
244     int len;
245 {
246     int i;
247     for (i = 0; i < len; i++)
248         put_byte(buf[i]);
249 }
250
251 /* added by Koji Arai */
252 void
253 convert_filename(name, len, size,
254                  from_code, to_code,
255                  from_delim, to_delim,
256                  case_to)
257     char *name;
258     int len;                    /* length of name */
259     int size;                   /* size of name buffer */
260     int from_code, to_code, case_to;
261     char *from_delim, *to_delim;
262
263 {
264     int i;
265 #ifdef MULTIBYTE_FILENAME
266     char tmp[FILENAME_LENGTH];
267     int to_code_save = NONE;
268
269     if (from_code == CODE_CAP) {
270         len = cap_to_sjis(tmp, name, sizeof(tmp));
271         strncpy(name, tmp, size);
272         name[size-1] = 0;
273         len = strlen(name);
274         from_code = CODE_SJIS;
275     }
276
277     if (to_code == CODE_CAP) {
278         to_code_save = CODE_CAP;
279         to_code = CODE_SJIS;
280     }
281
282     if (from_code == CODE_SJIS && to_code == CODE_UTF8) {
283         for (i = 0; i < len; i++)
284             /* FIXME: provisionally fix for the Mac OS CoreFoundation */
285             if ((unsigned char)name[i] == LHA_PATHSEP)  name[i] = '/';
286         sjis_to_utf8(tmp, name, sizeof(tmp));
287         strncpy(name, tmp, size);
288         name[size-1] = 0;
289         len = strlen(name);
290         for (i = 0; i < len; i++)
291             if (name[i] == '/')  name[i] = LHA_PATHSEP;
292         from_code = CODE_UTF8;
293     }
294     else if (from_code == CODE_UTF8 && to_code == CODE_SJIS) {
295         for (i = 0; i < len; i++)
296             /* FIXME: provisionally fix for the Mac OS CoreFoundation */
297             if ((unsigned char)name[i] == LHA_PATHSEP)  name[i] = '/';
298         utf8_to_sjis(tmp, name, sizeof(tmp));
299         strncpy(name, tmp, size);
300         name[size-1] = 0;
301         len = strlen(name);
302         for (i = 0; i < len; i++)
303             if (name[i] == '/')  name[i] = LHA_PATHSEP;
304         from_code = CODE_SJIS;
305     }
306 #endif
307
308     /* special case: if `name' has small lettter, not convert case. */
309     if (from_code == CODE_SJIS && case_to == TO_LOWER) {
310         for (i = 0; i < len; i++) {
311 #ifdef MULTIBYTE_FILENAME
312             if (SJIS_FIRST_P(name[i]) && SJIS_SECOND_P(name[i+1]))
313                 i++;
314             else
315 #endif
316             if (islower(name[i])) {
317                 case_to = NONE;
318                 break;
319             }
320         }
321     }
322
323     for (i = 0; i < len; i ++) {
324 #ifdef MULTIBYTE_FILENAME
325         if (from_code == CODE_EUC &&
326             (unsigned char)name[i] == 0x8e) {
327             if (to_code != CODE_SJIS) {
328                 i++;
329                 continue;
330             }
331
332             /* X0201 KANA */
333             memmove(name + i, name + i + 1, len - i);
334             len--;
335             continue;
336         }
337         if (from_code == CODE_SJIS && X0201_KANA_P(name[i])) {
338             if (to_code != CODE_EUC) {
339                 continue;
340             }
341
342             if (len == size - 1) /* check overflow */
343                 len--;
344             memmove(name+i+1, name+i, len-i);
345             name[i] = 0x8e;
346             i++;
347             len++;
348             continue;
349         }
350         if (from_code == CODE_EUC && (name[i] & 0x80) && (name[i+1] & 0x80)) {
351             int c1, c2;
352             if (to_code != CODE_SJIS) {
353                 i++;
354                 continue;
355             }
356
357             c1 = (unsigned char)name[i];
358             c2 = (unsigned char)name[i+1];
359             euc2sjis(&c1, &c2);
360             name[i] = c1;
361             name[i+1] = c2;
362             i++;
363             continue;
364         }
365         if (from_code == CODE_SJIS &&
366             SJIS_FIRST_P(name[i]) &&
367             SJIS_SECOND_P(name[i+1])) {
368             int c1, c2;
369
370             if (to_code != CODE_EUC) {
371                 i++;
372                 continue;
373             }
374
375             c1 = (unsigned char)name[i];
376             c2 = (unsigned char)name[i+1];
377             sjis2euc(&c1, &c2);
378             name[i] = c1;
379             name[i+1] = c2;
380             i++;
381             continue;
382         }
383 #endif /* MULTIBYTE_FILENAME */
384         {
385             char *ptr;
386
387             /* transpose from_delim to to_delim */
388
389             if ((ptr = strchr(from_delim, name[i])) != NULL) {
390                 name[i] = to_delim[ptr - from_delim];
391                 continue;
392             }
393         }
394
395         if (case_to == TO_UPPER && islower(name[i])) {
396             name[i] = toupper(name[i]);
397             continue;
398         }
399         if (case_to == TO_LOWER && isupper(name[i])) {
400             name[i] = tolower(name[i]);
401             continue;
402         }
403     }
404
405 #ifdef MULTIBYTE_FILENAME
406     if (to_code_save == CODE_CAP) {
407         len = sjis_to_cap(tmp, name, sizeof(tmp));
408         strncpy(name, tmp, size);
409         name[size-1] = 0;
410         len = strlen(name);
411     }
412 #endif /* MULTIBYTE_FILENAME */
413 }
414
415 /*
416  * Generic (MS-DOS style) time stamp format (localtime):
417  *
418  *  31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
419  * |<---- year-1980 --->|<- month ->|<--- day ---->|
420  *
421  *  15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
422  * |<--- hour --->|<---- minute --->|<- second/2 ->|
423  *
424  */
425
426 static time_t
427 generic_to_unix_stamp(t)
428     long t;
429 {
430     struct tm tm;
431
432 #define subbits(n, off, len) (((n) >> (off)) & ((1 << (len))-1))
433
434     tm.tm_sec  = subbits(t,  0, 5) * 2;
435     tm.tm_min  = subbits(t,  5, 6);
436     tm.tm_hour = subbits(t, 11, 5);
437     tm.tm_mday = subbits(t, 16, 5);
438     tm.tm_mon  = subbits(t, 21, 4) - 1;
439     tm.tm_year = subbits(t, 25, 7) + 80;
440     tm.tm_isdst = -1;
441
442 #if HAVE_MKTIME
443     return mktime(&tm);
444 #else
445     return timelocal(&tm);
446 #endif
447 }
448
449 static long
450 unix_to_generic_stamp(t)
451     time_t t;
452 {
453     struct tm *tm = localtime(&t);
454
455     tm->tm_year -= 80;
456     tm->tm_mon += 1;
457
458     return ((long)(tm->tm_year << 25) +
459             (tm->tm_mon  << 21) +
460             (tm->tm_mday << 16) +
461             (tm->tm_hour << 11) +
462             (tm->tm_min  << 5) +
463             (tm->tm_sec / 2));
464 }
465
466 static unsigned long
467 wintime_to_unix_stamp()
468 {
469 #if HAVE_UINT64_T
470     uint64_t t;
471     uint64_t epoch = ((uint64_t)0x019db1de << 32) + 0xd53e8000;
472                      /* 0x019db1ded53e8000ULL: 1970-01-01 00:00:00 (UTC) */
473
474     t = (unsigned long)get_longword();
475     t |= (uint64_t)(unsigned long)get_longword() << 32;
476     t = (t - epoch) / 10000000;
477     return t;
478 #else
479     int i, borrow;
480     unsigned long t, q, x;
481     unsigned long wintime[8];
482     unsigned long epoch[8] = {0x01,0x9d,0xb1,0xde, 0xd5,0x3e,0x80,0x00};
483                                 /* 1970-01-01 00:00:00 (UTC) */
484     /* wintime -= epoch */
485     borrow = 0;
486     for (i = 7; i >= 0; i--) {
487         wintime[i] = (unsigned)get_byte() - epoch[i] - borrow;
488         borrow = (wintime[i] > 0xff) ? 1 : 0;
489         wintime[i] &= 0xff;
490     }
491
492     /* q = wintime / 10000000 */
493     t = q = 0;
494     x = 10000000;               /* x: 24bit */
495     for (i = 0; i < 8; i++) {
496         t = (t << 8) + wintime[i]; /* 24bit + 8bit. t must be 32bit variable */
497         q <<= 8;                   /* q must be 32bit (time_t) */
498         q += t / x;
499         t %= x;     /* 24bit */
500     }
501     return q;
502 #endif
503 }
504
505 /*
506  * extended header
507  *
508  *             size  field name
509  *  --------------------------------
510  *  base header:         :
511  *           2 or 4  next-header size  [*1]
512  *  --------------------------------------
513  *  ext header:   1  ext-type            ^
514  *                ?  contents            | [*1] next-header size
515  *           2 or 4  next-header size    v
516  *  --------------------------------------
517  *
518  *  on level 1, 2 header:
519  *    size field is 2 bytes
520  *  on level 3 header:
521  *    size field is 4 bytes
522  */
523
524 static ssize_t
525 get_extended_header(fp, hdr, header_size, hcrc)
526     FILE *fp;
527     LzHeader *hdr;
528     size_t header_size;
529     unsigned int *hcrc;
530 {
531     char data[LZHEADER_STORAGE];
532     int name_length;
533     char dirname[FILENAME_LENGTH];
534     int dir_length = 0;
535     int i;
536     ssize_t whole_size = header_size;
537     int ext_type;
538     int n = 1 + hdr->size_field_length; /* `ext-type' + `next-header size' */
539
540     if (hdr->header_level == 0)
541         return 0;
542
543     name_length = strlen(hdr->name);
544
545     while (header_size) {
546 #if DUMP_HEADER
547         if (verbose_listing && verbose > 1)
548             printf("---\n");
549 #endif
550         setup_get(data);
551         if (sizeof(data) < header_size) {
552             error("header size (%ld) too large.", header_size);
553             exit(1);
554         }
555
556         if (fread(data, header_size, 1, fp) == 0) {
557             error("Invalid header (LHa file ?)");
558             return -1;
559         }
560
561         ext_type = get_byte();
562         switch (ext_type) {
563         case 0:
564 #if DUMP_HEADER
565             if (verbose_listing && verbose > 1) printf("     < header crc >\n");
566 #endif
567             /* header crc (CRC-16) */
568             hdr->header_crc = get_word();
569             /* clear buffer for CRC calculation. */
570             data[1] = data[2] = 0;
571             skip_bytes(header_size - n - 2);
572             break;
573         case 1:
574 #if DUMP_HEADER
575             if (verbose_listing && verbose > 1) printf("     < filename >\n");
576 #endif
577             /* filename */
578             name_length =
579                 get_bytes(hdr->name, header_size-n, sizeof(hdr->name)-1);
580             hdr->name[name_length] = 0;
581             break;
582         case 2:
583 #if DUMP_HEADER
584             if (verbose_listing && verbose > 1) printf("     < directory >\n");
585 #endif
586             /* directory */
587             dir_length = get_bytes(dirname, header_size-n, sizeof(dirname)-1);
588             dirname[dir_length] = 0;
589             break;
590         case 0x40:
591 #if DUMP_HEADER
592             if (verbose_listing && verbose > 1) printf("     < MS-DOS attribute >\n");
593 #endif
594             /* MS-DOS attribute */
595             hdr->attribute = get_word();
596             break;
597         case 0x41:
598 #if DUMP_HEADER
599             if (verbose_listing && verbose > 1) printf("     < Windows time stamp (FILETIME) >\n");
600 #endif
601             /* Windows time stamp (FILETIME structure) */
602             /* it is time in 100 nano seconds since 1601-01-01 00:00:00 */
603
604             skip_bytes(8); /* create time is ignored */
605
606             /* set last modified time */
607             if (hdr->header_level >= 2)
608                 skip_bytes(8);  /* time_t has been already set */
609             else
610                 hdr->unix_last_modified_stamp = wintime_to_unix_stamp();
611
612             skip_bytes(8); /* last access time is ignored */
613
614             break;
615         case 0x42:
616 #if DUMP_HEADER
617             if (verbose_listing && verbose > 1) printf("     < Windows file size header>\n");
618 #endif
619 #ifdef HAVE_UINT64_T
620             /* Windows file size header (UNLHA32 extension) */
621             hdr->packed_size = get_longlongword();
622             hdr->original_size = get_longlongword();
623 #else
624             skip_bytes(8);
625             skip_bytes(8);
626 #endif
627
628             break;
629         case 0x50:
630 #if DUMP_HEADER
631             if (verbose_listing && verbose > 1) printf("     < UNIX permission >\n");
632 #endif
633             /* UNIX permission */
634             hdr->unix_mode = get_word();
635             break;
636         case 0x51:
637 #if DUMP_HEADER
638             if (verbose_listing && verbose > 1) printf("     < UNIX gid and uid >\n");
639 #endif
640             /* UNIX gid and uid */
641             hdr->unix_gid = get_word();
642             hdr->unix_uid = get_word();
643             break;
644         case 0x52:
645 #if DUMP_HEADER
646             if (verbose_listing && verbose > 1) printf("     < UNIX group name >\n");
647 #endif
648             /* UNIX group name */
649             i = get_bytes(hdr->group, header_size-n, sizeof(hdr->group)-1);
650             hdr->group[i] = '\0';
651             break;
652         case 0x53:
653 #if DUMP_HEADER
654             if (verbose_listing && verbose > 1) printf("     < UNIX user name >\n");
655 #endif
656             /* UNIX user name */
657             i = get_bytes(hdr->user, header_size-n, sizeof(hdr->user)-1);
658             hdr->user[i] = '\0';
659             break;
660         case 0x54:
661 #if DUMP_HEADER
662             if (verbose_listing && verbose > 1) printf("     < UNIX last modifed time (time_t) >\n");
663 #endif
664             /* UNIX last modified time */
665             hdr->unix_last_modified_stamp = (time_t) get_longword();
666             break;
667         default:
668             /* other headers */
669             /* 0x39: multi-disk header
670                0x3f: uncompressed comment
671                0x42: 64bit large file size
672                0x48-0x4f(?): reserved for authenticity verification
673                0x7d: encapsulation
674                0x7e: extended attribute - platform information
675                0x7f: extended attribute - permission, owner-id and timestamp
676                      (level 3 on OS/2)
677                0xc4: compressed comment (dict size: 4096)
678                0xc5: compressed comment (dict size: 8192)
679                0xc6: compressed comment (dict size: 16384)
680                0xc7: compressed comment (dict size: 32768)
681                0xc8: compressed comment (dict size: 65536)
682                0xd0-0xdf(?): operating systemm specific information
683                0xfc: encapsulation (another opinion)
684                0xfe: extended attribute - platform information(another opinion)
685                0xff: extended attribute - permission, owner-id and timestamp
686                      (level 3 on UNLHA32) */
687             if (verbose)
688                 warning("unknown extended header 0x%02x", ext_type);
689             skip_bytes(header_size - n);
690             break;
691         }
692
693         if (hcrc)
694             *hcrc = calccrc(*hcrc, data, header_size);
695
696         if (hdr->size_field_length == 2)
697             whole_size += header_size = get_word();
698         else
699             whole_size += header_size = get_longword();
700     }
701
702     /* concatenate dirname and filename */
703     if (dir_length) {
704         if (name_length + dir_length >= sizeof(hdr->name)) {
705             warning("the length of pathname \"%s%s\" is too long.",
706                     dirname, hdr->name);
707             name_length = sizeof(hdr->name) - dir_length - 1;
708             hdr->name[name_length] = 0;
709         }
710         strcat(dirname, hdr->name); /* ok */
711         strcpy(hdr->name, dirname); /* ok */
712         name_length += dir_length;
713     }
714
715     return whole_size;
716 }
717
718 #define I_HEADER_SIZE           0               /* level 0,1,2   */
719 #define I_HEADER_CHECKSUM       1               /* level 0,1     */
720 #define I_METHOD                2               /* level 0,1,2,3 */
721 #define I_PACKED_SIZE           7               /* level 0,1,2,3 */
722 #define I_ATTRIBUTE             19              /* level 0,1,2,3 */
723 #define I_HEADER_LEVEL          20              /* level 0,1,2,3 */
724
725 #define COMMON_HEADER_SIZE      21      /* size of common part */
726
727 #define I_GENERIC_HEADER_SIZE 24 /* + name_length */
728 #define I_LEVEL0_HEADER_SIZE  36 /* + name_length (unix extended) */
729 #define I_LEVEL1_HEADER_SIZE  27 /* + name_length */
730 #define I_LEVEL2_HEADER_SIZE  26 /* + padding */
731 #define I_LEVEL3_HEADER_SIZE  32
732
733 /*
734  * level 0 header
735  *
736  *
737  * offset  size  field name
738  * ----------------------------------
739  *     0      1  header size    [*1]
740  *     1      1  header sum
741  *            ---------------------------------------
742  *     2      5  method ID                         ^
743  *     7      4  packed size    [*2]               |
744  *    11      4  original size                     |
745  *    15      2  time                              |
746  *    17      2  date                              |
747  *    19      1  attribute                         | [*1] header size (X+Y+22)
748  *    20      1  level (0x00 fixed)                |
749  *    21      1  name length                       |
750  *    22      X  pathname                          |
751  * X +22      2  file crc (CRC-16)                 |
752  * X +24      Y  ext-header(old style)             v
753  * -------------------------------------------------
754  * X+Y+24        data                              ^
755  *                 :                               | [*2] packed size
756  *                 :                               v
757  * -------------------------------------------------
758  *
759  * ext-header(old style)
760  *     0      1  ext-type ('U')
761  *     1      1  minor version
762  *     2      4  UNIX time
763  *     6      2  mode
764  *     8      2  uid
765  *    10      2  gid
766  *
767  * attribute (MS-DOS)
768  *    bit1  read only
769  *    bit2  hidden
770  *    bit3  system
771  *    bit4  volume label
772  *    bit5  directory
773  *    bit6  archive bit (need to backup)
774  *
775  */
776 static int
777 get_header_level0(fp, hdr, data)
778     FILE *fp;
779     LzHeader *hdr;
780     char *data;
781 {
782     size_t header_size;
783     ssize_t extend_size;
784     int checksum;
785     int name_length;
786     int i;
787
788     hdr->size_field_length = 2; /* in bytes */
789     hdr->header_size = header_size = get_byte();
790     checksum = get_byte();
791
792     if (fread(data + COMMON_HEADER_SIZE,
793               header_size + 2 - COMMON_HEADER_SIZE, 1, fp) == 0) {
794         error("Invalid header (LHarc file ?)");
795         return FALSE;   /* finish */
796     }
797
798     if (calc_sum(data + I_METHOD, header_size) != checksum) {
799         error("Checksum error (LHarc file?)");
800         return FALSE;
801     }
802
803     get_bytes(hdr->method, 5, sizeof(hdr->method));
804     hdr->packed_size = get_longword();
805     hdr->original_size = get_longword();
806     hdr->unix_last_modified_stamp = generic_to_unix_stamp(get_longword());
807     hdr->attribute = get_byte(); /* MS-DOS attribute */
808     hdr->header_level = get_byte();
809     name_length = get_byte();
810     i = get_bytes(hdr->name, name_length, sizeof(hdr->name)-1);
811     hdr->name[i] = '\0';
812
813     /* defaults for other type */
814     hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
815     hdr->unix_gid = 0;
816     hdr->unix_uid = 0;
817
818     extend_size = header_size+2 - name_length - 24;
819
820     if (extend_size < 0) {
821         if (extend_size == -2) {
822             /* CRC field is not given */
823             hdr->extend_type = EXTEND_GENERIC;
824             hdr->has_crc = FALSE;
825
826             return TRUE;
827         }
828
829         error("Unkonwn header (lha file?)");
830         exit(1);
831     }
832
833     hdr->has_crc = TRUE;
834     hdr->crc = get_word();
835
836     if (extend_size == 0)
837         return TRUE;
838
839     hdr->extend_type = get_byte();
840     extend_size--;
841
842     if (hdr->extend_type == EXTEND_UNIX) {
843         if (extend_size >= 11) {
844             hdr->minor_version = get_byte();
845             hdr->unix_last_modified_stamp = (time_t) get_longword();
846             hdr->unix_mode = get_word();
847             hdr->unix_uid = get_word();
848             hdr->unix_gid = get_word();
849             extend_size -= 11;
850         } else {
851             hdr->extend_type = EXTEND_GENERIC;
852         }
853     }
854     if (extend_size > 0)
855         skip_bytes(extend_size);
856
857     hdr->header_size += 2;
858     return TRUE;
859 }
860
861 /*
862  * level 1 header
863  *
864  *
865  * offset   size  field name
866  * -----------------------------------
867  *     0       1  header size   [*1]
868  *     1       1  header sum
869  *             -------------------------------------
870  *     2       5  method ID                        ^
871  *     7       4  skip size     [*2]               |
872  *    11       4  original size                    |
873  *    15       2  time                             |
874  *    17       2  date                             |
875  *    19       1  attribute (0x20 fixed)           | [*1] header size (X+Y+25)
876  *    20       1  level (0x01 fixed)               |
877  *    21       1  name length                      |
878  *    22       X  filename                         |
879  * X+ 22       2  file crc (CRC-16)                |
880  * X+ 24       1  OS ID                            |
881  * X +25       Y  ???                              |
882  * X+Y+25      2  next-header size                 v
883  * -------------------------------------------------
884  * X+Y+27      Z  ext-header                       ^
885  *                 :                               |
886  * -----------------------------------             | [*2] skip size
887  * X+Y+Z+27       data                             |
888  *                 :                               v
889  * -------------------------------------------------
890  *
891  */
892 static int
893 get_header_level1(fp, hdr, data)
894     FILE *fp;
895     LzHeader *hdr;
896     char *data;
897 {
898     size_t header_size;
899     ssize_t extend_size;
900     int checksum;
901     int name_length;
902     int i, dummy;
903
904     hdr->size_field_length = 2; /* in bytes */
905     hdr->header_size = header_size = get_byte();
906     checksum = get_byte();
907
908     if (fread(data + COMMON_HEADER_SIZE,
909               header_size + 2 - COMMON_HEADER_SIZE, 1, fp) == 0) {
910         error("Invalid header (LHarc file ?)");
911         return FALSE;   /* finish */
912     }
913
914     if (calc_sum(data + I_METHOD, header_size) != checksum) {
915         error("Checksum error (LHarc file?)");
916         return FALSE;
917     }
918
919     get_bytes(hdr->method, 5, sizeof(hdr->method));
920     hdr->packed_size = get_longword(); /* skip size */
921     hdr->original_size = get_longword();
922     hdr->unix_last_modified_stamp = generic_to_unix_stamp(get_longword());
923     hdr->attribute = get_byte(); /* 0x20 fixed */
924     hdr->header_level = get_byte();
925
926     name_length = get_byte();
927     i = get_bytes(hdr->name, name_length, sizeof(hdr->name)-1);
928     hdr->name[i] = '\0';
929
930     /* defaults for other type */
931     hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
932     hdr->unix_gid = 0;
933     hdr->unix_uid = 0;
934
935     hdr->has_crc = TRUE;
936     hdr->crc = get_word();
937     hdr->extend_type = get_byte();
938
939     dummy = header_size+2 - name_length - I_LEVEL1_HEADER_SIZE;
940     if (dummy > 0)
941         skip_bytes(dummy); /* skip old style extend header */
942
943     extend_size = get_word();
944     extend_size = get_extended_header(fp, hdr, extend_size, 0);
945     if (extend_size == -1)
946         return FALSE;
947
948     /* On level 1 header, size fields should be adjusted. */
949     /* the `packed_size' field contains the extended header size. */
950     /* the `header_size' field does not. */
951     hdr->packed_size -= extend_size;
952     hdr->header_size += extend_size + 2;
953
954     return TRUE;
955 }
956
957 /*
958  * level 2 header
959  *
960  *
961  * offset   size  field name
962  * --------------------------------------------------
963  *     0       2  total header size [*1]           ^
964  *             -----------------------             |
965  *     2       5  method ID                        |
966  *     7       4  packed size       [*2]           |
967  *    11       4  original size                    |
968  *    15       4  time                             |
969  *    19       1  RESERVED (0x20 fixed)            | [*1] total header size
970  *    20       1  level (0x02 fixed)               |      (X+26+(1))
971  *    21       2  file crc (CRC-16)                |
972  *    23       1  OS ID                            |
973  *    24       2  next-header size                 |
974  * -----------------------------------             |
975  *    26       X  ext-header                       |
976  *                 :                               |
977  * -----------------------------------             |
978  * X +26      (1) padding                          v
979  * -------------------------------------------------
980  * X +26+(1)      data                             ^
981  *                 :                               | [*2] packed size
982  *                 :                               v
983  * -------------------------------------------------
984  *
985  */
986 static int
987 get_header_level2(fp, hdr, data)
988     FILE *fp;
989     LzHeader *hdr;
990     char *data;
991 {
992     size_t header_size;
993     ssize_t extend_size;
994     int padding;
995     unsigned int hcrc;
996
997     hdr->size_field_length = 2; /* in bytes */
998     hdr->header_size = header_size = get_word();
999
1000     if (fread(data + COMMON_HEADER_SIZE,
1001               I_LEVEL2_HEADER_SIZE - COMMON_HEADER_SIZE, 1, fp) == 0) {
1002         error("Invalid header (LHarc file ?)");
1003         return FALSE;   /* finish */
1004     }
1005
1006     get_bytes(hdr->method, 5, sizeof(hdr->method));
1007     hdr->packed_size = get_longword();
1008     hdr->original_size = get_longword();
1009     hdr->unix_last_modified_stamp = get_longword();
1010     hdr->attribute = get_byte(); /* reserved */
1011     hdr->header_level = get_byte();
1012
1013     /* defaults for other type */
1014     hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
1015     hdr->unix_gid = 0;
1016     hdr->unix_uid = 0;
1017
1018     hdr->has_crc = TRUE;
1019     hdr->crc = get_word();
1020     hdr->extend_type = get_byte();
1021     extend_size = get_word();
1022
1023     INITIALIZE_CRC(hcrc);
1024     hcrc = calccrc(hcrc, data, get_ptr - data);
1025
1026     extend_size = get_extended_header(fp, hdr, extend_size, &hcrc);
1027     if (extend_size == -1)
1028         return FALSE;
1029
1030     padding = header_size - I_LEVEL2_HEADER_SIZE - extend_size;
1031     while (padding--)           /* padding should be 0 or 1 */
1032         hcrc = UPDATE_CRC(hcrc, fgetc(fp));
1033
1034     if (hdr->header_crc != hcrc)
1035         error("header CRC error");
1036
1037     return TRUE;
1038 }
1039
1040 /*
1041  * level 3 header
1042  *
1043  *
1044  * offset   size  field name
1045  * --------------------------------------------------
1046  *     0       2  size field length (4 fixed)      ^
1047  *     2       5  method ID                        |
1048  *     7       4  packed size       [*2]           |
1049  *    11       4  original size                    |
1050  *    15       4  time                             |
1051  *    19       1  RESERVED (0x20 fixed)            | [*1] total header size
1052  *    20       1  level (0x03 fixed)               |      (X+32)
1053  *    21       2  file crc (CRC-16)                |
1054  *    23       1  OS ID                            |
1055  *    24       4  total header size [*1]           |
1056  *    28       4  next-header size                 |
1057  * -----------------------------------             |
1058  *    32       X  ext-header                       |
1059  *                 :                               v
1060  * -------------------------------------------------
1061  * X +32          data                             ^
1062  *                 :                               | [*2] packed size
1063  *                 :                               v
1064  * -------------------------------------------------
1065  *
1066  */
1067 static int
1068 get_header_level3(fp, hdr, data)
1069     FILE *fp;
1070     LzHeader *hdr;
1071     char *data;
1072 {
1073     size_t header_size;
1074     ssize_t extend_size;
1075     int padding;
1076     unsigned int hcrc;
1077
1078     hdr->size_field_length = get_word();
1079
1080     if (fread(data + COMMON_HEADER_SIZE,
1081               I_LEVEL3_HEADER_SIZE - COMMON_HEADER_SIZE, 1, fp) == 0) {
1082         error("Invalid header (LHarc file ?)");
1083         return FALSE;   /* finish */
1084     }
1085
1086     get_bytes(hdr->method, 5, sizeof(hdr->method));
1087     hdr->packed_size = get_longword();
1088     hdr->original_size = get_longword();
1089     hdr->unix_last_modified_stamp = get_longword();
1090     hdr->attribute = get_byte(); /* reserved */
1091     hdr->header_level = get_byte();
1092
1093     /* defaults for other type */
1094     hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
1095     hdr->unix_gid = 0;
1096     hdr->unix_uid = 0;
1097
1098     hdr->has_crc = TRUE;
1099     hdr->crc = get_word();
1100     hdr->extend_type = get_byte();
1101     hdr->header_size = header_size = get_longword();
1102     extend_size = get_longword();
1103
1104     INITIALIZE_CRC(hcrc);
1105     hcrc = calccrc(hcrc, data, get_ptr - data);
1106
1107     extend_size = get_extended_header(fp, hdr, extend_size, &hcrc);
1108     if (extend_size == -1)
1109         return FALSE;
1110
1111     padding = header_size - I_LEVEL3_HEADER_SIZE - extend_size;
1112     while (padding--)           /* padding should be 0 */
1113         hcrc = UPDATE_CRC(hcrc, fgetc(fp));
1114
1115     if (hdr->header_crc != hcrc)
1116         error("header CRC error");
1117
1118     return TRUE;
1119 }
1120
1121 boolean
1122 get_header(fp, hdr)
1123     FILE *fp;
1124     LzHeader *hdr;
1125 {
1126     char data[LZHEADER_STORAGE];
1127
1128     int archive_kanji_code = CODE_SJIS;
1129     int system_kanji_code = default_system_kanji_code;
1130     char *archive_delim = "\377\\"; /* `\' is for level 0 header and
1131                                        broken archive. */
1132     char *system_delim = "//";
1133     int filename_case = NONE;
1134     int end_mark;
1135
1136     memset(hdr, 0, sizeof(LzHeader));
1137
1138     setup_get(data);
1139
1140     if ((end_mark = getc(fp)) == EOF || end_mark == 0) {
1141         return FALSE;           /* finish */
1142     }
1143     data[0] = end_mark;
1144
1145     if (fread(data + 1, COMMON_HEADER_SIZE - 1, 1, fp) == 0) {
1146         error("Invalid header (LHarc file ?)");
1147         return FALSE;           /* finish */
1148     }
1149
1150     switch (data[I_HEADER_LEVEL]) {
1151     case 0:
1152         if (get_header_level0(fp, hdr, data) == FALSE)
1153             return FALSE;
1154         break;
1155     case 1:
1156         if (get_header_level1(fp, hdr, data) == FALSE)
1157             return FALSE;
1158         break;
1159     case 2:
1160         if (get_header_level2(fp, hdr, data) == FALSE)
1161             return FALSE;
1162         break;
1163     case 3:
1164         if (get_header_level3(fp, hdr, data) == FALSE)
1165             return FALSE;
1166         break;
1167     default:
1168         error("Unknown level header (level %d)", data[I_HEADER_LEVEL]);
1169         return FALSE;
1170     }
1171
1172     /* filename conversion */
1173     switch (hdr->extend_type) {
1174     case EXTEND_MSDOS:
1175         filename_case = convertcase ? TO_LOWER : NONE;
1176         break;
1177     case EXTEND_HUMAN:
1178     case EXTEND_OS68K:
1179     case EXTEND_XOSK:
1180     case EXTEND_UNIX:
1181     case EXTEND_JAVA:
1182         filename_case = NONE;
1183         break;
1184
1185     case EXTEND_MACOS:
1186         archive_delim = "\377/:\\";
1187                           /* `\' is for level 0 header and broken archive. */
1188         system_delim = "/://";
1189         filename_case = NONE;
1190         break;
1191
1192     default:
1193         filename_case = convertcase ? TO_LOWER : NONE;
1194         break;
1195     }
1196
1197     if (optional_archive_kanji_code)
1198         archive_kanji_code = optional_archive_kanji_code;
1199     if (optional_system_kanji_code)
1200         system_kanji_code = optional_system_kanji_code;
1201     if (optional_archive_delim)
1202         archive_delim = optional_archive_delim;
1203     if (optional_system_delim)
1204         system_delim = optional_system_delim;
1205     if (optional_filename_case)
1206         filename_case = optional_filename_case;
1207
1208     /* kanji code and delimiter conversion */
1209     convert_filename(hdr->name, strlen(hdr->name), sizeof(hdr->name),
1210                      archive_kanji_code,
1211                      system_kanji_code,
1212                      archive_delim, system_delim, filename_case);
1213
1214     if ((hdr->unix_mode & UNIX_FILE_SYMLINK) == UNIX_FILE_SYMLINK) {
1215         char *p;
1216         /* split symbolic link */
1217         p = strchr(hdr->name, '|');
1218         if (p) {
1219             /* hdr->name is symbolic link name */
1220             /* hdr->realname is real name */
1221             *p = 0;
1222             strcpy(hdr->realname, p+1); /* ok */
1223         }
1224         else
1225             error("unknown symlink name \"%s\"", hdr->name);
1226     }
1227
1228     return TRUE;
1229 }
1230
1231 /* skip SFX header */
1232 int
1233 seek_lha_header(fp)
1234     FILE *fp;
1235 {
1236     unsigned char   buffer[64 * 1024]; /* max seek size */
1237     unsigned char  *p;
1238     int             n;
1239
1240     n = fread(buffer, 1, sizeof(buffer), fp);
1241
1242     for (p = buffer; p < buffer + n; p++) {
1243         if (! (p[I_METHOD]=='-' && p[I_METHOD+1]=='l' && p[I_METHOD+4]=='-'))
1244             continue;
1245         /* found "-l??-" keyword (as METHOD type string) */
1246
1247         /* level 0 or 1 header */
1248         if ((p[I_HEADER_LEVEL] == 0 || p[I_HEADER_LEVEL] == 1)
1249             && p[I_HEADER_SIZE] > 20
1250             && p[I_HEADER_CHECKSUM] == calc_sum(p+2, p[I_HEADER_SIZE])) {
1251             if (fseeko(fp, (p - buffer) - n, SEEK_CUR) == -1)
1252                 fatal_error("cannot seek header");
1253             return 0;
1254         }
1255
1256         /* level 2 header */
1257         if (p[I_HEADER_LEVEL] == 2
1258             && p[I_HEADER_SIZE] >= 24
1259             && p[I_ATTRIBUTE] == 0x20) {
1260             if (fseeko(fp, (p - buffer) - n, SEEK_CUR) == -1)
1261                 fatal_error("cannot seek header");
1262             return 0;
1263         }
1264     }
1265
1266     if (fseeko(fp, -n, SEEK_CUR) == -1)
1267         fatal_error("cannot seek header");
1268     return -1;
1269 }
1270
1271
1272 /* remove leading `xxxx/..' */
1273 static char *
1274 remove_leading_dots(char *path)
1275 {
1276     char *first = path;
1277     char *ptr = 0;
1278
1279     if (strcmp(first, "..") == 0) {
1280         warning("Removing leading `..' from member name.");
1281         return first+1;         /* change to "." */
1282     }
1283
1284     if (strstr(first, "..") == 0)
1285         return first;
1286
1287     while (path && *path) {
1288
1289         if (strcmp(path, "..") == 0)
1290             ptr = path = path+2;
1291         else if (strncmp(path, "../", 3) == 0)
1292             ptr = path = path+3;
1293         else
1294             path = strchr(path, '/');
1295
1296         if (path && *path == '/') {
1297             path++;
1298         }
1299     }
1300
1301     if (ptr) {
1302         warning("Removing leading `%.*s' from member name.", ptr-first, first);
1303         return ptr;
1304     }
1305
1306     return first;
1307 }
1308
1309 static int
1310 copy_path_element(char *dst, const char *src, int size)
1311 {
1312     int i;
1313
1314     if (size < 1) return 0;
1315
1316     for (i = 0; i < size; i++) {
1317         dst[i] = src[i];
1318         if (dst[i] == '\0')
1319             return i;
1320         if (dst[i] == '/') {
1321             dst[++i] = 0;
1322             return i;
1323         }
1324     }
1325
1326     dst[--i] = 0;
1327
1328     return i;
1329 }
1330
1331 /* remove leading "xxx/../" and "./" */
1332 static int
1333 remove_dots(char *newpath, char *path, size_t size)
1334 {
1335     int len;
1336     char *p = newpath;
1337
1338     path = remove_leading_dots(path);
1339
1340     while (*path) {
1341         if (path[0] == '.' && path[1] == '/')
1342             path += 2;
1343         else {
1344             int len;
1345             len = copy_path_element(newpath, path, size);
1346
1347             path += len;
1348             newpath += len;
1349             size -= len;
1350             if (size <= 1)
1351                 break;
1352         }
1353     }
1354
1355     /* When newpath is empty, set "." */
1356     if (newpath == p) {
1357         strcpy(newpath, ".");
1358         newpath++;
1359     }
1360
1361     return newpath - p;         /* string length */
1362 }
1363
1364 void
1365 init_header(name, v_stat, hdr)
1366     char           *name;
1367     struct stat    *v_stat;
1368     LzHeader       *hdr;
1369 {
1370     int             len;
1371
1372     memset(hdr, 0, sizeof(LzHeader));
1373
1374     /* the `method' member is rewrote by the encoding function.
1375        but need set for empty files */
1376     memcpy(hdr->method, LZHUFF0_METHOD, METHOD_TYPE_STORAGE);
1377
1378     hdr->packed_size = 0;
1379     hdr->original_size = v_stat->st_size;
1380     hdr->attribute = GENERIC_ATTRIBUTE;
1381     hdr->header_level = header_level;
1382
1383     len = remove_dots(hdr->name, name, sizeof(hdr->name));
1384
1385     hdr->crc = 0x0000;
1386     hdr->extend_type = EXTEND_UNIX;
1387     hdr->unix_last_modified_stamp = v_stat->st_mtime;
1388     /* since 00:00:00 JAN.1.1970 */
1389 #ifdef NOT_COMPATIBLE_MODE
1390     /* Please need your modification in this space. */
1391 #ifdef __DJGPP__
1392     hdr->unix_mode = 0;
1393     if (S_ISREG(v_stat->st_mode))
1394             hdr->unix_mode = hdr->unix_mode | UNIX_FILE_REGULAR;
1395     if (S_ISDIR(v_stat->st_mode))
1396             hdr->unix_mode = hdr->unix_mode | UNIX_FILE_DIRECTORY;
1397     if (S_ISLNK(v_stat->st_mode))
1398             hdr->unix_mode = hdr->unix_mode | UNIX_FILE_SYMLINK;
1399     if (v_stat->st_mode & S_IRUSR) 
1400             hdr->unix_mode = hdr->unix_mode | UNIX_OWNER_READ_PERM;
1401     if (v_stat->st_mode & S_IRGRP) 
1402             hdr->unix_mode = hdr->unix_mode | UNIX_GROUP_READ_PERM;
1403     if (v_stat->st_mode & S_IROTH) 
1404             hdr->unix_mode = hdr->unix_mode | UNIX_OTHER_READ_PERM;
1405     if (v_stat->st_mode & S_IWUSR) 
1406             hdr->unix_mode = hdr->unix_mode | UNIX_OWNER_WRITE_PERM;
1407     if (v_stat->st_mode & S_IWGRP) 
1408             hdr->unix_mode = hdr->unix_mode | UNIX_GROUP_WRITE_PERM;
1409     if (v_stat->st_mode & S_IWOTH) 
1410             hdr->unix_mode = hdr->unix_mode | UNIX_OTHER_WRITE_PERM;
1411     if (v_stat->st_mode & S_IXUSR) 
1412             hdr->unix_mode = hdr->unix_mode | UNIX_OWNER_EXEC_PERM;
1413     if (v_stat->st_mode & S_IXGRP) 
1414             hdr->unix_mode = hdr->unix_mode | UNIX_GROUP_EXEC_PERM;
1415     if (v_stat->st_mode & S_IXOTH) 
1416             hdr->unix_mode = hdr->unix_mode | UNIX_OTHER_EXEC_PERM;
1417     if (v_stat->st_mode & S_ISUID) 
1418             hdr->unix_mode = hdr->unix_mode | UNIX_SETUID;
1419     if (v_stat->st_mode & S_ISGID) 
1420             hdr->unix_mode = hdr->unix_mode | UNIX_SETGID;
1421 #endif /* __DJGPP__ */
1422 #else
1423     hdr->unix_mode = v_stat->st_mode;
1424 #endif
1425
1426     hdr->unix_uid = v_stat->st_uid;
1427     hdr->unix_gid = v_stat->st_gid;
1428
1429 #if INCLUDE_OWNER_NAME_IN_HEADER
1430 #if HAVE_GETPWUID
1431     {
1432         struct passwd *ent = getpwuid(hdr->unix_uid);
1433
1434         if (ent) {
1435             strncpy(hdr->user, ent->pw_name, sizeof(hdr->user));
1436             if (hdr->user[sizeof(hdr->user)-1])
1437                 hdr->user[sizeof(hdr->user)-1] = 0;
1438         }
1439     }
1440 #endif
1441 #if HAVE_GETGRGID
1442     {
1443         struct group *ent = getgrgid(hdr->unix_gid);
1444
1445         if (ent) {
1446             strncpy(hdr->group, ent->gr_name, sizeof(hdr->group));
1447             if (hdr->group[sizeof(hdr->group)-1])
1448                 hdr->group[sizeof(hdr->group)-1] = 0;
1449         }
1450     }
1451 #endif
1452 #endif /* INCLUDE_OWNER_NAME_IN_HEADER */
1453     if (is_directory(v_stat)) {
1454         memcpy(hdr->method, LZHDIRS_METHOD, METHOD_TYPE_STORAGE);
1455         hdr->attribute = GENERIC_DIRECTORY_ATTRIBUTE;
1456         hdr->original_size = 0;
1457         if (len > 0 && hdr->name[len - 1] != '/') {
1458             if (len < sizeof(hdr->name)-1)
1459                 strcpy(&hdr->name[len++], "/"); /* ok */
1460             else
1461                 warning("the length of dirname \"%s\" is too long.",
1462                         hdr->name);
1463         }
1464     }
1465
1466 #ifdef S_IFLNK
1467     if (is_symlink(v_stat)) {
1468         memcpy(hdr->method, LZHDIRS_METHOD, METHOD_TYPE_STORAGE);
1469         hdr->attribute = GENERIC_DIRECTORY_ATTRIBUTE;
1470         hdr->original_size = 0;
1471         readlink(name, hdr->realname, sizeof(hdr->realname));
1472     }
1473 #endif
1474 }
1475
1476 static void
1477 write_unix_info(hdr)
1478     LzHeader *hdr;
1479 {
1480     /* UNIX specific informations */
1481
1482     put_word(5);            /* size */
1483     put_byte(0x50);         /* permission */
1484     put_word(hdr->unix_mode);
1485
1486     put_word(7);            /* size */
1487     put_byte(0x51);         /* gid and uid */
1488     put_word(hdr->unix_gid);
1489     put_word(hdr->unix_uid);
1490
1491     if (hdr->group[0]) {
1492         int len = strlen(hdr->group);
1493         put_word(len + 3);  /* size */
1494         put_byte(0x52);     /* group name */
1495         put_bytes(hdr->group, len);
1496     }
1497
1498     if (hdr->user[0]) {
1499         int len = strlen(hdr->user);
1500         put_word(len + 3);  /* size */
1501         put_byte(0x53);     /* user name */
1502         put_bytes(hdr->user, len);
1503     }
1504
1505     if (hdr->header_level == 1) {
1506         put_word(7);        /* size */
1507         put_byte(0x54);     /* time stamp */
1508         put_longword(hdr->unix_last_modified_stamp);
1509     }
1510 }
1511
1512 static size_t
1513 write_header_level0(data, hdr, pathname)
1514     LzHeader *hdr;
1515     char *data, *pathname;
1516 {
1517     int limit;
1518     int name_length;
1519     size_t header_size;
1520
1521     setup_put(data);
1522     memset(data, 0, LZHEADER_STORAGE);
1523
1524     put_byte(0x00);             /* header size */
1525     put_byte(0x00);             /* check sum */
1526     put_bytes(hdr->method, 5);
1527     put_longword(hdr->packed_size);
1528     put_longword(hdr->original_size);
1529     put_longword(unix_to_generic_stamp(hdr->unix_last_modified_stamp));
1530     put_byte(hdr->attribute);
1531     put_byte(hdr->header_level); /* level 0 */
1532
1533     /* write pathname (level 0 header contains the directory part) */
1534     name_length = strlen(pathname);
1535     if (generic_format)
1536         limit = 255 - I_GENERIC_HEADER_SIZE + 2;
1537     else
1538         limit = 255 - I_LEVEL0_HEADER_SIZE + 2;
1539
1540     if (name_length > limit) {
1541         warning("the length of pathname \"%s\" is too long.", pathname);
1542         name_length = limit;
1543     }
1544     put_byte(name_length);
1545     put_bytes(pathname, name_length);
1546     put_word(hdr->crc);
1547
1548     if (generic_format) {
1549         header_size = I_GENERIC_HEADER_SIZE + name_length - 2;
1550         data[I_HEADER_SIZE] = header_size;
1551         data[I_HEADER_CHECKSUM] = calc_sum(data + I_METHOD, header_size);
1552     } else {
1553         /* write old-style extend header */
1554         put_byte(EXTEND_UNIX);
1555         put_byte(CURRENT_UNIX_MINOR_VERSION);
1556         put_longword(hdr->unix_last_modified_stamp);
1557         put_word(hdr->unix_mode);
1558         put_word(hdr->unix_uid);
1559         put_word(hdr->unix_gid);
1560
1561         /* size of extended header is 12 */
1562         header_size = I_LEVEL0_HEADER_SIZE + name_length - 2;
1563         data[I_HEADER_SIZE] = header_size;
1564         data[I_HEADER_CHECKSUM] = calc_sum(data + I_METHOD, header_size);
1565     }
1566
1567     return header_size + 2;
1568 }
1569
1570 static size_t
1571 write_header_level1(data, hdr, pathname)
1572     LzHeader *hdr;
1573     char *data, *pathname;
1574 {
1575     int name_length, dir_length, limit;
1576     char *basename, *dirname;
1577     size_t header_size;
1578     char *extend_header_top;
1579     size_t extend_header_size;
1580
1581     basename = strrchr(pathname, LHA_PATHSEP);
1582     if (basename) {
1583         basename++;
1584         name_length = strlen(basename);
1585         dirname = pathname;
1586         dir_length = basename - dirname;
1587     }
1588     else {
1589         basename = pathname;
1590         name_length = strlen(basename);
1591         dirname = "";
1592         dir_length = 0;
1593     }
1594
1595     setup_put(data);
1596     memset(data, 0, LZHEADER_STORAGE);
1597
1598     put_byte(0x00);             /* header size */
1599     put_byte(0x00);             /* check sum */
1600     put_bytes(hdr->method, 5);
1601     put_longword(hdr->packed_size);
1602     put_longword(hdr->original_size);
1603     put_longword(unix_to_generic_stamp(hdr->unix_last_modified_stamp));
1604     put_byte(0x20);
1605     put_byte(hdr->header_level); /* level 1 */
1606
1607     /* level 1 header: write filename (basename only) */
1608     limit = 255 - I_LEVEL1_HEADER_SIZE + 2;
1609     if (name_length > limit) {
1610         put_byte(0);            /* name length */
1611     }
1612     else {
1613         put_byte(name_length);
1614         put_bytes(basename, name_length);
1615     }
1616
1617     put_word(hdr->crc);
1618
1619     if (generic_format)
1620         put_byte(0x00);
1621     else
1622         put_byte(EXTEND_UNIX);
1623
1624     /* write extend header from here. */
1625
1626     extend_header_top = put_ptr+2; /* +2 for the field `next header size' */
1627     header_size = extend_header_top - data - 2;
1628
1629     /* write filename and dirname */
1630
1631     if (name_length > limit) {
1632         put_word(name_length + 3); /* size */
1633         put_byte(0x01);         /* filename */
1634         put_bytes(basename, name_length);
1635     }
1636
1637     if (dir_length > 0) {
1638         put_word(dir_length + 3); /* size */
1639         put_byte(0x02);         /* dirname */
1640         put_bytes(dirname, dir_length);
1641     }
1642
1643     if (!generic_format)
1644         write_unix_info(hdr);
1645
1646     put_word(0x0000);           /* next header size */
1647
1648     extend_header_size = put_ptr - extend_header_top;
1649     /* On level 1 header, the packed size field is contains the ext-header */
1650     hdr->packed_size += put_ptr - extend_header_top;
1651
1652     /* put `skip size' */
1653     setup_put(data + I_PACKED_SIZE);
1654     put_longword(hdr->packed_size);
1655
1656     data[I_HEADER_SIZE] = header_size;
1657     data[I_HEADER_CHECKSUM] = calc_sum(data + I_METHOD, header_size);
1658
1659     return header_size + extend_header_size + 2;
1660 }
1661
1662 static size_t
1663 write_header_level2(data, hdr, pathname)
1664     LzHeader *hdr;
1665     char *data, *pathname;
1666 {
1667     int name_length, dir_length;
1668     char *basename, *dirname;
1669     size_t header_size;
1670     char *extend_header_top;
1671     char *headercrc_ptr;
1672     unsigned int hcrc;
1673
1674     basename = strrchr(pathname, LHA_PATHSEP);
1675     if (basename) {
1676         basename++;
1677         name_length = strlen(basename);
1678         dirname = pathname;
1679         dir_length = basename - dirname;
1680     }
1681     else {
1682         basename = pathname;
1683         name_length = strlen(basename);
1684         dirname = "";
1685         dir_length = 0;
1686     }
1687
1688     setup_put(data);
1689     memset(data, 0, LZHEADER_STORAGE);
1690
1691     put_word(0x0000);           /* header size */
1692     put_bytes(hdr->method, 5);
1693     put_longword(hdr->packed_size);
1694     put_longword(hdr->original_size);
1695     put_longword(hdr->unix_last_modified_stamp);
1696     put_byte(0x20);
1697     put_byte(hdr->header_level); /* level 2 */
1698
1699     put_word(hdr->crc);
1700
1701     if (generic_format)
1702         put_byte(0x00);
1703     else
1704         put_byte(EXTEND_UNIX);
1705
1706     /* write extend header from here. */
1707
1708     extend_header_top = put_ptr+2; /* +2 for the field `next header size' */
1709
1710     /* write common header */
1711     put_word(5);
1712     put_byte(0x00);
1713     headercrc_ptr = put_ptr;
1714     put_word(0x0000);           /* header CRC */
1715
1716     /* write filename and dirname */
1717     /* must have this header, even if the name_length is 0. */
1718     put_word(name_length + 3);  /* size */
1719     put_byte(0x01);             /* filename */
1720     put_bytes(basename, name_length);
1721
1722     if (dir_length > 0) {
1723         put_word(dir_length + 3); /* size */
1724         put_byte(0x02);         /* dirname */
1725         put_bytes(dirname, dir_length);
1726     }
1727
1728     if (!generic_format)
1729         write_unix_info(hdr);
1730
1731     put_word(0x0000);           /* next header size */
1732
1733     header_size = put_ptr - data;
1734     if ((header_size & 0xff) == 0) {
1735         /* cannot put zero at the first byte on level 2 header. */
1736         /* adjust header size. */
1737         put_byte(0);            /* padding */
1738         header_size++;
1739     }
1740
1741     /* put header size */
1742     setup_put(data + I_HEADER_SIZE);
1743     put_word(header_size);
1744
1745     /* put header CRC in extended header */
1746     INITIALIZE_CRC(hcrc);
1747     hcrc = calccrc(hcrc, data, (unsigned int) header_size);
1748     setup_put(headercrc_ptr);
1749     put_word(hcrc);
1750
1751     return header_size;
1752 }
1753
1754 void
1755 write_header(fp, hdr)
1756     FILE           *fp;
1757     LzHeader       *hdr;
1758 {
1759     size_t header_size;
1760     char data[LZHEADER_STORAGE];
1761
1762     int archive_kanji_code = CODE_SJIS;
1763     int system_kanji_code = default_system_kanji_code;
1764     char *archive_delim = "\377";
1765     char *system_delim = "/";
1766     int filename_case = NONE;
1767     char pathname[FILENAME_LENGTH];
1768
1769     if (optional_archive_kanji_code)
1770         archive_kanji_code = optional_archive_kanji_code;
1771     if (optional_system_kanji_code)
1772         system_kanji_code = optional_system_kanji_code;
1773
1774     if (generic_format && convertcase)
1775         filename_case = TO_UPPER;
1776
1777     if (hdr->header_level == 0) {
1778         archive_delim = "\\";
1779     }
1780
1781     if ((hdr->unix_mode & UNIX_FILE_SYMLINK) == UNIX_FILE_SYMLINK) {
1782         char *p;
1783         p = strchr(hdr->name, '|');
1784         if (p) {
1785             error("symlink name \"%s\" contains '|' char. change it into '_'",
1786                   hdr->name);
1787             *p = '_';
1788         }
1789         if (xsnprintf(pathname, sizeof(pathname),
1790                       "%s|%s", hdr->name, hdr->realname) == -1)
1791             error("file name is too long (%s -> %s)", hdr->name, hdr->realname);
1792     }
1793     else {
1794         strncpy(pathname, hdr->name, sizeof(pathname));
1795         pathname[sizeof(pathname)-1] = 0;
1796     }
1797
1798     convert_filename(pathname, strlen(pathname), sizeof(pathname),
1799                      system_kanji_code,
1800                      archive_kanji_code,
1801                      system_delim, archive_delim, filename_case);
1802
1803     switch (hdr->header_level) {
1804     case 0:
1805         header_size = write_header_level0(data, hdr, pathname);
1806         break;
1807     case 1:
1808         header_size = write_header_level1(data, hdr, pathname);
1809         break;
1810     case 2:
1811         header_size = write_header_level2(data, hdr, pathname);
1812         break;
1813     default:
1814         error("Unknown level header (level %d)", hdr->header_level);
1815         exit(1);
1816     }
1817
1818     if (fwrite(data, header_size, 1, fp) == 0)
1819         fatal_error("Cannot write to temporary file");
1820 }
1821
1822 #if MULTIBYTE_FILENAME
1823
1824 #if defined(__APPLE__)  /* Added by Hiroto Sakai */
1825
1826 #include <CoreFoundation/CFString.h>
1827 #include <CoreFoundation/CFStringEncodingExt.h>
1828
1829 /* this is not need for Mac OS X v 10.2 later */
1830 enum {
1831   kCFStringEncodingAllowLossyConversion = 1,
1832   kCFStringEncodingBasicDirectionLeftToRight = (1 << 1),
1833   kCFStringEncodingBasicDirectionRightToLeft = (1 << 2),
1834   kCFStringEncodingSubstituteCombinings = (1 << 3),
1835   kCFStringEncodingComposeCombinings = (1 << 4),
1836   kCFStringEncodingIgnoreCombinings = (1 << 5),
1837   kCFStringEncodingUseCanonical = (1 << 6),
1838   kCFStringEncodingUseHFSPlusCanonical = (1 << 7),
1839   kCFStringEncodingPrependBOM = (1 << 8),
1840   kCFStringEncodingDisableCorporateArea = (1 << 9),
1841   kCFStringEncodingASCIICompatibleConversion = (1 << 10),
1842 };
1843
1844 static int
1845 ConvertEncodingToUTF8(const char* inCStr,
1846                       char* outUTF8Buffer,
1847                       int outUTF8BufferLength,
1848                       unsigned long scriptEncoding,
1849                       unsigned long flags)
1850 {
1851     unsigned long unicodeChars;
1852     unsigned long srcCharsUsed;
1853     unsigned long usedByteLen = 0;
1854     UniChar uniStr[512];
1855     unsigned long cfResult;
1856
1857     cfResult = CFStringEncodingBytesToUnicode(scriptEncoding,
1858                                               flags,
1859                                               (char *)inCStr,
1860                                               strlen(inCStr),
1861                                               &srcCharsUsed,
1862                                               uniStr,
1863                                               512,
1864                                               &unicodeChars);
1865     if (cfResult == 0) {
1866         cfResult = CFStringEncodingUnicodeToBytes(kCFStringEncodingUTF8,
1867                                                   flags,
1868                                                   uniStr,
1869                                                   unicodeChars,
1870                                                   &srcCharsUsed,
1871                                                   (char*)outUTF8Buffer,
1872                                                   outUTF8BufferLength - 1,
1873                                                   &usedByteLen);
1874         outUTF8Buffer[usedByteLen] = '\0';
1875     }
1876
1877     return cfResult;
1878 }
1879
1880 static int
1881 ConvertUTF8ToEncoding(const char* inUTF8Buf,
1882                       int inUTF8BufLength,
1883                       char* outCStrBuffer,
1884                       int outCStrBufferLength,
1885                       unsigned long scriptEncoding,
1886                       unsigned long flags)
1887 {
1888     unsigned long unicodeChars;
1889     unsigned long srcCharsUsed;
1890     unsigned long usedByteLen = 0;
1891     UniChar uniStr[256];
1892     unsigned long cfResult;
1893
1894     cfResult = CFStringEncodingBytesToUnicode(kCFStringEncodingUTF8,
1895                                               flags,
1896                                               (char*)inUTF8Buf,
1897                                               inUTF8BufLength,
1898                                               &srcCharsUsed,
1899                                               uniStr,
1900                                               255,
1901                                               &unicodeChars);
1902     if (cfResult == 0) {
1903         cfResult = CFStringEncodingUnicodeToBytes(scriptEncoding,
1904                                                   flags,
1905                                                   uniStr,
1906                                                   unicodeChars,
1907                                                   &srcCharsUsed,
1908                                                   (char*)outCStrBuffer,
1909                                                   outCStrBufferLength - 1,
1910                                                   &usedByteLen);
1911         outCStrBuffer[usedByteLen] = '\0';
1912     }
1913
1914     return cfResult;
1915 }
1916
1917 #elif HAVE_ICONV
1918 #include <iconv.h>
1919
1920 static int
1921 ConvertEncodingByIconv(const char *src, char *dst, int dstsize,
1922                        const char *srcEnc, const char *dstEnc)
1923 {
1924     iconv_t ic;
1925     static char szTmpBuf[2048];
1926     char *src_p;
1927     char *dst_p;
1928     size_t sLen;
1929     size_t iLen;
1930
1931     dst_p = &szTmpBuf[0];
1932     iLen = (size_t)sizeof(szTmpBuf)-1;
1933     src_p = (char *)src;
1934     sLen = (size_t)strlen(src);
1935     memset(szTmpBuf, 0, sizeof(szTmpBuf));
1936     memset(dst, 0, dstsize);
1937
1938     ic = iconv_open(dstEnc, srcEnc);
1939     if (ic == (iconv_t)-1) {
1940         error("iconv_open() failure: %s", strerror(errno));
1941         return -1;
1942     }
1943
1944     if (iconv(ic, &src_p, &sLen, &dst_p, &iLen) == (size_t)-1) {
1945         error("iconv() failure: %s", strerror(errno));
1946         iconv_close(ic);
1947         return -1;
1948     }
1949
1950     strncpy(dst, szTmpBuf, dstsize);
1951
1952     iconv_close(ic);
1953
1954     return 0;
1955 }
1956 #endif /* defined(__APPLE__) */
1957
1958 char *
1959 sjis_to_utf8(char *dst, const char *src, size_t dstsize)
1960 {
1961 #if defined(__APPLE__)
1962   dst[0] = '\0';
1963   if (ConvertEncodingToUTF8(src, dst, dstsize,
1964                             kCFStringEncodingDOSJapanese,
1965                             kCFStringEncodingUseHFSPlusCanonical) == 0)
1966       return dst;
1967 #elif HAVE_ICONV
1968   if (ConvertEncodingByIconv(src, dst, dstsize, "SJIS", "UTF-8") != -1)
1969       return dst;
1970 #else
1971   error("not support utf-8 conversion");
1972 #endif
1973
1974   if (dstsize < 1) return dst;
1975   dst[dstsize-1] = 0;
1976   return strncpy(dst, src, dstsize-1);
1977 }
1978
1979 char *
1980 utf8_to_sjis(char *dst, const char *src, size_t dstsize)
1981 {
1982 #if defined(__APPLE__)
1983   int srclen;
1984
1985   dst[0] = '\0';
1986   srclen = strlen(src);
1987   if (ConvertUTF8ToEncoding(src, srclen, dst, dstsize,
1988                             kCFStringEncodingDOSJapanese,
1989                             kCFStringEncodingUseHFSPlusCanonical) == 0)
1990       return dst;
1991 #elif HAVE_ICONV
1992   if (ConvertEncodingByIconv(src, dst, dstsize, "UTF-8", "SJIS") != -1)
1993       return dst;
1994 #else
1995   error("not support utf-8 conversion");
1996 #endif
1997
1998   if (dstsize < 1) return dst;
1999   dst[dstsize-1] = 0;
2000   return strncpy(dst, src, dstsize-1);
2001 }
2002
2003 /*
2004  * SJIS <-> EUC ÊÑ´¹´Ø¿ô
2005  * ¡ÖÆüËܸì¾ðÊó½èÍý¡×   ¥½¥Õ¥È¥Ð¥ó¥¯(³ô)
2006  *  ¤è¤êÈ´¿è(by Koji Arai)
2007  */
2008 void
2009 euc2sjis(int *p1, int *p2)
2010 {
2011     unsigned char c1 = *p1 & 0x7f;
2012     unsigned char c2 = *p2 & 0x7f;
2013     int rowoff = c1 < 0x5f ? 0x70 : 0xb0;
2014     int celoff = c1 % 2 ? (c2 > 0x5f ? 0x20 : 0x1f) : 0x7e;
2015     *p1 = ((c1 + 1) >> 1) + rowoff;
2016     *p2 += celoff - 0x80;
2017 }
2018
2019 void
2020 sjis2euc(int *p1, int *p2)
2021 {
2022     unsigned char c1 = *p1;
2023     unsigned char c2 = *p2;
2024     int adjust = c2 < 0x9f;
2025     int rowoff = c1 < 0xa0 ? 0x70 : 0xb0;
2026     int celoff = adjust ? (c2 > 0x7f ? 0x20 : 0x1f) : 0x7e;
2027     *p1 = ((c1 - rowoff) << 1) - adjust;
2028     *p2 -= celoff;
2029
2030     *p1 |= 0x80;
2031     *p2 |= 0x80;
2032 }
2033
2034 static int
2035 hex2int(int c)
2036 {
2037     switch (c) {
2038     case '0': case '1': case '2': case '3': case '4':
2039     case '5': case '6': case '7': case '8': case '9':
2040         return c - '0';
2041
2042     case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
2043         return c - 'a' + 10;
2044
2045     case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
2046         return c - 'A' + 10;
2047     default:
2048         return -1;
2049     }
2050 }
2051
2052 static int
2053 int2hex(int c)
2054 {
2055     switch (c) {
2056     case 0: case 1: case 2: case 3: case 4:
2057     case 5: case 6: case 7: case 8: case 9:
2058         return c + '0';
2059
2060     case 10: case 11: case 12: case 13: case 14: case 15:
2061         return c + 'a' - 10;
2062
2063     default:
2064         return -1;
2065     }
2066 }
2067
2068 int
2069 cap_to_sjis(char *dst, const char *src, size_t dstsize)
2070 {
2071     int i, j;
2072     size_t len = strlen(src);
2073     int a, b;
2074
2075     for (i = j = 0; i < len && i < dstsize; i++) {
2076         if (src[i] != ':') {
2077             dst[j++] = src[i];
2078             continue;
2079         }
2080
2081         i++;
2082         a = hex2int((unsigned char)src[i]);
2083         b = hex2int((unsigned char)src[i+1]);
2084
2085         if (a == -1 || b == -1) {
2086             /* leave as it */
2087             dst[j++] = ':';
2088             strncpy(dst+j, src+i, dstsize-j);
2089             dst[dstsize-1] = 0;
2090             return strlen(dst);
2091         }
2092
2093         i++;
2094
2095         dst[j++] = a * 16 + b;
2096     }
2097     dst[j] = 0;
2098     return j;
2099 }
2100
2101 int
2102 sjis_to_cap(char *dst, const char *src, size_t dstsize)
2103 {
2104     int i, j;
2105     size_t len = strlen(src);
2106     int a, b;
2107
2108     for (i = j = 0; i < len && i < dstsize; i++) {
2109         if (src[i] == ':') {
2110             strncpy(dst+j, ":3a", dstsize-j);
2111             dst[dstsize-1] = 0;
2112             j = strlen(dst);
2113             continue;
2114         }
2115         if (isprint(src[i])) {
2116             dst[j++] = src[i];
2117             continue;
2118         }
2119
2120         if (j + 3 >= dstsize) {
2121             dst[j] = 0;
2122             return j;
2123         }
2124
2125         a = int2hex((unsigned char)src[i] / 16);
2126         b = int2hex((unsigned char)src[i] % 16);
2127
2128         dst[j++] = ':';
2129         dst[j++] = a;
2130         dst[j++] = b;
2131     }
2132     dst[j] = 0;
2133     return j;
2134 }
2135 #endif /* MULTIBYTE_FILENAME */