better tests
[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/i18n.h"
35 #include "libpspp/str.h"
36 #include "libpspp/version.h"
37
38 #include "gl/dirname.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
52 /* Returns the extension part of FILE_NAME as a malloc()'d string.
53    If FILE_NAME does not have an extension, returns an empty
54    string. */
55 char *
56 fn_extension (const struct file_handle *fh)
57 {
58   const char *file_name = fh_get_file_name (fh);
59
60   const char *extension = strrchr (file_name, '.');
61   if (extension == NULL)
62     extension = "";
63   return xstrdup (extension);
64 }
65 \f
66 /* Find out information about files. */
67
68 /* Returns true iff NAME specifies an absolute file name. */
69 static bool
70 fn_is_absolute (const char *name)
71 {
72   return IS_ABSOLUTE_FILE_NAME (name);
73 }
74
75
76 /* Searches for a file with name NAME in the directories given in
77    PATH, which is terminated by a null pointer.  Returns the full name of the
78    first file found, which the caller is responsible for freeing with free(),
79    or NULL if none is found. */
80 char *
81 fn_search_path (const char *base_name, char **path)
82 {
83   size_t i;
84
85   if (fn_is_absolute (base_name))
86     return xstrdup (base_name);
87
88   for (i = 0; path[i] != NULL; i++)
89     {
90       const char *dir = path[i];
91       char *file;
92
93       if (!strcmp (dir, "") || !strcmp (dir, "."))
94         file = xstrdup (base_name);
95       else if (ISSLASH (dir[strlen (dir) - 1]))
96         file = xasprintf ("%s%s", dir, base_name);
97       else
98         file = xasprintf ("%s/%s", dir, base_name);
99
100       struct stat temp;
101       if (((stat (file, &temp) == 0) && (! S_ISDIR (temp.st_mode))))
102         return file;
103
104       free (file);
105     }
106
107   return NULL;
108 }
109
110
111 /* Returns true if file with name NAME exists, and that file is not a
112    directory */
113 bool
114 fn_exists (const struct file_handle *fh)
115 {
116   const char *name = fh_get_file_name (fh);
117   struct stat temp;
118   if (stat (name, &temp) != 0)
119     return false;
120
121   return ! S_ISDIR (temp.st_mode);
122 }
123
124 \f
125 /* Basic file handling. */
126
127 #if HAVE_POPEN
128 /* Used for giving an error message on a set_safer security
129    violation. */
130 static FILE *
131 safety_violation (const char *fn)
132 {
133   msg (SE, _("Not opening pipe file `%s' because %s option set."), fn, "SAFER");
134   errno = EPERM;
135   return NULL;
136 }
137 #endif
138
139 /* File open routine that understands `-' as stdin/stdout and `|cmd'
140    as a pipe to command `cmd'.  Returns resultant FILE on success,
141    NULL on failure.  If NULL is returned then errno is set to a
142    sensible value.  */
143 FILE *
144 fn_open (const struct file_handle *fh, const char *mode)
145 {
146   const char *fn = fh_get_file_name (fh);
147
148   assert (mode[0] == 'r' || mode[0] == 'w' || mode[0] == 'a');
149
150   if (mode[0] == 'r')
151     {
152       if (!strcmp (fn, "stdin") || !strcmp (fn, "-"))
153         return stdin;
154     }
155   else
156     {
157       if (!strcmp (fn, "stdout") || !strcmp (fn, "-"))
158         return stdout;
159       if (!strcmp (fn, "stderr"))
160         return stderr;
161     }
162
163 #if HAVE_POPEN
164   if (fn[0] == '|')
165     {
166       if (settings_get_safer_mode ())
167         return safety_violation (fn);
168
169       return popen (&fn[1], mode[0] == 'r' ? "r" : "w");
170     }
171   else if (*fn && fn[strlen (fn) - 1] == '|')
172     {
173       char *s;
174       FILE *f;
175
176       if (settings_get_safer_mode ())
177         return safety_violation (fn);
178
179       s = xmalloca (strlen (fn));
180       memcpy (s, fn, strlen (fn) - 1);
181       s[strlen (fn) - 1] = 0;
182
183       f = popen (s, mode[0] == 'r' ? "r" : "w");
184
185       freea (s);
186
187       return f;
188     }
189   else
190 #endif
191
192 #if WIN32
193     {
194       wchar_t *ss = convert_to_filename_encoding (fn, strlen (fn), fh_get_file_name_encoding (fh));
195       wchar_t *m =  (wchar_t *) recode_string ("UTF-16LE", "ASCII", mode, strlen (mode));
196       FILE *fp = _wfopen (ss, m);
197       free (m);
198       free (ss);
199       return fp;
200     }
201 #else
202     return fopen (fn, mode);
203 #endif
204 }
205
206 /* Counterpart to fn_open that closes file F with name FN; returns 0
207    on success, EOF on failure.  If EOF is returned, errno is set to a
208    sensible value. */
209 int
210 fn_close (const struct file_handle *fh, FILE *f)
211 {
212   const char *fn = fh_get_file_name (fh);
213   if (fileno (f) == STDIN_FILENO
214       || fileno (f) == STDOUT_FILENO
215       || fileno (f) == STDERR_FILENO)
216     return 0;
217 #if HAVE_POPEN
218   else if (fn[0] == '|' || (*fn && fn[strlen (fn) - 1] == '|'))
219     {
220       pclose (f);
221       return 0;
222     }
223 #endif
224   else
225     return fclose (f);
226 }
227
228
229 #ifdef WIN32
230
231 /* Apparently windoze users like to see output dumped into their home directory,
232    not the current directory (!) */
233 const char *
234 default_output_path (void)
235 {
236   static char *path = NULL;
237
238   if (path == NULL)
239     {
240       /* Windows NT defines HOMEDRIVE and HOMEPATH.  But give preference
241          to HOME, because the user can change HOME.  */
242       const char *home_dir = getenv ("HOME");
243       int i;
244
245       if (home_dir == NULL)
246         {
247           const char *home_drive = getenv ("HOMEDRIVE");
248           const char *home_path = getenv ("HOMEPATH");
249
250           if (home_drive != NULL && home_path != NULL)
251             home_dir = xasprintf ("%s%s",
252                                   home_drive, home_path);
253         }
254
255       if (home_dir == NULL)
256         home_dir = "c:/users/default"; /* poor default */
257
258       /* Copy home_dir into path.  Add a slash at the end but
259          only if there isn't already one there, because Windows
260          treats // specially. */
261       if (home_dir[0] == '\0'
262           || strchr ("/\\", home_dir[strlen (home_dir) - 1]) == NULL)
263         path = xasprintf ("%s%c", home_dir, '/');
264       else
265         path = xstrdup (home_dir);
266
267       for(i = 0; i < strlen (path); i++)
268         if (path[i] == '\\') path[i] = '/';
269     }
270
271   return path;
272 }
273
274 #else
275
276 /* ... whereas the rest of the world just likes it to be
277    put "here" for easy access. */
278 const char *
279 default_output_path (void)
280 {
281   static char current_dir[]  = "";
282
283   return current_dir;
284 }
285
286 #endif
287