OSDN Git Service

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