OSDN Git Service

should return value
[lha/olha.git] / ar.c
1 /***********************************************************
2         ar.c -- main file
3 ***********************************************************/
4
5 static char *version = "0.01";
6
7 static char *usage =
8     "ar -- compression archiver -- written by Haruhiko Okumura\n"
9     "  PC-VAN:SCIENCE        CompuServe:74050,1022\n"
10     "  NIFTY-Serve:PAF01022  INTERNET:74050.1022@compuserve.com\n"
11     "Usage: ar command archive [file ...]\n"
12     "Commands:\n"
13     "   a: Add files to archive (replace if present)\n"
14     "   x: Extract files from archive\n"
15     "   r: Replace files in archive\n"
16     "   d: Delete files from archive\n"
17     "   p: Print files on standard output\n"
18     "   l: List contents of archive\n"
19     "If no files are named, all files in archive are processed,\n"
20     "   except for commands 'a' and 'd'.\n"
21     "You may copy, distribute, and rewrite this program freely.\n";
22
23 /***********************************************************
24
25 Structure of archive block (low order byte first):
26 -----preheader
27  1      basic header size
28                 = 25 + strlen(filename) (= 0 if end of archive)
29  1      basic header algebraic sum (mod 256)
30 -----basic header
31  5      method ("-lh0-" = stored, "-lh5-" = compressed)
32  4      compressed size (including extended headers)
33  4      original size
34  4      not used
35  1      0x20
36  1      0x01
37  1      filename length (x)
38  x      filename
39  2      original file's CRC
40  1      0x20
41  2      first extended header size (0 if none)
42 -----first extended header, etc.
43 -----compressed file
44
45 ***********************************************************/
46
47 #include <stdlib.h>
48 #include <string.h>
49 #include <ctype.h>
50 #include <errno.h>
51 #include <sys/stat.h>
52 #include <time.h>
53 #include <utime.h>
54 #include <unistd.h>
55 #include "ar.h"
56
57 struct lha_method methods[] = {
58     /* id, dicbit, pbit, maxmatch */
59     /* note: dicbit == 0 means no compress */
60     /*       (1U << pbit) > np(dicbit+1) */
61     {"-lh0-", 0,  0,  0},  /* 0: no compress */
62     {"-lh1-", 12, 0, 60},  /* 1: 2^12 =  4KB dynamic huffman (LHarc) */
63     {"-lh2-", 13, 0,256},  /* 2: 2^13 =  8KB dynamic huffman */
64     {"-lh3-", 13, 0,256},  /* 3: 2^13 =  8KB static huffman */
65     {"-lh4-", 12, 4,256},  /* 4: 2^12 =  4KB static huffman (pos and len)*/
66     {"-lh5-", 13, 4,256},  /* 5: 2^13 =  8KB static huffman (pos and len)*/
67     {"-lh6-", 15, 5,256},  /* 6: 2^15 = 32KB static huffman (pos and len)*/
68     {"-lh7-", 16, 5,256},  /* 7: 2^16 = 64KB static huffman (pos and len)*/
69     {"-lzs-", 11, 0, 17},  /* 8: 2^11 =  2KB (LArc) */
70     {"-lz5-", 12, 0, 17},  /* 9: 2^12 =  4KB (LArc) */
71     {"-lz4-", 0,  0,  0},  /*10: no compress (LArc) */
72     {"-lhd-", 0,  0,  0},  /*11: directory */
73 };
74
75 struct lha_opts opts;
76
77 struct lha_method *
78 which_method(char *id)
79 {
80     int i;
81
82     for (i = 0; i < sizeof(methods)/sizeof(methods[0]); i++) {
83         if (strncmp(id, methods[i].id, sizeof(methods[0].id)) == 0) {
84             return &methods[i];
85         }
86     }
87     return NULL;
88 }
89
90 static void
91 print_usage()
92 {
93     printf("%s", usage);
94     exit(0);
95 }
96
97 static void
98 print_version()
99 {
100     printf("version %s\n", version);
101     exit(0);
102 }
103
104 uint
105 ratio(ulong a, ulong b)
106 {                               /* [(1000a + [b/2]) / b] */
107     int i;
108
109     for (i = 0; i < 3; i++)
110         if (a <= ULONG_MAX / 10)
111             a *= 10;
112         else
113             b /= 10;
114     if ((ulong) (a + (b >> 1)) < a) {
115         a >>= 1;
116         b >>= 1;
117     }
118     if (b == 0)
119         return 0;
120     return (uint) ((a + (b >> 1)) / b);
121 }
122
123 void
124 skip(FILE *fp, struct lzh_header *h)
125 {
126     int i;
127     if (opts.archive_to_stdio)
128         for (i = 0; i < h->compsize; i++)
129             fgetc(fp);
130     else
131         fseek(fp, h->compsize, SEEK_CUR);
132 }
133
134 static void
135 copy(FILE *arcfile, FILE *outfile, struct lzh_header *h)
136 {
137     uint n;
138     uchar buffer[MAXDICSIZ];
139
140     write_header(outfile, h);
141     while (h->compsize != 0) {
142         n = (uint) ((h->compsize > sizeof(buffer)) ? sizeof(buffer) : h->compsize);
143         if (fread((char *) buffer, 1, n, arcfile) != n)
144             error("Can't read");
145         if (fwrite((char *) buffer, 1, n, outfile) != n)
146             error("Can't write");
147         h->compsize -= n;
148     }
149 }
150
151 int
152 get_line(char *s, int n)
153 {
154     int i, c;
155
156     i = 0;
157     while ((c = getchar()) != EOF && c != '\n')
158         if (i < n)
159             s[i++] = (char) c;
160     s[i] = '\0';
161     return i;
162 }
163
164 static int
165 match(char *s1, char *s2)
166 {
167     for (;;) {
168         while (*s2 == '*' || *s2 == '?') {
169             if (*s2++ == '*')
170                 while (*s1 && *s1 != *s2)
171                     s1++;
172             else if (*s1 == 0)
173                 return 0;
174             else
175                 s1++;
176         }
177         if (*s1 != *s2)
178             return 0;
179         if (*s1 == 0)
180             return 1;
181         s1++;
182         s2++;
183     }
184 }
185
186 static int
187 search(int argc, char *argv[], struct lzh_header *h)
188 {
189     int i;
190
191     if (argc == 0)
192         return -1;
193     for (i = 0; i < argc; i++)
194         if (argv[i] && match(h->filename, argv[i]))
195             return i+1;
196     return 0;
197 }
198
199 #include "getopt_long.h"
200
201 void
202 parse_args(int argc, char **argv)
203 {
204     int c;
205
206     for (;;) {
207         /* int this_option_optind = optind ? optind : 1; */
208         int option_index = 0;
209
210         enum {
211             LHA_OPT_HELP = 128,
212             LHA_OPT_VERSION,
213         };
214
215         static struct option long_options[] = {
216             /* name, has_arg, *flag, val */
217             /* has_arg:
218                no_argument (0)
219                required_argument (1)
220                optional_argument (2)
221                flag:
222                NULL: getopt_long() return val
223                non-NULL: getopt_long() return 0, and *flag set val.
224             */
225             {"help", no_argument, NULL, LHA_OPT_HELP},
226             {"version", no_argument, NULL, LHA_OPT_VERSION},
227             {0, 0, 0, 0}
228         };
229
230         c = getopt_long(argc, argv, "012fgo[567]q[012]vw:z",
231                         long_options, &option_index);
232
233         if (c == -1) break;     /* end of parsing options */
234
235         switch (c) {
236         case '?':
237             print_usage();
238             break;
239         case 0:
240             /* set value by long option */
241             break;
242         case '0': case '1': case '2':
243             /* header level */
244             opts.header_level = c - '0';
245             break;
246         case 'f':
247             opts.force_extract = 1;
248             break;
249         case 'g':
250             opts.generic = 1;
251             opts.header_level = 0;
252             break;
253         case 'o':
254             /* compress method */
255             {
256                 int idx = 1;    /* -o means -lh1- method */
257
258                 if (optarg)
259                     idx = *optarg - '0'; /* -lh[567]- method */
260
261                 opts.method   = &methods[idx];
262             }
263             break;
264         case 'q':
265             /* quiet mode */
266             opts.quiet = 2;     /* -q is equivalent to -q2 */
267             if (optarg)
268                 opts.quiet = *optarg - '0';
269             break;
270         case 'v':
271             /* verbose mode */
272             opts.verbose = 1;
273             break;
274
275         case 'w':
276             /* extract directory */
277             if (!optarg)
278                 error("extract directory does not specified for `-w'");
279             if (*optarg == '=')
280                 optarg++;
281
282             opts.outdir = optarg;
283             break;
284         case 'z':               /* no compress */
285             opts.nocompress = 1;
286             break;
287         case LHA_OPT_HELP:
288             print_usage();
289             break;
290         case LHA_OPT_VERSION:
291             print_version();
292             break;
293         default:
294             break;
295         }
296     }
297 }
298
299 FILE *
300 open_tempfile()
301 {
302     FILE *outfile;
303
304     outfile = tmpfile();
305     if (outfile == NULL)
306         error("Can't open temporary file");
307
308     return outfile;
309 }
310
311 static void
312 op_add(int cmd, char *archive_file, int argc, char **argv)
313 {
314     int i, count, found, done;
315     struct lzh_header h;
316     struct lzh_ostream w, *wp;
317     FILE *arcfile = NULL;
318     FILE *outfile = NULL;
319
320     wp = &w;
321
322     count = done = 0;
323
324     outfile = open_tempfile();
325     wp->fp = outfile;
326     wp->buf = 0;
327     if (*argv == 0)
328         error("archived files are not specified.");
329
330     arcfile = fopen(archive_file, "rb");
331     if (arcfile == NULL)
332         error("Can't open archive '%s'", archive_file);
333
334     while (!done && read_header(arcfile, &h)) {
335
336         found = search(argc, argv, &h);
337         if (found>0) {
338             argv[found-1] = 0;
339
340             if (cmd == 'u') {
341                 time_t mtime;
342
343                 if (file_mtime(h.filename, &mtime) == -1 || h.mtime > mtime) {
344                     copy(arcfile, outfile, &h);
345                     continue;
346                 }
347             }
348
349             if (add(wp, 1, h.filename, h.namelen)) {
350                 skip(arcfile, &h);
351                 count++;
352             }
353             else
354                 copy(arcfile, outfile, &h);
355         }
356         else
357             copy(arcfile, outfile, &h);
358     }
359
360     for (i = 0; i < argc; i++) {
361         if (argv[i]) {
362             count++;
363             add(wp, 0, argv[i], strlen(argv[i]));
364         }
365     }
366
367     if (opts.quiet < 2)
368         printf("  %d files\n", count);
369
370     if (count > 0) {
371         fputc(0, outfile);      /* end of archive */
372         if (ferror(outfile))
373             error("Can't write");
374         if (!opts.archive_to_stdio)
375             unlink(archive_file);
376
377         fclose(arcfile);
378         rewind(outfile);
379         if (copy_stream_to_file(outfile, archive_file) == -1)
380             error("fail to copy_stream_to_file(): temp -> %s",archive_file);
381     }
382 }
383
384 static void
385 op_create(int cmd, char *archive_file, int argc, char **argv)
386 {
387     int i;
388     struct lzh_ostream w, *wp;
389     FILE *outfile = NULL;
390
391     wp = &w;
392
393     outfile = open_tempfile();
394     wp->fp = outfile;
395     wp->buf = 0;
396     if (*argv == 0)
397         error("archived files are not specified.");
398
399     for (i = 0; i < argc; i++) {
400         add(wp, 0, argv[i], strlen(argv[i]));
401     }
402
403     fputc(0, outfile);      /* end of archive */
404     if (ferror(outfile))
405         error("Can't write");
406     rewind(outfile);
407     if (opts.archive_to_stdio) {
408         if (copy_stream(outfile, stdout) == -1)
409             error("fail to copy_stream_to_file(): temp -> %s","stdout");
410     }
411     else {
412         if (copy_stream_to_file(outfile, archive_file) == -1)
413             error("fail to copy_stream_to_file(): temp -> %s",archive_file);
414     }
415
416     return;
417 }
418
419 static void
420 op_delete(int cmd, char *archive_file, int argc, char **argv)
421 {
422     int count, found, done;
423     struct lzh_header h;
424     int arc_count;
425     struct lzh_ostream w, *wp;
426     FILE *arcfile = NULL;
427     FILE *outfile = NULL;
428
429     wp = &w;
430
431     count = done = 0;
432
433     if (argc == 0) {
434         message("No files given in argument, do nothing.");
435         return;
436     }
437     outfile = open_tempfile();
438
439     /* Open archive. */
440     if (opts.archive_to_stdio) {
441         arcfile = stdin;
442     }
443     else {
444         arcfile = fopen(archive_file, "rb");
445     }
446     if (arcfile == NULL)
447         error("Can't open archive '%s'", archive_file);
448
449     arc_count = 0;
450
451     while (!done && read_header(arcfile, &h)) {
452
453         arc_count++;
454
455         found = search(argc, argv, &h);
456         if (found) {
457             count++;
458             message("'%s' deleted", h.filename);
459             skip(arcfile, &h);
460         }
461         else
462             copy(arcfile, outfile, &h);
463     }
464
465     if (opts.quiet < 2)
466         printf("  %d files\n", count);
467
468     if (count > 0) {
469         fputc(0, outfile);      /* end of archive */
470         if (ferror(outfile))
471             error("Can't write");
472         if (!opts.archive_to_stdio)
473             unlink(archive_file);
474
475         fclose(arcfile);
476         rewind(outfile);
477
478         if (arc_count > count) {
479             if (copy_stream_to_file(outfile, archive_file) == -1)
480                 error("fail to copy_stream_to_file(): temp -> %s",archive_file);
481         }
482         else {
483             message("The archive file \"%s\" was removed because it would be empty.", archive_file);
484         }
485     }
486 }
487
488 static void
489 op_extract(int cmd, char *archive_file, int argc, char **argv)
490 {
491     int count, nfiles, found, done;
492     struct lzh_header h;
493     struct lzh_istream r, *rp;
494     FILE *arcfile = NULL;
495
496     rp = &r;
497
498     count = done = nfiles = 0;
499
500     /* Open archive. */
501     if (opts.archive_to_stdio) {
502         arcfile = stdin;
503     }
504     else {
505         arcfile = fopen(archive_file, "rb");
506     }
507     if (arcfile == NULL)
508         error("Can't open archive '%s'", archive_file);
509
510     /* change directory to extract dir */
511     if (cmd == 'x') {
512         if (opts.outdir) {
513             if (mkdir(opts.outdir, 0777) == -1) {
514                 if (errno != EEXIST)
515                     error("cannot make directory \"%s\"", opts.outdir);
516             }
517
518             if (chdir(opts.outdir) == -1)
519                 error("cannot change directory \"%s\"", opts.outdir);
520         }
521     }
522
523     while (!done && read_header(arcfile, &h)) {
524
525         found = search(argc, argv, &h);
526         if (found != 0) {
527             rp->fp = arcfile;
528             rp->compsize = h.compsize;
529             extract(rp, cmd == 'x', &h);
530             if (++count == nfiles)
531                 done = 1;
532         }
533         else
534             skip(arcfile, &h);
535     }
536
537     if (cmd != 'p') {
538         if (opts.quiet < 2)
539             printf("  %d files\n", count);
540     }
541 }
542
543 static void
544 op_list(int cmd, char *archive_file, int argc, char **argv)
545 {
546     int count, nfiles, found, done;
547     struct lzh_header h;
548     struct lzh_istream r, *rp;
549     FILE *arcfile = NULL;
550
551     rp = &r;
552
553     count = done = nfiles = 0;
554
555     /* Open archive. */
556     if (opts.archive_to_stdio) {
557         arcfile = stdin;
558     }
559     else {
560         arcfile = fopen(archive_file, "rb");
561     }
562     if (arcfile == NULL)
563         error("Can't open archive '%s'", archive_file);
564
565     while (!done && read_header(arcfile, &h)) {
566
567         found = search(argc, argv, &h);
568
569         if (found != 0) {
570             if (count == 0)
571                 list_start();
572             list(&h);
573             if (++count == nfiles)
574                 done = 1;
575         }
576         skip(arcfile, &h);
577     }
578
579     if (opts.quiet < 2)
580         printf("  %d files\n", count);
581 }
582
583 int
584 main(int argc, char *argv[])
585 {
586     int cmd;
587     char *archive_file;
588
589     INITIALIZE_OPTS(opts);
590
591     if (argv[1] == 0)
592         print_usage();
593
594     /*take a command character */
595     {
596         char *arg1;
597
598         arg1 = argv[1];
599         if (arg1[0] == '-')
600             arg1++;
601         if (arg1[0] == 0)
602             print_usage();
603
604         cmd = *arg1;
605         if (arg1[1] == 0) {
606             /* -<cmd> -<opts> ... */
607             argv++;
608             argc--;
609         }
610         else {
611             /* -<cmd><opts> => -<opts> */
612             *arg1 = '-';
613         }
614     }
615
616     parse_args(argc, argv);
617     argv += optind;
618     argc -= optind;
619
620     archive_file = argv[0];
621
622     if (strcmp(archive_file, "-") == 0)
623         opts.archive_to_stdio = 1;
624     if (opts.archive_to_stdio)
625         opts.quiet = 2;
626
627     argv++;
628     argc--;
629
630     make_crctable();
631
632     switch (cmd) {
633     case 'a':
634     case 'u':
635         if (opts.archive_to_stdio || !file_exists(archive_file)) {
636             op_create(cmd, archive_file, argc, argv);
637         }
638         else {
639             op_add(cmd, archive_file, argc, argv);
640         }
641         break;
642
643     case 'c':
644         op_create(cmd, archive_file, argc, argv);
645         break;
646
647     case 'd':
648         op_delete(cmd, archive_file, argc, argv);
649         break;
650
651     case 'x':
652     case 'p':
653         op_extract(cmd, archive_file, argc, argv);
654         break;
655
656     case 'l':
657     case 'v':
658         op_list(cmd, archive_file, argc, argv);
659         break;
660
661     default:
662         print_usage();
663         break;
664     }
665     return EXIT_SUCCESS;
666 }