xref: /netbsd-src/external/gpl3/gdb/dist/libiberty/make-temp-file.c (revision 889f3bb010ad20d396fb291b89f202288dac2c87)
198b9484cSchristos /* Utility to pick a temporary filename prefix.
2*889f3bb0Schristos    Copyright (C) 1996-2024 Free Software Foundation, Inc.
398b9484cSchristos 
498b9484cSchristos This file is part of the libiberty library.
598b9484cSchristos Libiberty is free software; you can redistribute it and/or
698b9484cSchristos modify it under the terms of the GNU Library General Public
798b9484cSchristos License as published by the Free Software Foundation; either
898b9484cSchristos version 2 of the License, or (at your option) any later version.
998b9484cSchristos 
1098b9484cSchristos Libiberty is distributed in the hope that it will be useful,
1198b9484cSchristos but WITHOUT ANY WARRANTY; without even the implied warranty of
1298b9484cSchristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1398b9484cSchristos Library General Public License for more details.
1498b9484cSchristos 
1598b9484cSchristos You should have received a copy of the GNU Library General Public
1698b9484cSchristos License along with libiberty; see the file COPYING.LIB.  If not,
1798b9484cSchristos write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
1898b9484cSchristos Boston, MA 02110-1301, USA.  */
1998b9484cSchristos 
2098b9484cSchristos #ifdef HAVE_CONFIG_H
2198b9484cSchristos #include "config.h"
2298b9484cSchristos #endif
2398b9484cSchristos 
2498b9484cSchristos #include <stdio.h>	/* May get P_tmpdir.  */
2598b9484cSchristos #include <sys/types.h>
2698b9484cSchristos #include <errno.h>
2798b9484cSchristos #ifdef HAVE_UNISTD_H
2898b9484cSchristos #include <unistd.h>
2998b9484cSchristos #endif
3098b9484cSchristos #ifdef HAVE_STDLIB_H
3198b9484cSchristos #include <stdlib.h>
3298b9484cSchristos #endif
3398b9484cSchristos #ifdef HAVE_STRING_H
3498b9484cSchristos #include <string.h>
3598b9484cSchristos #endif
3698b9484cSchristos #ifdef HAVE_SYS_FILE_H
3798b9484cSchristos #include <sys/file.h>   /* May get R_OK, etc. on some systems.  */
3898b9484cSchristos #endif
3998b9484cSchristos #if defined(_WIN32) && !defined(__CYGWIN__)
40*889f3bb0Schristos #define WIN32_LEAN_AND_MEAN
4198b9484cSchristos #include <windows.h>
4298b9484cSchristos #endif
43924795e6Schristos #if HAVE_SYS_STAT_H
44924795e6Schristos #include <sys/stat.h>
45924795e6Schristos #endif
46924795e6Schristos 
4798b9484cSchristos 
4898b9484cSchristos #ifndef R_OK
4998b9484cSchristos #define R_OK 4
5098b9484cSchristos #define W_OK 2
5198b9484cSchristos #define X_OK 1
5298b9484cSchristos #endif
5398b9484cSchristos 
5498b9484cSchristos #include "libiberty.h"
5598b9484cSchristos extern int mkstemps (char *, int);
5698b9484cSchristos 
5798b9484cSchristos /* '/' works just fine on MS-DOS based systems.  */
5898b9484cSchristos #ifndef DIR_SEPARATOR
5998b9484cSchristos #define DIR_SEPARATOR '/'
6098b9484cSchristos #endif
6198b9484cSchristos 
6298b9484cSchristos /* Name of temporary file.
6398b9484cSchristos    mktemp requires 6 trailing X's.  */
64314094e7Schristos #define TEMP_FILE "XXXXXX"
6598b9484cSchristos #define TEMP_FILE_LEN (sizeof(TEMP_FILE) - 1)
6698b9484cSchristos 
6798b9484cSchristos #if !defined(_WIN32) || defined(__CYGWIN__)
6898b9484cSchristos 
6998b9484cSchristos /* Subroutine of choose_tmpdir.
7098b9484cSchristos    If BASE is non-NULL, return it.
7198b9484cSchristos    Otherwise it checks if DIR is a usable directory.
7298b9484cSchristos    If success, DIR is returned.
7398b9484cSchristos    Otherwise NULL is returned.  */
7498b9484cSchristos 
7598b9484cSchristos static inline const char *try_dir (const char *, const char *);
7698b9484cSchristos 
7798b9484cSchristos static inline const char *
7898b9484cSchristos try_dir (const char *dir, const char *base)
7998b9484cSchristos {
8098b9484cSchristos   if (base != 0)
8198b9484cSchristos     return base;
8298b9484cSchristos   if (dir != 0
8398b9484cSchristos       && access (dir, R_OK | W_OK | X_OK) == 0)
84924795e6Schristos     {
85924795e6Schristos       /* Check to make sure dir is actually a directory. */
86924795e6Schristos #ifdef S_ISDIR
87924795e6Schristos       struct stat s;
88924795e6Schristos       if (stat (dir, &s))
89924795e6Schristos 	return NULL;
90924795e6Schristos       if (!S_ISDIR (s.st_mode))
91924795e6Schristos 	return NULL;
92924795e6Schristos #endif
9398b9484cSchristos       return dir;
94924795e6Schristos     }
9598b9484cSchristos   return 0;
9698b9484cSchristos }
9798b9484cSchristos 
9898b9484cSchristos static const char tmp[] = { DIR_SEPARATOR, 't', 'm', 'p', 0 };
9998b9484cSchristos static const char vartmp[] =
10098b9484cSchristos { DIR_SEPARATOR, 'v', 'a', 'r', DIR_SEPARATOR, 't', 'm', 'p', 0 };
10198b9484cSchristos 
10298b9484cSchristos #endif
10398b9484cSchristos 
10498b9484cSchristos static char *memoized_tmpdir;
10598b9484cSchristos 
10698b9484cSchristos /*
10798b9484cSchristos 
1089d1da10bSchristos @deftypefn Replacement const char* choose_tmpdir ()
10998b9484cSchristos 
11098b9484cSchristos Returns a pointer to a directory path suitable for creating temporary
11198b9484cSchristos files in.
11298b9484cSchristos 
11398b9484cSchristos @end deftypefn
11498b9484cSchristos 
11598b9484cSchristos */
11698b9484cSchristos 
1179d1da10bSchristos const char *
11898b9484cSchristos choose_tmpdir (void)
11998b9484cSchristos {
12098b9484cSchristos   if (!memoized_tmpdir)
12198b9484cSchristos     {
12298b9484cSchristos #if !defined(_WIN32) || defined(__CYGWIN__)
12398b9484cSchristos       const char *base = 0;
12498b9484cSchristos       char *tmpdir;
12598b9484cSchristos       unsigned int len;
12698b9484cSchristos 
12798b9484cSchristos #ifdef VMS
12898b9484cSchristos       /* Try VMS standard temp logical.  */
12998b9484cSchristos       base = try_dir ("/sys$scratch", base);
13098b9484cSchristos #else
13198b9484cSchristos       base = try_dir (getenv ("TMPDIR"), base);
13298b9484cSchristos       base = try_dir (getenv ("TMP"), base);
13398b9484cSchristos       base = try_dir (getenv ("TEMP"), base);
13498b9484cSchristos #endif
13598b9484cSchristos 
13698b9484cSchristos #ifdef P_tmpdir
13798b9484cSchristos       /* We really want a directory name here as if concatenated with say \dir
13898b9484cSchristos 	 we do not end up with a double \\ which defines an UNC path.  */
13998b9484cSchristos       if (strcmp (P_tmpdir, "\\") == 0)
14098b9484cSchristos 	base = try_dir ("\\.", base);
14198b9484cSchristos       else
14298b9484cSchristos 	base = try_dir (P_tmpdir, base);
14398b9484cSchristos #endif
14498b9484cSchristos 
145924795e6Schristos       /* Try /var/tmp, then /tmp.  */
14698b9484cSchristos       base = try_dir (vartmp, base);
147543accdbSkamil       base = try_dir (tmp, base);
14898b9484cSchristos 
14998b9484cSchristos       /* If all else fails, use the current directory!  */
15098b9484cSchristos       if (base == 0)
15198b9484cSchristos 	base = ".";
15298b9484cSchristos       /* Append DIR_SEPARATOR to the directory we've chosen
15398b9484cSchristos 	 and return it.  */
15498b9484cSchristos       len = strlen (base);
15598b9484cSchristos       tmpdir = XNEWVEC (char, len + 2);
15698b9484cSchristos       strcpy (tmpdir, base);
15798b9484cSchristos       tmpdir[len] = DIR_SEPARATOR;
15898b9484cSchristos       tmpdir[len+1] = '\0';
15998b9484cSchristos       memoized_tmpdir = tmpdir;
16098b9484cSchristos #else /* defined(_WIN32) && !defined(__CYGWIN__) */
16198b9484cSchristos       DWORD len;
16298b9484cSchristos 
16398b9484cSchristos       /* Figure out how much space we need.  */
16498b9484cSchristos       len = GetTempPath(0, NULL);
16598b9484cSchristos       if (len)
16698b9484cSchristos 	{
16798b9484cSchristos 	  memoized_tmpdir = XNEWVEC (char, len);
16898b9484cSchristos 	  if (!GetTempPath(len, memoized_tmpdir))
16998b9484cSchristos 	    {
17098b9484cSchristos 	      XDELETEVEC (memoized_tmpdir);
17198b9484cSchristos 	      memoized_tmpdir = NULL;
17298b9484cSchristos 	    }
17398b9484cSchristos 	}
17498b9484cSchristos       if (!memoized_tmpdir)
17598b9484cSchristos 	/* If all else fails, use the current directory.  */
17698b9484cSchristos 	memoized_tmpdir = xstrdup (".\\");
17798b9484cSchristos #endif /* defined(_WIN32) && !defined(__CYGWIN__) */
17898b9484cSchristos     }
17998b9484cSchristos 
18098b9484cSchristos   return memoized_tmpdir;
18198b9484cSchristos }
18298b9484cSchristos 
18398b9484cSchristos /*
18498b9484cSchristos 
18598b9484cSchristos @deftypefn Replacement char* make_temp_file (const char *@var{suffix})
18698b9484cSchristos 
18798b9484cSchristos Return a temporary file name (as a string) or @code{NULL} if unable to
18898b9484cSchristos create one.  @var{suffix} is a suffix to append to the file name.  The
18998b9484cSchristos string is @code{malloc}ed, and the temporary file has been created.
19098b9484cSchristos 
19198b9484cSchristos @end deftypefn
19298b9484cSchristos 
19398b9484cSchristos */
19498b9484cSchristos 
19598b9484cSchristos char *
196314094e7Schristos make_temp_file_with_prefix (const char *prefix, const char *suffix)
19798b9484cSchristos {
19898b9484cSchristos   const char *base = choose_tmpdir ();
19998b9484cSchristos   char *temp_filename;
200314094e7Schristos   int base_len, suffix_len, prefix_len;
20198b9484cSchristos   int fd;
20298b9484cSchristos 
203314094e7Schristos   if (prefix == 0)
204314094e7Schristos     prefix = "cc";
205314094e7Schristos 
20698b9484cSchristos   if (suffix == 0)
20798b9484cSchristos     suffix = "";
20898b9484cSchristos 
20998b9484cSchristos   base_len = strlen (base);
210314094e7Schristos   prefix_len = strlen (prefix);
21198b9484cSchristos   suffix_len = strlen (suffix);
21298b9484cSchristos 
21398b9484cSchristos   temp_filename = XNEWVEC (char, base_len
21498b9484cSchristos 			   + TEMP_FILE_LEN
215314094e7Schristos 			   + suffix_len
216314094e7Schristos 			   + prefix_len + 1);
21798b9484cSchristos   strcpy (temp_filename, base);
218314094e7Schristos   strcpy (temp_filename + base_len, prefix);
219314094e7Schristos   strcpy (temp_filename + base_len + prefix_len, TEMP_FILE);
220314094e7Schristos   strcpy (temp_filename + base_len + prefix_len + TEMP_FILE_LEN, suffix);
22198b9484cSchristos 
22298b9484cSchristos   fd = mkstemps (temp_filename, suffix_len);
22398b9484cSchristos   /* Mkstemps failed.  It may be EPERM, ENOSPC etc.  */
22498b9484cSchristos   if (fd == -1)
22598b9484cSchristos     {
22698b9484cSchristos       fprintf (stderr, "Cannot create temporary file in %s: %s\n",
22798b9484cSchristos 	       base, strerror (errno));
22898b9484cSchristos       abort ();
22998b9484cSchristos     }
23098b9484cSchristos   /* We abort on failed close out of sheer paranoia.  */
23198b9484cSchristos   if (close (fd))
23298b9484cSchristos     abort ();
23398b9484cSchristos   return temp_filename;
23498b9484cSchristos }
235314094e7Schristos 
236314094e7Schristos char *
237314094e7Schristos make_temp_file (const char *suffix)
238314094e7Schristos {
239314094e7Schristos   return make_temp_file_with_prefix (NULL, suffix);
240314094e7Schristos }
241