OSDN Git Service

* src/lharc.c (init_variable): do not use HEADER_LEVELx macros.
[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     char *p;
61     int len;
62 {
63     int sum = 0;
64
65     while (len--) sum += *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 static int
165 get_bytes(buf, len, size)
166     char *buf;
167     int len, size;
168 {
169     int i;
170
171 #if DUMP_HEADER
172     if (verbose_listing && verbose > 1)
173         printf("%02d %2d: \"", get_ptr - start_ptr, len);
174
175     for (i = 0; i < len; i++) {
176         if (i < size) buf[i] = get_ptr[i];
177
178         if (verbose_listing && verbose > 1) {
179             if (isprint(buf[i]))
180                 printf("%c", buf[i]);
181             else
182                 printf("\\x%02x", (unsigned char)buf[i]);
183         }
184     }
185
186     if (verbose_listing && verbose > 1)
187         printf("\"\n");
188 #else
189     for (i = 0; i < len && i < size; i++)
190         buf[i] = get_ptr[i];
191 #endif
192
193     get_ptr += len;
194     return i;
195 }
196
197 static void
198 put_bytes(buf, len)
199     char *buf;
200     int len;
201 {
202     int i;
203     for (i = 0; i < len; i++)
204         put_byte(buf[i]);
205 }
206
207 /* added by Koji Arai */
208 void
209 convert_filename(name, len, size,
210                  from_code, to_code,
211                  from_delim, to_delim,
212                  case_to)
213     char *name;
214     int len;
215     int size;
216     int from_code, to_code, case_to;
217     char *from_delim, *to_delim;
218
219 {
220     int i;
221 #ifdef MULTIBYTE_FILENAME
222     char tmp[FILENAME_LENGTH];
223
224     if (from_code == CODE_SJIS && to_code == CODE_UTF8) {
225         for (i = 0; i < len; i++)
226             /* FIXME: provisionally fix for the Mac OS CoreFoundation */
227             if ((unsigned char)name[i] == LHA_PATHSEP)  name[i] = '/';
228         sjis_to_utf8(tmp, name, sizeof(tmp));
229         strncpy(name, tmp, size);
230         name[size-1] = 0;
231         len = strlen(name);
232         for (i = 0; i < len; i++)
233             if (name[i] == '/')  name[i] = LHA_PATHSEP;
234         from_code = CODE_UTF8;
235     }
236     else if (from_code == CODE_UTF8 && to_code == CODE_SJIS) {
237         for (i = 0; i < len; i++)
238             /* FIXME: provisionally fix for the Mac OS CoreFoundation */
239             if ((unsigned char)name[i] == LHA_PATHSEP)  name[i] = '/';
240         utf8_to_sjis(tmp, name, sizeof(tmp));
241         strncpy(name, tmp, size);
242         name[size-1] = 0;
243         len = strlen(name);
244         for (i = 0; i < len; i++)
245             if (name[i] == '/')  name[i] = LHA_PATHSEP;
246         from_code = CODE_SJIS;
247     }
248 #endif
249
250     /* special case: if `name' has small lettter, not convert case. */
251     if (from_code == CODE_SJIS && case_to == TO_LOWER) {
252         for (i = 0; i < len; i++) {
253 #ifdef MULTIBYTE_FILENAME
254             if (SJIS_FIRST_P(name[i]) && SJIS_SECOND_P(name[i+1]))
255                 i++;
256             else
257 #endif
258             if (islower(name[i])) {
259                 case_to = NONE;
260                 break;
261             }
262         }
263     }
264
265     for (i = 0; i < len; i ++) {
266 #ifdef MULTIBYTE_FILENAME
267         if (from_code == CODE_EUC &&
268             (unsigned char)name[i] == 0x8e) {
269             if (to_code != CODE_SJIS) {
270                 i++;
271                 continue;
272             }
273
274             /* X0201 KANA */
275             memmove(name + i, name + i + 1, len - i);
276             len--;
277             continue;
278         }
279         if (from_code == CODE_SJIS && X0201_KANA_P(name[i])) {
280             if (to_code != CODE_EUC) {
281                 continue;
282             }
283
284             if (len == size - 1) /* check overflow */
285                 len--;
286             memmove(name+i+1, name+i, len-i);
287             name[i] = 0x8e;
288             i++;
289             len++;
290             continue;
291         }
292         if (from_code == CODE_EUC && (name[i] & 0x80) && (name[i+1] & 0x80)) {
293             int c1, c2;
294             if (to_code != CODE_SJIS) {
295                 i++;
296                 continue;
297             }
298
299             c1 = (unsigned char)name[i];
300             c2 = (unsigned char)name[i+1];
301             euc2sjis(&c1, &c2);
302             name[i] = c1;
303             name[i+1] = c2;
304             i++;
305             continue;
306         }
307         if (from_code == CODE_SJIS &&
308             SJIS_FIRST_P(name[i]) &&
309             SJIS_SECOND_P(name[i+1])) {
310             int c1, c2;
311
312             if (to_code != CODE_EUC) {
313                 i++;
314                 continue;
315             }
316
317             c1 = (unsigned char)name[i];
318             c2 = (unsigned char)name[i+1];
319             sjis2euc(&c1, &c2);
320             name[i] = c1;
321             name[i+1] = c2;
322             i++;
323             continue;
324         }
325 #endif /* MULTIBYTE_FILENAME */
326         {
327             char *ptr;
328
329             /* transpose from_delim to to_delim */
330
331             if ((ptr = strchr(from_delim, name[i])) != NULL) {
332                 name[i] = to_delim[ptr - from_delim];
333                 continue;
334             }
335         }
336
337         if (case_to == TO_UPPER && islower(name[i])) {
338             name[i] = toupper(name[i]);
339             continue;
340         }
341         if (case_to == TO_LOWER && isupper(name[i])) {
342             name[i] = tolower(name[i]);
343             continue;
344         }
345     }
346 }
347
348 /*
349  * Generic (MS-DOS style) time stamp format (localtime):
350  *
351  *  31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
352  * |<---- year-1980 --->|<- month ->|<--- day ---->|
353  *
354  *  15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
355  * |<--- hour --->|<---- minute --->|<- second/2 ->|
356  *
357  */
358
359 static time_t
360 generic_to_unix_stamp(t)
361     long t;
362 {
363     struct tm tm;
364
365 #define subbits(n, off, len) (((n) >> (off)) & ((1 << (len))-1))
366
367     tm.tm_sec  = subbits(t,  0, 5) * 2;
368     tm.tm_min  = subbits(t,  5, 6);
369     tm.tm_hour = subbits(t, 11, 5);
370     tm.tm_mday = subbits(t, 16, 5);
371     tm.tm_mon  = subbits(t, 21, 4) - 1;
372     tm.tm_year = subbits(t, 25, 7) + 80;
373     tm.tm_isdst = -1;
374
375 #if HAVE_MKTIME
376     return mktime(&tm);
377 #else
378     return timelocal(&tm);
379 #endif
380 }
381
382 static long
383 unix_to_generic_stamp(t)
384     time_t t;
385 {
386     struct tm *tm = localtime(&t);
387
388     tm->tm_year -= 80;
389     tm->tm_mon += 1;
390
391     return ((long)(tm->tm_year << 25) +
392             (tm->tm_mon  << 21) +
393             (tm->tm_mday << 16) +
394             (tm->tm_hour << 11) +
395             (tm->tm_min  << 5) +
396             (tm->tm_sec / 2));
397 }
398
399 static unsigned long
400 wintime_to_unix_stamp()
401 {
402 #if HAVE_UINT64_T
403     uint64_t t;
404     uint64_t epoch = 0x019db1ded53e8000; /* 1970-01-01 00:00:00 (UTC) */
405
406     t = (unsigned long)get_longword();
407     t |= (uint64_t)(unsigned long)get_longword() << 32;
408     t = (t - epoch) / 10000000;
409     return t;
410 #else
411     int i, borrow;
412     unsigned long t, q, x;
413     unsigned long wintime[8];
414     unsigned long epoch[8] = {0x01,0x9d,0xb1,0xde, 0xd5,0x3e,0x80,0x00};
415                                 /* 1970-01-01 00:00:00 (UTC) */
416     /* wintime -= epoch */
417     borrow = 0;
418     for (i = 7; i >= 0; i--) {
419         wintime[i] = (unsigned)get_byte() - epoch[i] - borrow;
420         borrow = (wintime[i] > 0xff) ? 1 : 0;
421         wintime[i] &= 0xff;
422     }
423
424     /* q = wintime / 10000000 */
425     t = q = 0;
426     x = 10000000;               /* x: 24bit */
427     for (i = 0; i < 8; i++) {
428         t = (t << 8) + wintime[i]; /* 24bit + 8bit. t must be 32bit variable */
429         q <<= 8;                   /* q must be 32bit (time_t) */
430         q += t / x;
431         t %= x;     /* 24bit */
432     }
433     return q;
434 #endif
435 }
436
437 /*
438  * extended header
439  *
440  *             size  field name
441  *  --------------------------------
442  *  base header:         :
443  *           2 or 4  next-header size  [*1]
444  *  --------------------------------------
445  *  ext header:   1  ext-type            ^
446  *                ?  contents            | [*1] next-header size
447  *           2 or 4  next-header size    v
448  *  --------------------------------------
449  *
450  *  on level 1, 2 header:
451  *    size field is 2 bytes
452  *  on level 3 header:
453  *    size field is 4 bytes
454  */
455
456 static ssize_t
457 get_extended_header(fp, hdr, header_size, hcrc)
458     FILE *fp;
459     LzHeader *hdr;
460     size_t header_size;
461     unsigned int *hcrc;
462 {
463     char data[LZHEADER_STORAGE];
464     int name_length;
465     char dirname[FILENAME_LENGTH];
466     int dir_length = 0;
467     int i;
468     ssize_t whole_size = header_size;
469     int ext_type;
470     int n = 1 + hdr->size_field_length; /* `ext-type' + `next-header size' */
471
472     if (hdr->header_level == 0)
473         return 0;
474
475     name_length = strlen(hdr->name);
476
477     while (header_size) {
478         setup_get(data);
479         if (sizeof(data) < header_size) {
480             error("header size (%ld) too large.", header_size);
481             exit(1);
482         }
483
484         if (fread(data, header_size, 1, fp) == 0) {
485             error("Invalid header (LHa file ?)");
486             return -1;
487         }
488
489         ext_type = get_byte();
490         switch (ext_type) {
491         case 0:
492             /* header crc (CRC-16) */
493             hdr->header_crc = get_word();
494             /* clear buffer for CRC calculation. */
495             data[1] = data[2] = 0;
496             skip_bytes(header_size - n - 2);
497             break;
498         case 1:
499             /* filename */
500             name_length =
501                 get_bytes(hdr->name, header_size-n, sizeof(hdr->name)-1);
502             hdr->name[name_length] = 0;
503             break;
504         case 2:
505             /* directory */
506             dir_length = get_bytes(dirname, header_size-n, sizeof(dirname)-1);
507             dirname[dir_length] = 0;
508             break;
509         case 0x40:
510             /* MS-DOS attribute */
511             hdr->attribute = get_word();
512             break;
513         case 0x41:
514             /* Windows time stamp (FILETIME structure) */
515             /* it is time in 100 nano seconds since 1601-01-01 00:00:00 */
516
517             skip_bytes(8); /* create time is ignored */
518
519             /* set last modified time */
520             if (hdr->header_level >= 2)
521                 skip_bytes(8);  /* time_t has been already set */
522             else
523                 hdr->unix_last_modified_stamp = wintime_to_unix_stamp();
524
525             skip_bytes(8); /* last access time is ignored */
526
527             break;
528         case 0x50:
529             /* UNIX permission */
530             hdr->unix_mode = get_word();
531             break;
532         case 0x51:
533             /* UNIX gid and uid */
534             hdr->unix_gid = get_word();
535             hdr->unix_uid = get_word();
536             break;
537         case 0x52:
538             /* UNIX group name */
539             i = get_bytes(hdr->group, header_size-n, sizeof(hdr->group)-1);
540             hdr->group[i] = '\0';
541             break;
542         case 0x53:
543             /* UNIX user name */
544             i = get_bytes(hdr->user, header_size-n, sizeof(hdr->user)-1);
545             hdr->user[i] = '\0';
546             break;
547         case 0x54:
548             /* UNIX last modified time */
549             hdr->unix_last_modified_stamp = (time_t) get_longword();
550             break;
551         default:
552             /* other headers */
553             /* 0x39: multi-disk header
554                0x3f: uncompressed comment
555                0x42: 64bit large file size
556                0x48-0x4f(?): reserved for authenticity verification
557                0x7d: encapsulation
558                0x7e: extended attribute - platform information
559                0x7f: extended attribute - permission, owner-id and timestamp
560                      (level 3 on OS/2)
561                0xc4: compressed comment (dict size: 4096)
562                0xc5: compressed comment (dict size: 8192)
563                0xc6: compressed comment (dict size: 16384)
564                0xc7: compressed comment (dict size: 32768)
565                0xc8: compressed comment (dict size: 65536)
566                0xd0-0xdf(?): operating systemm specific information
567                0xfc: encapsulation (another opinion)
568                0xfe: extended attribute - platform information(another opinion)
569                0xff: extended attribute - permission, owner-id and timestamp
570                      (level 3 on UNLHA32) */
571             if (verbose)
572                 warning("unknown extended header 0x%02x", ext_type);
573             skip_bytes(header_size - n);
574             break;
575         }
576
577         if (hcrc)
578             *hcrc = calccrc(*hcrc, data, header_size);
579
580         if (hdr->size_field_length == 2)
581             whole_size += header_size = get_word();
582         else
583             whole_size += header_size = get_longword();
584     }
585
586     /* concatenate dirname and filename */
587     if (dir_length) {
588         if (name_length + dir_length >= sizeof(hdr->name)) {
589             warning("the length of pathname \"%s%s\" is too long.",
590                     dirname, hdr->name);
591             name_length = sizeof(hdr->name) - dir_length - 1;
592             hdr->name[name_length] = 0;
593         }
594         strcat(dirname, hdr->name);
595         strcpy(hdr->name, dirname);
596         name_length += dir_length;
597     }
598
599     return whole_size;
600 }
601
602 #define I_HEADER_SIZE           0               /* level 0,1,2   */
603 #define I_HEADER_CHECKSUM       1               /* level 0,1     */
604 #define I_METHOD                2               /* level 0,1,2,3 */
605 #define I_PACKED_SIZE           7               /* level 0,1,2,3 */
606 #define I_ATTRIBUTE             19              /* level 0,1,2,3 */
607 #define I_HEADER_LEVEL          20              /* level 0,1,2,3 */
608
609 #define COMMON_HEADER_SIZE      21      /* size of common part */
610
611 #define I_GENERIC_HEADER_SIZE 24 /* + name_length */
612 #define I_LEVEL0_HEADER_SIZE  36 /* + name_length (unix extended) */
613 #define I_LEVEL1_HEADER_SIZE  27 /* + name_length */
614 #define I_LEVEL2_HEADER_SIZE  26 /* + padding */
615 #define I_LEVEL3_HEADER_SIZE  32
616
617 /*
618  * level 0 header
619  *
620  *
621  * offset  size  field name
622  * ----------------------------------
623  *     0      1  header size    [*1]
624  *     1      1  header sum
625  *            ---------------------------------------
626  *     2      5  method ID                         ^
627  *     7      4  packed size    [*2]               |
628  *    11      4  original size                     |
629  *    15      2  time                              |
630  *    17      2  date                              |
631  *    19      1  attribute                         | [*1] header size (X+Y+22)
632  *    20      1  level (0x00 fixed)                |
633  *    21      1  name length                       |
634  *    22      X  pathname                          |
635  * X +22      2  file crc (CRC-16)                 |
636  * X +24      Y  ext-header(old style)             v
637  * -------------------------------------------------
638  * X+Y+24        data                              ^
639  *                 :                               | [*2] packed size
640  *                 :                               v
641  * -------------------------------------------------
642  *
643  * ext-header(old style)
644  *     0      1  ext-type ('U')
645  *     1      1  minor version
646  *     2      4  UNIX time
647  *     6      2  mode
648  *     8      2  uid
649  *    10      2  gid
650  *
651  * attribute (MS-DOS)
652  *    bit1  read only
653  *    bit2  hidden
654  *    bit3  system
655  *    bit4  volume label
656  *    bit5  directory
657  *    bit6  archive bit (need to backup)
658  *
659  */
660 static int
661 get_header_level0(fp, hdr, data)
662     FILE *fp;
663     LzHeader *hdr;
664     char *data;
665 {
666     size_t header_size;
667     ssize_t extend_size;
668     int checksum;
669     int name_length;
670     int i;
671
672     hdr->size_field_length = 2; /* in bytes */
673     hdr->header_size = header_size = get_byte();
674     checksum = get_byte();
675
676     if (fread(data + COMMON_HEADER_SIZE,
677               header_size + 2 - COMMON_HEADER_SIZE, 1, fp) == 0) {
678         error("Invalid header (LHarc file ?)");
679         return FALSE;   /* finish */
680     }
681
682     if (calc_sum(data + I_METHOD, header_size) != checksum) {
683         error("Checksum error (LHarc file?)");
684         return FALSE;
685     }
686
687     get_bytes(hdr->method, 5, sizeof(hdr->method));
688     hdr->packed_size = get_longword();
689     hdr->original_size = get_longword();
690     hdr->unix_last_modified_stamp = generic_to_unix_stamp(get_longword());
691     hdr->attribute = get_byte(); /* MS-DOS attribute */
692     hdr->header_level = get_byte();
693     name_length = get_byte();
694     i = get_bytes(hdr->name, name_length, sizeof(hdr->name)-1);
695     hdr->name[i] = '\0';
696
697     /* defaults for other type */
698     hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
699     hdr->unix_gid = 0;
700     hdr->unix_uid = 0;
701
702     extend_size = header_size+2 - name_length - 24;
703
704     if (extend_size < 0) {
705         if (extend_size == -2) {
706             /* CRC field is not given */
707             hdr->extend_type = EXTEND_GENERIC;
708             hdr->has_crc = FALSE;
709
710             return TRUE;
711         } 
712
713         error("Unkonwn header (lha file?)");
714         exit(1);
715     }
716
717     hdr->has_crc = TRUE;
718     hdr->crc = get_word();
719
720     if (extend_size == 0)
721         return TRUE;
722
723     hdr->extend_type = get_byte();
724     extend_size--;
725
726     if (hdr->extend_type == EXTEND_UNIX) {
727         if (extend_size >= 11) {
728             hdr->minor_version = get_byte();
729             hdr->unix_last_modified_stamp = (time_t) get_longword();
730             hdr->unix_mode = get_word();
731             hdr->unix_uid = get_word();
732             hdr->unix_gid = get_word();
733             extend_size -= 11;
734         } else {
735             hdr->extend_type = EXTEND_GENERIC;
736         }
737     }
738     if (extend_size > 0)
739         skip_bytes(extend_size);
740
741     hdr->header_size += 2;
742     return TRUE;
743 }
744
745 /*
746  * level 1 header
747  *
748  *
749  * offset   size  field name
750  * -----------------------------------
751  *     0       1  header size   [*1]
752  *     1       1  header sum
753  *             -------------------------------------
754  *     2       5  method ID                        ^
755  *     7       4  skip size     [*2]               |
756  *    11       4  original size                    |
757  *    15       2  time                             |
758  *    17       2  date                             |
759  *    19       1  attribute (0x20 fixed)           | [*1] header size (X+Y+25)
760  *    20       1  level (0x01 fixed)               |
761  *    21       1  name length                      |
762  *    22       X  filename                         |
763  * X+ 22       2  file crc (CRC-16)                |
764  * X+ 24       1  OS ID                            |
765  * X +25       Y  ???                              |
766  * X+Y+25      2  next-header size                 v
767  * -------------------------------------------------
768  * X+Y+27      Z  ext-header                       ^
769  *                 :                               |
770  * -----------------------------------             | [*2] skip size
771  * X+Y+Z+27       data                             |
772  *                 :                               v
773  * -------------------------------------------------
774  *
775  */
776 static int
777 get_header_level1(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, dummy;
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(); /* skip size */
805     hdr->original_size = get_longword();
806     hdr->unix_last_modified_stamp = generic_to_unix_stamp(get_longword());
807     hdr->attribute = get_byte(); /* 0x20 fixed */
808     hdr->header_level = get_byte();
809
810     name_length = get_byte();
811     i = get_bytes(hdr->name, name_length, sizeof(hdr->name)-1);
812     hdr->name[i] = '\0';
813
814     /* defaults for other type */
815     hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
816     hdr->unix_gid = 0;
817     hdr->unix_uid = 0;
818
819     hdr->has_crc = TRUE;
820     hdr->crc = get_word();
821     hdr->extend_type = get_byte();
822
823     dummy = header_size+2 - name_length - I_LEVEL1_HEADER_SIZE;
824     if (dummy > 0)
825         skip_bytes(dummy); /* skip old style extend header */
826
827     extend_size = get_word();
828     extend_size = get_extended_header(fp, hdr, extend_size, 0);
829     if (extend_size == -1)
830         return FALSE;
831
832     /* On level 1 header, size fields should be adjusted. */
833     /* the `packed_size' field contains the extended header size. */
834     /* the `header_size' field does not. */
835     hdr->packed_size -= extend_size;
836     hdr->header_size += extend_size + 2;
837
838     return TRUE;
839 }
840
841 /*
842  * level 2 header
843  *
844  *
845  * offset   size  field name
846  * --------------------------------------------------
847  *     0       2  total header size [*1]           ^
848  *             -----------------------             |
849  *     2       5  method ID                        |
850  *     7       4  packed size       [*2]           |
851  *    11       4  original size                    |
852  *    15       4  time                             |
853  *    19       1  RESERVED (0x20 fixed)            | [*1] total header size
854  *    20       1  level (0x02 fixed)               |      (X+26+(1))
855  *    21       2  file crc (CRC-16)                |
856  *    23       1  OS ID                            |
857  *    24       2  next-header size                 |
858  * -----------------------------------             |
859  *    26       X  ext-header                       |
860  *                 :                               |
861  * -----------------------------------             |
862  * X +26      (1) padding                          v
863  * -------------------------------------------------
864  * X +26+(1)      data                             ^
865  *                 :                               | [*2] packed size
866  *                 :                               v
867  * -------------------------------------------------
868  *
869  */
870 static int
871 get_header_level2(fp, hdr, data)
872     FILE *fp;
873     LzHeader *hdr;
874     char *data;
875 {
876     size_t header_size;
877     ssize_t extend_size;
878     int padding;
879     unsigned int hcrc;
880
881     hdr->size_field_length = 2; /* in bytes */
882     hdr->header_size = header_size = get_word();
883
884     if (fread(data + COMMON_HEADER_SIZE,
885               I_LEVEL2_HEADER_SIZE - COMMON_HEADER_SIZE, 1, fp) == 0) {
886         error("Invalid header (LHarc file ?)");
887         return FALSE;   /* finish */
888     }
889
890     get_bytes(hdr->method, 5, sizeof(hdr->method));
891     hdr->packed_size = get_longword();
892     hdr->original_size = get_longword();
893     hdr->unix_last_modified_stamp = get_longword();
894     hdr->attribute = get_byte(); /* reserved */
895     hdr->header_level = get_byte();
896
897     /* defaults for other type */
898     hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
899     hdr->unix_gid = 0;
900     hdr->unix_uid = 0;
901
902     hdr->has_crc = TRUE;
903     hdr->crc = get_word();
904     hdr->extend_type = get_byte();
905     extend_size = get_word();
906
907     INITIALIZE_CRC(hcrc);
908     hcrc = calccrc(hcrc, data, get_ptr - data);
909
910     extend_size = get_extended_header(fp, hdr, extend_size, &hcrc);
911     if (extend_size == -1)
912         return FALSE;
913
914     padding = header_size - I_LEVEL2_HEADER_SIZE - extend_size;
915     while (padding--)           /* padding should be 0 or 1 */
916         hcrc = UPDATE_CRC(hcrc, fgetc(fp));
917
918     if (hdr->header_crc != hcrc)
919         error("header CRC error");
920
921     return TRUE;
922 }
923
924 /*
925  * level 3 header
926  *
927  *
928  * offset   size  field name
929  * --------------------------------------------------
930  *     0       2  size field length (4 fixed)      ^
931  *     2       5  method ID                        |
932  *     7       4  packed size       [*2]           |
933  *    11       4  original size                    |
934  *    15       4  time                             |
935  *    19       1  RESERVED (0x20 fixed)            | [*1] total header size
936  *    20       1  level (0x03 fixed)               |      (X+32)
937  *    21       2  file crc (CRC-16)                |
938  *    23       1  OS ID                            |
939  *    24       4  total header size [*1]           |
940  *    28       4  next-header size                 |
941  * -----------------------------------             |
942  *    32       X  ext-header                       |
943  *                 :                               v
944  * -------------------------------------------------
945  * X +32          data                             ^
946  *                 :                               | [*2] packed size
947  *                 :                               v
948  * -------------------------------------------------
949  *
950  */
951 static int
952 get_header_level3(fp, hdr, data)
953     FILE *fp;
954     LzHeader *hdr;
955     char *data;
956 {
957     size_t header_size;
958     ssize_t extend_size;
959     int padding;
960     unsigned int hcrc;
961
962     hdr->size_field_length = get_word();
963
964     if (fread(data + COMMON_HEADER_SIZE,
965               I_LEVEL3_HEADER_SIZE - COMMON_HEADER_SIZE, 1, fp) == 0) {
966         error("Invalid header (LHarc file ?)");
967         return FALSE;   /* finish */
968     }
969
970     get_bytes(hdr->method, 5, sizeof(hdr->method));
971     hdr->packed_size = get_longword();
972     hdr->original_size = get_longword();
973     hdr->unix_last_modified_stamp = get_longword();
974     hdr->attribute = get_byte(); /* reserved */
975     hdr->header_level = get_byte();
976
977     /* defaults for other type */
978     hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
979     hdr->unix_gid = 0;
980     hdr->unix_uid = 0;
981
982     hdr->has_crc = TRUE;
983     hdr->crc = get_word();
984     hdr->extend_type = get_byte();
985     hdr->header_size = header_size = get_longword();
986     extend_size = get_longword();
987
988     INITIALIZE_CRC(hcrc);
989     hcrc = calccrc(hcrc, data, get_ptr - data);
990
991     extend_size = get_extended_header(fp, hdr, extend_size, &hcrc);
992     if (extend_size == -1)
993         return FALSE;
994
995     padding = header_size - I_LEVEL3_HEADER_SIZE - extend_size;
996     while (padding--)           /* padding should be 0 */
997         hcrc = UPDATE_CRC(hcrc, fgetc(fp));
998
999     if (hdr->header_crc != hcrc)
1000         error("header CRC error");
1001
1002     return TRUE;
1003 }
1004
1005 boolean
1006 get_header(fp, hdr)
1007     FILE *fp;
1008     LzHeader *hdr;
1009 {
1010     char data[LZHEADER_STORAGE];
1011
1012     int archive_kanji_code = CODE_SJIS;
1013     int system_kanji_code = default_system_kanji_code;
1014     char *archive_delim = "\377\\"; /* `\' is for level 0 header and
1015                                        broken archive. */
1016     char *system_delim = "//";
1017     int filename_case = NONE;
1018     int end_mark;
1019
1020     memset(hdr, 0, sizeof(LzHeader));
1021
1022     setup_get(data);
1023
1024     if ((end_mark = getc(fp)) == EOF || end_mark == 0) {
1025         return FALSE;           /* finish */
1026     }
1027     data[0] = end_mark;
1028
1029     if (fread(data + 1, COMMON_HEADER_SIZE - 1, 1, fp) == 0) {
1030         error("Invalid header (LHarc file ?)");
1031         return FALSE;           /* finish */
1032     }
1033
1034     switch (data[I_HEADER_LEVEL]) {
1035     case 0:
1036         if (get_header_level0(fp, hdr, data) == FALSE)
1037             return FALSE;
1038         break;
1039     case 1:
1040         if (get_header_level1(fp, hdr, data) == FALSE)
1041             return FALSE;
1042         break;
1043     case 2:
1044         if (get_header_level2(fp, hdr, data) == FALSE)
1045             return FALSE;
1046         break;
1047     case 3:
1048         if (get_header_level3(fp, hdr, data) == FALSE)
1049             return FALSE;
1050         break;
1051     default:
1052         error("Unknown level header (level %d)", data[I_HEADER_LEVEL]);
1053         return FALSE;
1054     }
1055
1056     /* filename conversion */
1057     switch (hdr->extend_type) {
1058     case EXTEND_MSDOS:
1059         filename_case = noconvertcase ? NONE : TO_LOWER;
1060         break;
1061     case EXTEND_HUMAN:
1062     case EXTEND_OS68K:
1063     case EXTEND_XOSK:
1064     case EXTEND_UNIX:
1065     case EXTEND_JAVA:
1066         filename_case = NONE;
1067         break;
1068
1069     case EXTEND_MACOS:
1070         archive_delim = "\377/:\\";
1071                           /* `\' is for level 0 header and broken archive. */
1072         system_delim = "/://";
1073         filename_case = NONE;
1074         break;
1075
1076     default:
1077         filename_case = noconvertcase ? NONE : TO_LOWER;
1078         break;
1079     }
1080
1081     if (optional_archive_kanji_code)
1082         archive_kanji_code = optional_archive_kanji_code;
1083     if (optional_system_kanji_code)
1084         system_kanji_code = optional_system_kanji_code;
1085     if (optional_archive_delim)
1086         archive_delim = optional_archive_delim;
1087     if (optional_system_delim)
1088         system_delim = optional_system_delim;
1089     if (optional_filename_case)
1090         filename_case = optional_filename_case;
1091
1092     /* kanji code and delimiter conversion */
1093     convert_filename(hdr->name, strlen(hdr->name), sizeof(hdr->name),
1094                      archive_kanji_code,
1095                      system_kanji_code,
1096                      archive_delim, system_delim, filename_case);
1097
1098     if ((hdr->unix_mode & UNIX_FILE_SYMLINK) == UNIX_FILE_SYMLINK) {
1099         char *p;
1100         /* split symbolic link */
1101         p = strchr(hdr->name, '|');
1102         if (p) {
1103             /* hdr->name is symbolic link name */
1104             /* hdr->realname is real name */
1105             *p = 0;
1106             strncpy(hdr->realname, p+1, sizeof(hdr->realname));
1107         }
1108         else
1109             error("unknown symlink name \"%s\"", hdr->name);
1110     }
1111
1112     return TRUE;
1113 }
1114
1115 /* skip SFX header */
1116 int
1117 seek_lha_header(fp)
1118     FILE *fp;
1119 {
1120     unsigned char   buffer[64 * 1024]; /* max seek size */
1121     unsigned char  *p;
1122     int             n;
1123
1124     n = fread(buffer, 1, sizeof(buffer), fp);
1125
1126     for (p = buffer; p < buffer + n; p++) {
1127         if (! (p[I_METHOD]=='-' && p[I_METHOD+1]=='l' && p[I_METHOD+4]=='-'))
1128             continue;
1129         /* found "-l??-" keyword (as METHOD type string) */
1130
1131         /* level 0 or 1 header */
1132         if ((p[I_HEADER_LEVEL] == 0 || p[I_HEADER_LEVEL] == 1)
1133             && p[I_HEADER_SIZE] > 20
1134             && p[I_HEADER_CHECKSUM] == calc_sum(p+2, p[I_HEADER_SIZE])) {
1135             if (fseek(fp, (p - buffer) - n, SEEK_CUR) == -1)
1136                 fatal_error("cannot seek header");
1137             return 0;
1138         }
1139
1140         /* level 2 header */
1141         if (p[I_HEADER_LEVEL] == 2
1142             && p[I_HEADER_SIZE] >= 24
1143             && p[I_ATTRIBUTE] == 0x20) {
1144             if (fseek(fp, (p - buffer) - n, SEEK_CUR) == -1)
1145                 fatal_error("cannot seek header");
1146             return 0;
1147         }
1148     }
1149
1150     if (fseek(fp, -n, SEEK_CUR) == -1)
1151         fatal_error("cannot seek header");
1152     return -1;
1153 }
1154
1155 void
1156 init_header(name, v_stat, hdr)
1157     char           *name;
1158     struct stat    *v_stat;
1159     LzHeader       *hdr;
1160 {
1161     int             len;
1162
1163     memset(hdr, 0, sizeof(LzHeader));
1164
1165     /* the `method' member is rewrote by the encoding function.
1166        but need set for empty files */
1167     memcpy(hdr->method, LZHUFF0_METHOD, METHOD_TYPE_STORAGE);
1168
1169     hdr->packed_size = 0;
1170     hdr->original_size = v_stat->st_size;
1171     hdr->attribute = GENERIC_ATTRIBUTE;
1172     hdr->header_level = header_level;
1173     strcpy(hdr->name, name);
1174     len = strlen(name);
1175     hdr->crc = 0x0000;
1176     hdr->extend_type = EXTEND_UNIX;
1177     hdr->unix_last_modified_stamp = v_stat->st_mtime;
1178     /* since 00:00:00 JAN.1.1970 */
1179 #ifdef NOT_COMPATIBLE_MODE
1180     /* Please need your modification in this space. */
1181 #else
1182     hdr->unix_mode = v_stat->st_mode;
1183 #endif
1184
1185     hdr->unix_uid = v_stat->st_uid;
1186     hdr->unix_gid = v_stat->st_gid;
1187
1188 #if INCLUDE_OWNER_NAME_IN_HEADER
1189 #if HAVE_GETPWUID
1190     {
1191         struct passwd *ent = getpwuid(hdr->unix_uid);
1192
1193         if (ent) {
1194             strncpy(hdr->user, ent->pw_name, sizeof(hdr->user));
1195             if (hdr->user[sizeof(hdr->user)-1])
1196                 hdr->user[sizeof(hdr->user)-1] = 0;
1197         }
1198     }
1199 #endif
1200 #if HAVE_GETGRGID
1201     {
1202         struct group *ent = getgrgid(hdr->unix_gid);
1203
1204         if (ent) {
1205             strncpy(hdr->group, ent->gr_name, sizeof(hdr->group));
1206             if (hdr->group[sizeof(hdr->group)-1])
1207                 hdr->group[sizeof(hdr->group)-1] = 0;
1208         }
1209     }
1210 #endif
1211 #endif /* INCLUDE_OWNER_NAME_IN_HEADER */
1212     if (is_directory(v_stat)) {
1213         memcpy(hdr->method, LZHDIRS_METHOD, METHOD_TYPE_STORAGE);
1214         hdr->attribute = GENERIC_DIRECTORY_ATTRIBUTE;
1215         hdr->original_size = 0;
1216         if (len > 0 && hdr->name[len - 1] != '/')
1217             strcpy(&hdr->name[len++], "/");
1218     }
1219
1220 #ifdef S_IFLNK
1221     if (is_symlink(v_stat)) {
1222         memcpy(hdr->method, LZHDIRS_METHOD, METHOD_TYPE_STORAGE);
1223         hdr->attribute = GENERIC_DIRECTORY_ATTRIBUTE;
1224         hdr->original_size = 0;
1225         readlink(name, hdr->realname, sizeof(hdr->realname));
1226     }
1227 #endif
1228 }
1229
1230 static void
1231 write_unix_info(hdr)
1232     LzHeader *hdr;
1233 {
1234     /* UNIX specific informations */
1235
1236     put_word(5);            /* size */
1237     put_byte(0x50);         /* permission */
1238     put_word(hdr->unix_mode);
1239
1240     put_word(7);            /* size */
1241     put_byte(0x51);         /* gid and uid */
1242     put_word(hdr->unix_gid);
1243     put_word(hdr->unix_uid);
1244
1245     if (hdr->group[0]) {
1246         int len = strlen(hdr->group);
1247         put_word(len + 3);  /* size */
1248         put_byte(0x52);     /* group name */
1249         put_bytes(hdr->group, len);
1250     }
1251
1252     if (hdr->user[0]) {
1253         int len = strlen(hdr->user);
1254         put_word(len + 3);  /* size */
1255         put_byte(0x53);     /* user name */
1256         put_bytes(hdr->user, len);
1257     }
1258
1259     if (hdr->header_level == 1) {
1260         put_word(7);        /* size */
1261         put_byte(0x54);     /* time stamp */
1262         put_longword(hdr->unix_last_modified_stamp);
1263     }
1264 }
1265
1266 static size_t
1267 write_header_level0(data, hdr, pathname)
1268     LzHeader *hdr;
1269     char *data, *pathname;
1270 {
1271     int limit;
1272     int name_length;
1273     size_t header_size;
1274
1275     setup_put(data);
1276     memset(data, 0, LZHEADER_STORAGE);
1277
1278     put_byte(0x00);             /* header size */
1279     put_byte(0x00);             /* check sum */
1280     put_bytes(hdr->method, 5);
1281     put_longword(hdr->packed_size);
1282     put_longword(hdr->original_size);
1283     put_longword(unix_to_generic_stamp(hdr->unix_last_modified_stamp));
1284     put_byte(hdr->attribute);
1285     put_byte(hdr->header_level); /* level 0 */
1286
1287     /* write pathname (level 0 header contains the directory part) */
1288     name_length = strlen(pathname);
1289     if (generic_format)
1290         limit = 255 - I_GENERIC_HEADER_SIZE + 2;
1291     else
1292         limit = 255 - I_LEVEL0_HEADER_SIZE + 2;
1293
1294     if (name_length > limit) {
1295         warning("the length of pathname \"%s\" is too long.", pathname);
1296         name_length = limit;
1297     }
1298     put_byte(name_length);
1299     put_bytes(pathname, name_length);
1300     put_word(hdr->crc);
1301
1302     if (generic_format) {
1303         header_size = I_GENERIC_HEADER_SIZE + name_length - 2;
1304         data[I_HEADER_SIZE] = header_size;
1305         data[I_HEADER_CHECKSUM] = calc_sum(data + I_METHOD, header_size);
1306     } else {
1307         /* write old-style extend header */
1308         put_byte(EXTEND_UNIX);
1309         put_byte(CURRENT_UNIX_MINOR_VERSION);
1310         put_longword(hdr->unix_last_modified_stamp);
1311         put_word(hdr->unix_mode);
1312         put_word(hdr->unix_uid);
1313         put_word(hdr->unix_gid);
1314
1315         /* size of extended header is 12 */
1316         header_size = I_LEVEL0_HEADER_SIZE + name_length - 2;
1317         data[I_HEADER_SIZE] = header_size;
1318         data[I_HEADER_CHECKSUM] = calc_sum(data + I_METHOD, header_size);
1319     }
1320
1321     return header_size + 2;
1322 }
1323
1324 static size_t
1325 write_header_level1(data, hdr, pathname)
1326     LzHeader *hdr;
1327     char *data, *pathname;
1328 {
1329     int name_length, dir_length, limit;
1330     char *basename, *dirname;
1331     size_t header_size;
1332     char *extend_header_top;
1333     size_t extend_header_size;
1334
1335     basename = strrchr(pathname, LHA_PATHSEP);
1336     if (basename) {
1337         basename++;
1338         name_length = strlen(basename);
1339         dirname = pathname;
1340         dir_length = basename - dirname;
1341     }
1342     else {
1343         basename = pathname;
1344         name_length = strlen(basename);
1345         dirname = "";
1346         dir_length = 0;
1347     }
1348
1349     setup_put(data);
1350     memset(data, 0, LZHEADER_STORAGE);
1351
1352     put_byte(0x00);             /* header size */
1353     put_byte(0x00);             /* check sum */
1354     put_bytes(hdr->method, 5);
1355     put_longword(hdr->packed_size);
1356     put_longword(hdr->original_size);
1357     put_longword(unix_to_generic_stamp(hdr->unix_last_modified_stamp));
1358     put_byte(0x20);
1359     put_byte(hdr->header_level); /* level 1 */
1360
1361     /* level 1 header: write filename (basename only) */
1362     limit = 255 - I_LEVEL1_HEADER_SIZE + 2;
1363     if (name_length > limit) {
1364         put_byte(0);            /* name length */
1365     }
1366     else {
1367         put_byte(name_length);
1368         put_bytes(basename, name_length);
1369     }
1370
1371     put_word(hdr->crc);
1372
1373     if (generic_format)
1374         put_byte(0x00);
1375     else
1376         put_byte(EXTEND_UNIX);
1377
1378     /* write extend header from here. */
1379
1380     extend_header_top = put_ptr+2; /* +2 for the field `next header size' */
1381     header_size = extend_header_top - data - 2;
1382
1383     /* write filename and dirname */
1384
1385     if (name_length > limit) {
1386         put_word(name_length + 3); /* size */
1387         put_byte(0x01);         /* filename */
1388         put_bytes(basename, name_length);
1389     }
1390
1391     if (dir_length > 0) {
1392         put_word(dir_length + 3); /* size */
1393         put_byte(0x02);         /* dirname */
1394         put_bytes(dirname, dir_length);
1395     }
1396
1397     if (!generic_format)
1398         write_unix_info(hdr);
1399
1400     put_word(0x0000);           /* next header size */
1401
1402     extend_header_size = put_ptr - extend_header_top;
1403     /* On level 1 header, the packed size field is contains the ext-header */
1404     hdr->packed_size += put_ptr - extend_header_top;
1405
1406     /* put `skip size' */
1407     setup_put(data + I_PACKED_SIZE);
1408     put_longword(hdr->packed_size);
1409
1410     data[I_HEADER_SIZE] = header_size;
1411     data[I_HEADER_CHECKSUM] = calc_sum(data + I_METHOD, header_size);
1412
1413     return header_size + extend_header_size + 2;
1414 }
1415
1416 static size_t
1417 write_header_level2(data, hdr, pathname)
1418     LzHeader *hdr;
1419     char *data, *pathname;
1420 {
1421     int name_length, dir_length;
1422     char *basename, *dirname;
1423     size_t header_size;
1424     char *extend_header_top;
1425     char *headercrc_ptr;
1426     unsigned int hcrc;
1427
1428     basename = strrchr(pathname, LHA_PATHSEP);
1429     if (basename) {
1430         basename++;
1431         name_length = strlen(basename);
1432         dirname = pathname;
1433         dir_length = basename - dirname;
1434     }
1435     else {
1436         basename = pathname;
1437         name_length = strlen(basename);
1438         dirname = "";
1439         dir_length = 0;
1440     }
1441
1442     setup_put(data);
1443     memset(data, 0, LZHEADER_STORAGE);
1444
1445     put_word(0x0000);           /* header size */
1446     put_bytes(hdr->method, 5);
1447     put_longword(hdr->packed_size);
1448     put_longword(hdr->original_size);
1449     put_longword(hdr->unix_last_modified_stamp);
1450     put_byte(0x20);
1451     put_byte(hdr->header_level); /* level 2 */
1452
1453     put_word(hdr->crc);
1454
1455     if (generic_format)
1456         put_byte(0x00);
1457     else
1458         put_byte(EXTEND_UNIX);
1459
1460     /* write extend header from here. */
1461
1462     extend_header_top = put_ptr+2; /* +2 for the field `next header size' */
1463
1464     /* write common header */
1465     put_word(5);
1466     put_byte(0x00);
1467     headercrc_ptr = put_ptr;
1468     put_word(0x0000);           /* header CRC */
1469
1470     /* write filename and dirname */
1471     /* must have this header, even if the name_length is 0. */
1472     put_word(name_length + 3);  /* size */
1473     put_byte(0x01);             /* filename */
1474     put_bytes(basename, name_length);
1475
1476     if (dir_length > 0) {
1477         put_word(dir_length + 3); /* size */
1478         put_byte(0x02);         /* dirname */
1479         put_bytes(dirname, dir_length);
1480     }
1481
1482     if (!generic_format)
1483         write_unix_info(hdr);
1484
1485     put_word(0x0000);           /* next header size */
1486
1487     header_size = put_ptr - data;
1488     if ((header_size & 0xff) == 0) {
1489         /* cannot put zero at the first byte on level 2 header. */
1490         /* adjust header size. */
1491         put_byte(0);            /* padding */
1492         header_size++;
1493     }
1494
1495     /* put hader size */
1496     setup_put(data + I_HEADER_SIZE);
1497     put_word(header_size);
1498
1499     /* put hader CRC in extended header */
1500     INITIALIZE_CRC(hcrc);
1501     hcrc = calccrc(hcrc, data, (unsigned int) header_size);
1502     setup_put(headercrc_ptr);
1503     put_word(hcrc);
1504
1505     return header_size;
1506 }
1507
1508 void
1509 write_header(fp, hdr)
1510     FILE           *fp;
1511     LzHeader       *hdr;
1512 {
1513     size_t header_size;
1514     char data[LZHEADER_STORAGE];
1515
1516     int archive_kanji_code = CODE_SJIS;
1517     int system_kanji_code = default_system_kanji_code;
1518     char *archive_delim = "\377";
1519     char *system_delim = "/";
1520     int filename_case = NONE;
1521     char pathname[FILENAME_LENGTH];
1522
1523     if (optional_archive_kanji_code)
1524         archive_kanji_code = optional_archive_kanji_code;
1525     if (optional_system_kanji_code)
1526         system_kanji_code = optional_system_kanji_code;
1527
1528     if (generic_format)
1529         filename_case = TO_UPPER;
1530
1531     if (hdr->header_level == 0) {
1532         archive_delim = "\\";
1533     }
1534
1535     if ((hdr->unix_mode & UNIX_FILE_SYMLINK) == UNIX_FILE_SYMLINK) {
1536         char *p;
1537         p = strchr(hdr->name, '|');
1538         if (p) {
1539             error("symlink name \"%s\" contains '|' char. change it into '_'",
1540                   hdr->name);
1541             *p = '_';
1542         }
1543         if (xsnprintf(pathname, sizeof(pathname),
1544                       "%s|%s", hdr->name, hdr->realname) == -1)
1545             error("file name is too long (%s -> %s)", hdr->name, hdr->realname);
1546     }
1547     else {
1548         strncpy(pathname, hdr->name, sizeof(pathname));
1549         pathname[sizeof(pathname)-1] = 0;
1550     }
1551
1552     convert_filename(pathname, strlen(pathname), sizeof(pathname),
1553                      system_kanji_code,
1554                      archive_kanji_code,
1555                      system_delim, archive_delim, filename_case);
1556
1557     switch (hdr->header_level) {
1558     case 0:
1559         header_size = write_header_level0(data, hdr, pathname);
1560         break;
1561     case 1:
1562         header_size = write_header_level1(data, hdr, pathname);
1563         break;
1564     case 2:
1565         header_size = write_header_level2(data, hdr, pathname);
1566         break;
1567     default:
1568         error("Unknown level header (level %d)", hdr->header_level);
1569         exit(1);
1570     }
1571
1572     if (fwrite(data, header_size, 1, fp) == 0)
1573         fatal_error("Cannot write to temporary file");
1574 }
1575
1576 #if MULTIBYTE_FILENAME
1577
1578 #if defined(__APPLE__)  /* Added by Hiroto Sakai */
1579
1580 #include <CoreFoundation/CFString.h>
1581 #include <CoreFoundation/CFStringEncodingExt.h>
1582
1583 /* this is not need for Mac OS X v 10.2 later */
1584 enum {
1585   kCFStringEncodingAllowLossyConversion = 1,
1586   kCFStringEncodingBasicDirectionLeftToRight = (1 << 1),
1587   kCFStringEncodingBasicDirectionRightToLeft = (1 << 2),
1588   kCFStringEncodingSubstituteCombinings = (1 << 3),
1589   kCFStringEncodingComposeCombinings = (1 << 4),
1590   kCFStringEncodingIgnoreCombinings = (1 << 5),
1591   kCFStringEncodingUseCanonical = (1 << 6),
1592   kCFStringEncodingUseHFSPlusCanonical = (1 << 7),
1593   kCFStringEncodingPrependBOM = (1 << 8),
1594   kCFStringEncodingDisableCorporateArea = (1 << 9),
1595   kCFStringEncodingASCIICompatibleConversion = (1 << 10),
1596 };
1597
1598 static int
1599 ConvertEncodingToUTF8(const char* inCStr,
1600                       char* outUTF8Buffer,
1601                       int outUTF8BufferLength,
1602                       unsigned long scriptEncoding,
1603                       unsigned long flags)
1604 {
1605     unsigned long unicodeChars;
1606     unsigned long srcCharsUsed;
1607     unsigned long usedByteLen = 0;
1608     UniChar uniStr[512];
1609     unsigned long cfResult;
1610
1611     cfResult = CFStringEncodingBytesToUnicode(scriptEncoding,
1612                                               flags,
1613                                               (char *)inCStr,
1614                                               strlen(inCStr),
1615                                               &srcCharsUsed,
1616                                               uniStr,
1617                                               512,
1618                                               &unicodeChars);
1619     if (cfResult == 0) {
1620         cfResult = CFStringEncodingUnicodeToBytes(kCFStringEncodingUTF8,
1621                                                   flags,
1622                                                   uniStr,
1623                                                   unicodeChars,
1624                                                   &srcCharsUsed,
1625                                                   (char*)outUTF8Buffer,
1626                                                   outUTF8BufferLength - 1,
1627                                                   &usedByteLen);
1628         outUTF8Buffer[usedByteLen] = '\0';
1629     }
1630
1631     return cfResult;
1632 }
1633
1634 static int
1635 ConvertUTF8ToEncoding(const char* inUTF8Buf,
1636                       int inUTF8BufLength,
1637                       char* outCStrBuffer,
1638                       int outCStrBufferLength,
1639                       unsigned long scriptEncoding,
1640                       unsigned long flags)
1641 {
1642     unsigned long unicodeChars;
1643     unsigned long srcCharsUsed;
1644     unsigned long usedByteLen = 0;
1645     UniChar uniStr[256];
1646     unsigned long cfResult;
1647
1648     cfResult = CFStringEncodingBytesToUnicode(kCFStringEncodingUTF8,
1649                                               flags,
1650                                               (char*)inUTF8Buf,
1651                                               inUTF8BufLength,
1652                                               &srcCharsUsed,
1653                                               uniStr,
1654                                               255,
1655                                               &unicodeChars);
1656     if (cfResult == 0) {
1657         cfResult = CFStringEncodingUnicodeToBytes(scriptEncoding,
1658                                                   flags,
1659                                                   uniStr,
1660                                                   unicodeChars,
1661                                                   &srcCharsUsed,
1662                                                   (char*)outCStrBuffer,
1663                                                   outCStrBufferLength - 1,
1664                                                   &usedByteLen);
1665         outCStrBuffer[usedByteLen] = '\0';
1666     }
1667
1668     return cfResult;
1669 }
1670
1671 #elif HAVE_ICONV
1672 #include <iconv.h>
1673
1674 static int
1675 ConvertEncodingByIconv(const char *src, char *dst, int dstsize,
1676                        const char *srcEnc, const char *dstEnc)
1677 {
1678     iconv_t ic;
1679     static char szTmpBuf[2048];
1680     char *src_p;
1681     char *dst_p;
1682     size_t sLen;
1683     size_t iLen;
1684
1685     dst_p = &szTmpBuf[0];
1686     iLen = (size_t)sizeof(szTmpBuf)-1;
1687     src_p = (char *)src;
1688     sLen = (size_t)strlen(src);
1689     memset(szTmpBuf, 0, sizeof(szTmpBuf));
1690     memset(dst, 0, dstsize);
1691
1692     ic = iconv_open(dstEnc, srcEnc);
1693     if (ic == (iconv_t)-1) {
1694         error("iconv_open() failure: %s", strerror(errno));
1695         return -1;
1696     }
1697
1698     if (iconv(ic, &src_p, &sLen, &dst_p, &iLen) == (size_t)-1) {
1699         error("iconv() failure: %s", strerror(errno));
1700         iconv_close(ic);
1701         return -1;
1702     }
1703
1704     strncpy(dst, szTmpBuf, dstsize);
1705
1706     iconv_close(ic);
1707
1708     return 0;
1709 }
1710 #endif /* defined(__APPLE__) */
1711
1712 char *
1713 sjis_to_utf8(char *dst, const char *src, size_t dstsize)
1714 {
1715 #if defined(__APPLE__)
1716   dst[0] = '\0';
1717   if (ConvertEncodingToUTF8(src, dst, dstsize,
1718                             kCFStringEncodingDOSJapanese,
1719                             kCFStringEncodingUseHFSPlusCanonical) == 0)
1720       return dst;
1721 #elif HAVE_ICONV
1722   if (ConvertEncodingByIconv(src, dst, dstsize, "SJIS", "UTF-8") != -1)
1723       return dst;
1724 #else
1725   error("not support utf-8 conversion");
1726 #endif
1727
1728   if (dstsize < 1) return dst;
1729   dst[dstsize-1] = 0;
1730   return strncpy(dst, src, dstsize-1);
1731 }
1732
1733 char *
1734 utf8_to_sjis(char *dst, const char *src, size_t dstsize)
1735 {
1736 #if defined(__APPLE__)
1737   int srclen;
1738
1739   dst[0] = '\0';
1740   srclen = strlen(src);
1741   if (ConvertUTF8ToEncoding(src, srclen, dst, dstsize,
1742                             kCFStringEncodingDOSJapanese,
1743                             kCFStringEncodingUseHFSPlusCanonical) == 0)
1744       return dst;
1745 #elif HAVE_ICONV
1746   if (ConvertEncodingByIconv(src, dst, dstsize, "UTF-8", "SJIS") != -1)
1747       return dst;
1748 #else
1749   error("not support utf-8 conversion");
1750 #endif
1751
1752   if (dstsize < 1) return dst;
1753   dst[dstsize-1] = 0;
1754   return strncpy(dst, src, dstsize-1);
1755 }
1756
1757 /*
1758  * SJIS <-> EUC ÊÑ´¹´Ø¿ô
1759  * ¡ÖÆüËܸì¾ðÊó½èÍý¡×   ¥½¥Õ¥È¥Ð¥ó¥¯(³ô)
1760  *  ¤è¤êÈ´¿è(by Koji Arai)
1761  */
1762 void
1763 euc2sjis(int *p1, int *p2)
1764 {
1765     unsigned char c1 = *p1 & 0x7f;
1766     unsigned char c2 = *p2 & 0x7f;
1767     int rowoff = c1 < 0x5f ? 0x70 : 0xb0;
1768     int celoff = c1 % 2 ? (c2 > 0x5f ? 0x20 : 0x1f) : 0x7e;
1769     *p1 = ((c1 + 1) >> 1) + rowoff;
1770     *p2 += celoff - 0x80;
1771 }
1772
1773 void
1774 sjis2euc(int *p1, int *p2)
1775 {
1776     unsigned char c1 = *p1;
1777     unsigned char c2 = *p2;
1778     int adjust = c2 < 0x9f;
1779     int rowoff = c1 < 0xa0 ? 0x70 : 0xb0;
1780     int celoff = adjust ? (c2 > 0x7f ? 0x20 : 0x1f) : 0x7e;
1781     *p1 = ((c1 - rowoff) << 1) - adjust;
1782     *p2 -= celoff;
1783
1784     *p1 |= 0x80;
1785     *p2 |= 0x80;
1786 }
1787 #endif /* MULTIBYTE_FILENAME */