make-temp-file.c revision 1.7 1 1.1 skrll /* Utility to pick a temporary filename prefix.
2 1.6 christos Copyright (C) 1996-2022 Free Software Foundation, Inc.
3 1.1 skrll
4 1.1 skrll This file is part of the libiberty library.
5 1.1 skrll Libiberty is free software; you can redistribute it and/or
6 1.1 skrll modify it under the terms of the GNU Library General Public
7 1.1 skrll License as published by the Free Software Foundation; either
8 1.1 skrll version 2 of the License, or (at your option) any later version.
9 1.1 skrll
10 1.1 skrll Libiberty is distributed in the hope that it will be useful,
11 1.1 skrll but WITHOUT ANY WARRANTY; without even the implied warranty of
12 1.1 skrll MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 1.1 skrll Library General Public License for more details.
14 1.1 skrll
15 1.1 skrll You should have received a copy of the GNU Library General Public
16 1.1 skrll License along with libiberty; see the file COPYING.LIB. If not,
17 1.1 skrll write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
18 1.1 skrll Boston, MA 02110-1301, USA. */
19 1.1 skrll
20 1.1 skrll #ifdef HAVE_CONFIG_H
21 1.1 skrll #include "config.h"
22 1.1 skrll #endif
23 1.1 skrll
24 1.1 skrll #include <stdio.h> /* May get P_tmpdir. */
25 1.1 skrll #include <sys/types.h>
26 1.1 skrll #include <errno.h>
27 1.1 skrll #ifdef HAVE_UNISTD_H
28 1.1 skrll #include <unistd.h>
29 1.1 skrll #endif
30 1.1 skrll #ifdef HAVE_STDLIB_H
31 1.1 skrll #include <stdlib.h>
32 1.1 skrll #endif
33 1.1 skrll #ifdef HAVE_STRING_H
34 1.1 skrll #include <string.h>
35 1.1 skrll #endif
36 1.1 skrll #ifdef HAVE_SYS_FILE_H
37 1.1 skrll #include <sys/file.h> /* May get R_OK, etc. on some systems. */
38 1.1 skrll #endif
39 1.2 jakllsch #if defined(_WIN32) && !defined(__CYGWIN__)
40 1.2 jakllsch #include <windows.h>
41 1.2 jakllsch #endif
42 1.6 christos #if HAVE_SYS_STAT_H
43 1.6 christos #include <sys/stat.h>
44 1.6 christos #endif
45 1.6 christos
46 1.1 skrll
47 1.1 skrll #ifndef R_OK
48 1.1 skrll #define R_OK 4
49 1.1 skrll #define W_OK 2
50 1.1 skrll #define X_OK 1
51 1.1 skrll #endif
52 1.1 skrll
53 1.1 skrll #include "libiberty.h"
54 1.1 skrll extern int mkstemps (char *, int);
55 1.1 skrll
56 1.1 skrll /* '/' works just fine on MS-DOS based systems. */
57 1.1 skrll #ifndef DIR_SEPARATOR
58 1.1 skrll #define DIR_SEPARATOR '/'
59 1.1 skrll #endif
60 1.1 skrll
61 1.1 skrll /* Name of temporary file.
62 1.1 skrll mktemp requires 6 trailing X's. */
63 1.5 christos #define TEMP_FILE "XXXXXX"
64 1.1 skrll #define TEMP_FILE_LEN (sizeof(TEMP_FILE) - 1)
65 1.1 skrll
66 1.2 jakllsch #if !defined(_WIN32) || defined(__CYGWIN__)
67 1.2 jakllsch
68 1.1 skrll /* Subroutine of choose_tmpdir.
69 1.1 skrll If BASE is non-NULL, return it.
70 1.1 skrll Otherwise it checks if DIR is a usable directory.
71 1.1 skrll If success, DIR is returned.
72 1.1 skrll Otherwise NULL is returned. */
73 1.1 skrll
74 1.1 skrll static inline const char *try_dir (const char *, const char *);
75 1.1 skrll
76 1.1 skrll static inline const char *
77 1.1 skrll try_dir (const char *dir, const char *base)
78 1.1 skrll {
79 1.1 skrll if (base != 0)
80 1.1 skrll return base;
81 1.1 skrll if (dir != 0
82 1.1 skrll && access (dir, R_OK | W_OK | X_OK) == 0)
83 1.6 christos {
84 1.6 christos /* Check to make sure dir is actually a directory. */
85 1.6 christos #ifdef S_ISDIR
86 1.6 christos struct stat s;
87 1.6 christos if (stat (dir, &s))
88 1.6 christos return NULL;
89 1.6 christos if (!S_ISDIR (s.st_mode))
90 1.6 christos return NULL;
91 1.6 christos #endif
92 1.6 christos return dir;
93 1.6 christos }
94 1.1 skrll return 0;
95 1.1 skrll }
96 1.1 skrll
97 1.1 skrll static const char tmp[] = { DIR_SEPARATOR, 't', 'm', 'p', 0 };
98 1.1 skrll static const char usrtmp[] =
99 1.1 skrll { DIR_SEPARATOR, 'u', 's', 'r', DIR_SEPARATOR, 't', 'm', 'p', 0 };
100 1.1 skrll static const char vartmp[] =
101 1.1 skrll { DIR_SEPARATOR, 'v', 'a', 'r', DIR_SEPARATOR, 't', 'm', 'p', 0 };
102 1.1 skrll
103 1.2 jakllsch #endif
104 1.2 jakllsch
105 1.1 skrll static char *memoized_tmpdir;
106 1.1 skrll
107 1.1 skrll /*
108 1.1 skrll
109 1.3 christos @deftypefn Replacement const char* choose_tmpdir ()
110 1.1 skrll
111 1.1 skrll Returns a pointer to a directory path suitable for creating temporary
112 1.1 skrll files in.
113 1.1 skrll
114 1.1 skrll @end deftypefn
115 1.1 skrll
116 1.1 skrll */
117 1.1 skrll
118 1.3 christos const char *
119 1.1 skrll choose_tmpdir (void)
120 1.1 skrll {
121 1.2 jakllsch if (!memoized_tmpdir)
122 1.2 jakllsch {
123 1.2 jakllsch #if !defined(_WIN32) || defined(__CYGWIN__)
124 1.2 jakllsch const char *base = 0;
125 1.2 jakllsch char *tmpdir;
126 1.2 jakllsch unsigned int len;
127 1.2 jakllsch
128 1.2 jakllsch #ifdef VMS
129 1.2 jakllsch /* Try VMS standard temp logical. */
130 1.2 jakllsch base = try_dir ("/sys$scratch", base);
131 1.2 jakllsch #else
132 1.2 jakllsch base = try_dir (getenv ("TMPDIR"), base);
133 1.2 jakllsch base = try_dir (getenv ("TMP"), base);
134 1.2 jakllsch base = try_dir (getenv ("TEMP"), base);
135 1.2 jakllsch #endif
136 1.2 jakllsch
137 1.1 skrll #ifdef P_tmpdir
138 1.2 jakllsch /* We really want a directory name here as if concatenated with say \dir
139 1.2 jakllsch we do not end up with a double \\ which defines an UNC path. */
140 1.2 jakllsch if (strcmp (P_tmpdir, "\\") == 0)
141 1.2 jakllsch base = try_dir ("\\.", base);
142 1.2 jakllsch else
143 1.2 jakllsch base = try_dir (P_tmpdir, base);
144 1.2 jakllsch #endif
145 1.2 jakllsch
146 1.7 rin /* Try /tmp, /var/tmp, then /usr/tmp. */
147 1.7 rin base = try_dir (tmp, base);
148 1.2 jakllsch base = try_dir (vartmp, base);
149 1.2 jakllsch base = try_dir (usrtmp, base);
150 1.2 jakllsch
151 1.2 jakllsch /* If all else fails, use the current directory! */
152 1.2 jakllsch if (base == 0)
153 1.2 jakllsch base = ".";
154 1.2 jakllsch /* Append DIR_SEPARATOR to the directory we've chosen
155 1.2 jakllsch and return it. */
156 1.2 jakllsch len = strlen (base);
157 1.2 jakllsch tmpdir = XNEWVEC (char, len + 2);
158 1.2 jakllsch strcpy (tmpdir, base);
159 1.2 jakllsch tmpdir[len] = DIR_SEPARATOR;
160 1.2 jakllsch tmpdir[len+1] = '\0';
161 1.2 jakllsch memoized_tmpdir = tmpdir;
162 1.2 jakllsch #else /* defined(_WIN32) && !defined(__CYGWIN__) */
163 1.2 jakllsch DWORD len;
164 1.2 jakllsch
165 1.2 jakllsch /* Figure out how much space we need. */
166 1.2 jakllsch len = GetTempPath(0, NULL);
167 1.2 jakllsch if (len)
168 1.2 jakllsch {
169 1.2 jakllsch memoized_tmpdir = XNEWVEC (char, len);
170 1.2 jakllsch if (!GetTempPath(len, memoized_tmpdir))
171 1.2 jakllsch {
172 1.2 jakllsch XDELETEVEC (memoized_tmpdir);
173 1.2 jakllsch memoized_tmpdir = NULL;
174 1.2 jakllsch }
175 1.2 jakllsch }
176 1.2 jakllsch if (!memoized_tmpdir)
177 1.2 jakllsch /* If all else fails, use the current directory. */
178 1.2 jakllsch memoized_tmpdir = xstrdup (".\\");
179 1.2 jakllsch #endif /* defined(_WIN32) && !defined(__CYGWIN__) */
180 1.2 jakllsch }
181 1.1 skrll
182 1.2 jakllsch return memoized_tmpdir;
183 1.1 skrll }
184 1.1 skrll
185 1.1 skrll /*
186 1.1 skrll
187 1.1 skrll @deftypefn Replacement char* make_temp_file (const char *@var{suffix})
188 1.1 skrll
189 1.1 skrll Return a temporary file name (as a string) or @code{NULL} if unable to
190 1.1 skrll create one. @var{suffix} is a suffix to append to the file name. The
191 1.1 skrll string is @code{malloc}ed, and the temporary file has been created.
192 1.1 skrll
193 1.1 skrll @end deftypefn
194 1.1 skrll
195 1.1 skrll */
196 1.1 skrll
197 1.1 skrll char *
198 1.5 christos make_temp_file_with_prefix (const char *prefix, const char *suffix)
199 1.1 skrll {
200 1.1 skrll const char *base = choose_tmpdir ();
201 1.1 skrll char *temp_filename;
202 1.5 christos int base_len, suffix_len, prefix_len;
203 1.1 skrll int fd;
204 1.1 skrll
205 1.5 christos if (prefix == 0)
206 1.5 christos prefix = "cc";
207 1.5 christos
208 1.1 skrll if (suffix == 0)
209 1.1 skrll suffix = "";
210 1.1 skrll
211 1.1 skrll base_len = strlen (base);
212 1.5 christos prefix_len = strlen (prefix);
213 1.1 skrll suffix_len = strlen (suffix);
214 1.1 skrll
215 1.1 skrll temp_filename = XNEWVEC (char, base_len
216 1.1 skrll + TEMP_FILE_LEN
217 1.5 christos + suffix_len
218 1.5 christos + prefix_len + 1);
219 1.1 skrll strcpy (temp_filename, base);
220 1.5 christos strcpy (temp_filename + base_len, prefix);
221 1.5 christos strcpy (temp_filename + base_len + prefix_len, TEMP_FILE);
222 1.5 christos strcpy (temp_filename + base_len + prefix_len + TEMP_FILE_LEN, suffix);
223 1.1 skrll
224 1.1 skrll fd = mkstemps (temp_filename, suffix_len);
225 1.1 skrll /* Mkstemps failed. It may be EPERM, ENOSPC etc. */
226 1.1 skrll if (fd == -1)
227 1.1 skrll {
228 1.1 skrll fprintf (stderr, "Cannot create temporary file in %s: %s\n",
229 1.1 skrll base, strerror (errno));
230 1.1 skrll abort ();
231 1.1 skrll }
232 1.1 skrll /* We abort on failed close out of sheer paranoia. */
233 1.1 skrll if (close (fd))
234 1.1 skrll abort ();
235 1.1 skrll return temp_filename;
236 1.1 skrll }
237 1.5 christos
238 1.5 christos char *
239 1.5 christos make_temp_file (const char *suffix)
240 1.5 christos {
241 1.5 christos return make_temp_file_with_prefix (NULL, suffix);
242 1.5 christos }
243