OSDN Git Service

ar.c was refined.
[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, nfiles, found, done;
315     struct lzh_header h;
316     int arc_count;
317     struct lzh_ostream w, *wp;
318     FILE *arcfile = NULL;
319     FILE *outfile = NULL;
320
321     wp = &w;
322
323     count = done = nfiles = 0;
324
325     {
326         outfile = open_tempfile();
327         wp->fp = outfile;
328         wp->buf = 0;
329         if (*argv == 0)
330             error("archived files are not specified.");
331
332         if (!opts.archive_to_stdio && (cmd == 'a' || cmd == 'u')) {
333             if (file_exists(archive_file)) {
334                 arcfile = fopen(archive_file, "rb");
335                 if (arcfile == NULL)
336                     error("Can't open archive '%s'", archive_file);
337
338                 goto xxx;
339             }
340         }
341         for (i = 0; i < argc; i++) {
342             add(wp, 0, argv[i], strlen(argv[i]));
343         }
344
345         fputc(0, outfile);      /* end of archive */
346         if (ferror(outfile))
347             error("Can't write");
348         rewind(outfile);
349         if (opts.archive_to_stdio) {
350             if (copy_stream(outfile, stdout) == -1)
351                 error("fail to copy_stream_to_file(): temp -> %s","stdout");
352         }
353         else {
354             if (copy_stream_to_file(outfile, archive_file) == -1)
355                 error("fail to copy_stream_to_file(): temp -> %s",archive_file);
356         }
357
358         return;
359     }
360
361 xxx:
362     arc_count = 0;
363
364     while (!done && read_header(arcfile, &h)) {
365
366         arc_count++;
367
368         found = search(argc, argv, &h);
369         switch (cmd) {
370         case 'a':
371         case 'u':
372             if (found>0) {
373                 argv[found-1] = 0;
374
375                 if (cmd == 'u') {
376                     time_t mtime;
377
378                     if (file_mtime(h.filename, &mtime) == -1 || h.mtime > mtime) {
379                         copy(arcfile, outfile, &h);
380                         break;
381                     }
382                 }
383
384                 if (add(wp, 1, h.filename, h.namelen)) {
385                     skip(arcfile, &h);
386                     count++;
387                 }
388                 else
389                     copy(arcfile, outfile, &h);
390             }
391             else
392                 copy(arcfile, outfile, &h);
393             break;
394         case 'd':
395             if (found) {
396                 count++;
397                 message("'%s' deleted", h.filename);
398                 skip(arcfile, &h);
399             }
400             else
401                 copy(arcfile, outfile, &h);
402             break;
403         }
404     }
405
406     for (i = 0; i < argc; i++) {
407         if (argv[i]) {
408             count++;
409             add(wp, 0, argv[i], strlen(argv[i]));
410         }
411     }
412
413     if (cmd != 'p') {
414         if (opts.quiet < 2)
415             printf("  %d files\n", count);
416     }
417
418     if (count > 0) {
419         fputc(0, outfile);      /* end of archive */
420         if (ferror(outfile))
421             error("Can't write");
422         if (!opts.archive_to_stdio)
423             unlink(archive_file);
424
425         fclose(arcfile);
426         rewind(outfile);
427         if (copy_stream_to_file(outfile, archive_file) == -1)
428             error("fail to copy_stream_to_file(): temp -> %s",archive_file);
429         exit(0);
430     }
431 }
432
433 static void
434 op_update(int cmd, char *archive_file, int argc, char **argv)
435 {
436     int i, count, nfiles, found, done;
437     struct lzh_header h;
438     int arc_count;
439     struct lzh_ostream w, *wp;
440     FILE *arcfile = NULL;
441     FILE *outfile = NULL;
442
443     wp = &w;
444
445     count = done = nfiles = 0;
446
447     {
448         outfile = open_tempfile();
449         wp->fp = outfile;
450         wp->buf = 0;
451         if (*argv == 0)
452             error("archived files are not specified.");
453
454         if (!opts.archive_to_stdio && (cmd == 'a' || cmd == 'u')) {
455             if (file_exists(archive_file)) {
456                 arcfile = fopen(archive_file, "rb");
457                 if (arcfile == NULL)
458                     error("Can't open archive '%s'", archive_file);
459
460                 goto xxx;
461             }
462         }
463         for (i = 0; i < argc; i++) {
464             add(wp, 0, argv[i], strlen(argv[i]));
465         }
466
467         fputc(0, outfile);      /* end of archive */
468         if (ferror(outfile))
469             error("Can't write");
470         rewind(outfile);
471         if (opts.archive_to_stdio) {
472             if (copy_stream(outfile, stdout) == -1)
473                 error("fail to copy_stream_to_file(): temp -> %s","stdout");
474         }
475         else {
476             if (copy_stream_to_file(outfile, archive_file) == -1)
477                 error("fail to copy_stream_to_file(): temp -> %s",archive_file);
478         }
479
480         return;
481     }
482
483 xxx:
484     arc_count = 0;
485
486     while (!done && read_header(arcfile, &h)) {
487
488         arc_count++;
489
490         found = search(argc, argv, &h);
491         switch (cmd) {
492         case 'a':
493         case 'u':
494             if (found>0) {
495                 argv[found-1] = 0;
496
497                 if (cmd == 'u') {
498                     time_t mtime;
499
500                     if (file_mtime(h.filename, &mtime) == -1 || h.mtime > mtime) {
501                         copy(arcfile, outfile, &h);
502                         break;
503                     }
504                 }
505
506                 if (add(wp, 1, h.filename, h.namelen)) {
507                     skip(arcfile, &h);
508                     count++;
509                 }
510                 else
511                     copy(arcfile, outfile, &h);
512             }
513             else
514                 copy(arcfile, outfile, &h);
515             break;
516         case 'd':
517             if (found) {
518                 count++;
519                 message("'%s' deleted", h.filename);
520                 skip(arcfile, &h);
521             }
522             else
523                 copy(arcfile, outfile, &h);
524             break;
525         }
526     }
527
528     for (i = 0; i < argc; i++) {
529         if (argv[i]) {
530             count++;
531             add(wp, 0, argv[i], strlen(argv[i]));
532         }
533     }
534
535     if (cmd != 'p') {
536         if (opts.quiet < 2)
537             printf("  %d files\n", count);
538     }
539
540     if (count > 0) {
541         fputc(0, outfile);      /* end of archive */
542         if (ferror(outfile))
543             error("Can't write");
544         if (!opts.archive_to_stdio)
545             unlink(archive_file);
546
547         fclose(arcfile);
548         rewind(outfile);
549         if (copy_stream_to_file(outfile, archive_file) == -1)
550             error("fail to copy_stream_to_file(): temp -> %s",archive_file);
551         exit(0);
552     }
553 }
554
555 static void
556 op_create(int cmd, char *archive_file, int argc, char **argv)
557 {
558     int i;
559     struct lzh_ostream w, *wp;
560     FILE *outfile = NULL;
561
562     wp = &w;
563
564     {
565         outfile = open_tempfile();
566         wp->fp = outfile;
567         wp->buf = 0;
568         if (*argv == 0)
569             error("archived files are not specified.");
570
571         for (i = 0; i < argc; i++) {
572             add(wp, 0, argv[i], strlen(argv[i]));
573         }
574
575         fputc(0, outfile);      /* end of archive */
576         if (ferror(outfile))
577             error("Can't write");
578         rewind(outfile);
579         if (opts.archive_to_stdio) {
580             if (copy_stream(outfile, stdout) == -1)
581                 error("fail to copy_stream_to_file(): temp -> %s","stdout");
582         }
583         else {
584             if (copy_stream_to_file(outfile, archive_file) == -1)
585                 error("fail to copy_stream_to_file(): temp -> %s",archive_file);
586         }
587
588         return;
589     }
590 }
591
592 static void
593 op_delete(int cmd, char *archive_file, int argc, char **argv)
594 {
595     int count, nfiles, found, done;
596     struct lzh_header h;
597     int arc_count;
598     struct lzh_ostream w, *wp;
599     FILE *arcfile = NULL;
600     FILE *outfile = NULL;
601
602     wp = &w;
603
604     count = done = nfiles = 0;
605
606     {
607         if (argc == 0) {
608             message("No files given in argument, do nothing.");
609             exit(0);
610         }
611         outfile = open_tempfile();
612
613         /* Open archive. */
614         if (opts.archive_to_stdio) {
615             arcfile = stdin;
616         }
617         else {
618             arcfile = fopen(archive_file, "rb");
619         }
620         if (arcfile == NULL)
621             error("Can't open archive '%s'", archive_file);
622     }
623
624     arc_count = 0;
625
626     while (!done && read_header(arcfile, &h)) {
627
628         arc_count++;
629
630         found = search(argc, argv, &h);
631         switch (cmd) {
632         case 'd':
633             if (found) {
634                 count++;
635                 message("'%s' deleted", h.filename);
636                 skip(arcfile, &h);
637             }
638             else
639                 copy(arcfile, outfile, &h);
640             break;
641         }
642     }
643
644     if (cmd != 'p') {
645         if (opts.quiet < 2)
646             printf("  %d files\n", count);
647     }
648
649     if (count > 0) {
650         fputc(0, outfile);      /* end of archive */
651         if (ferror(outfile))
652             error("Can't write");
653         if (!opts.archive_to_stdio)
654             unlink(archive_file);
655
656         fclose(arcfile);
657         rewind(outfile);
658
659             if (arc_count > count) {
660                 if (copy_stream_to_file(outfile, archive_file) == -1)
661                     error("fail to copy_stream_to_file(): temp -> %s",archive_file);
662             }
663             else {
664                 message("The archive file \"%s\" was removed because it would be empty.", archive_file);
665             }
666
667         exit(0);
668     }
669 }
670
671 static void
672 op_extract(int cmd, char *archive_file, int argc, char **argv)
673 {
674     int count, nfiles, found, done;
675     struct lzh_header h;
676     int arc_count;
677     struct lzh_istream r, *rp;
678     FILE *arcfile = NULL;
679
680     rp = &r;
681
682     count = done = nfiles = 0;
683
684     switch (cmd) {
685     case 'x':
686     case 'p':
687         /* Open archive. */
688         if (opts.archive_to_stdio) {
689             arcfile = stdin;
690         }
691         else {
692             arcfile = fopen(archive_file, "rb");
693         }
694         if (arcfile == NULL)
695             error("Can't open archive '%s'", archive_file);
696
697         break;
698     }
699
700     /* change directory to extract dir */
701     if (cmd == 'x') {
702         if (opts.outdir) {
703             if (mkdir(opts.outdir, 0777) == -1) {
704                 if (errno != EEXIST)
705                     error("cannot make directory \"%s\"", opts.outdir);
706             }
707
708             if (chdir(opts.outdir) == -1)
709                 error("cannot change directory \"%s\"", opts.outdir);
710         }
711     }
712
713     arc_count = 0;
714
715     while (!done && read_header(arcfile, &h)) {
716
717         arc_count++;
718
719         found = search(argc, argv, &h);
720         switch (cmd) {
721         case 'x':
722         case 'p':
723             if (found != 0) {
724                 rp->fp = arcfile;
725                 rp->compsize = h.compsize;
726                 extract(rp, cmd == 'x', &h);
727                 if (++count == nfiles)
728                     done = 1;
729             }
730             else
731                 skip(arcfile, &h);
732             break;
733         }
734     }
735
736     if (cmd != 'p') {
737         if (opts.quiet < 2)
738             printf("  %d files\n", count);
739     }
740 }
741
742 static void
743 op_list(int cmd, char *archive_file, int argc, char **argv)
744 {
745     int count, nfiles, found, done;
746     struct lzh_header h;
747     int arc_count;
748     struct lzh_istream r, *rp;
749     FILE *arcfile = NULL;
750
751     rp = &r;
752
753     count = done = nfiles = 0;
754
755     /* Open archive. */
756     if (opts.archive_to_stdio) {
757         arcfile = stdin;
758     }
759     else {
760         arcfile = fopen(archive_file, "rb");
761     }
762     if (arcfile == NULL)
763         error("Can't open archive '%s'", archive_file);
764
765     arc_count = 0;
766
767     while (!done && read_header(arcfile, &h)) {
768
769         arc_count++;
770
771         found = search(argc, argv, &h);
772
773         if (found != 0) {
774             if (count == 0)
775                 list_start();
776             list(&h);
777             if (++count == nfiles)
778                 done = 1;
779         }
780         skip(arcfile, &h);
781     }
782
783     if (opts.quiet < 2)
784         printf("  %d files\n", count);
785 }
786
787 int
788 main(int argc, char *argv[])
789 {
790     int cmd;
791     char *archive_file;
792
793     INITIALIZE_OPTS(opts);
794
795     if (argv[1] == 0)
796         print_usage();
797
798     /*take a command character */
799     {
800         char *arg1;
801
802         arg1 = argv[1];
803         if (arg1[0] == '-')
804             arg1++;
805         if (arg1[0] == 0)
806             print_usage();
807
808         cmd = *arg1;
809         if (arg1[1] == 0) {
810             /* -<cmd> -<opts> ... */
811             argv++;
812             argc--;
813         }
814         else {
815             /* -<cmd><opts> => -<opts> */
816             *arg1 = '-';
817         }
818     }
819
820     parse_args(argc, argv);
821     argv += optind;
822     argc -= optind;
823
824     archive_file = argv[0];
825
826     if (strcmp(archive_file, "-") == 0)
827         opts.archive_to_stdio = 1;
828     if (opts.archive_to_stdio)
829         opts.quiet = 2;
830
831     argv++;
832     argc--;
833
834     make_crctable();
835
836     switch (cmd) {
837     case 'a':
838         op_add(cmd, archive_file, argc, argv);
839         break;
840
841     case 'u':
842         op_update(cmd, archive_file, argc, argv);
843         break;
844
845     case 'c':
846         op_create(cmd, archive_file, argc, argv);
847         break;
848
849     case 'd':
850         op_delete(cmd, archive_file, argc, argv);
851         break;
852
853     case 'x':
854     case 'p':
855         op_extract(cmd, archive_file, argc, argv);
856         break;
857
858     case 'l':
859     case 'v':
860         op_list(cmd, archive_file, argc, argv);
861         break;
862
863     default:
864         print_usage();
865         break;
866     }
867     return EXIT_SUCCESS;
868 }