OSDN Git Service

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