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