OSDN Git Service

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