* Fixes and improvements to documentation contributed by Dennis Davis.
[alpine.git] / pith / osdep / temp_nam.c
blob42096d6eb3f3ff853d2ac9f11d14a628aab7f7f3
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: temp_nam.c 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2020 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include <system.h>
21 #if HAVE_FCNTL_H
22 #include <fcntl.h>
23 #endif
25 #include "canaccess.h"
26 #include "temp_nam.h"
27 #include "../charconv/utf8.h"
28 #include "../charconv/filesys.h"
31 #ifdef _WINDOWS
33 #include <process.h>
35 #define ACCESSIBLE (WRITE_ACCESS)
36 #define PATH_SEP "\\"
38 #else /* UNIX */
40 #define ACCESSIBLE (WRITE_ACCESS|EXECUTE_ACCESS)
41 #define PATH_SEP "/"
43 #endif /* UNIX */
47 * Internal Prototypes
49 char *was_nonexistent_tmp_name(char *, size_t, char *);
54 * This routine is derived from BSD4.3 code,
55 * Copyright (c) 1987 Regents of the University of California.
56 * All rights reserved.
58 #if defined(LIBC_SCCS) && !defined(lint)
59 static char sccsid[] = "@(#)mktemp.c 5.7 (Berkeley) 6/27/88";
60 #endif /* LIBC_SCCS and not lint */
62 char *
63 was_nonexistent_tmp_name(char *as, size_t aslen, char *ext)
65 register char *start, *trv;
66 struct stat sbuf;
67 unsigned pid;
68 static unsigned n = 0;
69 int i;
70 int fd, tries = 0;
71 int f;
72 static unsigned long r = 0;
74 if(r == 0L)
75 r = (unsigned)getpid() + time((time_t *)0);
77 for(i = 5; i > 0; i--)
78 r = 1664525 * r + 1013904223;
80 pid = ((unsigned)getpid() * 100) + n++;
82 pid += (r % 50000L);
84 /* extra X's get set to 0's */
85 for(trv = as; *trv; ++trv)
89 * We should probably make the name random instead of having it
90 * be the pid.
92 while(*--trv == 'X'){
93 *trv = (pid % 10) + '0';
94 pid /= 10;
97 /* add the extension, enough room guaranteed by caller */
98 if(ext && *ext){
99 strncat(as, ".", aslen-strlen(as)-1);
100 as[aslen-1] = '\0';
101 strncat(as, ext, aslen-strlen(as)-1);
102 as[aslen-1] = '\0';
106 * Check for write permission on target directory; if you have
107 * six X's and you can't write the directory, this will run for
108 * a *very* long time.
110 for(start = ++trv; trv > as && *trv != PATH_SEP[0]; --trv)
113 if(*trv == PATH_SEP[0]){
114 #ifdef _WINDOWS
115 char treplace;
117 if((trv - as == 2) && isalpha(as[0]) && as[1] == ':')
118 trv++;
119 treplace = *trv;
120 *trv = '\0';
121 if(our_stat(as==trv ? PATH_SEP : as, &sbuf) || !(sbuf.st_mode & S_IFDIR))
122 return((char *)NULL);
124 *trv = treplace;
126 #else /* UNIX */
128 *trv = '\0';
130 if(our_stat(as==trv ? PATH_SEP : as, &sbuf) || !(sbuf.st_mode & S_IFDIR))
131 return((char *)NULL);
133 *trv = PATH_SEP[0];
135 #endif
137 else if (our_stat(".", &sbuf) == -1)
138 return((char *)NULL);
140 for(;;){
142 * Check with lstat to be sure we don't have
143 * a symlink. If lstat fails and no such file, then we
144 * have a winner. Otherwise, lstat shouldn't fail.
145 * If lstat succeeds, then skip it because it exists.
147 #ifndef _WINDOWS
148 if(our_lstat(as, &sbuf)){ /* lstat failed */
149 if(errno == ENOENT){ /* no such file, success */
150 #endif /* !_WINDOWS */
152 * Create the file so that the
153 * evil ones don't have a chance to put something there
154 * that they can read or write before we create it
155 * ourselves.
157 f = O_CREAT|O_EXCL|O_WRONLY|O_BINARY;
159 if((fd=our_open(as, f, 0600)) >= 0 && close(fd) == 0)
160 return(as);
161 else if(++tries > 3) /* open failed unexpectedly */
162 return((char *)NULL);
163 #ifndef _WINDOWS
165 else /* failed for unknown reason */
166 return((char *)NULL);
168 #endif /* !_WINDOWS */
170 for(trv = start;;){
171 if(!*trv)
172 return((char *)NULL);
175 * Change the digits from the initial values into
176 * lower case letters and try again.
178 if(*trv == 'z')
179 *trv++ = 'a';
180 else{
181 if(isdigit((unsigned char)*trv))
182 *trv = 'a';
183 else
184 ++*trv;
186 break;
190 /*NOTREACHED*/
195 * This routine is derived from BSD4.3 code,
196 * Copyright (c) 1988 Regents of the University of California.
197 * All rights reserved.
199 #if defined(LIBC_SCCS) && !defined(lint)
200 static char sccsid[] = "@(#)tmpnam.c 4.5 (Berkeley) 6/27/88";
201 #endif /* LIBC_SCCS and not lint */
202 /*----------------------------------------------------------------------
203 Return a unique file name in a given directory. This is not quite
204 the same as the usual tempnam() function, though it is similar.
205 We want it to use the TMPDIR/TMP/TEMP environment variable only if dir
206 is NULL, instead of using it regardless if it is set.
207 We also want it to be safer than tempnam().
208 If we return a filename, we are saying that the file did not exist
209 at the time this function was called (and it wasn't a symlink pointing
210 to a file that didn't exist, either).
211 If dir is NULL this is a temp file in a public directory. In that
212 case we create the file with permission 0600 before returning.
214 Args: dir -- The directory to create the name in
215 prefix -- Prefix of the name
217 Result: Malloc'd string equal to new name is returned. It must be free'd
218 by the caller. Returns the string on success and NULL on failure.
219 ----*/
220 char *
221 temp_nam(char *dir, char *prefix)
223 return(temp_nam_ext(dir, prefix, NULL));
227 /*----------------------------------------------------------------------
229 Like temp_nam but create a unique name with an extension.
231 Result: Malloc'd string equal to new name is returned. It must be free'd
232 by the caller. Returns the string on success and NULL on failure.
233 ----*/
234 char *
235 temp_nam_ext(char *dir, char *prefix, char *ext)
237 struct stat buf;
238 size_t l, ll;
239 char *f, *name;
241 if(ext == NULL)
242 ext = "";
244 if(!(name = (char *)malloc(MAXPATH * sizeof(char))))
245 return((char *)NULL);
247 if(!dir && (f = getenv("TMPDIR")) && !our_stat(f, &buf) &&
248 (buf.st_mode&S_IFMT) == S_IFDIR &&
249 !can_access(f, ACCESSIBLE)){
250 strncpy(name, f, MAXPATH-1);
251 name[MAXPATH-1] = '\0';
252 goto done;
255 if(!dir && (f = getenv("TMP")) && !our_stat(f, &buf) &&
256 (buf.st_mode&S_IFMT) == S_IFDIR &&
257 !can_access(f, ACCESSIBLE)){
258 strncpy(name, f, MAXPATH-1);
259 name[MAXPATH-1] = '\0';
260 goto done;
263 if(!dir && (f = getenv("TEMP")) && !our_stat(f, &buf) &&
264 (buf.st_mode&S_IFMT) == S_IFDIR &&
265 !can_access(f, ACCESSIBLE)){
266 strncpy(name, f, MAXPATH-1);
267 name[MAXPATH-1] = '\0';
268 goto done;
271 if(dir){
272 strncpy(name, dir, MAXPATH-1);
273 name[MAXPATH-1] = '\0';
275 #ifdef _WINDOWS
276 if(!*dir || (isalpha(*dir) && *(dir+1) == ':' && !*(dir+2))){
277 strncat(name, PATH_SEP, MAXPATH-strlen(name)-1);
278 name[MAXPATH-1] = '\0';
280 #endif
282 if(!our_stat(name, &buf)
283 && (buf.st_mode&S_IFMT) == S_IFDIR
284 && !can_access(name, ACCESSIBLE)){
285 strncpy(name, dir, MAXPATH-1);
286 name[MAXPATH-1] = '\0';
287 goto done;
291 #ifndef P_tmpdir
292 #ifdef _WINDOWS
293 #define P_tmpdir "\\tmp"
294 #else /* UNIX */
295 #define P_tmpdir "/usr/tmp"
296 #endif /* UNIX */
297 #endif
299 if(!our_stat(P_tmpdir, &buf) &&
300 (buf.st_mode&S_IFMT) == S_IFDIR &&
301 !can_access(P_tmpdir, ACCESSIBLE)){
302 strncpy(name, P_tmpdir, MAXPATH-1);
303 name[MAXPATH-1] = '\0';
304 goto done;
307 #ifndef _WINDOWS
308 if(!our_stat("/tmp", &buf) &&
309 (buf.st_mode&S_IFMT) == S_IFDIR &&
310 !can_access("/tmp", ACCESSIBLE)){
311 strncpy(name, "/tmp", MAXPATH-1);
312 name[MAXPATH-1] = '\0';
313 goto done;
315 #endif
317 free((void *)name);
318 return((char *)NULL);
320 done:
321 f = NULL;
322 if(name[0] && *((f = &name[l=strlen(name)]) - 1) != PATH_SEP[0] && l+1 < MAXPATH){
323 *f++ = PATH_SEP[0];
324 *f = '\0';
325 l++;
328 if(prefix && (ll = strlen(prefix)) && l+ll < MAXPATH){
329 strncpy(f, prefix, MAXPATH-(f-name));
330 name[MAXPATH-1] = '\0';
331 f += ll;
332 l += ll;
335 if(l+5+(ext[0] ? strlen(ext)+1 : 0) < MAXPATH){
336 strncpy(f, "XXXXX", MAXPATH-(f-name));
337 name[MAXPATH-1] = '\0';
339 else{
340 free((void *)name);
341 return((char *)NULL);
344 return(was_nonexistent_tmp_name(name, MAXPATH, ext));