OSDN Git Service

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