Imported Upstream version 1.4.16
[platform/upstream/m4.git] / lib / clean-temp.c
1 /* Temporary directories and temporary files with automatic cleanup.
2    Copyright (C) 2001, 2003, 2006-2007, 2009-2011 Free Software Foundation,
3    Inc.
4    Written by Bruno Haible <bruno@clisp.org>, 2006.
5
6    This program is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19
20 #include <config.h>
21
22 /* Specification.  */
23 #include "clean-temp.h"
24
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <limits.h>
28 #include <stdbool.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
34 # define WIN32_LEAN_AND_MEAN  /* avoid including junk */
35 # include <windows.h>
36 #endif
37
38 #include "error.h"
39 #include "fatal-signal.h"
40 #include "pathmax.h"
41 #include "tmpdir.h"
42 #include "xalloc.h"
43 #include "xmalloca.h"
44 #include "gl_xlist.h"
45 #include "gl_linkedhash_list.h"
46 #include "gettext.h"
47 #if GNULIB_FWRITEERROR
48 # include "fwriteerror.h"
49 #endif
50 #if GNULIB_CLOSE_STREAM
51 # include "close-stream.h"
52 #endif
53 #if GNULIB_FCNTL_SAFER
54 # include "fcntl--.h"
55 #endif
56 #if GNULIB_FOPEN_SAFER
57 # include "stdio--.h"
58 #endif
59
60 #define _(str) gettext (str)
61
62 /* GNU Hurd doesn't have PATH_MAX.  */
63 #ifndef PATH_MAX
64 # ifdef MAXPATHLEN
65 #  define PATH_MAX MAXPATHLEN
66 # else
67 #  define PATH_MAX 1024
68 # endif
69 #endif
70
71 #ifndef uintptr_t
72 # define uintptr_t unsigned long
73 #endif
74
75 #if !GNULIB_FCNTL_SAFER
76 /* The results of open() in this file are not used with fchdir,
77    therefore save some unnecessary work in fchdir.c.  */
78 # undef open
79 # undef close
80 #endif
81
82
83 /* The use of 'volatile' in the types below (and ISO C 99 section 5.1.2.3.(5))
84    ensure that while constructing or modifying the data structures, the field
85    values are written to memory in the order of the C statements.  So the
86    signal handler can rely on these field values to be up to date.  */
87
88
89 /* Registry for a single temporary directory.
90    'struct temp_dir' from the public header file overlaps with this.  */
91 struct tempdir
92 {
93   /* The absolute pathname of the directory.  */
94   char * volatile dirname;
95   /* Whether errors during explicit cleanup are reported to standard error.  */
96   bool cleanup_verbose;
97   /* Absolute pathnames of subdirectories.  */
98   gl_list_t /* <char *> */ volatile subdirs;
99   /* Absolute pathnames of files.  */
100   gl_list_t /* <char *> */ volatile files;
101 };
102
103 /* List of all temporary directories.  */
104 static struct
105 {
106   struct tempdir * volatile * volatile tempdir_list;
107   size_t volatile tempdir_count;
108   size_t tempdir_allocated;
109 } cleanup_list /* = { NULL, 0, 0 } */;
110
111 /* List of all open file descriptors to temporary files.  */
112 static gl_list_t /* <int> */ volatile descriptors;
113
114
115 /* For the subdirs and for the files, we use a gl_list_t of type LINKEDHASH.
116    Why?  We need a data structure that
117
118      1) Can contain an arbitrary number of 'char *' values.  The strings
119         are compared via strcmp, not pointer comparison.
120      2) Has insertion and deletion operations that are fast: ideally O(1),
121         or possibly O(log n).  This is important for GNU sort, which may
122         create a large number of temporary files.
123      3) Allows iteration through all elements from within a signal handler.
124      4) May or may not allow duplicates.  It doesn't matter here, since
125         any file or subdir can only be removed once.
126
127    Criterion 1) would allow any gl_list_t or gl_oset_t implementation.
128
129    Criterion 2) leaves only GL_LINKEDHASH_LIST, GL_TREEHASH_LIST, or
130    GL_TREE_OSET.
131
132    Criterion 3) puts at disadvantage GL_TREEHASH_LIST and GL_TREE_OSET.
133    Namely, iteration through the elements of a binary tree requires access
134    to many ->left, ->right, ->parent pointers. However, the rebalancing
135    code for insertion and deletion in an AVL or red-black tree is so
136    complicated that we cannot assume that >left, ->right, ->parent pointers
137    are in a consistent state throughout these operations.  Therefore, to
138    avoid a crash in the signal handler, all destructive operations to the
139    lists would have to be protected by a
140        block_fatal_signals ();
141        ...
142        unblock_fatal_signals ();
143    pair.  Which causes extra system calls.
144
145    Criterion 3) would also discourage GL_ARRAY_LIST and GL_CARRAY_LIST,
146    if they were not already excluded.  Namely, these implementations use
147    xrealloc(), leaving a time window in which in the list->elements pointer
148    points to already deallocated memory.  To avoid a crash in the signal
149    handler at such a moment, all destructive operations would have to
150    protected by block/unblock_fatal_signals (), in this case too.
151
152    A list of type GL_LINKEDHASH_LIST without duplicates fulfills all
153    requirements:
154      2) Insertion and deletion are O(1) on average.
155      3) The gl_list_iterator, gl_list_iterator_next implementations do
156         not trigger memory allocations, nor other system calls, and are
157         therefore safe to be called from a signal handler.
158         Furthermore, since SIGNAL_SAFE_LIST is defined, the implementation
159         of the destructive functions ensures that the list structure is
160         safe to be traversed at any moment, even when interrupted by an
161         asynchronous signal.
162  */
163
164 /* String equality and hash code functions used by the lists.  */
165
166 static bool
167 string_equals (const void *x1, const void *x2)
168 {
169   const char *s1 = (const char *) x1;
170   const char *s2 = (const char *) x2;
171   return strcmp (s1, s2) == 0;
172 }
173
174 #define SIZE_BITS (sizeof (size_t) * CHAR_BIT)
175
176 /* A hash function for NUL-terminated char* strings using
177    the method described by Bruno Haible.
178    See http://www.haible.de/bruno/hashfunc.html.  */
179 static size_t
180 string_hash (const void *x)
181 {
182   const char *s = (const char *) x;
183   size_t h = 0;
184
185   for (; *s; s++)
186     h = *s + ((h << 9) | (h >> (SIZE_BITS - 9)));
187
188   return h;
189 }
190
191
192 /* The signal handler.  It gets called asynchronously.  */
193 static void
194 cleanup ()
195 {
196   size_t i;
197
198   /* First close all file descriptors to temporary files.  */
199   {
200     gl_list_t fds = descriptors;
201
202     if (fds != NULL)
203       {
204         gl_list_iterator_t iter;
205         const void *element;
206
207         iter = gl_list_iterator (fds);
208         while (gl_list_iterator_next (&iter, &element, NULL))
209           {
210             int fd = (int) (uintptr_t) element;
211             close (fd);
212           }
213         gl_list_iterator_free (&iter);
214       }
215   }
216
217   for (i = 0; i < cleanup_list.tempdir_count; i++)
218     {
219       struct tempdir *dir = cleanup_list.tempdir_list[i];
220
221       if (dir != NULL)
222         {
223           gl_list_iterator_t iter;
224           const void *element;
225
226           /* First cleanup the files in the subdirectories.  */
227           iter = gl_list_iterator (dir->files);
228           while (gl_list_iterator_next (&iter, &element, NULL))
229             {
230               const char *file = (const char *) element;
231               unlink (file);
232             }
233           gl_list_iterator_free (&iter);
234
235           /* Then cleanup the subdirectories.  */
236           iter = gl_list_iterator (dir->subdirs);
237           while (gl_list_iterator_next (&iter, &element, NULL))
238             {
239               const char *subdir = (const char *) element;
240               rmdir (subdir);
241             }
242           gl_list_iterator_free (&iter);
243
244           /* Then cleanup the temporary directory itself.  */
245           rmdir (dir->dirname);
246         }
247     }
248 }
249
250 /* Create a temporary directory.
251    PREFIX is used as a prefix for the name of the temporary directory. It
252    should be short and still give an indication about the program.
253    PARENTDIR can be used to specify the parent directory; if NULL, a default
254    parent directory is used (either $TMPDIR or /tmp or similar).
255    CLEANUP_VERBOSE determines whether errors during explicit cleanup are
256    reported to standard error.
257    Return a fresh 'struct temp_dir' on success.  Upon error, an error message
258    is shown and NULL is returned.  */
259 struct temp_dir *
260 create_temp_dir (const char *prefix, const char *parentdir,
261                  bool cleanup_verbose)
262 {
263   struct tempdir * volatile *tmpdirp = NULL;
264   struct tempdir *tmpdir;
265   size_t i;
266   char *xtemplate;
267   char *tmpdirname;
268
269   /* See whether it can take the slot of an earlier temporary directory
270      already cleaned up.  */
271   for (i = 0; i < cleanup_list.tempdir_count; i++)
272     if (cleanup_list.tempdir_list[i] == NULL)
273       {
274         tmpdirp = &cleanup_list.tempdir_list[i];
275         break;
276       }
277   if (tmpdirp == NULL)
278     {
279       /* See whether the array needs to be extended.  */
280       if (cleanup_list.tempdir_count == cleanup_list.tempdir_allocated)
281         {
282           /* Note that we cannot use xrealloc(), because then the cleanup()
283              function could access an already deallocated array.  */
284           struct tempdir * volatile *old_array = cleanup_list.tempdir_list;
285           size_t old_allocated = cleanup_list.tempdir_allocated;
286           size_t new_allocated = 2 * cleanup_list.tempdir_allocated + 1;
287           struct tempdir * volatile *new_array =
288             XNMALLOC (new_allocated, struct tempdir * volatile);
289
290           if (old_allocated == 0)
291             /* First use of this facility.  Register the cleanup handler.  */
292             at_fatal_signal (&cleanup);
293           else
294             {
295               /* Don't use memcpy() here, because memcpy takes non-volatile
296                  arguments and is therefore not guaranteed to complete all
297                  memory stores before the next statement.  */
298               size_t k;
299
300               for (k = 0; k < old_allocated; k++)
301                 new_array[k] = old_array[k];
302             }
303
304           cleanup_list.tempdir_list = new_array;
305           cleanup_list.tempdir_allocated = new_allocated;
306
307           /* Now we can free the old array.  */
308           if (old_array != NULL)
309             free ((struct tempdir **) old_array);
310         }
311
312       tmpdirp = &cleanup_list.tempdir_list[cleanup_list.tempdir_count];
313       /* Initialize *tmpdirp before incrementing tempdir_count, so that
314          cleanup() will skip this entry before it is fully initialized.  */
315       *tmpdirp = NULL;
316       cleanup_list.tempdir_count++;
317     }
318
319   /* Initialize a 'struct tempdir'.  */
320   tmpdir = XMALLOC (struct tempdir);
321   tmpdir->dirname = NULL;
322   tmpdir->cleanup_verbose = cleanup_verbose;
323   tmpdir->subdirs = gl_list_create_empty (GL_LINKEDHASH_LIST,
324                                           string_equals, string_hash, NULL,
325                                           false);
326   tmpdir->files = gl_list_create_empty (GL_LINKEDHASH_LIST,
327                                         string_equals, string_hash, NULL,
328                                         false);
329
330   /* Create the temporary directory.  */
331   xtemplate = (char *) xmalloca (PATH_MAX);
332   if (path_search (xtemplate, PATH_MAX, parentdir, prefix, parentdir == NULL))
333     {
334       error (0, errno,
335              _("cannot find a temporary directory, try setting $TMPDIR"));
336       goto quit;
337     }
338   block_fatal_signals ();
339   tmpdirname = mkdtemp (xtemplate);
340   if (tmpdirname != NULL)
341     {
342       tmpdir->dirname = tmpdirname;
343       *tmpdirp = tmpdir;
344     }
345   unblock_fatal_signals ();
346   if (tmpdirname == NULL)
347     {
348       error (0, errno,
349              _("cannot create a temporary directory using template \"%s\""),
350              xtemplate);
351       goto quit;
352     }
353   /* Replace tmpdir->dirname with a copy that has indefinite extent.
354      We cannot do this inside the block_fatal_signals/unblock_fatal_signals
355      block because then the cleanup handler would not remove the directory
356      if xstrdup fails.  */
357   tmpdir->dirname = xstrdup (tmpdirname);
358   freea (xtemplate);
359   return (struct temp_dir *) tmpdir;
360
361  quit:
362   freea (xtemplate);
363   return NULL;
364 }
365
366 /* Register the given ABSOLUTE_FILE_NAME as being a file inside DIR, that
367    needs to be removed before DIR can be removed.
368    Should be called before the file ABSOLUTE_FILE_NAME is created.  */
369 void
370 register_temp_file (struct temp_dir *dir,
371                     const char *absolute_file_name)
372 {
373   struct tempdir *tmpdir = (struct tempdir *)dir;
374
375   /* Add absolute_file_name to tmpdir->files, without duplicates.  */
376   if (gl_list_search (tmpdir->files, absolute_file_name) == NULL)
377     gl_list_add_first (tmpdir->files, xstrdup (absolute_file_name));
378 }
379
380 /* Unregister the given ABSOLUTE_FILE_NAME as being a file inside DIR, that
381    needs to be removed before DIR can be removed.
382    Should be called when the file ABSOLUTE_FILE_NAME could not be created.  */
383 void
384 unregister_temp_file (struct temp_dir *dir,
385                       const char *absolute_file_name)
386 {
387   struct tempdir *tmpdir = (struct tempdir *)dir;
388   gl_list_t list = tmpdir->files;
389   gl_list_node_t node;
390
391   node = gl_list_search (list, absolute_file_name);
392   if (node != NULL)
393     {
394       char *old_string = (char *) gl_list_node_value (list, node);
395
396       gl_list_remove_node (list, node);
397       free (old_string);
398     }
399 }
400
401 /* Register the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR,
402    that needs to be removed before DIR can be removed.
403    Should be called before the subdirectory ABSOLUTE_DIR_NAME is created.  */
404 void
405 register_temp_subdir (struct temp_dir *dir,
406                       const char *absolute_dir_name)
407 {
408   struct tempdir *tmpdir = (struct tempdir *)dir;
409
410   /* Add absolute_dir_name to tmpdir->subdirs, without duplicates.  */
411   if (gl_list_search (tmpdir->subdirs, absolute_dir_name) == NULL)
412     gl_list_add_first (tmpdir->subdirs, xstrdup (absolute_dir_name));
413 }
414
415 /* Unregister the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR,
416    that needs to be removed before DIR can be removed.
417    Should be called when the subdirectory ABSOLUTE_DIR_NAME could not be
418    created.  */
419 void
420 unregister_temp_subdir (struct temp_dir *dir,
421                         const char *absolute_dir_name)
422 {
423   struct tempdir *tmpdir = (struct tempdir *)dir;
424   gl_list_t list = tmpdir->subdirs;
425   gl_list_node_t node;
426
427   node = gl_list_search (list, absolute_dir_name);
428   if (node != NULL)
429     {
430       char *old_string = (char *) gl_list_node_value (list, node);
431
432       gl_list_remove_node (list, node);
433       free (old_string);
434     }
435 }
436
437 /* Remove a file, with optional error message.
438    Return 0 upon success, or -1 if there was some problem.  */
439 static int
440 do_unlink (struct temp_dir *dir, const char *absolute_file_name)
441 {
442   if (unlink (absolute_file_name) < 0 && dir->cleanup_verbose
443       && errno != ENOENT)
444     {
445       error (0, errno, _("cannot remove temporary file %s"), absolute_file_name);
446       return -1;
447     }
448   return 0;
449 }
450
451 /* Remove a directory, with optional error message.
452    Return 0 upon success, or -1 if there was some problem.  */
453 static int
454 do_rmdir (struct temp_dir *dir, const char *absolute_dir_name)
455 {
456   if (rmdir (absolute_dir_name) < 0 && dir->cleanup_verbose
457       && errno != ENOENT)
458     {
459       error (0, errno,
460              _("cannot remove temporary directory %s"), absolute_dir_name);
461       return -1;
462     }
463   return 0;
464 }
465
466 /* Remove the given ABSOLUTE_FILE_NAME and unregister it.
467    Return 0 upon success, or -1 if there was some problem.  */
468 int
469 cleanup_temp_file (struct temp_dir *dir,
470                    const char *absolute_file_name)
471 {
472   int err;
473
474   err = do_unlink (dir, absolute_file_name);
475   unregister_temp_file (dir, absolute_file_name);
476
477   return err;
478 }
479
480 /* Remove the given ABSOLUTE_DIR_NAME and unregister it.
481    Return 0 upon success, or -1 if there was some problem.  */
482 int
483 cleanup_temp_subdir (struct temp_dir *dir,
484                      const char *absolute_dir_name)
485 {
486   int err;
487
488   err = do_rmdir (dir, absolute_dir_name);
489   unregister_temp_subdir (dir, absolute_dir_name);
490
491   return err;
492 }
493
494 /* Remove all registered files and subdirectories inside DIR.
495    Return 0 upon success, or -1 if there was some problem.  */
496 int
497 cleanup_temp_dir_contents (struct temp_dir *dir)
498 {
499   struct tempdir *tmpdir = (struct tempdir *)dir;
500   int err = 0;
501   gl_list_t list;
502   gl_list_iterator_t iter;
503   const void *element;
504   gl_list_node_t node;
505
506   /* First cleanup the files in the subdirectories.  */
507   list = tmpdir->files;
508   iter = gl_list_iterator (list);
509   while (gl_list_iterator_next (&iter, &element, &node))
510     {
511       char *file = (char *) element;
512
513       err |= do_unlink (dir, file);
514       gl_list_remove_node (list, node);
515       /* Now only we can free file.  */
516       free (file);
517     }
518   gl_list_iterator_free (&iter);
519
520   /* Then cleanup the subdirectories.  */
521   list = tmpdir->subdirs;
522   iter = gl_list_iterator (list);
523   while (gl_list_iterator_next (&iter, &element, &node))
524     {
525       char *subdir = (char *) element;
526
527       err |= do_rmdir (dir, subdir);
528       gl_list_remove_node (list, node);
529       /* Now only we can free subdir.  */
530       free (subdir);
531     }
532   gl_list_iterator_free (&iter);
533
534   return err;
535 }
536
537 /* Remove all registered files and subdirectories inside DIR and DIR itself.
538    DIR cannot be used any more after this call.
539    Return 0 upon success, or -1 if there was some problem.  */
540 int
541 cleanup_temp_dir (struct temp_dir *dir)
542 {
543   struct tempdir *tmpdir = (struct tempdir *)dir;
544   int err = 0;
545   size_t i;
546
547   err |= cleanup_temp_dir_contents (dir);
548   err |= do_rmdir (dir, tmpdir->dirname);
549
550   for (i = 0; i < cleanup_list.tempdir_count; i++)
551     if (cleanup_list.tempdir_list[i] == tmpdir)
552       {
553         /* Remove cleanup_list.tempdir_list[i].  */
554         if (i + 1 == cleanup_list.tempdir_count)
555           {
556             while (i > 0 && cleanup_list.tempdir_list[i - 1] == NULL)
557               i--;
558             cleanup_list.tempdir_count = i;
559           }
560         else
561           cleanup_list.tempdir_list[i] = NULL;
562         /* Now only we can free the tmpdir->dirname and tmpdir itself.  */
563         free (tmpdir->dirname);
564         free (tmpdir);
565         return err;
566       }
567
568   /* The user passed an invalid DIR argument.  */
569   abort ();
570 }
571
572
573 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
574
575 /* On Windows, opening a file with _O_TEMPORARY has the effect of passing
576    the FILE_FLAG_DELETE_ON_CLOSE flag to CreateFile(), which has the effect
577    of deleting the file when it is closed - even when the program crashes.
578    But (according to the Cygwin sources) it works only on Windows NT or newer.
579    So we cache the info whether we are running on Windows NT or newer.  */
580
581 static bool
582 supports_delete_on_close ()
583 {
584   static int known; /* 1 = yes, -1 = no, 0 = unknown */
585   if (!known)
586     {
587       OSVERSIONINFO v;
588
589       if (GetVersionEx (&v))
590         known = (v.dwPlatformId == VER_PLATFORM_WIN32_NT ? 1 : -1);
591       else
592         known = -1;
593     }
594   return (known > 0);
595 }
596
597 #endif
598
599
600 /* Register a file descriptor to be closed.  */
601 static void
602 register_fd (int fd)
603 {
604   if (descriptors == NULL)
605     descriptors = gl_list_create_empty (GL_LINKEDHASH_LIST, NULL, NULL, NULL,
606                                         false);
607   gl_list_add_first (descriptors, (void *) (uintptr_t) fd);
608 }
609
610 /* Unregister a file descriptor to be closed.  */
611 static void
612 unregister_fd (int fd)
613 {
614   gl_list_t fds = descriptors;
615   gl_list_node_t node;
616
617   if (fds == NULL)
618     /* descriptors should already contain fd.  */
619     abort ();
620   node = gl_list_search (fds, (void *) (uintptr_t) fd);
621   if (node == NULL)
622     /* descriptors should already contain fd.  */
623     abort ();
624   gl_list_remove_node (fds, node);
625 }
626
627 /* Open a temporary file in a temporary directory.
628    Registers the resulting file descriptor to be closed.  */
629 int
630 open_temp (const char *file_name, int flags, mode_t mode)
631 {
632   int fd;
633   int saved_errno;
634
635   block_fatal_signals ();
636   /* Note: 'open' here is actually open() or open_safer().  */
637 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
638   /* Use _O_TEMPORARY when possible, to increase the chances that the
639      temporary file is removed when the process crashes.  */
640   if (supports_delete_on_close ())
641     fd = open (file_name, flags | _O_TEMPORARY, mode);
642   else
643 #endif
644     fd = open (file_name, flags, mode);
645   saved_errno = errno;
646   if (fd >= 0)
647     register_fd (fd);
648   unblock_fatal_signals ();
649   errno = saved_errno;
650   return fd;
651 }
652
653 /* Open a temporary file in a temporary directory.
654    Registers the resulting file descriptor to be closed.  */
655 FILE *
656 fopen_temp (const char *file_name, const char *mode)
657 {
658   FILE *fp;
659   int saved_errno;
660
661   block_fatal_signals ();
662   /* Note: 'fopen' here is actually fopen() or fopen_safer().  */
663 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
664   /* Use _O_TEMPORARY when possible, to increase the chances that the
665      temporary file is removed when the process crashes.  */
666   if (supports_delete_on_close ())
667     {
668       size_t mode_len = strlen (mode);
669       char *augmented_mode = (char *) xmalloca (mode_len + 2);
670       memcpy (augmented_mode, mode, mode_len);
671       memcpy (augmented_mode + mode_len, "D", 2);
672
673       fp = fopen (file_name, augmented_mode);
674       saved_errno = errno;
675
676       freea (augmented_mode);
677     }
678   else
679 #endif
680     {
681       fp = fopen (file_name, mode);
682       saved_errno = errno;
683     }
684   if (fp != NULL)
685     {
686       /* It is sufficient to register fileno (fp) instead of the entire fp,
687          because at cleanup time there is no need to do an fflush (fp); a
688          close (fileno (fp)) will be enough.  */
689       int fd = fileno (fp);
690       if (!(fd >= 0))
691         abort ();
692       register_fd (fd);
693     }
694   unblock_fatal_signals ();
695   errno = saved_errno;
696   return fp;
697 }
698
699 /* Close a temporary file in a temporary directory.
700    Unregisters the previously registered file descriptor.  */
701 int
702 close_temp (int fd)
703 {
704   if (fd >= 0)
705     {
706       /* No blocking of signals is needed here, since a double close of a
707          file descriptor is harmless.  */
708       int result = close (fd);
709       int saved_errno = errno;
710
711       /* No race condition here: we assume a single-threaded program, hence
712          fd cannot be re-opened here.  */
713
714       unregister_fd (fd);
715
716       errno = saved_errno;
717       return result;
718     }
719   else
720     return close (fd);
721 }
722
723 /* Close a temporary file in a temporary directory.
724    Unregisters the previously registered file descriptor.  */
725 int
726 fclose_temp (FILE *fp)
727 {
728   int fd = fileno (fp);
729   /* No blocking of signals is needed here, since a double close of a
730      file descriptor is harmless.  */
731   int result = fclose (fp);
732   int saved_errno = errno;
733
734   /* No race condition here: we assume a single-threaded program, hence
735      fd cannot be re-opened here.  */
736
737   unregister_fd (fd);
738
739   errno = saved_errno;
740   return result;
741 }
742
743 #if GNULIB_FWRITEERROR
744 /* Like fwriteerror.
745    Unregisters the previously registered file descriptor.  */
746 int
747 fwriteerror_temp (FILE *fp)
748 {
749   int fd = fileno (fp);
750   /* No blocking of signals is needed here, since a double close of a
751      file descriptor is harmless.  */
752   int result = fwriteerror (fp);
753   int saved_errno = errno;
754
755   /* No race condition here: we assume a single-threaded program, hence
756      fd cannot be re-opened here.  */
757
758   unregister_fd (fd);
759
760   errno = saved_errno;
761   return result;
762 }
763 #endif
764
765 #if GNULIB_CLOSE_STREAM
766 /* Like close_stream.
767    Unregisters the previously registered file descriptor.  */
768 int
769 close_stream_temp (FILE *fp)
770 {
771   int fd = fileno (fp);
772   /* No blocking of signals is needed here, since a double close of a
773      file descriptor is harmless.  */
774   int result = close_stream (fp);
775   int saved_errno = errno;
776
777   /* No race condition here: we assume a single-threaded program, hence
778      fd cannot be re-opened here.  */
779
780   unregister_fd (fd);
781
782   errno = saved_errno;
783   return result;
784 }
785 #endif