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