OSDN Git Service

should return value
[lha/olha.git] / header.c
1 #include <string.h>
2 #include <time.h>
3 #include "ar.h"
4
5 static void
6 put_header(char *buf, int *i, int n, uint32_t x)
7 {
8     while (--n >= 0) {
9         buf[(*i)++] = (uchar) (x & 0xFF);
10         x >>= 8;
11     }
12 }
13
14 static void
15 put_header_tmp(char *buf, int i, int n, uint32_t x)
16 {
17     put_header(buf, &i, n, x);
18 }
19
20 static void
21 put_string(char *buf, int *n, size_t size, char *p)
22 {
23     memcpy(buf + *n, p, size);
24     *n += size;
25 }
26
27 static uint32_t
28 get_header(char *buf, int *i, int size)
29 {
30     uint32_t s;
31     int n;
32
33     s = 0;
34     for (n = size-1; n >= 0; n--) {
35         s = (s << 8) + (unsigned char)buf[*i + n];   /* little endian */
36     }
37     *i += size;
38     return s;
39 }
40
41 static void
42 get_string(char *buf, int *i, size_t size, char *p)
43 {
44     memcpy(p, buf + *i, size);
45     *i += size;
46 }
47
48 static uint
49 calc_headersum(char *buf, int size)
50 {
51     int i;
52     uint s;
53
54     s = 0;
55     for (i = 0; i < size; i++)
56         s += buf[i];
57     return s & 0xFF;
58 }
59
60 #if 0
61 static int
62 get_byte(char *buf)
63 {
64     return *(unsigned char*)buf;
65 }
66
67 static uint16_t
68 get_word(char *buf)
69 {
70     return get_byte(buf) | (get_byte(buf+1) << 8);
71 }
72
73 static uint32_t
74 get_dword(char *buf)
75 {
76     return get_byte(buf) |
77         (get_byte(buf+1) << 8) |
78         (get_byte(buf+2) << 16) |
79         (get_byte(buf+3) << 24);
80 }
81
82 static void
83 get_char(char *buf, char *p, size_t size)
84 {
85     memcpy(p, buf, size);
86 }
87
88 static void
89 put_byte(char *buf, int c)
90 {
91     *buf = (unsigned char)(c & 0xff);
92 }
93
94 static void
95 put_word(char *buf, uint16_t c)
96 {
97     put_byte(buf,   c);
98     put_byte(buf+1, c>>8);
99 }
100
101 static void
102 put_dword(char *buf, uint32_t c)
103 {
104     put_byte(buf, c);
105     put_byte(buf+1, c>>8);
106     put_byte(buf+2, c>>16);
107     put_byte(buf+3, c>>24);
108 }
109
110 static void
111 put_char(char *buf, char *p, size_t size)
112 {
113     memcpy(buf, p, size);
114 }
115
116 #endif
117 static time_t
118 ftime_to_time_t(uint32_t ftime)
119 {
120     struct tm tm;
121     /* ftime is time structure on MS-DOS
122
123     32              24              16               8
124      0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
125       |-------------|-------|---------|---------|-----------|---------|
126        year(-1980)   month   day       hour      minute      second/2
127        (1-127)       (1-12)  (1-31)    (0-23)    (0-59)      (0-29)
128     */
129
130     memset(&tm, 0, sizeof(tm));
131     tm.tm_year = (ftime >> 25) + 1980 - 1900;
132     tm.tm_mon  = ((ftime >> 21) & 0x0f) - 1;
133     tm.tm_mday = (ftime >> 16) & 0x1f;
134     tm.tm_hour = (ftime >> 11) & 0x1f;
135     tm.tm_min  = (ftime >>  5) & 0x3f;
136     tm.tm_sec  = (ftime & 0x1f) * 2;
137
138     return mktime(&tm);
139 }
140
141 static uint32_t
142 time_t_to_ftime(time_t t)
143 {
144     struct tm tm;
145     /* ftime is time structure on MS-DOS
146
147     32              24              16               8
148      0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
149       |-------------|-------|---------|---------|-----------|---------|
150        year(-1980)   month   day       hour      minute      second/2
151        (1-127)       (1-12)  (1-31)    (0-23)    (0-59)      (0-29)
152     */
153
154 #if HAVE_LOCALTIME_R
155     localtime_r(&t, &tm);
156 #else
157     tm = *localtime(&t);
158 #endif
159
160     return (uint32_t)(tm.tm_year + 1900 - 1980) << 25
161         | (uint32_t)(tm.tm_mon + 1) << 21
162         | (uint32_t)(tm.tm_mday)    << 16
163         | (uint32_t)(tm.tm_hour)    << 11
164         | (uint32_t)(tm.tm_min)     <<  5
165         | (uint32_t)(tm.tm_sec / 2);
166 }
167
168 /*
169  * level 0 header
170  *
171  *
172  * offset  size  field name
173  * ----------------------------------
174  *     0      1  header size    [*1]
175  *     1      1  header sum
176  *            ---------------------------------------
177  *     2      5  method ID                         ^
178  *     7      4  packed size    [*2]               |
179  *    11      4  original size                     |
180  *    15      2  time                              |
181  *    17      2  date                              |
182  *    19      1  attribute                         | [*1] header size (X+Y+22)
183  *    20      1  level (0x00 fixed)                |
184  *    21      1  name length                       |
185  *    22      X  pathname                          |
186  * X +22      2  file crc (CRC-16)                 |
187  * X +24      Y  ext-header(old style)             v
188  * -------------------------------------------------
189  * X+Y+24        data                              ^
190  *                 :                               | [*2] packed size
191  *                 :                               v
192  * -------------------------------------------------
193  *
194  * ext-header(old style)
195  *     0      1  ext-type ('U')
196  *     1      1  minor version
197  *     2      4  UNIX time
198  *     6      2  mode
199  *     8      2  uid
200  *    10      2  gid
201  *
202  * attribute (MS-DOS)
203  *    bit1  read only
204  *    bit2  hidden
205  *    bit3  system
206  *    bit4  volume label
207  *    bit5  directory
208  *    bit6  archive bit (need to backup)
209  *
210  */
211 static int
212 read_header_lv0(FILE *fp, char *buf, struct lzh_header *h)
213 {
214     int headersize;
215     int headersum;
216     int pos = 0;
217     int ext_size;
218
219     headersize = get_header(buf, &pos, 1);
220     headersum = get_header(buf, &pos, 1);
221
222     fread_crc(buf + 21, headersize - (21 - pos), fp, 0);     /* CRC not used */
223
224     if (calc_headersum(buf+pos, headersize) != headersum)
225         error("Header sum error");
226
227     get_string(buf, &pos, 5, h->method);
228     h->compsize = get_header(buf, &pos, 4);
229     h->origsize = get_header(buf, &pos, 4);
230     h->mtime    = ftime_to_time_t(get_header(buf, &pos, 4));
231     /* attrib   = */ get_header(buf, &pos, 1);
232     h->level    = get_header(buf, &pos, 1); /* header level */
233     h->namelen  = get_header(buf, &pos, 1);
234     if (pos + h->namelen > headersize+2) {
235         warn("path name is too long");
236         h->namelen = headersize+2-pos;
237     }
238     get_string(buf, &pos, h->namelen, h->filename);
239     h->filename[h->namelen] = 0;
240     h->file_crc = get_header(buf, &pos, 2);
241     h->os_id    = 0;            /* generic */
242
243     ext_size = headersize - pos;
244
245     if (ext_size > 0) {
246         if (ext_size > 1) {
247             h->os_id = get_header(buf, &pos, 1);
248             ext_size--;
249         }
250         if (ext_size > 1) {
251             get_header(buf, &pos, 1); /* minor version */
252             ext_size--;
253         }
254         if (ext_size > 4) {
255             h->mtime = get_header(buf, &pos, 4);
256             ext_size -= 4;
257         }
258         if (ext_size > 2) {
259             get_header(buf, &pos, 2);         /* mode */
260             ext_size -= 2;
261         }
262         if (ext_size > 2) {
263             get_header(buf, &pos, 2);         /* uid */
264             ext_size -= 2;
265         }
266         if (ext_size > 2) {
267             get_header(buf, &pos, 2);         /* gid */
268             ext_size -= 2;
269         }
270     }
271
272     return 1;                   /* success */
273 }
274
275 /*
276  * level 1 header
277  *
278  *
279  * offset   size  field name
280  * -----------------------------------
281  *     0       1  header size   [*1]
282  *     1       1  header sum
283  *             -------------------------------------
284  *     2       5  method ID                        ^
285  *     7       4  skip size     [*2]               |
286  *    11       4  original size                    |
287  *    15       2  time                             |
288  *    17       2  date                             |
289  *    19       1  attribute (0x20 fixed)           | [*1] header size (X+Y+25)
290  *    20       1  level (0x01 fixed)               |
291  *    21       1  name length                      |
292  *    22       X  filename                         |
293  * X+ 22       2  file crc (CRC-16)                |
294  * X+ 24       1  OS ID                            |
295  * X +25       Y  ???                              |
296  * X+Y+25      2  next-header size                 v
297  * -------------------------------------------------
298  * X+Y+27      Z  ext-header                       ^
299  *                 :                               |
300  * -----------------------------------             | [*2] skip size
301  * X+Y+Z+27       data                             |
302  *                 :                               v
303  * -------------------------------------------------
304  *
305  */
306 static int
307 read_header_lv1(FILE *fp, char *buf, struct lzh_header *h)
308 {
309     int headersize;
310     int headersum;
311     int ext_headersize;
312     char dirname[1024] = "";
313     int dirnamelen = 0;
314     int pos = 0;
315
316     headersize = get_header(buf, &pos, 1);
317     headersum = get_header(buf, &pos, 1);
318
319     fread_crc(buf + 21, headersize - (21 - 2), fp, 0);     /* CRC not used */
320
321     if (calc_headersum(&buf[pos], headersize) != headersum)
322         error("Header sum error");
323
324     get_string(buf, &pos, 5, h->method);
325     h->compsize = get_header(buf, &pos, 4);
326     h->origsize = get_header(buf, &pos, 4);
327     h->mtime    = ftime_to_time_t(get_header(buf, &pos, 4));
328     get_header(buf, &pos, 1);   /* attribute */
329     h->level = get_header(buf, &pos, 1); /* header level */
330     h->namelen = get_header(buf, &pos, 1);
331     get_string(buf, &pos, h->namelen, h->filename);
332     h->filename[h->namelen] = 0;
333     h->file_crc = get_header(buf, &pos, 2);
334     h->os_id = get_header(buf, &pos, 1);
335
336     ext_headersize = get_header(buf, &pos, 2);
337
338     while (ext_headersize != 0) {
339         char extbuf[4096];
340         uchar ext_type;
341         int extpos = 0;
342
343         h->compsize -= ext_headersize;
344
345         if (fread(extbuf, ext_headersize, 1, fp) != 1) {
346             error("can't read ext header");
347         }
348
349         ext_type = get_header(extbuf, &extpos, 1);
350         switch (ext_type) {
351         case 1:
352             /* filename header */
353             h->namelen = ext_headersize - 3;
354             get_string(extbuf, &extpos, h->namelen, h->filename);
355             h->filename[h->namelen] = 0;
356             break;
357         case 2:
358             /* dirname header */
359             dirnamelen = ext_headersize - 3;
360             get_string(extbuf, &extpos, dirnamelen, dirname);
361             dirname[dirnamelen] = 0;
362             break;
363         case 0x54:
364             h->mtime = get_header(extbuf, &extpos, 4);
365             break;
366         default:
367             break;
368         }
369         extpos = ext_headersize - 2;
370         ext_headersize = get_header(extbuf, &extpos, 2);
371     }
372
373     if (dirnamelen > 0 && dirname[dirnamelen-1] != '/') {
374         dirname[dirnamelen++] = '/';
375     }
376
377     strcat(dirname, h->filename);
378     h->namelen = strlen(dirname);
379     strcpy(h->filename, dirname);
380
381     return 1;                   /* success */
382 }
383
384 /*
385  * level 2 header
386  *
387  *
388  * offset   size  field name
389  * --------------------------------------------------
390  *     0       2  total header size [*1]           ^
391  *             -----------------------             |
392  *     2       5  method ID                        |
393  *     7       4  packed size       [*2]           |
394  *    11       4  original size                    |
395  *    15       4  time                             |
396  *    19       1  RESERVED (0x20 fixed)            | [*1] total header size
397  *    20       1  level (0x02 fixed)               |      (X+26+(1))
398  *    21       2  file crc (CRC-16)                |
399  *    23       1  OS ID                            |
400  *    24       2  next-header size                 |
401  * -----------------------------------             |
402  *    26       X  ext-header                       |
403  *                 :                               |
404  * -----------------------------------             |
405  * X +26      (1) padding                          v
406  * -------------------------------------------------
407  * X +26+(1)      data                             ^
408  *                 :                               | [*2] packed size
409  *                 :                               v
410  * -------------------------------------------------
411  *
412  */
413 static int
414 read_header_lv2(FILE *fp, char *buf, struct lzh_header *h)
415 {
416     int headersize;
417     int ext_headersize;
418     int remainder;
419     char dirname[1024] = "";
420     int dirnamelen = 0;
421     int pos = 0;
422
423     headersize = get_header(buf, &pos, 2);
424
425     fread_crc(buf + 21, 26 - 21, fp, 0);     /* CRC not used */
426
427     get_string(buf, &pos, 5, h->method);
428     h->compsize = get_header(buf, &pos, 4);
429     h->origsize = get_header(buf, &pos, 4);
430     h->mtime    = get_header(buf, &pos, 4);
431     get_header(buf, &pos, 1);         /* attrib */
432     h->level    = get_header(buf, &pos, 1); /* header level */
433     h->file_crc = get_header(buf, &pos, 2);
434     h->os_id    = get_header(buf, &pos, 1);
435
436     ext_headersize = get_header(buf, &pos, 2);
437
438     remainder = headersize - pos;
439
440     while (ext_headersize != 0) {
441         char extbuf[4096];
442         uchar ext_type;
443         int extpos = 0;
444
445         remainder -= ext_headersize;
446
447         if (fread(extbuf, ext_headersize, 1, fp) != 1) {
448             error("can't read ext header");
449         }
450         ext_type = get_header(extbuf, &extpos, 1);
451         switch (ext_type) {
452         case 0:
453             /* header crc */
454             break;
455         case 1:
456             /* filename header */
457             h->namelen = ext_headersize - 3;
458             get_string(extbuf, &extpos, h->namelen, h->filename);
459             h->filename[h->namelen] = 0;
460             break;
461         case 2:
462             /* dirname header */
463             dirnamelen = ext_headersize - 3;
464             get_string(extbuf, &extpos, dirnamelen, dirname);
465             dirname[dirnamelen] = 0;
466             break;
467         default:
468             break;
469         }
470         extpos = ext_headersize - 2;
471         ext_headersize = get_header(extbuf, &extpos, 2);
472     }
473
474     if (dirnamelen > 0 && dirname[dirnamelen-1] != '/') {
475         dirname[dirnamelen++] = '/';
476     }
477
478     strcat(dirname, h->filename);
479     h->namelen = strlen(dirname);
480     strcpy(h->filename, dirname);
481
482     while (remainder > 0) {
483         fgetc(fp); /* skip padding */
484         remainder--;
485     }
486
487     return 1;                   /* success */
488 }
489
490 int
491 read_header(FILE *fp, struct lzh_header *h)
492 {
493     char buf[4096];
494     int ret;
495
496     ret = fgetc(fp);
497     buf[0] = (uchar)ret;
498     if (buf[0] == 0 || ret == EOF)
499         return 0;               /* end of archive */
500     fread_crc(buf + 1, 21 - 1, fp, 0);
501     switch (buf[20]) {
502     case 0:
503         return read_header_lv0(fp, buf, h);
504         break;
505     case 1:
506         return read_header_lv1(fp, buf, h);
507         break;
508     case 2:
509         return read_header_lv2(fp, buf, h);
510         break;
511     default:
512         error("unknown level (%d)\n", buf[20]);
513         break;
514     }
515
516     return 1;                   /* success */
517 }
518
519
520 static void
521 write_header_lv0(FILE *fp, struct lzh_header *h)
522 {
523     char buf[4096];
524     int sum;
525     int headersize;
526     int pos = 0;
527
528     headersize = 22;
529     if (!opts.generic)
530         headersize += 12;       /* extended header size */
531
532     if (headersize + h->namelen > 255) {
533         warn("path name is too long");
534         h->namelen = 255 - headersize;
535         headersize = 255;
536     }
537     else {
538         headersize += h->namelen;
539     }
540
541     put_header(buf, &pos, 1, headersize);
542     put_header(buf, &pos, 1, 0); /* dummy */
543
544     put_string(buf, &pos, 5, h->method);
545     put_header(buf, &pos, 4, h->compsize); /* packed size */
546     put_header(buf, &pos, 4, h->origsize); /* original size */
547     put_header(buf, &pos, 4, time_t_to_ftime(h->mtime)); /* ftime */
548     put_header(buf, &pos, 1, 0x20);     /* attribute */
549     put_header(buf, &pos, 1, 0);        /* level */
550     put_header(buf, &pos, 1, h->namelen); /* length of pathname */
551     put_string(buf, &pos, h->namelen, h->filename);
552     put_header(buf, &pos, 2, h->file_crc);
553
554     if (!opts.generic) {
555         /* extended header for Unix */
556         put_header(buf, &pos, 1, 'U');  /* OS type */
557         put_header(buf, &pos, 1, '\0'); /* minor version */
558         put_header(buf, &pos, 4, h->mtime); /* time_t */
559         put_header(buf, &pos, 2, 0100000);  /* mode */
560         put_header(buf, &pos, 2, 0);  /* uid */
561         put_header(buf, &pos, 2, 0);  /* gid */
562     }
563
564     sum = calc_headersum(buf+2, headersize);
565     put_header_tmp(buf, 1, 1, sum);
566
567     fwrite_crc(buf, headersize+2, fp, 0);
568 }
569
570 static void
571 write_header_lv1(FILE *fp, struct lzh_header *h)
572 {
573     char buf[4096];
574     int sum;
575     int headersize;
576     int extsize = 0;
577     int pos = 0;
578     int extpos;
579     char *dirname, *fname;
580     int dirnamelen;
581
582     fname  = xbasename(h->filename);
583     dirname   = h->filename;
584     dirnamelen = fname - dirname;
585     h->namelen = strlen(fname);
586
587     headersize = 25;
588
589     put_header(buf, &pos, 1, 0); /* dummy */
590     put_header(buf, &pos, 1, 0); /* dummy */
591     put_string(buf, &pos, 5, h->method);
592     put_header(buf, &pos, 4, h->compsize); /* packed size */
593     put_header(buf, &pos, 4, h->origsize); /* original size */
594     put_header(buf, &pos, 4, time_t_to_ftime(h->mtime)); /* ftime */
595     put_header(buf, &pos, 1, 0x20);     /* attribute */
596     put_header(buf, &pos, 1, 1);        /* level */
597     if (headersize + h->namelen > 255)
598         put_header(buf, &pos, 1, 0);            /* length of pathname */
599     else {
600         put_header(buf, &pos, 1, h->namelen);   /* length of pathname */
601         put_string(buf, &pos, h->namelen, fname);
602         headersize += h->namelen;
603     }
604     put_header_tmp(buf, 0, 1, headersize); /* header size */
605     put_header(buf, &pos, 2, h->file_crc);
606     if (opts.generic)
607         put_header(buf, &pos, 1, '\0');
608     else
609         put_header(buf, &pos, 1, 'U');
610
611     extpos = pos;
612     put_header(buf, &pos, 2, 7); /* next header size */
613     put_header(buf, &pos, 1, 0x54); /* time stamp */
614     put_header(buf, &pos, 4, h->mtime); /* time_t */
615
616     if (h->namelen > 0) {
617         put_header(buf, &pos, 2, 3 + h->namelen);
618         put_header(buf, &pos, 1, 1); /* 0x01: filename header */
619         put_string(buf, &pos, h->namelen, fname); /* filename */
620     }
621
622     if (dirnamelen > 0) {
623         put_header(buf, &pos, 2, 3 + dirnamelen);
624         put_header(buf, &pos, 1, 2); /* 0x02: dirname header */
625         put_string(buf, &pos, dirnamelen, dirname); /* dirname */
626     }
627
628     extsize = pos - extpos;
629     put_header(buf, &pos, 2, 0); /* next header size (end of header) */
630
631     put_header_tmp(buf, 7, 4, h->compsize+extsize);    /* packed size */
632
633     sum = calc_headersum(buf+2, headersize);
634     put_header_tmp(buf, 1, 1, sum);
635
636     fwrite_crc(buf, headersize+2+extsize, fp, 0);
637 }
638
639 static void
640 write_header_lv2(FILE *fp, struct lzh_header *h)
641 {
642     char buf[4096], *crcptr;
643     int headersize;
644     extern ushort crctable[];
645     char dirname[1024] = "", *fname;
646     int dirnamelen, len;
647     int pos = 0;
648
649     put_header(buf, &pos, 2, 0); /* dummy */
650     put_string(buf, &pos, 5, h->method);
651     put_header(buf, &pos, 4, h->compsize); /* packed size */
652     put_header(buf, &pos, 4, h->origsize); /* original size */
653     put_header(buf, &pos, 4, h->mtime);    /* time_t */
654     put_header(buf, &pos, 1, 0x20);        /* DOS attribute (0x20 fixed) */
655     put_header(buf, &pos, 1, 2);           /* level */
656     put_header(buf, &pos, 2, h->file_crc);
657     if (opts.generic)
658         put_header(buf, &pos, 1, '\0');
659     else
660         put_header(buf, &pos, 1, 'U');
661
662     put_header(buf, &pos, 2, 5);
663     put_header(buf, &pos, 1, 0); /* 0x00: header crc */
664     crcptr = &buf[pos];
665     put_header(buf, &pos, 2, 0); /* crc (dummy) */
666
667     fname = xbasename(h->filename);
668     len = strlen(fname);
669
670     put_header(buf, &pos, 2, 3 + len);
671     put_header(buf, &pos, 1, 1); /* 0x01: filename header */
672     put_string(buf, &pos, len, fname); /* filename */
673
674     {
675         char *ptr;
676
677         ptr = strrchr(h->filename, '/');
678         if (ptr) {
679             dirnamelen = ptr - h->filename;
680             strncpy(dirname, h->filename, dirnamelen);
681             dirname[dirnamelen+ 1] = 0;
682         }
683     }
684
685     if (*dirname) {
686         put_header(buf, &pos, 2, 3 + dirnamelen);
687         put_header(buf, &pos, 1, 2); /* 0x02: dirname header */
688         put_string(buf, &pos, dirnamelen, dirname); /* dirname */
689     }
690
691     put_header(buf, &pos, 2, 0); /* next header size (end of header) */
692
693     /* padding */
694     if (pos % 256 == 0) {
695         put_header(buf, &pos, 1, 0);
696     }
697     headersize = pos;
698
699     put_header_tmp(buf, 0, 2, headersize);
700
701     {
702         int i;
703         unsigned int crc;
704
705         crc = INIT_CRC;
706         for (i = 0; i < headersize; i++)
707             UPDATE_CRC(crc, buf[i]);
708         put_header_tmp(crcptr, 0, 2, crc);
709     }
710
711     fwrite_crc(buf, headersize, fp, 0);
712 }
713
714 void
715 write_header(FILE *fp, struct lzh_header *h)
716 {
717     switch (h->level) {
718     case 0:
719         write_header_lv0(fp, h);
720         break;
721     case 1:
722         write_header_lv1(fp, h);
723         break;
724     case 2:
725         write_header_lv2(fp, h);
726         break;
727     default:
728         error("unknown level (%d)", h->level);
729         break;
730     }
731 }