File Open: Don't try to analyse the file if it's a directory.
[pspp-builds.git] / 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
21 #include <ctype.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28
29 #include "data/settings.h"
30 #include "libpspp/hash-functions.h"
31 #include "libpspp/message.h"
32 #include "libpspp/str.h"
33 #include "libpspp/version.h"
34
35 #include "gl/dirname.h"
36 #include "gl/dosname.h"
37 #include "gl/intprops.h"
38 #include "gl/minmax.h"
39 #include "gl/relocatable.h"
40 #include "gl/xalloc.h"
41 #include "gl/xmalloca.h"
42
43 #include "gettext.h"
44 #define _(msgid) gettext (msgid)
45
46 #if defined _WIN32 || defined __WIN32__
47 #define WIN32_LEAN_AND_MEAN  /* avoid including junk */
48 #include <windows.h>
49 #endif
50 \f
51 /* Functions for performing operations on file names. */
52
53 /* Searches for a configuration file with name NAME in the directories given in
54    PATH, which is terminated by a null pointer.  Returns the full name of the
55    first file found, which the caller is responsible for freeing with free(),
56    or NULL if none is found. */
57 char *
58 fn_search_path (const char *base_name, char **path)
59 {
60   size_t i;
61
62   if (fn_is_absolute (base_name))
63     return xstrdup (base_name);
64
65   for (i = 0; path[i] != NULL; i++)
66     {
67       const char *dir = path[i];
68       char *file;
69
70       if (!strcmp (dir, "") || !strcmp (dir, "."))
71         file = xstrdup (base_name);
72       else if (ISSLASH (dir[strlen (dir) - 1]))
73         file = xasprintf ("%s%s", dir, base_name);
74       else
75         file = xasprintf ("%s/%s", dir, base_name);
76
77       if (fn_exists (file))
78         return file;
79       free (file);
80     }
81
82   return NULL;
83 }
84
85 /* Returns the directory part of FILE_NAME, as a malloc()'d
86    string. */
87 char *
88 fn_dir_name (const char *file_name)
89 {
90   return dir_name (file_name);
91 }
92
93 /* Returns the extension part of FILE_NAME as a malloc()'d string.
94    If FILE_NAME does not have an extension, returns an empty
95    string. */
96 char *
97 fn_extension (const char *file_name)
98 {
99   const char *extension = strrchr (file_name, '.');
100   if (extension == NULL)
101     extension = "";
102   return xstrdup (extension);
103 }
104 \f
105 /* Find out information about files. */
106
107 /* Returns true iff NAME specifies an absolute file name. */
108 bool
109 fn_is_absolute (const char *name)
110 {
111   return IS_ABSOLUTE_FILE_NAME (name);
112 }
113
114 /* Returns true if FILE_NAME is a virtual file that doesn't
115    really exist on disk, false if it's a real file name. */
116 bool
117 fn_is_special (const char *file_name)
118 {
119   if (!strcmp (file_name, "-") || !strcmp (file_name, "stdin")
120       || !strcmp (file_name, "stdout") || !strcmp (file_name, "stderr")
121 #ifdef HAVE_POPEN
122       || file_name[0] == '|'
123       || (*file_name && file_name[strlen (file_name) - 1] == '|')
124 #endif
125       )
126     return true;
127
128   return false;
129 }
130
131 /* Returns true if file with name NAME exists, and that file is not a
132    directory */
133 bool
134 fn_exists (const char *name)
135 {
136   struct stat temp;
137   if ( stat (name, &temp) != 0 )
138     return false;
139
140   return ! S_ISDIR (temp.st_mode);
141 }
142 \f
143 /* Environment variables. */
144
145 /* Simulates $VER and $ARCH environment variables. */
146 const char *
147 fn_getenv (const char *s)
148 {
149   if (!strcmp (s, "VER"))
150     return fn_getenv_default ("STAT_VER", bare_version);
151   else if (!strcmp (s, "ARCH"))
152     return fn_getenv_default ("STAT_ARCH", host_system);
153   else
154     return getenv (s);
155 }
156
157 /* Returns getenv(KEY) if that's non-NULL; else returns DEF. */
158 const char *
159 fn_getenv_default (const char *key, const char *def)
160 {
161   const char *value = getenv (key);
162   return value ? value : def;
163 }
164 \f
165 /* Basic file handling. */
166
167 #if HAVE_POPEN
168 /* Used for giving an error message on a set_safer security
169    violation. */
170 static FILE *
171 safety_violation (const char *fn)
172 {
173   msg (SE, _("Not opening pipe file `%s' because SAFER option set."), fn);
174   errno = EPERM;
175   return NULL;
176 }
177 #endif
178
179 /* File open routine that understands `-' as stdin/stdout and `|cmd'
180    as a pipe to command `cmd'.  Returns resultant FILE on success,
181    NULL on failure.  If NULL is returned then errno is set to a
182    sensible value.  */
183 FILE *
184 fn_open (const char *fn, const char *mode)
185 {
186   assert (mode[0] == 'r' || mode[0] == 'w' || mode[0] == 'a');
187
188   if (mode[0] == 'r')
189     {
190       if (!strcmp (fn, "stdin") || !strcmp (fn, "-"))
191         return stdin;
192     }
193   else
194     {
195       if (!strcmp (fn, "stdout") || !strcmp (fn, "-"))
196         return stdout;
197       if (!strcmp (fn, "stderr"))
198         return stderr;
199     }
200
201 #if HAVE_POPEN
202   if (fn[0] == '|')
203     {
204       if (settings_get_safer_mode ())
205         return safety_violation (fn);
206
207       return popen (&fn[1], mode[0] == 'r' ? "r" : "w");
208     }
209   else if (*fn && fn[strlen (fn) - 1] == '|')
210     {
211       char *s;
212       FILE *f;
213
214       if (settings_get_safer_mode ())
215         return safety_violation (fn);
216
217       s = xmalloca (strlen (fn));
218       memcpy (s, fn, strlen (fn) - 1);
219       s[strlen (fn) - 1] = 0;
220
221       f = popen (s, mode[0] == 'r' ? "r" : "w");
222
223       freea (s);
224
225       return f;
226     }
227   else
228 #endif
229     return fopen (fn, mode);
230 }
231
232 /* Counterpart to fn_open that closes file F with name FN; returns 0
233    on success, EOF on failure.  If EOF is returned, errno is set to a
234    sensible value. */
235 int
236 fn_close (const char *fn, FILE *f)
237 {
238   if (fileno (f) == STDIN_FILENO
239       || fileno (f) == STDOUT_FILENO
240       || fileno (f) == STDERR_FILENO)
241     return 0;
242 #if HAVE_POPEN
243   else if (fn[0] == '|' || (*fn && fn[strlen (fn) - 1] == '|'))
244     {
245       pclose (f);
246       return 0;
247     }
248 #endif
249   else
250     return fclose (f);
251 }
252
253 /* Creates a new file named FN with the given PERMISSIONS bits,
254    and returns a stream for it or a null pointer on failure.
255    MODE should be "w" or "wb". */
256 FILE *
257 create_stream (const char *fn, const char *mode, mode_t permissions)
258 {
259   int fd;
260   FILE *stream;
261
262   fd = open (fn, O_WRONLY | O_CREAT | O_TRUNC, permissions);
263   if (fd < 0)
264     return NULL;
265
266   stream = fdopen (fd, mode);
267   if (stream == NULL)
268     {
269       int save_errno = errno;
270       close (fd);
271       errno = save_errno;
272     }
273
274   return stream;
275 }
276
277 /* A file's identity:
278
279    - For a file that exists, this is its device and inode.
280
281    - For a file that does not exist, but which has a directory
282      name that exists, this is the device and inode of the
283      directory, plus the file's base name.
284
285    - For a file that does not exist and has a nonexistent
286      directory, this is the file name.
287
288    Windows doesn't have inode numbers, so we just use the name
289    there. */
290 struct file_identity
291 {
292   dev_t device;               /* Device number. */
293   ino_t inode;                /* Inode number. */
294   char *name;                 /* File name, where needed, otherwise NULL. */
295 };
296
297 /* Returns a pointer to a dynamically allocated structure whose
298    value can be used to tell whether two files are actually the
299    same file.  Returns a null pointer if no information about the
300    file is available, perhaps because it does not exist.  The
301    caller is responsible for freeing the structure with
302    fn_free_identity() when finished. */
303 struct file_identity *
304 fn_get_identity (const char *file_name)
305 {
306   struct file_identity *identity = xmalloc (sizeof *identity);
307
308 #if !(defined _WIN32 || defined __WIN32__)
309   struct stat s;
310   if (lstat (file_name, &s) == 0)
311     {
312       identity->device = s.st_dev;
313       identity->inode = s.st_ino;
314       identity->name = NULL;
315     }
316   else
317     {
318       char *dir = dir_name (file_name);
319       if (last_component (file_name) != NULL && stat (dir, &s) == 0)
320         {
321           identity->device = s.st_dev;
322           identity->inode = s.st_ino;
323           identity->name = base_name (file_name);
324         }
325       else
326         {
327           identity->device = 0;
328           identity->inode = 0;
329           identity->name = xstrdup (file_name);
330         }
331       free (dir);
332     }
333 #else /* Windows */
334   char cname[PATH_MAX];
335   int ok = GetFullPathName (file_name, sizeof cname, cname, NULL);
336   identity->device = 0;
337   identity->inode = 0;
338   identity->name = xstrdup (ok ? cname : file_name);
339   str_lowercase (identity->name);
340 #endif /* Windows */
341
342   return identity;
343 }
344
345 /* Frees IDENTITY obtained from fn_get_identity(). */
346 void
347 fn_free_identity (struct file_identity *identity)
348 {
349   if (identity != NULL)
350     {
351       free (identity->name);
352       free (identity);
353     }
354 }
355
356 /* Compares A and B, returning a strcmp()-type result. */
357 int
358 fn_compare_file_identities (const struct file_identity *a,
359                             const struct file_identity *b)
360 {
361   if (a->device != b->device)
362     return a->device < b->device ? -1 : 1;
363   else if (a->inode != b->inode)
364     return a->inode < b->inode ? -1 : 1;
365   else if (a->name != NULL)
366     return b->name != NULL ? strcmp (a->name, b->name) : 1;
367   else
368     return b->name != NULL ? -1 : 0;
369 }
370
371 /* Returns a hash value for IDENTITY. */
372 unsigned int
373 fn_hash_identity (const struct file_identity *identity)
374 {
375   unsigned int hash = hash_int (identity->device, identity->inode);
376   if (identity->name != NULL)
377     hash = hash_string (identity->name, hash);
378   return hash;
379 }
380
381 #ifdef WIN32
382
383 /* Apparently windoze users like to see output dumped into their home directory,
384    not the current directory (!) */
385 const char *
386 default_output_path (void)
387 {
388   static char *path = NULL;
389
390   if ( path == NULL)
391     {
392       /* Windows NT defines HOMEDRIVE and HOMEPATH.  But give preference
393          to HOME, because the user can change HOME.  */
394
395       const char *home_dir = getenv ("HOME");
396       int i;
397
398       if (home_dir == NULL)
399         {
400           const char *home_drive = getenv ("HOMEDRIVE");
401           const char *home_path = getenv ("HOMEPATH");
402
403           if (home_drive != NULL && home_path != NULL)
404             home_dir = xasprintf ("%s%s",
405                                   home_drive, home_path);
406         }
407
408       if (home_dir == NULL)
409         home_dir = "c:/users/default"; /* poor default */
410
411       /* Copy home_dir into path.  Add a slash at the end but
412          only if there isn't already one there, because Windows
413          treats // specially. */
414       if (home_dir[0] == '\0'
415           || strchr ("/\\", home_dir[strlen (home_dir) - 1]) == NULL)
416         path = xasprintf ("%s%c", home_dir, '/');
417       else
418         path = xstrdup (home_dir);
419
420       for(i = 0; i < strlen (path); i++)
421         if (path[i] == '\\') path[i] = '/';
422     }
423
424   return path;
425 }
426
427 #else
428
429 /* ... whereas the rest of the world just likes it to be
430    put "here" for easy access. */
431 const char *
432 default_output_path (void)
433 {
434   static char current_dir[]  = "";
435
436   return current_dir;
437 }
438
439 #endif
440