OSDN Git Service

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