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