OSDN Git Service

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