Update from main archive 960809
[kopensolaris-gnu/glibc.git] / sysdeps / posix / tempname.c
1 /* Copyright (C) 1991, 92, 93, 94, 95, 96 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
8
9 The GNU C Library 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 GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB.  If
16 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
17 Cambridge, MA 02139, USA.  */
18
19 #include <errno.h>
20 #include <stddef.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28
29 #ifdef USE_IN_LIBIO
30 # include "libioP.h"
31 # include <libio.h>
32 #endif
33
34 /* Return nonzero if DIR is an existent directory.  */
35 static int
36 diraccess (const char *dir)
37 {
38   struct stat buf;
39   return __stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
40 }
41
42 /* Return nonzero if FILE exists.  */
43 static int
44 exists (const char *file)
45 {
46   /* We can stat the file even if we can't read its data.  */
47   struct stat st;
48   int save = errno;
49   if (__stat (file, &st) == 0)
50     return 1;
51   else
52     {
53       /* We report that the file exists if stat failed for a reason other
54          than nonexistence.  In this case, it may or may not exist, and we
55          don't know; but reporting that it does exist will never cause any
56          trouble, while reporting that it doesn't exist when it does would
57          violate the interface of __stdio_gen_tempname.  */
58       int exists = errno != ENOENT;
59       errno = save;
60       return exists;
61     }
62 }
63
64
65 /* These are the characters used in temporary filenames.  */
66 static const char letters[] =
67   "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
68
69 /* Generate a temporary filename and return it (in a static buffer).  If
70    STREAMPTR is not NULL, open a stream "w+b" on the file and set
71    *STREAMPTR to it.  If DIR_SEARCH is nonzero, DIR and PFX are used as
72    described for tempnam.  If not, a temporary filename in P_tmpdir with no
73    special prefix is generated.  If LENPTR is not NULL, *LENPTR is set the
74    to length (including the terminating '\0') of the resultant filename,
75    which is returned.  This goes through a cyclic pattern of all possible
76    filenames consisting of five decimal digits of the current pid and three
77    of the characters in `letters'.  Data for tempnam and tmpnam is kept
78    separate, but when tempnam is using P_tmpdir and no prefix (i.e, it is
79    identical to tmpnam), the same data is used.  Each potential filename is
80    tested for an already-existing file of the same name, and no name of an
81    existing file will be returned.  When the cycle reaches its end
82    (12345ZZZ), NULL is returned.  */
83 char *
84 __stdio_gen_tempname (const char *dir, const char *pfx, int dir_search,
85                       size_t *lenptr, FILE **streamptr)
86 {
87   int saverrno = errno;
88   static const char tmpdir[] = P_tmpdir;
89   static size_t indices[2];
90   size_t *idx;
91   static char buf[FILENAME_MAX];
92   static pid_t oldpid = (pid_t) 0;
93   pid_t pid = __getpid();
94   register size_t len, plen, dlen;
95
96   if (dir_search)
97     {
98       register const char *d = getenv ("TMPDIR");
99       if (d != NULL && !diraccess (d))
100         d = NULL;
101       if (d == NULL && dir != NULL && diraccess (dir))
102         d = dir;
103       if (d == NULL && diraccess (tmpdir))
104         d = tmpdir;
105       if (d == NULL && diraccess ("/tmp"))
106         d = "/tmp";
107       if (d == NULL)
108         {
109           errno = ENOENT;
110           return NULL;
111         }
112       dir = d;
113     }
114   else
115     dir = tmpdir;
116
117   dlen = strlen (dir);
118
119  /* Remove trailing slashes from the directory name.  */
120   while (dlen > 1 && dir[dlen - 1] == '/')
121     --dlen;
122
123   if (pfx != NULL && *pfx != '\0')
124     {
125       plen = strlen (pfx);
126       if (plen > 5)
127         plen = 5;
128     }
129   else
130     plen = 0;
131
132   if (dir != tmpdir && !strcmp (dir, tmpdir))
133     dir = tmpdir;
134   idx = &indices[(plen == 0 && dir == tmpdir) ? 1 : 0];
135
136   if (pid != oldpid)
137     {
138       oldpid = pid;
139       indices[0] = indices[1] = 0;
140     }
141
142   len = dlen + 1 + plen + 5 + 3;
143   while (*idx < ((sizeof (letters) - 1) * (sizeof (letters) - 1) *
144                  (sizeof (letters) - 1)))
145     {
146       const size_t i = (*idx)++;
147
148       /* Construct a file name and see if it already exists.
149
150          We use a single counter in *IDX to cycle each of three
151          character positions through each of 62 possible letters.  */
152
153       if (sizeof (buf) < len ||
154           sprintf (buf, "%.*s/%.*s%.5d%c%c%c",
155                    (int) dlen, dir, (int) plen,
156                    pfx, pid % 100000,
157                    letters[i % (sizeof (letters) - 1)],
158                    letters[(i / (sizeof (letters) - 1))
159                            % (sizeof (letters) - 1)],
160                    letters[(i / ((sizeof (letters) - 1) *
161                                  (sizeof (letters) - 1)))
162                            % (sizeof (letters) - 1)]
163                    ) != (int) len)
164         return NULL;
165
166       if (streamptr != NULL)
167         {
168           /* Try to create the file atomically.  */
169           int fd = __open (buf, O_RDWR|O_CREAT|O_EXCL, 0666);
170           if (fd >= 0)
171             {
172               /* We got a new file that did not previously exist.
173                  Create a stream for it.  */
174 #ifdef USE_IN_LIBIO
175               int save;
176               struct _IO_FILE_plus *fp;
177
178               fp = (struct _IO_FILE_plus *)
179                 malloc(sizeof (struct _IO_FILE_plus));
180               if (fp == NULL)
181                 {
182                   /* We lost trying to create a stream (out of memory?).
183                      Nothing to do but remove the file, close the descriptor,
184                      and return failure.  */
185                   save = errno;
186                 lose:
187                   (void) remove (buf);
188                   (void) __close (fd);
189                   errno = save;
190                   return NULL;
191                 }
192               _IO_init (&fp->file, 0);
193               _IO_JUMPS (&fp->file) = &_IO_file_jumps;
194               _IO_file_init (&fp->file);
195 # if !_IO_UNIFIED_JUMPTABLES
196               fp->vtable = NULL;
197 # endif
198               if (_IO_file_attach (&fp->file, fd) == NULL)
199                 {
200                   save = errno;
201                   free (fp);
202                   goto lose;
203                 }
204               fp->file._flags &= ~_IO_DELETE_DONT_CLOSE;
205
206               *streamptr = (FILE *) fp;
207 #else
208               *streamptr = __newstream ();
209               if (*streamptr == NULL)
210                 {
211                   /* We lost trying to create a stream (out of memory?).
212                      Nothing to do but remove the file, close the descriptor,
213                      and return failure.  */
214                   const int save = errno;
215                   (void) remove (buf);
216                   (void) __close (fd);
217                   errno = save;
218                   return NULL;
219                 }
220               (*streamptr)->__cookie = (__ptr_t) (long int) fd;
221               (*streamptr)->__mode.__write = 1;
222               (*streamptr)->__mode.__read = 1;
223               (*streamptr)->__mode.__binary = 1;
224 #endif
225             }
226           else
227             continue;
228         }
229       else if (exists (buf))
230         continue;
231
232       /* If the file already existed we have continued the loop above,
233          so we only get here when we have a winning name to return.  */
234
235       errno = saverrno;
236
237       if (lenptr != NULL)
238         *lenptr = len + 1;
239       return buf;
240     }
241
242   /* We got out of the loop because we ran out of combinations to try.  */
243   errno = EEXIST;               /* ? */
244   return NULL;
245 }