Merge from coreutils.
[gnulib.git] / lib / tempname.c
index d008c98..b3d0874 100644 (file)
@@ -1,20 +1,21 @@
-/* Copyright (C) 1991-1999, 2000 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
+/* tempname.c - generate the name of a temporary file.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
+   Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+   2000, 2001, 2002, 2003 Free Software Foundation, Inc.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Library General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Library General Public
-   License along with the GNU C Library; see the file COPYING.LIB.  If not,
-   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   You should have received a copy of the GNU General Public License along
+   with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #if HAVE_CONFIG_H
 # include <config.h>
 # define __GT_NOCREATE 3
 #endif
 
-#if STDC_HEADERS || _LIBC
-# include <stddef.h>
-# include <stdlib.h>
-# include <string.h>
-#endif
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
 
 #if HAVE_FCNTL_H || _LIBC
 # include <fcntl.h>
@@ -59,6 +58,9 @@
 #if HAVE_STDINT_H || _LIBC
 # include <stdint.h>
 #endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
 
 #if HAVE_UNISTD_H || _LIBC
 # include <unistd.h>
 # define __secure_getenv getenv
 #endif
 
+#ifdef _LIBC
+# include <hp-timing.h>
+# if HP_TIMING_AVAIL
+#  define RANDOM_BITS(Var) \
+  if (__builtin_expect (value == UINT64_C (0), 0))                           \
+    {                                                                        \
+      /* If this is the first time this function is used initialize          \
+        the variable we accumulate the value in to some somewhat             \
+        random value.  If we'd not do this programs at startup time          \
+        might have a reduced set of possible names, at least on slow         \
+        machines.  */                                                        \
+      struct timeval tv;                                                     \
+      __gettimeofday (&tv, NULL);                                            \
+      value = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec;                     \
+    }                                                                        \
+  HP_TIMING_NOW (Var)
+# endif
+#endif
+
+/* Use the widest available unsigned type if uint64_t is not
+   available.  The algorithm below extracts a number less than 62**6
+   (approximately 2**35.725) from uint64_t, so ancient hosts where
+   uintmax_t is only 32 bits lose about 3.725 bits of randomness,
+   which is better than not having mkstemp at all.  */
+#if !defined UINT64_MAX && !defined uint64_t
+# define uint64_t uintmax_t
+#endif
+
 /* Return nonzero if DIR is an existent directory.  */
 static int
 direxists (const char *dir)
@@ -203,10 +233,23 @@ __gen_tempname (char *tmpl, int kind)
   char *XXXXXX;
   static uint64_t value;
   uint64_t random_time_bits;
-  int count, fd = -1;
+  unsigned int count;
+  int fd = -1;
   int save_errno = errno;
   struct_stat64 st;
 
+  /* A lower bound on the number of temporary files to attempt to
+     generate.  The maximum total number of temporary file names that
+     can exist for a given template is 62**6.  It should never be
+     necessary to try all these combinations.  Instead if a reasonable
+     number of names is tried (we define reasonable as 62**3) fail to
+     give the system administrator the chance to remove the problems.  */
+  unsigned int attempts_min = 62 * 62 * 62;
+
+  /* The number of times to attempt to generate a temporary file.  To
+     conform to POSIX, this must be no smaller than TMP_MAX.  */
+  unsigned int attempts = attempts_min < TMP_MAX ? TMP_MAX : attempts_min;
+
   len = strlen (tmpl);
   if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
     {
@@ -218,18 +261,22 @@ __gen_tempname (char *tmpl, int kind)
   XXXXXX = &tmpl[len - 6];
 
   /* Get some more or less random data.  */
-#if HAVE_GETTIMEOFDAY || _LIBC
+#ifdef RANDOM_BITS
+  RANDOM_BITS (random_time_bits);
+#else
+# if HAVE_GETTIMEOFDAY || _LIBC
   {
     struct timeval tv;
     __gettimeofday (&tv, NULL);
     random_time_bits = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec;
   }
-#else
+# else
   random_time_bits = time (NULL);
+# endif
 #endif
   value += random_time_bits ^ __getpid ();
 
-  for (count = 0; count < TMP_MAX; value += 7777, ++count)
+  for (count = 0; count < attempts; value += 7777, ++count)
     {
       uint64_t v = value;