OSDN Git Service

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