OSDN Git Service

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