OSDN Git Service

global variable, outfile was removed
[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     {"-lh0-", 0,  0,  0},  /* 0: no compress */
61     {"-lh1-", 12, 0, 60},  /* 1: 2^12 =  4KB dynamic huffman (LHarc) */
62     {"-lh2-", 13, 0,256},  /* 2: 2^13 =  8KB dynamic huffman */
63     {"-lh3-", 13, 0,256},  /* 3: 2^13 =  8KB static huffman */
64     {"-lh4-", 12, 4,256},  /* 4: 2^12 =  4KB static huffman (pos and len)*/
65     {"-lh5-", 13, 4,256},  /* 5: 2^13 =  8KB static huffman (pos and len)*/
66     {"-lh6-", 15, 5,256},  /* 6: 2^15 = 32KB static huffman (pos and len)*/
67     {"-lh7-", 16, 5,256},  /* 7: 2^16 = 64KB static huffman (pos and len)*/
68     {"-lzs-", 11, 0, 17},  /* 8: 2^11 =  2KB (LArc) */
69     {"-lz5-", 12, 0, 17},  /* 9: 2^12 =  4KB (LArc) */
70     {"-lz4-", 0,  0,  0},  /*10: no compress (LArc) */
71     {"-lhd-", 0,  0,  0},  /*11: directory */
72 };
73
74 struct lha_opts opts;
75
76 struct lha_method *
77 which_method(char *id)
78 {
79     int i;
80
81     for (i = 0; i < sizeof(methods)/sizeof(methods[0]); i++) {
82         if (strncmp(id, methods[i].id, sizeof(methods[0].id)) == 0) {
83             return &methods[i];
84         }
85     }
86     return NULL;
87 }
88
89 int unpackable;                 /* global, set in io.c */
90 ulong compsize, origsize;       /* global */
91
92 static char *temp_name = NULL;
93
94 static void
95 print_usage()
96 {
97     printf("%s", usage);
98     exit(0);
99 }
100
101 static void
102 print_version()
103 {
104     printf("version %s\n", version);
105     exit(0);
106 }
107
108 uint
109 ratio(ulong a, ulong b)
110 {                               /* [(1000a + [b/2]) / b] */
111     int i;
112
113     for (i = 0; i < 3; i++)
114         if (a <= ULONG_MAX / 10)
115             a *= 10;
116         else
117             b /= 10;
118     if ((ulong) (a + (b >> 1)) < a) {
119         a >>= 1;
120         b >>= 1;
121     }
122     if (b == 0)
123         return 0;
124     return (uint) ((a + (b >> 1)) / b);
125 }
126
127 void
128 skip(FILE *fp, struct lzh_header *h)
129 {
130     int i;
131     if (opts.archive_to_stdio)
132         for (i = 0; i < h->compsize; i++)
133             fgetc(fp);
134     else
135         fseek(fp, h->compsize, SEEK_CUR);
136 }
137
138 static void
139 copy(FILE *arcfile, FILE *outfile, struct lzh_header *h)
140 {
141     uint n;
142     uchar buffer[MAXDICSIZ];
143
144     write_header(outfile, h);
145     while (h->compsize != 0) {
146         n = (uint) ((h->compsize > sizeof(buffer)) ? sizeof(buffer) : h->compsize);
147         if (fread((char *) buffer, 1, n, arcfile) != n)
148             error("Can't read");
149         if (fwrite((char *) buffer, 1, n, outfile) != n)
150             error("Can't write");
151         h->compsize -= n;
152     }
153 }
154
155 int
156 get_line(char *s, int n)
157 {
158     int i, c;
159
160     i = 0;
161     while ((c = getchar()) != EOF && c != '\n')
162         if (i < n)
163             s[i++] = (char) c;
164     s[i] = '\0';
165     return i;
166 }
167
168 static int
169 match(char *s1, char *s2)
170 {
171     for (;;) {
172         while (*s2 == '*' || *s2 == '?') {
173             if (*s2++ == '*')
174                 while (*s1 && *s1 != *s2)
175                     s1++;
176             else if (*s1 == 0)
177                 return 0;
178             else
179                 s1++;
180         }
181         if (*s1 != *s2)
182             return 0;
183         if (*s1 == 0)
184             return 1;
185         s1++;
186         s2++;
187     }
188 }
189
190 static int
191 search(int argc, char *argv[], struct lzh_header *h)
192 {
193     int i;
194
195     if (argc == 0)
196         return -1;
197     for (i = 0; i < argc; i++)
198         if (argv[i] && match(h->filename, argv[i]))
199             return i+1;
200     return 0;
201 }
202
203 static void
204 exitfunc(void)
205 {
206     if (temp_name)
207         remove(temp_name);
208 }
209
210 #include "getopt_long.h"
211
212 void
213 parse_args(int argc, char **argv)
214 {
215     int c;
216
217     for (;;) {
218         /* int this_option_optind = optind ? optind : 1; */
219         int option_index = 0;
220
221         enum {
222             LHA_OPT_HELP = 128,
223             LHA_OPT_VERSION,
224         };
225
226         static struct option long_options[] = {
227             /* name, has_arg, *flag, val */
228             /* has_arg:
229                no_argument (0)
230                required_argument (1)
231                optional_argument (2)
232                flag:
233                NULL: getopt_long() return val
234                non-NULL: getopt_long() return 0, and *flag set val.
235             */
236             {"help", no_argument, NULL, LHA_OPT_HELP},
237             {"version", no_argument, NULL, LHA_OPT_VERSION},
238             {0, 0, 0, 0}
239         };
240
241         c = getopt_long(argc, argv, "012fgo[567]q[012]vw:z",
242                         long_options, &option_index);
243
244         if (c == -1) break;     /* end of parsing options */
245
246         switch (c) {
247         case '?':
248             print_usage();
249             break;
250         case 0:
251             /* set value by long option */
252             break;
253         case '0': case '1': case '2':
254             /* header level */
255             opts.header_level = c - '0';
256             break;
257         case 'f':
258             opts.force_extract = 1;
259             break;
260         case 'g':
261             opts.generic = 1;
262             opts.header_level = 0;
263             break;
264         case 'o':
265             /* compress method */
266             {
267                 int idx = 1;    /* -o means -lh1- method */
268
269                 if (optarg)
270                     idx = *optarg - '0'; /* -lh[567]- method */
271
272                 opts.method   = &methods[idx];
273             }
274             break;
275         case 'q':
276             /* quiet mode */
277             opts.quiet = 2;     /* -q is equivalent to -q2 */
278             if (optarg)
279                 opts.quiet = *optarg - '0';
280             break;
281         case 'v':
282             /* verbose mode */
283             opts.verbose = 1;
284             break;
285
286         case 'w':
287             /* extract directory */
288             if (!optarg)
289                 error("extract directory does not specified for `-w'");
290             if (*optarg == '=')
291                 optarg++;
292
293             opts.outdir = optarg;
294             break;
295         case 'z':               /* no compress */
296             opts.nocompress = 1;
297             break;
298         case LHA_OPT_HELP:
299             print_usage();
300             break;
301         case LHA_OPT_VERSION:
302             print_version();
303             break;
304         default:
305             break;
306         }
307     }
308 }
309
310 FILE *
311 open_tempfile()
312 {
313     FILE *outfile;
314
315     temp_name = tmpnam(NULL);
316     outfile = fopen(temp_name, "wb");
317     if (outfile == NULL)
318         error("Can't open temporary file");
319     atexit(exitfunc);
320
321     return outfile;
322 }
323
324 int
325 main(int argc, char *argv[])
326 {
327     int i, cmd, count, nfiles, found, done;
328     char *archive_file;
329     struct lzh_header h;
330     int arc_count;
331     struct lzh_istream r, *rp;
332     struct lzh_ostream w, *wp;
333     FILE *arcfile = NULL;
334     FILE *outfile = NULL;
335
336     rp = &r;
337     wp = &w;
338
339     INITIALIZE_OPTS(opts);
340
341     if (argv[1] == 0)
342         print_usage();
343
344     /*take a command character */
345     {
346         char *arg1;
347
348         arg1 = argv[1];
349         if (arg1[0] == '-')
350             arg1++;
351         if (arg1[0] == 0)
352             print_usage();
353
354         cmd = *arg1;
355         if (arg1[1] == 0) {
356             /* -<cmd> -<opts> ... */
357             argv++;
358             argc--;
359         }
360         else {
361             /* -<cmd><opts> => -<opts> */
362             *arg1 = '-';
363         }
364     }
365
366     parse_args(argc, argv);
367     argv += optind;
368     argc -= optind;
369
370     archive_file = argv[0];
371
372     if (strcmp(archive_file, "-") == 0)
373         opts.archive_to_stdio = 1;
374
375     argv++;
376     argc--;
377
378     make_crctable();
379     count = done = nfiles = 0;
380
381     switch (cmd) {
382     case 'a':
383     case 'u':
384     case 'c':
385         if (opts.archive_to_stdio)
386             opts.quiet = 2;
387
388         outfile = open_tempfile();
389         wp->fp = outfile;
390         if (*argv == 0)
391             error("archived files are not specified.");
392
393         if (!opts.archive_to_stdio && (cmd == 'a' || cmd == 'u')) {
394             if (file_exists(archive_file)) {
395                 arcfile = fopen(archive_file, "rb");
396                 if (arcfile == NULL)
397                     error("Can't open archive '%s'", archive_file);
398
399                 break;
400             }
401         }
402         for (i = 0; i < argc; i++) {
403             add(wp, 0, argv[i], strlen(argv[i]));
404         }
405
406         fputc(0, outfile);      /* end of archive */
407         if (ferror(outfile))
408             error("Can't write");
409         fclose(outfile);
410         if (opts.archive_to_stdio) {
411             if (move_file_to_stream(temp_name, stdout) == -1)
412                 error("fail to move_file_to_stream(): %s -> %s",temp_name,"stdout");
413         }
414         else {
415             unlink(archive_file);
416             if (xrename(temp_name, archive_file) == -1)
417                 error("fail to rename(): %s -> %s",temp_name,archive_file);
418         }
419         exit(0);
420         break;
421     case 'r':
422     case 'd':
423         if (argc == 0) {
424             message("No files given in argument, do nothing.");
425             exit(0);
426         }
427         outfile = open_tempfile();
428     case 'x':
429     case 'p':
430     case 'l':
431     case 'v':
432         /* Open archive. */
433         if (opts.archive_to_stdio) {
434             arcfile = stdin;
435         }
436         else {
437             arcfile = fopen(archive_file, "rb");
438         }
439         if (arcfile == NULL)
440             error("Can't open archive '%s'", archive_file);
441
442         break;
443     default:
444         print_usage();
445         break;
446     }
447
448     /* change directory to extract dir */
449     if (cmd == 'x') {
450         if (opts.outdir) {
451             if (mkdir(opts.outdir, 0777) == -1) {
452                 if (errno != EEXIST)
453                     error("cannot make directory \"%s\"", opts.outdir);
454             }
455
456             if (chdir(opts.outdir) == -1)
457                 error("cannot change directory \"%s\"", opts.outdir);
458         }
459     }
460
461     arc_count = 0;
462
463     while (!done && read_header(arcfile, &h)) {
464
465         arc_count++;
466
467         compsize = h.compsize;
468         origsize = h.origsize;
469
470         found = search(argc, argv, &h);
471         switch (cmd) {
472         case 'a':
473         case 'u':
474             if (found>0) {
475                 argv[found-1] = 0;
476
477                 if (cmd == 'u') {
478                     time_t mtime;
479
480                     if (file_mtime(h.filename, &mtime) == -1 || h.mtime > mtime) {
481                         copy(arcfile, outfile, &h);
482                         break;
483                     }
484                 }
485
486                 if (add(wp, 1, h.filename, h.namelen)) {
487                     skip(arcfile, &h);
488                     count++;
489                 }
490                 else
491                     copy(arcfile, outfile, &h);
492             }
493             else
494                 copy(arcfile, outfile, &h);
495             break;
496         case 'd':
497             if (found) {
498                 count++;
499                 message("'%s' deleted", h.filename);
500                 skip(arcfile, &h);
501             }
502             else
503                 copy(arcfile, outfile, &h);
504             break;
505         case 'x':
506         case 'p':
507             if (found != 0) {
508                 rp->fp = arcfile;
509                 extract(rp, cmd == 'x', &h);
510                 if (++count == nfiles)
511                     done = 1;
512             }
513             else
514                 skip(arcfile, &h);
515             break;
516         case 'l':
517         case 'v':
518             if (found != 0) {
519                 if (count == 0)
520                     list_start();
521                 list(&h);
522                 if (++count == nfiles)
523                     done = 1;
524             }
525             skip(arcfile, &h);
526             break;
527         }
528     }
529
530     if (cmd == 'a' || cmd == 'u') {
531         for (i = 0; i < argc; i++) {
532             if (argv[i]) {
533                 count++;
534                 add(wp, 0, argv[i], strlen(argv[i]));
535             }
536         }
537     }
538
539     if (cmd != 'p') {
540         if (opts.quiet < 2)
541             printf("  %d files\n", count);
542     }
543
544     if (count > 0 && (cmd == 'd' || cmd == 'a' || cmd == 'u')) {
545         fputc(0, outfile);      /* end of archive */
546         if (ferror(outfile))
547             error("Can't write");
548         if (!opts.archive_to_stdio)
549             unlink(archive_file);
550         fclose(outfile);
551         fclose(arcfile);
552         if (cmd == 'd') {
553             if (arc_count > count) {
554                 if (xrename(temp_name, archive_file) == -1)
555                     error("fail to rename(): %s -> %s",temp_name,archive_file);
556             }
557             else {
558                 message("The archive file \"%s\" was removed because it would be empty.", archive_file);
559             }
560         }
561         else {
562             if (xrename(temp_name, archive_file) == -1)
563                 error("fail to rename(): %s -> %s",temp_name,archive_file);
564         }
565         exit(0);
566     }
567     return EXIT_SUCCESS;
568 }