OSDN Git Service

e662e9446b5068024a3b05e95dd581b7f3acb4f1
[lha/lha.git] / src / lhadd.c
1 /* ------------------------------------------------------------------------ */
2 /* LHa for UNIX                                                             */
3 /*              lhadd.c -- LHarc Add Command                                */
4 /*                                                                          */
5 /*      Copyright (C) MCMLXXXIX Yooichi.Tagawa                              */
6 /*      Modified                Nobutaka Watazaki                           */
7 /*                                                                          */
8 /*  Ver. 1.14   Source All chagned              1995.01.14  N.Watazaki      */
9 /* ------------------------------------------------------------------------ */
10 #include "lha.h"
11 /* ------------------------------------------------------------------------ */
12 static void     remove_files();
13
14 static char     new_archive_name_buffer[FILENAME_LENGTH];
15 static char    *new_archive_name;
16 /* ------------------------------------------------------------------------ */
17 static void
18 add_one(fp, nafp, hdr)
19     FILE           *fp, *nafp;
20     LzHeader       *hdr;
21 {
22     off_t header_pos, next_pos, org_pos, data_pos;
23     size_t v_original_size, v_packed_size;
24
25     reading_filename = hdr->name;
26     writing_filename = temporary_name;
27
28     if (!fp && generic_format)  /* [generic] doesn't need directory info. */
29         return;
30     header_pos = ftello(nafp);
31     write_header(nafp, hdr);/* DUMMY */
32
33     if ((hdr->unix_mode & UNIX_FILE_SYMLINK) == UNIX_FILE_SYMLINK) {
34         if (!quiet)
35             printf("%s -> %s\t- Symbolic Link\n", hdr->realname, hdr->name);
36     }
37
38     if (hdr->original_size == 0) {  /* empty file, symlink or directory */
39         finish_indicator2(hdr->name, "Frozen", 0);
40         return;     /* previous write_header is not DUMMY. (^_^) */
41     }
42     org_pos = ftello(fp);
43     data_pos = ftello(nafp);
44
45     hdr->crc = encode_lzhuf(fp, nafp, hdr->original_size,
46           &v_original_size, &v_packed_size, hdr->name, hdr->method);
47
48     if (v_packed_size < v_original_size) {
49         next_pos = ftello(nafp);
50     }
51     else {          /* retry by stored method */
52         fseeko(fp, org_pos, SEEK_SET);
53         fseeko(nafp, data_pos, SEEK_SET);
54         hdr->crc = encode_stored_crc(fp, nafp, hdr->original_size,
55                       &v_original_size, &v_packed_size);
56         fflush(nafp);
57         next_pos = ftello(nafp);
58 #if HAVE_FTRUNCATE
59         if (ftruncate(fileno(nafp), next_pos) == -1)
60             error("cannot truncate archive");
61 #elif HAVE_CHSIZE
62         if (chsize(fileno(nafp), next_pos) == -1)
63             error("cannot truncate archive");
64 #else
65         CAUSE COMPILE ERROR
66 #endif
67         memcpy(hdr->method, LZHUFF0_METHOD, METHOD_TYPE_STORAGE);
68     }
69     hdr->original_size = v_original_size;
70     hdr->packed_size = v_packed_size;
71     fseeko(nafp, header_pos, SEEK_SET);
72     write_header(nafp, hdr);
73     fseeko(nafp, next_pos, SEEK_SET);
74 }
75
76 static char *
77 remove_leading_dots(char *path)
78 {
79     char *first = path;
80     char *ptr = 0;
81
82     if (strcmp(first, "..") == 0) {
83         first[1] = 0;
84         return first;
85     }
86
87     if (strstr(first, "..") == 0)
88         return first;
89
90     while (path && *path) {
91
92         if (strcmp(path, "..") == 0)
93             ptr = path = path+2;
94         else if (strncmp(path, "../", 3) == 0)
95             ptr = path = path+3;
96         else
97             path = strchr(path, '/');
98
99         if (path && *path == '/') {
100             path++;
101         }
102     }
103
104     if (ptr) {
105         warning("Removing leading `%.*s' from member name.", ptr-first, first);
106         return ptr;
107     }
108
109     return first;
110 }
111
112 FILE           *
113 append_it(name, oafp, nafp)
114     char           *name;
115     FILE           *oafp, *nafp;
116 {
117     LzHeader        ahdr, hdr;
118     FILE           *fp;
119     long            old_header;
120     int             cmp;
121     int             filec;
122     char          **filev;
123     int             i;
124     struct stat     stbuf;
125
126     boolean         directory, symlink;
127
128     if (GETSTAT(name, &stbuf) < 0) {
129         error("Cannot access file \"%s\"", name);   /* See cleaning_files, Why? */
130         return oafp;
131     }
132
133     directory = is_directory(&stbuf);
134 #ifdef S_IFLNK
135     symlink = is_symlink(&stbuf);
136 #else
137     symlink = 0;
138 #endif
139
140     fp = NULL;
141     if (!directory && !symlink && !noexec) {
142         fp = fopen(name, READ_BINARY);
143         if (!fp) {
144             error("Cannot open file \"%s\": %s", name, strerror(errno));
145             return oafp;
146         }
147     }
148
149     init_header(remove_leading_dots(name), &stbuf, &hdr);
150
151     cmp = 0;                    /* avoid compiler warnings `uninitialized' */
152     while (oafp) {
153         old_header = ftello(oafp);
154         if (!get_header(oafp, &ahdr)) {
155             /* end of archive or error occurred */
156             fclose(oafp);
157             oafp = NULL;
158             break;
159         }
160
161         cmp = strcmp(ahdr.name, hdr.name);
162         if (cmp < 0) {          /* SKIP */
163             /* copy old to new */
164             if (!noexec) {
165                 fseeko(oafp, old_header, SEEK_SET);
166                 copy_old_one(oafp, nafp, &ahdr);
167             }
168             else
169                 fseeko(oafp, ahdr.packed_size, SEEK_CUR);
170         } else if (cmp == 0) {  /* REPLACE */
171             /* drop old archive's */
172             fseeko(oafp, ahdr.packed_size, SEEK_CUR);
173             break;
174         } else {                /* cmp > 0, INSERT */
175             fseeko(oafp, old_header, SEEK_SET);
176             break;
177         }
178     }
179
180     if (!oafp || cmp > 0) { /* not in archive */
181         if (noexec)
182             printf("ADD %s\n", name);
183         else
184             add_one(fp, nafp, &hdr);
185     }
186     else {      /* cmp == 0 */
187         if (!update_if_newer ||
188             ahdr.unix_last_modified_stamp < hdr.unix_last_modified_stamp) {
189                                 /* newer than archive's */
190             if (noexec)
191                 printf("REPLACE %s\n", name);
192             else
193                 add_one(fp, nafp, &hdr);
194         }
195         else {                  /* copy old to new */
196             if (!noexec) {
197                 fseeko(oafp, old_header, SEEK_SET);
198                 copy_old_one(oafp, nafp, &ahdr);
199             }
200         }
201     }
202
203     if (fp) fclose(fp);
204
205     if (directory) {            /* recursive call */
206         if (find_files(name, &filec, &filev)) {
207             for (i = 0; i < filec; i++)
208                 oafp = append_it(filev[i], oafp, nafp);
209             free_files(filec, filev);
210         }
211     }
212     return oafp;
213 }
214
215 /* ------------------------------------------------------------------------ */
216 static void
217 find_update_files(oafp)
218     FILE           *oafp;   /* old archive */
219 {
220     char            name[FILENAME_LENGTH];
221     struct string_pool sp;
222     LzHeader        hdr;
223     off_t           pos;
224     struct stat     stbuf;
225     int             len;
226
227     pos = ftello(oafp);
228
229     init_sp(&sp);
230     while (get_header(oafp, &hdr)) {
231         if ((hdr.unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_REGULAR) {
232             if (stat(hdr.name, &stbuf) >= 0)    /* exist ? */
233                 add_sp(&sp, hdr.name, strlen(hdr.name) + 1);
234         }
235         else if ((hdr.unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_DIRECTORY) {
236             strcpy(name, hdr.name); /* ok */
237             len = strlen(name);
238             if (len > 0 && name[len - 1] == '/')
239                 name[--len] = '\0'; /* strip tail '/' */
240             if (stat(name, &stbuf) >= 0)    /* exist ? */
241                 add_sp(&sp, name, len + 1);
242         }
243         fseeko(oafp, hdr.packed_size, SEEK_CUR);
244     }
245
246     fseeko(oafp, pos, SEEK_SET);
247
248     finish_sp(&sp, &cmd_filec, &cmd_filev);
249 }
250
251 /* ------------------------------------------------------------------------ */
252 static void
253 delete(oafp, nafp)
254     FILE           *oafp, *nafp;
255 {
256     LzHeader ahdr;
257     off_t old_header_pos;
258
259     old_header_pos = ftello(oafp);
260     while (get_header(oafp, &ahdr)) {
261         if (need_file(ahdr.name)) { /* skip */
262             fseeko(oafp, ahdr.packed_size, SEEK_CUR);
263             if (noexec || !quiet) {
264                 if ((ahdr.unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK)
265                     message("delete %s -> %s", ahdr.realname, ahdr.name);
266                 else
267                     message("delete %s", ahdr.name);
268             }
269         }
270         else {      /* copy */
271             if (noexec) {
272                 fseeko(oafp, ahdr.packed_size, SEEK_CUR);
273             }
274             else {
275                 fseeko(oafp, old_header_pos, SEEK_SET);
276                 copy_old_one(oafp, nafp, &ahdr);
277             }
278         }
279         old_header_pos = ftello(oafp);
280     }
281     return;
282 }
283
284 /* ------------------------------------------------------------------------ */
285 /*                                                                          */
286 /* ------------------------------------------------------------------------ */
287 static FILE    *
288 build_temporary_file()
289 {
290     FILE *afp;
291
292     signal(SIGINT, interrupt);
293 #ifdef SIGHUP
294     signal(SIGHUP, interrupt);
295 #endif
296
297     temporary_fd = build_temporary_name();
298     if (temporary_fd == -1)
299         fatal_error("Cannot open temporary file \"%s\"", temporary_name);
300
301     afp = fdopen(temporary_fd, WRITE_BINARY);
302     if (afp == NULL)
303         fatal_error("Cannot open temporary file \"%s\"", temporary_name);
304
305     return afp;
306 }
307
308 /* ------------------------------------------------------------------------ */
309 static void
310 build_backup_file()
311 {
312
313     build_backup_name(backup_archive_name, archive_name,
314                       sizeof(backup_archive_name));
315     if (!noexec) {
316         signal(SIGINT, SIG_IGN);
317 #ifdef SIGHUP
318         signal(SIGHUP, SIG_IGN);
319 #endif
320         if (rename(archive_name, backup_archive_name) < 0) {
321 #if __MINGW32__
322             /* On MinGW, cannot rename when
323                newfile (backup_archive_name) already exists */
324             if (unlink(backup_archive_name) < 0 ||
325                 rename(archive_name, backup_archive_name) < 0)
326 #endif
327             fatal_error("Cannot make backup file \"%s\"", archive_name);
328         }
329         recover_archive_when_interrupt = TRUE;
330         signal(SIGINT, interrupt);
331 #ifdef SIGHUP
332         signal(SIGHUP, interrupt);
333 #endif
334     }
335 }
336
337 /* ------------------------------------------------------------------------ */
338 static void
339 report_archive_name_if_different()
340 {
341     if (!quiet && new_archive_name == new_archive_name_buffer) {
342         /* warning at old archive is SFX */
343         message("New archive file is \"%s\"", new_archive_name);
344     }
345 }
346
347 /* ------------------------------------------------------------------------ */
348 void
349 temporary_to_new_archive_file(new_archive_size)
350     size_t new_archive_size;
351 {
352     FILE *oafp, *nafp;
353
354     if (!strcmp(new_archive_name, "-")) {
355         nafp = stdout;
356         writing_filename = "starndard output";
357 #if __MINGW32__
358         setmode(fileno(stdout), O_BINARY);
359 #endif
360     }
361     else {
362         unlink(new_archive_name);
363         if (rename(temporary_name, new_archive_name) == 0)
364             return;
365         nafp = xfopen(new_archive_name, WRITE_BINARY);
366         writing_filename = archive_name;
367     }
368
369     oafp = xfopen(temporary_name, READ_BINARY);
370     reading_filename = temporary_name;
371     copyfile(oafp, nafp, new_archive_size, 0, 0);
372     if (nafp != stdout)
373         fclose(nafp);
374     fclose(oafp);
375
376     recover_archive_when_interrupt = FALSE;
377     unlink(temporary_name);
378 }
379
380 /* ------------------------------------------------------------------------ */
381 static void
382 set_archive_file_mode()
383 {
384     int             umask_value;
385     struct stat     stbuf;
386
387     if (archive_file_gid < 0) {
388         umask(umask_value = umask(0));
389         archive_file_mode = (~umask_value) & 0666;  /* rw-rw-rw- */
390         if (stat(".", &stbuf) >= 0)
391             archive_file_gid = stbuf.st_gid;
392     }
393     if (archive_file_gid >= 0)
394         chown(new_archive_name, getuid(), archive_file_gid);
395
396     chmod(new_archive_name, archive_file_mode);
397 }
398
399 /* ------------------------------------------------------------------------ */
400 /*                          REMOVE FILE/DIRECTORY                           */
401 /* ------------------------------------------------------------------------ */
402 static void
403 remove_one(name)
404     char           *name;
405 {
406     struct stat     stbuf;
407     int             filec;
408     char          **filev;
409
410     if (GETSTAT(name, &stbuf) < 0) {
411         warning("Cannot access \"%s\": %s", name, strerror(errno));
412     }
413     else if (is_directory(&stbuf)) {
414         if (find_files(name, &filec, &filev)) {
415             remove_files(filec, filev);
416             free_files(filec, filev);
417         }
418         else
419             warning("Cannot open directory \"%s\"", name);
420
421         if (noexec)
422             message("REMOVE DIRECTORY %s", name);
423         else if (rmdir(name) < 0)
424             warning("Cannot remove directory \"%s\"", name);
425         else if (verbose)
426             message("Removed %s.", name);
427     }
428     else if (is_regularfile(&stbuf)) {
429         if (noexec)
430             message("REMOVE FILE %s.", name);
431         else if (unlink(name) < 0)
432             warning("Cannot remove \"%s\"", name);
433         else if (verbose)
434             message("Removed %s.", name);
435     }
436 #ifdef S_IFLNK
437     else if (is_symlink(&stbuf)) {
438         if (noexec)
439             printf("REMOVE SYMBOLIC LINK %s.\n", name);
440         else if (unlink(name) < 0)
441             warning("Cannot remove", name);
442         else if (verbose)
443             message("Removed %s.", name);
444     }
445 #endif
446     else {
447         error("Cannot remove file \"%s\" (not a file or directory)", name);
448     }
449 }
450
451 static void
452 remove_files(filec, filev)
453     int             filec;
454     char          **filev;
455 {
456     int             i;
457
458     for (i = 0; i < filec; i++)
459         remove_one(filev[i]);
460 }
461
462 /* ------------------------------------------------------------------------ */
463 /*                                                                          */
464 /* ------------------------------------------------------------------------ */
465 void
466 cmd_add()
467 {
468     LzHeader        ahdr;
469     FILE           *oafp, *nafp;
470     int             i;
471     long            old_header;
472     boolean         old_archive_exist;
473     size_t          new_archive_size;
474
475     /* exit if no operation */
476     if (!update_if_newer && cmd_filec == 0) {
477         error("No files given in argument, do nothing.");
478         exit(1);
479     }
480
481     /* open old archive if exist */
482     if ((oafp = open_old_archive()) == NULL)
483         old_archive_exist = FALSE;
484     else
485         old_archive_exist = TRUE;
486
487     if (update_if_newer && cmd_filec == 0) {
488         warning("No files given in argument");
489         if (!oafp) {
490             error("archive file \"%s\" does not exists.",
491                   archive_name);
492             exit(1);
493         }
494     }
495
496     if (new_archive && old_archive_exist) {
497         fclose(oafp);
498         oafp = NULL;
499     }
500
501     if (oafp && archive_is_msdos_sfx1(archive_name)) {
502         seek_lha_header(oafp);
503         build_standard_archive_name(new_archive_name_buffer,
504                                     archive_name,
505                                     sizeof(new_archive_name_buffer));
506         new_archive_name = new_archive_name_buffer;
507     }
508     else {
509         new_archive_name = archive_name;
510     }
511
512     /* build temporary file */
513     nafp = NULL;                /* avoid compiler warnings `uninitialized' */
514     if (!noexec)
515         nafp = build_temporary_file();
516
517     /* find needed files when automatic update */
518     if (update_if_newer && cmd_filec == 0)
519         find_update_files(oafp);
520
521     /* build new archive file */
522     /* cleaning arguments */
523     cleaning_files(&cmd_filec, &cmd_filev);
524     if (cmd_filec == 0) {
525         if (oafp)
526             fclose(oafp);
527         if (!noexec)
528             fclose(nafp);
529         return;
530     }
531
532     for (i = 0; i < cmd_filec; i++) {
533         int j;
534
535         if (strcmp(cmd_filev[i], archive_name) == 0) {
536             /* exclude target archive */
537             warning("specified file \"%s\" is the generating archive. skip",
538                     cmd_filev[i]);
539             for (j = i; j < cmd_filec-1; j++)
540                 cmd_filev[j] = cmd_filev[j+1];
541             cmd_filec--;
542             i--;
543             continue;
544         }
545
546         /* exclude files specified by -x option */
547         for (j = 0; exclude_files && exclude_files[j]; j++) {
548             if (fnmatch(exclude_files[j], basename(cmd_filev[i]),
549                         FNM_PATHNAME|FNM_NOESCAPE|FNM_PERIOD) == 0)
550                 goto next;
551         }
552
553         oafp = append_it(cmd_filev[i], oafp, nafp);
554     next:
555         ;
556     }
557
558     if (oafp) {
559         old_header = ftello(oafp);
560         while (get_header(oafp, &ahdr)) {
561             if (noexec)
562                 fseeko(oafp, ahdr.packed_size, SEEK_CUR);
563             else {
564                 fseeko(oafp, old_header, SEEK_SET);
565                 copy_old_one(oafp, nafp, &ahdr);
566             }
567             old_header = ftello(oafp);
568         }
569         fclose(oafp);
570     }
571
572     new_archive_size = 0;       /* avoid compiler warnings `uninitialized' */
573     if (!noexec) {
574         off_t tmp;
575
576         write_archive_tail(nafp);
577         tmp = ftello(nafp);
578         if (tmp == -1) {
579             warning("ftello(): %s", strerror(errno));
580             new_archive_size = 0;
581         }
582         else
583             new_archive_size = tmp;
584
585         fclose(nafp);
586     }
587
588     /* build backup archive file */
589     if (old_archive_exist && backup_old_archive)
590         build_backup_file();
591
592     report_archive_name_if_different();
593
594     /* copy temporary file to new archive file */
595     if (!noexec) {
596         if (strcmp(new_archive_name, "-") == 0 ||
597             rename(temporary_name, new_archive_name) < 0) {
598
599             temporary_to_new_archive_file(new_archive_size);
600         }
601     }
602
603     /* set new archive file mode/group */
604     set_archive_file_mode();
605
606     /* remove archived files */
607     if (delete_after_append)
608         remove_files(cmd_filec, cmd_filev);
609
610     return;
611 }
612
613 /* ------------------------------------------------------------------------ */
614 void
615 cmd_delete()
616 {
617     FILE *oafp, *nafp;
618     size_t new_archive_size;
619
620     /* open old archive if exist */
621     if ((oafp = open_old_archive()) == NULL)
622         fatal_error("Cannot open archive file \"%s\"", archive_name);
623
624     /* exit if no operation */
625     if (cmd_filec == 0) {
626         fclose(oafp);
627         warning("No files given in argument, do nothing.");
628         return;
629     }
630
631     if (archive_is_msdos_sfx1(archive_name)) {
632         seek_lha_header(oafp);
633         build_standard_archive_name(new_archive_name_buffer,
634                                     archive_name,
635                                     sizeof(new_archive_name_buffer));
636         new_archive_name = new_archive_name_buffer;
637     }
638     else {
639         new_archive_name = archive_name;
640     }
641
642     /* build temporary file */
643     nafp = NULL;                /* avoid compiler warnings `uninitialized' */
644     if (!noexec)
645         nafp = build_temporary_file();
646
647     /* build new archive file */
648     delete(oafp, nafp);
649     fclose(oafp);
650
651     new_archive_size = 0;       /* avoid compiler warnings `uninitialized' */
652     if (!noexec) {
653         off_t tmp;
654
655         write_archive_tail(nafp);
656         tmp = ftello(nafp);
657         if (tmp == -1) {
658             warning("ftello(): %s", strerror(errno));
659             new_archive_size = 0;
660         }
661         else
662             new_archive_size = tmp;
663
664         fclose(nafp);
665     }
666
667     /* build backup archive file */
668     if (backup_old_archive)
669         build_backup_file();
670
671     /* 1999.5.24 t.oka */
672     if(!noexec && new_archive_size <= 1){
673         unlink(temporary_name);
674         if (!backup_old_archive)
675             unlink(archive_name);
676         warning("The archive file \"%s\" was removed because it would be empty.", new_archive_name);
677         return;
678     }
679
680     report_archive_name_if_different();
681
682     /* copy temporary file to new archive file */
683     if (!noexec) {
684         if (rename(temporary_name, new_archive_name) < 0)
685             temporary_to_new_archive_file(new_archive_size);
686     }
687
688     /* set new archive file mode/group */
689     set_archive_file_mode();
690
691     return;
692 }