OSDN Git Service

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