1 /* Copyright (C) 1998,99,2001,2005,2006,2007,2009 Free Software Foundation, Inc.
2    This file is derived from the one in the GNU C Library.
3 
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU Lesser General Public License as
6    published by the Free Software Foundation; either version 2.1 of the
7    License, or (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with GU Smalltalk; see the file COPYING.LIB.  If not,
16    write to the Free Software Foundation, 51 Franklin Street, Fifth Floor,
17    Boston, MA 02110-1301, USA.  */
18 
19 #if !_LIBC
20 # include <config.h>
21 #endif
22 
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <stddef.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <fcntl.h>
29 #include <sys/time.h>
30 #include <limits.h>
31 #include <unistd.h>
32 
33 #include <errno.h>
34 #ifndef __set_errno
35 # define __set_errno(Val) errno = (Val)
36 #endif
37 
38 /* These are the characters used in temporary file names.  */
39 static const char letters[] =
40 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
41 
42 static int
my_mkdir(name,mode)43 my_mkdir (name, mode)
44     const char* name;
45     mode_t mode;
46 {
47   int retstat;
48 #ifdef _WIN32
49   retstat = mkdir (name);
50   if (retstat == 0)
51     retstat = chmod (name, mode);
52 #else
53   retstat = mkdir (name, mode);
54 #endif
55   return retstat;
56 }
57 
58 /* Generate a unique temporary file name from TEMPLATE.
59    The last six characters of TEMPLATE must be "XXXXXX";
60    they are replaced with a string that makes the file name unique.
61    Then open the file and return the template or NULL. */
62 char *
mkdtemp(template)63 mkdtemp (template)
64      char *template;
65 {
66   int len;
67   char *XXXXXX;
68   static long long value;
69   long long random_time_bits;
70   unsigned int count;
71   int save_errno = errno;
72   struct timeval tv;
73 
74   /* A lower bound on the number of temporary files to attempt to
75      generate.  The maximum total number of temporary file names that
76      can exist for a given template is 62**6.  It should never be
77      necessary to try all these combinations.  Instead if a reasonable
78      number of names is tried (we define reasonable as 62**3) fail to
79      give the system administrator the chance to remove the problems.  */
80 #define ATTEMPTS_MIN (62 * 62 * 62)
81 
82   /* The number of times to attempt to generate a temporary file.  To
83      conform to POSIX, this must be no smaller than TMP_MAX.  */
84 #if defined TMP_MAX
85   unsigned int attempts = ATTEMPTS_MIN < TMP_MAX ? TMP_MAX : ATTEMPTS_MIN;
86 #else
87   unsigned int attempts = ATTEMPTS_MIN;
88 #endif
89 
90   len = strlen (template);
91   if (len < 6 || strcmp (&template[len - 6], "XXXXXX"))
92     {
93       __set_errno (EINVAL);
94       return NULL;
95     }
96 
97   /* This is where the Xs start.  */
98   XXXXXX = &template[len - 6];
99 
100   /* Get some more or less random data.  */
101   gettimeofday (&tv, NULL);
102   random_time_bits = ((long long) tv.tv_usec << 16) ^ tv.tv_sec;
103   value += random_time_bits ^ getpid ();
104 
105   for (count = 0; count < attempts; value += 7777, ++count)
106     {
107       long long v = value;
108 
109       /* Fill in the random bits.  */
110       XXXXXX[0] = letters[v % 62];
111       v /= 62;
112       XXXXXX[1] = letters[v % 62];
113       v /= 62;
114       XXXXXX[2] = letters[v % 62];
115       v /= 62;
116       XXXXXX[3] = letters[v % 62];
117       v /= 62;
118       XXXXXX[4] = letters[v % 62];
119       v /= 62;
120       XXXXXX[5] = letters[v % 62];
121 
122       if (my_mkdir(template, 0700) == 0)
123 	{
124 	  __set_errno (save_errno);
125 	  return template;
126 	}
127       else if (errno != EEXIST)
128 	return NULL;
129     }
130 
131   /* We got out of the loop because we ran out of combinations to try.  */
132   __set_errno (EEXIST);
133   return NULL;
134 }
135