Move fn_identity from file-name.c to file-handle-def.c
[pspp] / src / data / file-name.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "data/file-name.h"
20 #include "data/file-handle-def.h"
21
22 #include <ctype.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <sys/stat.h>
28
29 #include <unistd.h>
30
31 #include "data/settings.h"
32 #include "libpspp/hash-functions.h"
33 #include "libpspp/message.h"
34 #include "libpspp/str.h"
35 #include "libpspp/version.h"
36
37 #include "gl/dirname.h"
38 #include "gl/dosname.h"
39 #include "gl/intprops.h"
40 #include "gl/minmax.h"
41 #include "gl/relocatable.h"
42 #include "gl/xalloc.h"
43 #include "gl/xmalloca.h"
44
45 #include "gettext.h"
46 #define _(msgid) gettext (msgid)
47
48 \f
49 /* Functions for performing operations on file names. */
50
51 /* Searches for a configuration file with name NAME in the directories given in
52    PATH, which is terminated by a null pointer.  Returns the full name of the
53    first file found, which the caller is responsible for freeing with free(),
54    or NULL if none is found. */
55 char *
56 fn_search_path (const char *base_name, char **path)
57 {
58   size_t i;
59
60   if (fn_is_absolute (base_name))
61     return xstrdup (base_name);
62
63   for (i = 0; path[i] != NULL; i++)
64     {
65       const char *dir = path[i];
66       char *file;
67
68       if (!strcmp (dir, "") || !strcmp (dir, "."))
69         file = xstrdup (base_name);
70       else if (ISSLASH (dir[strlen (dir) - 1]))
71         file = xasprintf ("%s%s", dir, base_name);
72       else
73         file = xasprintf ("%s/%s", dir, base_name);
74
75       if (fn_exists (file))
76         return file;
77       free (file);
78     }
79
80   return NULL;
81 }
82
83 /* Returns the extension part of FILE_NAME as a malloc()'d string.
84    If FILE_NAME does not have an extension, returns an empty
85    string. */
86 char *
87 fn_extension (const char *file_name)
88 {
89   const char *extension = strrchr (file_name, '.');
90   if (extension == NULL)
91     extension = "";
92   return xstrdup (extension);
93 }
94 \f
95 /* Find out information about files. */
96
97 /* Returns true iff NAME specifies an absolute file name. */
98 bool
99 fn_is_absolute (const char *name)
100 {
101   return IS_ABSOLUTE_FILE_NAME (name);
102 }
103
104 /* Returns true if file with name NAME exists, and that file is not a
105    directory */
106 bool
107 fn_exists (const char *name)
108 {
109   struct stat temp;
110   if ( stat (name, &temp) != 0 )
111     return false;
112
113   return ! S_ISDIR (temp.st_mode);
114 }
115
116 \f
117 /* Basic file handling. */
118
119 #if HAVE_POPEN
120 /* Used for giving an error message on a set_safer security
121    violation. */
122 static FILE *
123 safety_violation (const char *fn)
124 {
125   msg (SE, _("Not opening pipe file `%s' because %s option set."), fn, "SAFER");
126   errno = EPERM;
127   return NULL;
128 }
129 #endif
130
131 /* File open routine that understands `-' as stdin/stdout and `|cmd'
132    as a pipe to command `cmd'.  Returns resultant FILE on success,
133    NULL on failure.  If NULL is returned then errno is set to a
134    sensible value.  */
135 FILE *
136 fn_open (const char *fn, const char *mode)
137 {
138   assert (mode[0] == 'r' || mode[0] == 'w' || mode[0] == 'a');
139
140   if (mode[0] == 'r')
141     {
142       if (!strcmp (fn, "stdin") || !strcmp (fn, "-"))
143         return stdin;
144     }
145   else
146     {
147       if (!strcmp (fn, "stdout") || !strcmp (fn, "-"))
148         return stdout;
149       if (!strcmp (fn, "stderr"))
150         return stderr;
151     }
152
153 #if HAVE_POPEN
154   if (fn[0] == '|')
155     {
156       if (settings_get_safer_mode ())
157         return safety_violation (fn);
158
159       return popen (&fn[1], mode[0] == 'r' ? "r" : "w");
160     }
161   else if (*fn && fn[strlen (fn) - 1] == '|')
162     {
163       char *s;
164       FILE *f;
165
166       if (settings_get_safer_mode ())
167         return safety_violation (fn);
168
169       s = xmalloca (strlen (fn));
170       memcpy (s, fn, strlen (fn) - 1);
171       s[strlen (fn) - 1] = 0;
172
173       f = popen (s, mode[0] == 'r' ? "r" : "w");
174
175       freea (s);
176
177       return f;
178     }
179   else
180 #endif
181     return fopen (fn, mode);
182 }
183
184 /* Counterpart to fn_open that closes file F with name FN; returns 0
185    on success, EOF on failure.  If EOF is returned, errno is set to a
186    sensible value. */
187 int
188 fn_close (const char *fn, FILE *f)
189 {
190   if (fileno (f) == STDIN_FILENO
191       || fileno (f) == STDOUT_FILENO
192       || fileno (f) == STDERR_FILENO)
193     return 0;
194 #if HAVE_POPEN
195   else if (fn[0] == '|' || (*fn && fn[strlen (fn) - 1] == '|'))
196     {
197       pclose (f);
198       return 0;
199     }
200 #endif
201   else
202     return fclose (f);
203 }
204
205
206 #ifdef WIN32
207
208 /* Apparently windoze users like to see output dumped into their home directory,
209    not the current directory (!) */
210 const char *
211 default_output_path (void)
212 {
213   static char *path = NULL;
214
215   if ( path == NULL)
216     {
217       /* Windows NT defines HOMEDRIVE and HOMEPATH.  But give preference
218          to HOME, because the user can change HOME.  */
219
220       const char *home_dir = getenv ("HOME");
221       int i;
222
223       if (home_dir == NULL)
224         {
225           const char *home_drive = getenv ("HOMEDRIVE");
226           const char *home_path = getenv ("HOMEPATH");
227
228           if (home_drive != NULL && home_path != NULL)
229             home_dir = xasprintf ("%s%s",
230                                   home_drive, home_path);
231         }
232
233       if (home_dir == NULL)
234         home_dir = "c:/users/default"; /* poor default */
235
236       /* Copy home_dir into path.  Add a slash at the end but
237          only if there isn't already one there, because Windows
238          treats // specially. */
239       if (home_dir[0] == '\0'
240           || strchr ("/\\", home_dir[strlen (home_dir) - 1]) == NULL)
241         path = xasprintf ("%s%c", home_dir, '/');
242       else
243         path = xstrdup (home_dir);
244
245       for(i = 0; i < strlen (path); i++)
246         if (path[i] == '\\') path[i] = '/';
247     }
248
249   return path;
250 }
251
252 #else
253
254 /* ... whereas the rest of the world just likes it to be
255    put "here" for easy access. */
256 const char *
257 default_output_path (void)
258 {
259   static char current_dir[]  = "";
260
261   return current_dir;
262 }
263
264 #endif
265