OSDN Git Service

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