]>
Commit | Line | Data |
---|---|---|
28f540f4 RM |
1 | /* Copyright (C) 1991, 1992, 1993, 1994, 1995 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 <ansidecl.h> | |
20 | #include <errno.h> | |
21 | #include <stddef.h> | |
22 | #include <stdio.h> | |
23 | #include <stdlib.h> | |
24 | #include <string.h> | |
25 | #include <sys/types.h> | |
26 | #include <sys/stat.h> | |
27 | #include <fcntl.h> | |
28 | #include <unistd.h> | |
29 | ||
30 | /* Return nonzero if DIR is an existent directory. */ | |
31 | static int | |
32 | DEFUN(diraccess, (dir), CONST char *dir) | |
33 | { | |
34 | struct stat buf; | |
35 | return __stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode); | |
36 | } | |
37 | ||
38 | /* Return nonzero if FILE exists. */ | |
39 | static int | |
40 | DEFUN(exists, (file), CONST char *file) | |
41 | { | |
42 | /* We can stat the file even if we can't read its data. */ | |
43 | struct stat st; | |
44 | int save = errno; | |
45 | if (__stat (file, &st) == 0) | |
46 | return 1; | |
47 | else | |
48 | { | |
49 | /* We report that the file exists if stat failed for a reason other | |
50 | than nonexistence. In this case, it may or may not exist, and we | |
51 | don't know; but reporting that it does exist will never cause any | |
52 | trouble, while reporting that it doesn't exist when it does would | |
53 | violate the interface of __stdio_gen_tempname. */ | |
54 | int exists = errno != ENOENT; | |
55 | errno = save; | |
56 | return exists; | |
57 | } | |
58 | } | |
59 | ||
60 | ||
61 | /* These are the characters used in temporary filenames. */ | |
62 | static CONST char letters[] = | |
63 | "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; | |
64 | ||
65 | /* Generate a temporary filename and return it (in a static buffer). If | |
66 | STREAMPTR is not NULL, open a stream "w+b" on the file and set | |
67 | *STREAMPTR to it. If DIR_SEARCH is nonzero, DIR and PFX are used as | |
68 | described for tempnam. If not, a temporary filename in P_tmpdir with no | |
69 | special prefix is generated. If LENPTR is not NULL, *LENPTR is set the | |
70 | to length (including the terminating '\0') of the resultant filename, | |
71 | which is returned. This goes through a cyclic pattern of all possible | |
72 | filenames consisting of five decimal digits of the current pid and three | |
73 | of the characters in `letters'. Data for tempnam and tmpnam is kept | |
74 | separate, but when tempnam is using P_tmpdir and no prefix (i.e, it is | |
75 | identical to tmpnam), the same data is used. Each potential filename is | |
76 | tested for an already-existing file of the same name, and no name of an | |
77 | existing file will be returned. When the cycle reaches its end | |
78 | (12345ZZZ), NULL is returned. */ | |
79 | char * | |
80 | DEFUN(__stdio_gen_tempname, (dir, pfx, dir_search, lenptr, streamptr), | |
81 | CONST char *dir AND CONST char *pfx AND | |
82 | int dir_search AND size_t *lenptr AND | |
83 | FILE **streamptr) | |
84 | { | |
85 | int saverrno = errno; | |
86 | static CONST char tmpdir[] = P_tmpdir; | |
87 | static size_t indices[2]; | |
88 | size_t *idx; | |
89 | static char buf[FILENAME_MAX]; | |
90 | static pid_t oldpid = (pid_t) 0; | |
91 | pid_t pid = __getpid(); | |
92 | register size_t len, plen, dlen; | |
93 | ||
94 | if (dir_search) | |
95 | { | |
96 | register CONST char *d = getenv ("TMPDIR"); | |
97 | if (d != NULL && !diraccess (d)) | |
98 | d = NULL; | |
99 | if (d == NULL && dir != NULL && diraccess (dir)) | |
100 | d = dir; | |
101 | if (d == NULL && diraccess (tmpdir)) | |
102 | d = tmpdir; | |
103 | if (d == NULL && diraccess ("/tmp")) | |
104 | d = "/tmp"; | |
105 | if (d == NULL) | |
106 | { | |
107 | errno = ENOENT; | |
108 | return NULL; | |
109 | } | |
110 | dir = d; | |
111 | } | |
112 | else | |
113 | dir = tmpdir; | |
114 | ||
115 | dlen = strlen (dir); | |
116 | ||
117 | /* Remove trailing slashes from the directory name. */ | |
118 | while (dlen > 1 && dir[dlen - 1] == '/') | |
119 | --dlen; | |
120 | ||
121 | if (pfx != NULL && *pfx != '\0') | |
122 | { | |
123 | plen = strlen (pfx); | |
124 | if (plen > 5) | |
125 | plen = 5; | |
126 | } | |
127 | else | |
128 | plen = 0; | |
129 | ||
130 | if (dir != tmpdir && !strcmp (dir, tmpdir)) | |
131 | dir = tmpdir; | |
132 | idx = &indices[(plen == 0 && dir == tmpdir) ? 1 : 0]; | |
133 | ||
134 | if (pid != oldpid) | |
135 | { | |
136 | oldpid = pid; | |
137 | indices[0] = indices[1] = 0; | |
138 | } | |
139 | ||
140 | len = dlen + 1 + plen + 5 + 3; | |
141 | while (*idx < ((sizeof (letters) - 1) * (sizeof (letters) - 1) * | |
142 | (sizeof (letters) - 1))) | |
143 | { | |
144 | const size_t i = (*idx)++; | |
145 | ||
146 | /* Construct a file name and see if it already exists. | |
147 | ||
148 | We use a single counter in *IDX to cycle each of three | |
149 | character positions through each of 62 possible letters. */ | |
150 | ||
151 | if (sizeof (buf) < len || | |
152 | sprintf (buf, "%.*s/%.*s%.5d%c%c%c", | |
153 | (int) dlen, dir, (int) plen, | |
154 | pfx, pid % 100000, | |
155 | letters[i % (sizeof (letters) - 1)], | |
156 | letters[(i / (sizeof (letters) - 1)) | |
157 | % (sizeof (letters) - 1)], | |
158 | letters[(i / ((sizeof (letters) - 1) * | |
159 | (sizeof (letters) - 1))) | |
160 | % (sizeof (letters) - 1)] | |
161 | ) != (int) len) | |
162 | return NULL; | |
163 | ||
164 | if (streamptr != NULL) | |
165 | { | |
166 | /* Try to create the file atomically. */ | |
167 | int fd = __open (buf, O_RDWR|O_CREAT|O_EXCL, 0666); | |
168 | if (fd >= 0) | |
169 | { | |
170 | /* We got a new file that did not previously exist. | |
171 | Create a stream for it. */ | |
172 | *streamptr = __newstream (); | |
173 | if (*streamptr == NULL) | |
174 | { | |
175 | /* We lost trying to create a stream (out of memory?). | |
176 | Nothing to do but remove the file, close the descriptor, | |
177 | and return failure. */ | |
178 | const int save = errno; | |
179 | (void) remove (buf); | |
180 | (void) __close (fd); | |
181 | errno = save; | |
182 | return NULL; | |
183 | } | |
184 | (*streamptr)->__cookie = (PTR) (long int) fd; | |
185 | (*streamptr)->__mode.__write = 1; | |
186 | (*streamptr)->__mode.__read = 1; | |
187 | (*streamptr)->__mode.__binary = 1; | |
188 | } | |
189 | else | |
190 | continue; | |
191 | } | |
192 | else if (exists (buf)) | |
193 | continue; | |
194 | ||
195 | /* If the file already existed we have continued the loop above, | |
196 | so we only get here when we have a winning name to return. */ | |
197 | ||
198 | errno = saverrno; | |
199 | ||
200 | if (lenptr != NULL) | |
201 | *lenptr = len + 1; | |
202 | return buf; | |
203 | } | |
204 | ||
205 | /* We got out of the loop because we ran out of combinations to try. */ | |
206 | errno = EEXIST; /* ? */ | |
207 | return NULL; | |
208 | } |