OSDN Git Service

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