3e9fa6914a72d3a5dcc8e011bbcde94d274a782e
[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
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 %s option set."), fn, "SAFER");
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
254 /* A file's identity:
255
256    - For a file that exists, this is its device and inode.
257
258    - For a file that does not exist, but which has a directory
259      name that exists, this is the device and inode of the
260      directory, plus the file's base name.
261
262    - For a file that does not exist and has a nonexistent
263      directory, this is the file name.
264
265    Windows doesn't have inode numbers, so we just use the name
266    there. */
267 struct file_identity
268 {
269   dev_t device;               /* Device number. */
270   ino_t inode;                /* Inode number. */
271   char *name;                 /* File name, where needed, otherwise NULL. */
272 };
273
274 /* Returns a pointer to a dynamically allocated structure whose
275    value can be used to tell whether two files are actually the
276    same file.  Returns a null pointer if no information about the
277    file is available, perhaps because it does not exist.  The
278    caller is responsible for freeing the structure with
279    fn_free_identity() when finished. */
280 struct file_identity *
281 fn_get_identity (const char *file_name)
282 {
283   struct file_identity *identity = xmalloc (sizeof *identity);
284
285 #if !(defined _WIN32 || defined __WIN32__)
286   struct stat s;
287   if (lstat (file_name, &s) == 0)
288     {
289       identity->device = s.st_dev;
290       identity->inode = s.st_ino;
291       identity->name = NULL;
292     }
293   else
294     {
295       char *dir = dir_name (file_name);
296       if (last_component (file_name) != NULL && stat (dir, &s) == 0)
297         {
298           identity->device = s.st_dev;
299           identity->inode = s.st_ino;
300           identity->name = base_name (file_name);
301         }
302       else
303         {
304           identity->device = 0;
305           identity->inode = 0;
306           identity->name = xstrdup (file_name);
307         }
308       free (dir);
309     }
310 #else /* Windows */
311   char cname[PATH_MAX];
312   int ok = GetFullPathName (file_name, sizeof cname, cname, NULL);
313   identity->device = 0;
314   identity->inode = 0;
315   identity->name = xstrdup (ok ? cname : file_name);
316   str_lowercase (identity->name);
317 #endif /* Windows */
318
319   return identity;
320 }
321
322 /* Frees IDENTITY obtained from fn_get_identity(). */
323 void
324 fn_free_identity (struct file_identity *identity)
325 {
326   if (identity != NULL)
327     {
328       free (identity->name);
329       free (identity);
330     }
331 }
332
333 /* Compares A and B, returning a strcmp()-type result. */
334 int
335 fn_compare_file_identities (const struct file_identity *a,
336                             const struct file_identity *b)
337 {
338   if (a->device != b->device)
339     return a->device < b->device ? -1 : 1;
340   else if (a->inode != b->inode)
341     return a->inode < b->inode ? -1 : 1;
342   else if (a->name != NULL)
343     return b->name != NULL ? strcmp (a->name, b->name) : 1;
344   else
345     return b->name != NULL ? -1 : 0;
346 }
347
348 /* Returns a hash value for IDENTITY. */
349 unsigned int
350 fn_hash_identity (const struct file_identity *identity)
351 {
352   unsigned int hash = hash_int (identity->device, identity->inode);
353   if (identity->name != NULL)
354     hash = hash_string (identity->name, hash);
355   return hash;
356 }
357
358 #ifdef WIN32
359
360 /* Apparently windoze users like to see output dumped into their home directory,
361    not the current directory (!) */
362 const char *
363 default_output_path (void)
364 {
365   static char *path = NULL;
366
367   if ( path == NULL)
368     {
369       /* Windows NT defines HOMEDRIVE and HOMEPATH.  But give preference
370          to HOME, because the user can change HOME.  */
371
372       const char *home_dir = getenv ("HOME");
373       int i;
374
375       if (home_dir == NULL)
376         {
377           const char *home_drive = getenv ("HOMEDRIVE");
378           const char *home_path = getenv ("HOMEPATH");
379
380           if (home_drive != NULL && home_path != NULL)
381             home_dir = xasprintf ("%s%s",
382                                   home_drive, home_path);
383         }
384
385       if (home_dir == NULL)
386         home_dir = "c:/users/default"; /* poor default */
387
388       /* Copy home_dir into path.  Add a slash at the end but
389          only if there isn't already one there, because Windows
390          treats // specially. */
391       if (home_dir[0] == '\0'
392           || strchr ("/\\", home_dir[strlen (home_dir) - 1]) == NULL)
393         path = xasprintf ("%s%c", home_dir, '/');
394       else
395         path = xstrdup (home_dir);
396
397       for(i = 0; i < strlen (path); i++)
398         if (path[i] == '\\') path[i] = '/';
399     }
400
401   return path;
402 }
403
404 #else
405
406 /* ... whereas the rest of the world just likes it to be
407    put "here" for easy access. */
408 const char *
409 default_output_path (void)
410 {
411   static char current_dir[]  = "";
412
413   return current_dir;
414 }
415
416 #endif
417