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