Home | History | Annotate | Download | only in libiberty
      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 const 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 const 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