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