OSDN Git Service

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