OSDN Git Service

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