OSDN Git Service

70a211156355585370adf0180e096b129b588641
[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         wp->buf = 0;
388         if (*argv == 0)
389             error("archived files are not specified.");
390
391         if (!opts.archive_to_stdio && (cmd == 'a' || cmd == 'u')) {
392             if (file_exists(archive_file)) {
393                 arcfile = fopen(archive_file, "rb");
394                 if (arcfile == NULL)
395                     error("Can't open archive '%s'", archive_file);
396
397                 break;
398             }
399         }
400         for (i = 0; i < argc; i++) {
401             add(wp, 0, argv[i], strlen(argv[i]));
402         }
403
404         fputc(0, outfile);      /* end of archive */
405         if (ferror(outfile))
406             error("Can't write");
407         fclose(outfile);
408         if (opts.archive_to_stdio) {
409             if (move_file_to_stream(temp_name, stdout) == -1)
410                 error("fail to move_file_to_stream(): %s -> %s",temp_name,"stdout");
411         }
412         else {
413             unlink(archive_file);
414             if (xrename(temp_name, archive_file) == -1)
415                 error("fail to rename(): %s -> %s",temp_name,archive_file);
416         }
417         exit(0);
418         break;
419     case 'r':
420     case 'd':
421         if (argc == 0) {
422             message("No files given in argument, do nothing.");
423             exit(0);
424         }
425         outfile = open_tempfile();
426     case 'x':
427     case 'p':
428     case 'l':
429     case 'v':
430         /* Open archive. */
431         if (opts.archive_to_stdio) {
432             arcfile = stdin;
433         }
434         else {
435             arcfile = fopen(archive_file, "rb");
436         }
437         if (arcfile == NULL)
438             error("Can't open archive '%s'", archive_file);
439
440         break;
441     default:
442         print_usage();
443         break;
444     }
445
446     /* change directory to extract dir */
447     if (cmd == 'x') {
448         if (opts.outdir) {
449             if (mkdir(opts.outdir, 0777) == -1) {
450                 if (errno != EEXIST)
451                     error("cannot make directory \"%s\"", opts.outdir);
452             }
453
454             if (chdir(opts.outdir) == -1)
455                 error("cannot change directory \"%s\"", opts.outdir);
456         }
457     }
458
459     arc_count = 0;
460
461     while (!done && read_header(arcfile, &h)) {
462
463         arc_count++;
464
465         found = search(argc, argv, &h);
466         switch (cmd) {
467         case 'a':
468         case 'u':
469             if (found>0) {
470                 argv[found-1] = 0;
471
472                 if (cmd == 'u') {
473                     time_t mtime;
474
475                     if (file_mtime(h.filename, &mtime) == -1 || h.mtime > mtime) {
476                         copy(arcfile, outfile, &h);
477                         break;
478                     }
479                 }
480
481                 if (add(wp, 1, h.filename, h.namelen)) {
482                     skip(arcfile, &h);
483                     count++;
484                 }
485                 else
486                     copy(arcfile, outfile, &h);
487             }
488             else
489                 copy(arcfile, outfile, &h);
490             break;
491         case 'd':
492             if (found) {
493                 count++;
494                 message("'%s' deleted", h.filename);
495                 skip(arcfile, &h);
496             }
497             else
498                 copy(arcfile, outfile, &h);
499             break;
500         case 'x':
501         case 'p':
502             if (found != 0) {
503                 rp->fp = arcfile;
504                 rp->compsize = h.compsize;
505                 extract(rp, cmd == 'x', &h);
506                 if (++count == nfiles)
507                     done = 1;
508             }
509             else
510                 skip(arcfile, &h);
511             break;
512         case 'l':
513         case 'v':
514             if (found != 0) {
515                 if (count == 0)
516                     list_start();
517                 list(&h);
518                 if (++count == nfiles)
519                     done = 1;
520             }
521             skip(arcfile, &h);
522             break;
523         }
524     }
525
526     if (cmd == 'a' || cmd == 'u') {
527         for (i = 0; i < argc; i++) {
528             if (argv[i]) {
529                 count++;
530                 add(wp, 0, argv[i], strlen(argv[i]));
531             }
532         }
533     }
534
535     if (cmd != 'p') {
536         if (opts.quiet < 2)
537             printf("  %d files\n", count);
538     }
539
540     if (count > 0 && (cmd == 'd' || cmd == 'a' || cmd == 'u')) {
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         fclose(outfile);
547         fclose(arcfile);
548         if (cmd == 'd') {
549             if (arc_count > count) {
550                 if (xrename(temp_name, archive_file) == -1)
551                     error("fail to rename(): %s -> %s",temp_name,archive_file);
552             }
553             else {
554                 message("The archive file \"%s\" was removed because it would be empty.", archive_file);
555             }
556         }
557         else {
558             if (xrename(temp_name, archive_file) == -1)
559                 error("fail to rename(): %s -> %s",temp_name,archive_file);
560         }
561         exit(0);
562     }
563     return EXIT_SUCCESS;
564 }