OSDN Git Service

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