Remove unused function fn_is_special
[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 with name NAME exists, and that file is not a
115    directory */
116 bool
117 fn_exists (const char *name)
118 {
119   struct stat temp;
120   if ( stat (name, &temp) != 0 )
121     return false;
122
123   return ! S_ISDIR (temp.st_mode);
124 }
125 \f
126 /* Environment variables. */
127
128 /* Simulates $VER and $ARCH environment variables. */
129 const char *
130 fn_getenv (const char *s)
131 {
132   if (!strcmp (s, "VER"))
133     return fn_getenv_default ("STAT_VER", bare_version);
134   else if (!strcmp (s, "ARCH"))
135     return fn_getenv_default ("STAT_ARCH", host_system);
136   else
137     return getenv (s);
138 }
139
140 /* Returns getenv(KEY) if that's non-NULL; else returns DEF. */
141 const char *
142 fn_getenv_default (const char *key, const char *def)
143 {
144   const char *value = getenv (key);
145   return value ? value : def;
146 }
147 \f
148 /* Basic file handling. */
149
150 #if HAVE_POPEN
151 /* Used for giving an error message on a set_safer security
152    violation. */
153 static FILE *
154 safety_violation (const char *fn)
155 {
156   msg (SE, _("Not opening pipe file `%s' because %s option set."), fn, "SAFER");
157   errno = EPERM;
158   return NULL;
159 }
160 #endif
161
162 /* File open routine that understands `-' as stdin/stdout and `|cmd'
163    as a pipe to command `cmd'.  Returns resultant FILE on success,
164    NULL on failure.  If NULL is returned then errno is set to a
165    sensible value.  */
166 FILE *
167 fn_open (const char *fn, const char *mode)
168 {
169   assert (mode[0] == 'r' || mode[0] == 'w' || mode[0] == 'a');
170
171   if (mode[0] == 'r')
172     {
173       if (!strcmp (fn, "stdin") || !strcmp (fn, "-"))
174         return stdin;
175     }
176   else
177     {
178       if (!strcmp (fn, "stdout") || !strcmp (fn, "-"))
179         return stdout;
180       if (!strcmp (fn, "stderr"))
181         return stderr;
182     }
183
184 #if HAVE_POPEN
185   if (fn[0] == '|')
186     {
187       if (settings_get_safer_mode ())
188         return safety_violation (fn);
189
190       return popen (&fn[1], mode[0] == 'r' ? "r" : "w");
191     }
192   else if (*fn && fn[strlen (fn) - 1] == '|')
193     {
194       char *s;
195       FILE *f;
196
197       if (settings_get_safer_mode ())
198         return safety_violation (fn);
199
200       s = xmalloca (strlen (fn));
201       memcpy (s, fn, strlen (fn) - 1);
202       s[strlen (fn) - 1] = 0;
203
204       f = popen (s, mode[0] == 'r' ? "r" : "w");
205
206       freea (s);
207
208       return f;
209     }
210   else
211 #endif
212     return fopen (fn, mode);
213 }
214
215 /* Counterpart to fn_open that closes file F with name FN; returns 0
216    on success, EOF on failure.  If EOF is returned, errno is set to a
217    sensible value. */
218 int
219 fn_close (const char *fn, FILE *f)
220 {
221   if (fileno (f) == STDIN_FILENO
222       || fileno (f) == STDOUT_FILENO
223       || fileno (f) == STDERR_FILENO)
224     return 0;
225 #if HAVE_POPEN
226   else if (fn[0] == '|' || (*fn && fn[strlen (fn) - 1] == '|'))
227     {
228       pclose (f);
229       return 0;
230     }
231 #endif
232   else
233     return fclose (f);
234 }
235
236
237 /* A file's identity:
238
239    - For a file that exists, this is its device and inode.
240
241    - For a file that does not exist, but which has a directory
242      name that exists, this is the device and inode of the
243      directory, plus the file's base name.
244
245    - For a file that does not exist and has a nonexistent
246      directory, this is the file name.
247
248    Windows doesn't have inode numbers, so we just use the name
249    there. */
250 struct file_identity
251 {
252   dev_t device;               /* Device number. */
253   ino_t inode;                /* Inode number. */
254   char *name;                 /* File name, where needed, otherwise NULL. */
255 };
256
257 /* Returns a pointer to a dynamically allocated structure whose
258    value can be used to tell whether two files are actually the
259    same file.  Returns a null pointer if no information about the
260    file is available, perhaps because it does not exist.  The
261    caller is responsible for freeing the structure with
262    fn_free_identity() when finished. */
263 struct file_identity *
264 fn_get_identity (const char *file_name)
265 {
266   struct file_identity *identity = xmalloc (sizeof *identity);
267
268 #if !(defined _WIN32 || defined __WIN32__)
269   struct stat s;
270   if (lstat (file_name, &s) == 0)
271     {
272       identity->device = s.st_dev;
273       identity->inode = s.st_ino;
274       identity->name = NULL;
275     }
276   else
277     {
278       char *dir = dir_name (file_name);
279       if (last_component (file_name) != NULL && stat (dir, &s) == 0)
280         {
281           identity->device = s.st_dev;
282           identity->inode = s.st_ino;
283           identity->name = base_name (file_name);
284         }
285       else
286         {
287           identity->device = 0;
288           identity->inode = 0;
289           identity->name = xstrdup (file_name);
290         }
291       free (dir);
292     }
293 #else /* Windows */
294   char cname[PATH_MAX];
295   int ok = GetFullPathName (file_name, sizeof cname, cname, NULL);
296   identity->device = 0;
297   identity->inode = 0;
298   identity->name = xstrdup (ok ? cname : file_name);
299   str_lowercase (identity->name);
300 #endif /* Windows */
301
302   return identity;
303 }
304
305 /* Frees IDENTITY obtained from fn_get_identity(). */
306 void
307 fn_free_identity (struct file_identity *identity)
308 {
309   if (identity != NULL)
310     {
311       free (identity->name);
312       free (identity);
313     }
314 }
315
316 /* Compares A and B, returning a strcmp()-type result. */
317 int
318 fn_compare_file_identities (const struct file_identity *a,
319                             const struct file_identity *b)
320 {
321   if (a->device != b->device)
322     return a->device < b->device ? -1 : 1;
323   else if (a->inode != b->inode)
324     return a->inode < b->inode ? -1 : 1;
325   else if (a->name != NULL)
326     return b->name != NULL ? strcmp (a->name, b->name) : 1;
327   else
328     return b->name != NULL ? -1 : 0;
329 }
330
331 /* Returns a hash value for IDENTITY. */
332 unsigned int
333 fn_hash_identity (const struct file_identity *identity)
334 {
335   unsigned int hash = hash_int (identity->device, identity->inode);
336   if (identity->name != NULL)
337     hash = hash_string (identity->name, hash);
338   return hash;
339 }
340
341 #ifdef WIN32
342
343 /* Apparently windoze users like to see output dumped into their home directory,
344    not the current directory (!) */
345 const char *
346 default_output_path (void)
347 {
348   static char *path = NULL;
349
350   if ( path == NULL)
351     {
352       /* Windows NT defines HOMEDRIVE and HOMEPATH.  But give preference
353          to HOME, because the user can change HOME.  */
354
355       const char *home_dir = getenv ("HOME");
356       int i;
357
358       if (home_dir == NULL)
359         {
360           const char *home_drive = getenv ("HOMEDRIVE");
361           const char *home_path = getenv ("HOMEPATH");
362
363           if (home_drive != NULL && home_path != NULL)
364             home_dir = xasprintf ("%s%s",
365                                   home_drive, home_path);
366         }
367
368       if (home_dir == NULL)
369         home_dir = "c:/users/default"; /* poor default */
370
371       /* Copy home_dir into path.  Add a slash at the end but
372          only if there isn't already one there, because Windows
373          treats // specially. */
374       if (home_dir[0] == '\0'
375           || strchr ("/\\", home_dir[strlen (home_dir) - 1]) == NULL)
376         path = xasprintf ("%s%c", home_dir, '/');
377       else
378         path = xstrdup (home_dir);
379
380       for(i = 0; i < strlen (path); i++)
381         if (path[i] == '\\') path[i] = '/';
382     }
383
384   return path;
385 }
386
387 #else
388
389 /* ... whereas the rest of the world just likes it to be
390    put "here" for easy access. */
391 const char *
392 default_output_path (void)
393 {
394   static char current_dir[]  = "";
395
396   return current_dir;
397 }
398
399 #endif
400