OSDN Git Service

842b3e0914c22af16f33423a7ead9a20401f08c1
[lha/lha.git] / src / lhext.c
1 /* ------------------------------------------------------------------------ */
2 /* LHa for UNIX                                                             */
3 /*              lhext.c -- LHarc extract                                    */
4 /*                                                                          */
5 /*      Copyright (C) MCMLXXXIX Yooichi.Tagawa                              */
6 /*      Modified                Nobutaka Watazaki                           */
7 /*                                                                          */
8 /*  Ver. 0.00  Original                             1988.05.23  Y.Tagawa    */
9 /*  Ver. 1.00  Fixed                                1989.09.22  Y.Tagawa    */
10 /*  Ver. 0.03  LHa for UNIX                         1991.12.17  M.Oki       */
11 /*  Ver. 1.12  LHa for UNIX                         1993.10.01  N.Watazaki  */
12 /*  Ver. 1.13b Symbolic Link Update Bug Fix         1994.06.21  N.Watazaki  */
13 /*  Ver. 1.14  Source All chagned                   1995.01.14  N.Watazaki  */
14 /*  Ver. 1.14e bugfix                               1999.04.30  T.Okamoto   */
15 /* ------------------------------------------------------------------------ */
16 #include "lha.h"
17 /* ------------------------------------------------------------------------ */
18 static int      skip_flg = FALSE;   /* FALSE..No Skip , TRUE..Skip */
19 static char    *methods[] =
20 {
21     LZHUFF0_METHOD, LZHUFF1_METHOD, LZHUFF2_METHOD, LZHUFF3_METHOD,
22     LZHUFF4_METHOD, LZHUFF5_METHOD, LZHUFF6_METHOD, LZHUFF7_METHOD,
23     LARC_METHOD, LARC5_METHOD, LARC4_METHOD,
24     LZHDIRS_METHOD,
25     PMARC0_METHOD, PMARC2_METHOD,
26     NULL
27 };
28
29 static void add_dirinfo(char* name, LzHeader* hdr);
30 static void adjust_dirinfo();
31
32 #ifdef HAVE_LIBAPPLEFILE
33 static boolean decode_macbinary(FILE *ofp, off_t size, const char *outPath);
34 #endif
35
36 /* ------------------------------------------------------------------------ */
37 static          boolean
38 inquire_extract(name)
39     char           *name;
40 {
41     struct stat     stbuf;
42
43     skip_flg = FALSE;
44     if (stat(name, &stbuf) >= 0) {
45         if (!is_regularfile(&stbuf)) {
46             error("\"%s\" already exists (not a file)", name);
47             return FALSE;
48         }
49
50         if (noexec) {
51             printf("EXTRACT %s but file is exist.\n", name);
52             return FALSE;
53         }
54         else if (!force) {
55             if (!isatty(0)) {
56                 warning("skip to extract %s.", name);
57                 return FALSE;
58             }
59
60             switch (inquire("OverWrite ?(Yes/[No]/All/Skip)", name, "YyNnAaSs\n")) {
61             case 0:
62             case 1:/* Y/y */
63                 break;
64             case 2:
65             case 3:/* N/n */
66             case 8:/* Return */
67                 return FALSE;
68             case 4:
69             case 5:/* A/a */
70                 force = TRUE;
71                 break;
72             case 6:
73             case 7:/* S/s */
74                 skip_flg = TRUE;
75                 break;
76             }
77         }
78     }
79
80     if (noexec)
81         printf("EXTRACT %s\n", name);
82
83     return TRUE;
84 }
85
86 static boolean
87 make_name_with_pathcheck(char *name, size_t namesz, const char *q)
88 {
89     int offset = 0;
90     const char *p;
91     int sz;
92     struct stat stbuf;
93
94     if (extract_directory) {
95         sz = xsnprintf(name, namesz, "%s/", extract_directory);
96         if (sz == -1) {
97             return FALSE;
98         }
99         offset += sz;
100     }
101
102 #ifdef S_IFLNK
103     while ((p = strchr(q, '/')) != NULL) {
104         if (namesz - offset < (p - q) + 2) {
105             return FALSE;
106         }
107         memcpy(name + offset, q, (p - q));
108         name[offset + (p - q)] = 0;
109
110         offset += (p - q);
111         q = p + 1;
112
113         if (lstat(name, &stbuf) < 0) {
114             name[offset++] = '/';
115             break;
116         }
117         if (is_symlink(&stbuf)) {
118             return FALSE;
119         }
120         name[offset++] = '/';
121     }
122 #endif
123
124     str_safe_copy(name + offset, q, namesz - offset);
125
126     return TRUE;
127 }
128
129 /* ------------------------------------------------------------------------ */
130 static          boolean
131 make_parent_path(name)
132     char           *name;
133 {
134     char            path[FILENAME_LENGTH];
135     struct stat     stbuf;
136     register char  *p;
137
138     /* make parent directory name into PATH for recursive call */
139     str_safe_copy(path, name, sizeof(path));
140     for (p = path + strlen(path); p > path; p--)
141         if (p[-1] == '/') {
142             *--p = '\0';
143             break;
144         }
145
146     if (p == path) {
147         message("invalid path name \"%s\"", name);
148         return FALSE;   /* no more parent. */
149     }
150
151     if (GETSTAT(path, &stbuf) >= 0) {
152         if (is_directory(&stbuf))
153             return TRUE;
154     }
155
156     if (verbose)
157         message("Making directory \"%s\".", path);
158
159 #if defined __MINGW32__
160     if (mkdir(path) >= 0)
161         return TRUE;
162 #else
163     if (mkdir(path, 0777) >= 0) /* try */
164         return TRUE;    /* successful done. */
165 #endif
166
167     if (!make_parent_path(path))
168         return FALSE;
169
170 #if defined __MINGW32__
171     if (mkdir(path) < 0) {      /* try again */
172         error("Cannot make directory \"%s\"", path);
173         return FALSE;
174     }
175 #else
176     if (mkdir(path, 0777) < 0) {    /* try again */
177         error("Cannot make directory \"%s\"", path);
178         return FALSE;
179     }
180 #endif
181
182     return TRUE;
183 }
184
185 /* ------------------------------------------------------------------------ */
186 static FILE    *
187 open_with_make_path(name)
188     char           *name;
189 {
190     FILE           *fp;
191
192     if ((fp = fopen(name, WRITE_BINARY)) == NULL) {
193         if (!make_parent_path(name) ||
194             (fp = fopen(name, WRITE_BINARY)) == NULL)
195             error("Cannot extract a file \"%s\"", name);
196     }
197     return fp;
198 }
199
200 /* ------------------------------------------------------------------------ */
201 static int
202 symlink_with_make_path(realname, name)
203     const char     *realname;
204     const char     *name;
205 {
206     int l_code;
207
208     l_code = symlink(realname, name);
209     if (l_code < 0) {
210         make_parent_path(name);
211         l_code = symlink(realname, name);
212     }
213
214     return l_code;
215 }
216
217 /* ------------------------------------------------------------------------ */
218 static void
219 adjust_info(name, hdr)
220     char           *name;
221     LzHeader       *hdr;
222 {
223     struct utimbuf utimebuf;
224
225     /* adjust file stamp */
226     utimebuf.actime = utimebuf.modtime = hdr->unix_last_modified_stamp;
227
228     if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK)
229         utime(name, &utimebuf);
230
231     if (hdr->extend_type == EXTEND_UNIX
232         || hdr->extend_type == EXTEND_OS68K
233         || hdr->extend_type == EXTEND_XOSK) {
234
235         if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK) {
236             chmod(name, hdr->unix_mode);
237         }
238
239         if (!getuid()){
240             uid_t uid = hdr->unix_uid;
241             gid_t gid = hdr->unix_gid;
242
243 #if HAVE_GETPWNAM && HAVE_GETGRNAM
244             if (hdr->user[0]) {
245                 struct passwd *ent = getpwnam(hdr->user);
246                 if (ent) uid = ent->pw_uid;
247             }
248             if (hdr->group[0]) {
249                 struct group *ent = getgrnam(hdr->group);
250                 if (ent) gid = ent->gr_gid;
251             }
252 #endif
253
254 #if HAVE_LCHOWN
255             if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK)
256                 lchown(name, uid, gid);
257             else
258 #endif /* HAVE_LCHWON */
259                 chown(name, uid, gid);
260         }
261     }
262 #if __CYGWIN__
263     else {
264         /* On Cygwin, execute permission should be set for .exe or .dll. */
265         mode_t m;
266
267         umask(m = umask(0));    /* get current umask */
268         chmod(name, 0777 & ~m);
269     }
270 #endif
271 }
272
273 /* ------------------------------------------------------------------------ */
274 static off_t
275 extract_one(afp, hdr)
276     FILE           *afp;    /* archive file */
277     LzHeader       *hdr;
278 {
279     FILE           *fp; /* output file */
280 #if HAVE_LIBAPPLEFILE
281     FILE           *tfp; /* temporary output file */
282 #endif
283     struct stat     stbuf;
284     char            name[FILENAME_LENGTH];
285     unsigned int crc;
286     int             method;
287     boolean         save_quiet, save_verbose, up_flag;
288     char           *q = hdr->name, c;
289     off_t read_size = 0;
290
291     if (ignore_directory && strrchr(hdr->name, '/')) {
292         q = (char *) strrchr(hdr->name, '/') + 1;
293     }
294     else {
295         if (is_directory_traversal(q)) {
296             error("Possible directory traversal hack attempt in %s", q);
297             exit(1);
298         }
299
300         if (*q == '/') {
301             while (*q == '/') { q++; }
302
303             /*
304              * if OSK then strip device name
305              */
306             if (hdr->extend_type == EXTEND_OS68K
307                 || hdr->extend_type == EXTEND_XOSK) {
308                 do
309                     c = (*q++);
310                 while (c && c != '/');
311                 if (!c || !*q)
312                     q = ".";    /* if device name only */
313             }
314         }
315     }
316
317     if (!make_name_with_pathcheck(name, sizeof(name), q)) {
318         error("Possible symlink traversal hack attempt in %s", q);
319         exit(1);
320     }
321
322     /* LZHDIRS_METHODを持つヘッダをチェックする */
323     /* 1999.4.30 t.okamoto */
324     for (method = 0;; method++) {
325         if (methods[method] == NULL) {
326             error("Unknown method \"%.*s\"; \"%s\" will be skipped ...",
327                   5, hdr->method, name);
328             return read_size;
329         }
330         if (memcmp(hdr->method, methods[method], 5) == 0)
331             break;
332     }
333
334     if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_REGULAR
335         && method != LZHDIRS_METHOD_NUM) {
336     extract_regular:
337 #if 0
338         for (method = 0;; method++) {
339             if (methods[method] == NULL) {
340                 error("Unknown method \"%.*s\"; \"%s\" will be skipped ...",
341                       5, hdr->method, name);
342                 return read_size;
343             }
344             if (memcmp(hdr->method, methods[method], 5) == 0)
345                 break;
346         }
347 #endif
348
349         reading_filename = archive_name;
350         writing_filename = name;
351         if (output_to_stdout || verify_mode) {
352             /* "Icon\r" should be a resource fork file encoded in MacBinary
353                format, so that it should be skipped. */
354             if (hdr->extend_type == EXTEND_MACOS
355                 && strcmp(basename(name), "Icon\r") == 0
356                 && decode_macbinary_contents) {
357                 return read_size;
358             }
359
360             if (noexec) {
361                 printf("%s %s\n", verify_mode ? "VERIFY" : "EXTRACT", name);
362                 return read_size;
363             }
364
365             save_quiet = quiet;
366             save_verbose = verbose;
367             if (!quiet && output_to_stdout) {
368                 printf("::::::::\n%s\n::::::::\n", name);
369                 quiet = TRUE;
370                 verbose = FALSE;
371             }
372             else if (verify_mode) {
373                 quiet = FALSE;
374                 verbose = TRUE;
375             }
376
377 #if defined(__MINGW32__) || defined(__DJGPP__)
378             {
379                 int old_mode;
380                 fflush(stdout);
381                 old_mode = setmode(fileno(stdout), O_BINARY);
382 #endif
383
384 #if HAVE_LIBAPPLEFILE
385             /* On default, MacLHA encodes into MacBinary. */
386             if (hdr->extend_type == EXTEND_MACOS && !verify_mode && decode_macbinary_contents) {
387                 /* build temporary file */
388                 tfp = NULL; /* avoid compiler warnings `uninitialized' */
389                 tfp = build_temporary_file();
390
391                 crc = decode_lzhuf(afp, tfp,
392                                    hdr->original_size, hdr->packed_size,
393                                    name, method, &read_size);
394                 fclose(tfp);
395                 decode_macbinary(stdout, hdr->original_size, name);
396                 unlink(temporary_name);
397             } else {
398                 crc = decode_lzhuf(afp, stdout,
399                                    hdr->original_size, hdr->packed_size,
400                                    name, method, &read_size);
401             }
402 #else
403             crc = decode_lzhuf(afp, stdout,
404                                hdr->original_size, hdr->packed_size,
405                                name, method, &read_size);
406 #endif /* HAVE_LIBAPPLEFILE */
407 #if defined(__MINGW32__) || defined(__DJGPP__)
408                 fflush(stdout);
409                 setmode(fileno(stdout), old_mode);
410             }
411 #endif
412             quiet = save_quiet;
413             verbose = save_verbose;
414         }
415         else {
416 #ifndef __APPLE__
417             /* "Icon\r" should be a resource fork of parent folder's icon,
418                so that it can be skipped when system is not Mac OS X. */
419             if (hdr->extend_type == EXTEND_MACOS
420                 && strcmp(basename(name), "Icon\r") == 0
421                 && decode_macbinary_contents) {
422                 make_parent_path(name); /* create directory only */
423                 return read_size;
424             }
425 #endif /* __APPLE__ */
426             if (skip_flg == FALSE)  {
427                 up_flag = inquire_extract(name);
428                 if (up_flag == FALSE && force == FALSE) {
429                     return read_size;
430                 }
431             }
432
433             if (skip_flg == TRUE) { /* if skip_flg */
434                 if (stat(name, &stbuf) == 0 && force != TRUE) {
435                     /* if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) {*/
436                         if (quiet != TRUE)
437                             printf("%s : Skipped...\n", name);
438                         return read_size;
439                     /* } */
440                 }
441             }
442             if (noexec) {
443                 return read_size;
444             }
445
446             signal(SIGINT, interrupt);
447 #ifdef SIGHUP
448             signal(SIGHUP, interrupt);
449 #endif
450
451             unlink(name);
452             remove_extracting_file_when_interrupt = TRUE;
453
454             if ((fp = open_with_make_path(name)) != NULL) {
455 #if HAVE_LIBAPPLEFILE
456                 if (hdr->extend_type == EXTEND_MACOS && !verify_mode && decode_macbinary_contents) {
457                     /* build temporary file */
458                     tfp = NULL; /* avoid compiler warnings `uninitialized' */
459                     tfp = build_temporary_file();
460
461                     crc = decode_lzhuf(afp, tfp,
462                                        hdr->original_size, hdr->packed_size,
463                                        name, method, &read_size);
464                     fclose(tfp);
465                     decode_macbinary(fp, hdr->original_size, name);
466 #ifdef __APPLE__
467                     /* TODO: set resource fork */
468                     /* after processing, "Icon\r" is not needed. */
469                     if (strcmp(basename(name), "Icon\r") == 0) {
470                         unlink(name);
471                     }
472 #endif /* __APPLE__ */
473                     unlink(temporary_name);
474                 } else {
475                     crc = decode_lzhuf(afp, fp,
476                                        hdr->original_size, hdr->packed_size,
477                                        name, method, &read_size);
478                 }
479 #else /* HAVE_LIBAPPLEFILE */
480                 crc = decode_lzhuf(afp, fp,
481                                    hdr->original_size, hdr->packed_size,
482                                    name, method, &read_size);
483 #endif /* HAVE_LIBAPPLEFILE */
484                 fclose(fp);
485             }
486             remove_extracting_file_when_interrupt = FALSE;
487             signal(SIGINT, SIG_DFL);
488 #ifdef SIGHUP
489             signal(SIGHUP, SIG_DFL);
490 #endif
491             if (!fp)
492                 return read_size;
493         }
494
495         if (hdr->has_crc && crc != hdr->crc)
496             error("CRC error: \"%s\"", name);
497     }
498     else if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_DIRECTORY
499              || (hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK
500              || method == LZHDIRS_METHOD_NUM) {
501         /* ↑これで、Symbolic Link は、大丈夫か? */
502         if (!ignore_directory && !verify_mode && !output_to_stdout) {
503             if (noexec) {
504                 if (quiet != TRUE)
505                     printf("EXTRACT %s (directory)\n", name);
506                 return read_size;
507             }
508             /* NAME has trailing SLASH '/', (^_^) */
509             if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK) {
510                 int             l_code;
511
512 #ifdef S_IFLNK
513                 if (skip_flg == FALSE)  {
514                     up_flag = inquire_extract(name);
515                     if (up_flag == FALSE && force == FALSE) {
516                         return read_size;
517                     }
518                 }
519
520                 if (skip_flg == TRUE) { /* if skip_flg */
521                     if (GETSTAT(name, &stbuf) == 0 && force != TRUE) {
522                         /* if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) { */
523                             if (quiet != TRUE)
524                                 printf("%s : Skipped...\n", name);
525                             return read_size;
526                         /* } */
527                     }
528                 }
529
530                 unlink(name);
531                 l_code = symlink_with_make_path(hdr->realname, name);
532                 if (l_code < 0) {
533                     if (quiet != TRUE)
534                         warning("Can't make Symbolic Link \"%s\" -> \"%s\"",
535                                 name, hdr->realname);
536                 }
537                 if (quiet != TRUE) {
538                     message("Symbolic Link %s -> %s",
539                             name, hdr->realname);
540                 }
541 #else
542                 warning("Can't make Symbolic Link %s -> %s",
543                         name, hdr->realname);
544                 return read_size;
545 #endif
546             }
547             else { /* make directory */
548                 if (!make_parent_path(name))
549                     return read_size;
550                 /* save directory information */
551                 add_dirinfo(name, hdr);
552             }
553         }
554     }
555     else {
556         if (force)              /* force extract */
557             goto extract_regular;
558         else
559             error("Unknown file type: \"%s\". use `f' option to force extract.", name);
560     }
561
562     if (!output_to_stdout && !verify_mode) {
563         if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_DIRECTORY)
564             adjust_info(name, hdr);
565     }
566
567     return read_size;
568 }
569
570 static int
571 skip_to_nextpos(FILE *fp, off_t pos, off_t off, off_t read_size)
572 {
573     if (pos != -1) {
574         if (fseeko(fp, pos + off, SEEK_SET) != 0) {
575             return -1;
576         }
577     }
578     else {
579         off_t i = off - read_size;
580         while (i--) {
581             if (fgetc(fp) == EOF) {
582                 return -1;
583             }
584         }
585     }
586     return 0;
587 }
588
589 /* ------------------------------------------------------------------------ */
590 /* EXTRACT COMMAND MAIN                                                     */
591 /* ------------------------------------------------------------------------ */
592 void
593 cmd_extract()
594 {
595     LzHeader        hdr;
596     off_t           pos;
597     FILE           *afp;
598     off_t read_size;
599
600     /* open archive file */
601     if ((afp = open_old_archive()) == NULL)
602         fatal_error("Cannot open archive file \"%s\"", archive_name);
603
604     if (archive_is_msdos_sfx1(archive_name))
605         seek_lha_header(afp);
606
607     /* extract each files */
608     while (get_header(afp, &hdr)) {
609         pos = ftello(afp);
610         if (need_file(hdr.name)) {
611             read_size = extract_one(afp, &hdr);
612             if (read_size != hdr.packed_size) {
613                 /* when error occurred in extract_one(), should adjust
614                    point of file stream */
615                 if (skip_to_nextpos(afp, pos, hdr.packed_size, read_size) == -1) {
616                     fatal_error("Cannot seek to next header position from \"%s\"", hdr.name);
617                 }
618             }
619         } else {
620             if (skip_to_nextpos(afp, pos, hdr.packed_size, 0) == -1) {
621                 fatal_error("Cannot seek to next header position from \"%s\"", hdr.name);
622             }
623         }
624     }
625
626     /* close archive file */
627     fclose(afp);
628
629     /* adjust directory information */
630     adjust_dirinfo();
631
632     return;
633 }
634
635 int
636 is_directory_traversal(char *path)
637 {
638     int state = 0;
639
640     for (; *path; path++) {
641         switch (state) {
642         case 0:
643             if (*path == '.') state = 1;
644             else state = 3;
645             break;
646         case 1:
647             if (*path == '.') state = 2;
648             else if (*path == '/') state = 0;
649             else state = 3;
650             break;
651         case 2:
652             if (*path == '/') return 1;
653             else state = 3;
654             break;
655         case 3:
656             if (*path == '/') state = 0;
657             break;
658         }
659     }
660
661     return state == 2;
662 }
663
664 /*
665  * restore directory information (timestamp, permission and uid/gid).
666  * added by A.Iriyama  2003.12.12
667  */
668
669 typedef struct LzHeaderList_t {
670     struct LzHeaderList_t *next;
671     LzHeader hdr;
672 } LzHeaderList;
673
674 static LzHeaderList *dirinfo;
675
676 static void add_dirinfo(char *name, LzHeader *hdr)
677 {
678     LzHeaderList *p, *tmp, top;
679
680     if (memcmp(hdr->method, LZHDIRS_METHOD, 5) != 0)
681         return;
682
683     p = xmalloc(sizeof(LzHeaderList));
684
685     memcpy(&p->hdr, hdr, sizeof(LzHeader));
686     strncpy(p->hdr.name, name, sizeof(p->hdr.name));
687     p->hdr.name[sizeof(p->hdr.name)-1] = 0;
688
689 #if 0
690     /* push front */
691     {
692         tmp = dirinfo;
693         dirinfo = p;
694         dirinfo->next = tmp;
695     }
696 #else
697
698     /*
699       reverse sorted by pathname order
700
701          p->hdr.name = "a"
702
703          dirinfo->hdr.name             = "a/b/d"
704          dirinfo->next->hdr.name       = "a/b/c"
705          dirinfo->next->next->hdr.name = "a/b"
706
707        result:
708
709          dirinfo->hdr.name                   = "a/b/d"
710          dirinfo->next->hdr.name             = "a/b/c"
711          dirinfo->next->next->hdr.name       = "a/b"
712          dirinfo->next->next->next->hdr.name = "a"
713     */
714
715     top.next = dirinfo;
716
717     for (tmp = &top; tmp->next; tmp = tmp->next) {
718         if (strcmp(p->hdr.name, tmp->next->hdr.name) > 0) {
719             p->next = tmp->next;
720             tmp->next = p;
721             break;
722         }
723     }
724     if (tmp->next == NULL) {
725         p->next = NULL;
726         tmp->next = p;
727     }
728
729     dirinfo = top.next;
730 #endif
731 }
732
733 static void adjust_dirinfo()
734 {
735     while (dirinfo) {
736         /* message("adjusting [%s]", dirinfo->hdr.name); */
737         adjust_info(dirinfo->hdr.name, &dirinfo->hdr);
738
739         {
740             LzHeaderList *tmp = dirinfo;
741             dirinfo = dirinfo->next;
742             free(tmp);
743         }
744     }
745 }
746
747 #if HAVE_LIBAPPLEFILE
748 static boolean
749 decode_macbinary(ofp, size, outPath)
750     FILE *ofp;
751     off_t size;
752     const char *outPath;
753 {
754     af_file_t *afp = NULL;
755     FILE *ifp = NULL;
756     unsigned char *datap;
757     size_t dlen;
758
759     if ((afp = af_open(temporary_name)) != NULL) {
760         /* fetch datafork */
761         datap = af_data(afp, &dlen);
762         fwrite(datap, sizeof(unsigned char), dlen, ofp);
763         af_close(afp);
764         return TRUE;
765     } else { /* it may be not encoded in MacBinary */
766         /* try to copy */
767         if ((ifp = fopen(temporary_name, READ_BINARY)) == NULL) {
768             error("Cannot open a temporary file \"%s\"", temporary_name);
769             return FALSE;
770         }
771         copyfile(ifp, ofp, size, 0, 0);
772         fclose(ifp);
773         return TRUE;
774     }
775
776     return FALSE;
777 }
778 #endif /* HAVE_LIBAPPLEFILE */