e0c6e7b4611a2215e3dbffb4b7abebd96947a682
[pspp-builds.git] / src / data / file-name.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include <libpspp/message.h>
22 #include "file-name.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <libpspp/alloc.h>
28 #include "intprops.h"
29 #include <libpspp/message.h>
30 #include <libpspp/str.h>
31 #include "settings.h"
32 #include <libpspp/version.h>
33 #include "xreadlink.h"
34
35 #include "gettext.h"
36 #define _(msgid) gettext (msgid)
37
38 /* PORTME: Everything in this file is system dependent. */
39
40 #ifdef unix
41 #include <pwd.h>
42 #include <unistd.h>
43 #include <sys/stat.h>
44 #include "stat-macros.h"
45 #endif
46
47 #ifdef __WIN32__
48 #define NOGDI
49 #define NOUSER
50 #define NONLS
51 #include <win32/windows.h>
52 #endif
53
54 #if __DJGPP__
55 #include <sys/stat.h>
56 #endif
57 \f
58 /* Initialization. */
59
60 const char *config_path;
61
62 void
63 fn_init (void)
64 {
65   config_path = fn_getenv_default ("STAT_CONFIG_PATH", default_config_path);
66 }
67 \f
68 /* Functions for performing operations on filenames. */
69
70
71 /* Substitutes $variables as defined by GETENV into TARGET.
72    TARGET must be a string containing the text for which substitution 
73    is required.
74    Supports $var and ${var} syntaxes;
75    $$ substitutes as $. 
76 */
77 void 
78 fn_interp_vars (struct string *target, 
79                 const char *(*getenv) (const char *))
80 {
81   char *input ;
82   char *s ;
83
84   assert (target);
85
86   input = xmalloc(ds_length(target) + 1);
87   s = input;
88
89   strcpy(input, ds_c_str(target));
90
91   if (NULL == strchr (ds_c_str(target), '$'))
92     goto done;
93
94   ds_clear(target);
95
96   for (;;)
97     {
98       switch (*s)
99         {
100         case '\0':
101           goto done ;
102         
103         case '$':
104           s++;
105
106           if (*s == '$')
107             {
108               ds_putc (target, '$');
109               s++;
110             }
111           else
112             {
113               int stop;
114               int start;
115               const char *value;
116
117               start = ds_length (target);
118
119               if (*s == '(')
120                 {
121                   stop = ')';
122                   s++;
123                 }
124               else if (*s == '{')
125                 {
126                   stop = '}';
127                   s++;
128                 }
129               else
130                 stop = 0;
131
132               while (*s && *s != stop
133                      && (stop || isalpha ((unsigned char) *s)))
134                 {
135                   ds_putc (target, *s++);
136                 }
137             
138               value = getenv (ds_c_str (target) + start);
139               ds_truncate (target, start);
140               ds_puts (target, value);
141
142               if (stop && *s == stop)
143                 s++;
144             }
145           break;
146
147         default:
148           ds_putc (target, *s++);
149         }
150     }
151
152  done:
153   free(input);
154   return;
155 }
156
157 #ifdef unix
158 /* Expands csh tilde notation from the path INPUT into a malloc()'d
159    returned string. */
160 char *
161 fn_tilde_expand (const char *input)
162 {
163   struct string output = DS_INITIALIZER;
164   if (input[0] == '~')
165     {
166       const char *home = NULL;
167       const char *remainder = NULL;
168       if (input[1] == '/' || input[1] == '\0')
169         {
170           home = fn_getenv ("HOME");
171           remainder = input + 1; 
172         }
173       else
174         {
175           struct string user_name = DS_INITIALIZER;
176           struct passwd *pwd;
177
178           ds_assign_buffer (&user_name, input + 1, strcspn (input + 1, "/"));
179           pwd = getpwnam (ds_c_str (&user_name));
180           if (pwd != NULL && pwd->pw_dir[0] != '\0')
181             {
182               home = pwd->pw_dir;
183               remainder = input + 1 + ds_length (&user_name);
184             }
185           ds_destroy (&user_name);
186         }
187
188       if (home != NULL) 
189         {
190           ds_puts (&output, home);
191           if (*remainder != '\0')
192             ds_puts (&output, remainder);
193         }
194     }
195   if (ds_is_empty (&output))
196     ds_puts (&output, input);
197   return ds_c_str (&output);
198 }
199 #else /* !unix */
200 char *
201 fn_tilde_expand (const char *input)
202 {
203   return xstrdup (input);
204 }
205 #endif /* !unix */
206
207 /* Searches for a configuration file with name NAME in the path
208    given by PATH, which is tilde- and environment-interpolated.
209    Directories in PATH are delimited by ':'.  Returns the
210    malloc'd full name of the first file found, or NULL if none is
211    found.
212
213    If PREFIX is non-NULL, then it is prefixed to each filename;
214    i.e., it looks like PREFIX/PATH_COMPONENT/NAME.  This is not done
215    with absolute directories in the path. */
216 char *
217 fn_search_path (const char *base_name, const char *path_, const char *prefix)
218 {
219   struct string path;
220   struct string dir = DS_INITIALIZER;
221   struct string file = DS_INITIALIZER;
222   size_t save_idx = 0;
223
224   if (fn_is_absolute (base_name))
225     return fn_tilde_expand (base_name);
226
227   /* Interpolate environment variables. */
228   ds_create (&path, path_);
229   fn_interp_vars (&path, fn_getenv);
230
231   verbose_msg (2, _("searching for \"%s\" in path \"%s\""),
232                base_name, ds_c_str (&path));
233   while (ds_separate (&path, &dir, ":", &save_idx))
234     {
235       /* Do tilde expansion. */
236       if (ds_first (&dir) == '~') 
237         {
238           char *tmp_str = fn_tilde_expand (ds_c_str (&dir));
239           ds_assign_c_str (&dir, tmp_str);
240           free (tmp_str); 
241         }
242
243       /* Construct file name. */
244       ds_clear (&file);
245       if (prefix != NULL && !fn_is_absolute (ds_c_str (&dir)))
246         {
247           ds_puts (&file, prefix);
248           ds_putc (&file, '/');
249         }
250       ds_puts (&file, ds_c_str (&dir));
251       if (ds_length (&dir) && ds_last (&file) != '/')
252         ds_putc (&file, '/');
253       ds_puts (&file, base_name);
254
255       /* Check whether file exists. */
256       if (fn_exists (ds_c_str (&file)))
257         {
258           verbose_msg (2, _("...found \"%s\""), ds_c_str (&file));
259           ds_destroy (&path);
260           ds_destroy (&dir);
261           return ds_c_str (&file);
262         }
263     }
264
265   /* Failure. */
266   verbose_msg (2, _("...not found"));
267   ds_destroy (&path);
268   ds_destroy (&dir);
269   ds_destroy (&file);
270   return NULL;
271 }
272
273 /* fn_normalize(): This very OS-dependent routine canonicalizes
274    filename FN1.  The filename should not need to be the name of an
275    existing file.  Returns a malloc()'d copy of the canonical name.
276    This function must always succeed; if it needs to bail out then it
277    should return xstrdup(FN1).  */
278 #ifdef unix
279 char *
280 fn_normalize (const char *filename)
281 {
282   const char *src;
283   char *fn1, *fn2, *dest;
284   int maxlen;
285
286   if (fn_is_special (filename))
287     return xstrdup (filename);
288   
289   fn1 = fn_tilde_expand (filename);
290
291   /* Follow symbolic links. */
292   for (;;)
293     {
294       fn2 = fn1;
295       fn1 = fn_readlink (fn1);
296       if (!fn1)
297         {
298           fn1 = fn2;
299           break;
300         }
301       free (fn2);
302     }
303
304   maxlen = strlen (fn1) * 2;
305   if (maxlen < 31)
306     maxlen = 31;
307   dest = fn2 = xmalloc (maxlen + 1);
308   src = fn1;
309
310   if (*src == '/')
311     *dest++ = *src++;
312   else
313     {
314       errno = 0;
315       while (getcwd (dest, maxlen - (dest - fn2)) == NULL && errno == ERANGE)
316         {
317           maxlen *= 2;
318           dest = fn2 = xrealloc (fn2, maxlen + 1);
319           errno = 0;
320         }
321       if (errno)
322         {
323           free (fn1);
324           free (fn2);
325           return NULL;
326         }
327       dest = strchr (fn2, '\0');
328       if (dest - fn2 >= maxlen)
329         {
330           int ofs = dest - fn2;
331           maxlen *= 2;
332           fn2 = xrealloc (fn2, maxlen + 1);
333           dest = fn2 + ofs;
334         }
335       if (dest[-1] != '/')
336         *dest++ = '/';
337     }
338
339   for (;;)
340     {
341       int c, f;
342
343       c = *src++;
344
345       f = 0;
346       if (c == '/' || c == 0)
347         {
348           /* remove `./', `../' from directory */
349           if (dest[-1] == '.' && dest[-2] == '/')
350             dest--;
351           else if (dest[-1] == '.' && dest[-2] == '.' && dest[-3] == '/')
352             {
353               dest -= 3;
354               if (dest == fn2)
355                 dest++;
356               while (dest[-1] != '/')
357                 dest--;
358             }
359           else if (dest[-1] != '/')     /* remove extra slashes */
360             f = 1;
361
362           if (c == 0)
363             {
364               if (dest[-1] == '/' && dest > fn2 + 1)
365                 dest--;
366               *dest = 0;
367               free (fn1);
368
369               return xrealloc (fn2, strlen (fn2) + 1);
370             }
371         }
372       else
373         f = 1;
374
375       if (f)
376         {
377           if (dest - fn2 >= maxlen)
378             {
379               int ofs = dest - fn2;
380               maxlen *= 2;
381               fn2 = xrealloc (fn2, maxlen + 1);
382               dest = fn2 + ofs;
383             }
384           *dest++ = c;
385         }
386     }
387 }
388 #elif defined (__WIN32__)
389 char *
390 fn_normalize (const char *fn1)
391 {
392   DWORD len;
393   DWORD success;
394   char *fn2;
395
396   /* Don't change special filenames. */
397   if (is_special_filename (filename))
398     return xstrdup (filename);
399
400   /* First find the required buffer length. */
401   len = GetFullPathName (fn1, 0, NULL, NULL);
402   if (!len)
403     {
404       fn2 = xstrdup (fn1);
405       return fn2;
406     }
407
408   /* Then make a buffer that big. */
409   fn2 = xmalloc (len);
410   success = GetFullPathName (fn1, len, fn2, NULL);
411   if (success >= len || success == 0)
412     {
413       free (fn2);
414       fn2 = xstrdup (fn1);
415       return fn2;
416     }
417   return fn2;
418 }
419 #elif __BORLANDC__
420 char *
421 fn_normalize (const char *fn1)
422 {
423   char *fn2 = _fullpath (NULL, fn1, 0);
424   if (fn2)
425     {
426       char *cp;
427       for (cp = fn2; *cp; cp++)
428         *cp = toupper ((unsigned char) (*cp));
429       return fn2;
430     }
431   return xstrdup (fn1);
432 }
433 #elif __DJGPP__
434 char *
435 fn_normalize (const char *fn1)
436 {
437   char *fn2 = xmalloc (1024);
438   _fixpath (fn1, fn2);
439   fn2 = xrealloc (fn2, strlen (fn2) + 1);
440   return fn2;
441 }
442 #else /* not Lose32, Unix, or DJGPP */
443 char *
444 fn_normalize (const char *fn)
445 {
446   return xstrdup (fn);
447 }
448 #endif /* not Lose32, Unix, or DJGPP */
449
450 /* Returns the directory part of FILENAME, as a malloc()'d
451    string. */
452 char *
453 fn_dir_name (const char *filename)
454 {
455   const char *p;
456   char *s;
457   size_t len;
458
459   len = strlen (filename);
460   if (len == 1 && filename[0] == '/')
461     p = filename + 1;
462   else if (len && filename[len - 1] == '/')
463     p = buf_find_reverse (filename, len - 1, filename + len - 1, 1);
464   else
465     p = strrchr (filename, '/');
466   if (p == NULL)
467     p = filename;
468
469   s = xmalloc (p - filename + 1);
470   memcpy (s, filename, p - filename);
471   s[p - filename] = 0;
472
473   return s;
474 }
475
476 /* Returns the extension part of FILENAME as a malloc()'d string.
477    If FILENAME does not have an extension, returns an empty
478    string. */
479 char *
480 fn_extension (const char *filename) 
481 {
482   const char *extension = strrchr (filename, '.');
483   if (extension == NULL)
484     extension = "";
485   return xstrdup (extension);
486 }
487 \f
488 #if unix
489 /* Returns the current working directory, as a malloc()'d string.
490    From libc.info. */
491 char *
492 fn_get_cwd (void)
493 {
494   int size = 100;
495   char *buffer = xmalloc (size);
496      
497   for (;;)
498     {
499       char *value = getcwd (buffer, size);
500       if (value != 0)
501         return buffer;
502
503       size *= 2;
504       free (buffer);
505       buffer = xmalloc (size);
506     }
507 }
508 #else
509 char *
510 fn_get_cwd (void)
511 {
512   int size = 2;
513   char *buffer = xmalloc (size);
514   if ( buffer) 
515     {
516       buffer[0]='.';
517       buffer[1]='\0';
518     }
519
520   return buffer;
521      
522 }
523 #endif
524 \f
525 /* Find out information about files. */
526
527 /* Returns nonzero iff NAME specifies an absolute filename. */
528 int
529 fn_is_absolute (const char *name)
530 {
531 #ifdef unix
532   if (name[0] == '/'
533       || !strncmp (name, "./", 2)
534       || !strncmp (name, "../", 3)
535       || name[0] == '~')
536     return 1;
537 #elif defined (__MSDOS__)
538   if (name[0] == '\\'
539       || !strncmp (name, ".\\", 2)
540       || !strncmp (name, "..\\", 3)
541       || (name[0] && name[1] == ':'))
542     return 1;
543 #endif
544   
545   return 0;
546 }
547   
548 /* Returns 1 if the filename specified is a virtual file that doesn't
549    really exist on disk, 0 if it's a real filename. */
550 int
551 fn_is_special (const char *filename)
552 {
553   if (!strcmp (filename, "-") || !strcmp (filename, "stdin")
554       || !strcmp (filename, "stdout") || !strcmp (filename, "stderr")
555 #ifdef unix
556       || filename[0] == '|'
557       || (*filename && filename[strlen (filename) - 1] == '|')
558 #endif
559       )
560     return 1;
561
562   return 0;
563 }
564
565 /* Returns nonzero if file with name NAME exists. */
566 int
567 fn_exists (const char *name)
568 {
569 #ifdef unix
570   struct stat temp;
571
572   return stat (name, &temp) == 0;
573 #else
574   FILE *f = fopen (name, "r");
575   if (!f)
576     return 0;
577   fclose (f);
578   return 1;
579 #endif
580 }
581
582 /* Returns the symbolic link value for FILENAME as a dynamically
583    allocated buffer, or a null pointer on failure. */
584 char *
585 fn_readlink (const char *filename)
586 {
587   return xreadlink (filename, 32);
588 }
589 \f
590 /* Environment variables. */
591
592 /* Simulates $VER and $ARCH environment variables. */
593 const char *
594 fn_getenv (const char *s)
595 {
596   if (!strcmp (s, "VER"))
597     return fn_getenv_default ("STAT_VER", bare_version);
598   else if (!strcmp (s, "ARCH"))
599     return fn_getenv_default ("STAT_ARCH", host_system);
600   else
601     return getenv (s);
602 }
603
604 /* Returns getenv(KEY) if that's non-NULL; else returns DEF. */
605 const char *
606 fn_getenv_default (const char *key, const char *def)
607 {
608   const char *value = getenv (key);
609   return value ? value : def;
610 }
611 \f
612 /* Basic file handling. */
613
614 /* Used for giving an error message on a set_safer security
615    violation. */
616 static FILE *
617 safety_violation (const char *fn)
618 {
619   msg (SE, _("Not opening pipe file `%s' because SAFER option set."), fn);
620   errno = EPERM;
621   return NULL;
622 }
623
624 /* As a general comment on the following routines, a `sensible value'
625    for errno includes 0 if there is no associated system error.  The
626    routines will only set errno to 0 if there is an error in a
627    callback that sets errno to 0; they themselves won't. */
628
629 /* File open routine that understands `-' as stdin/stdout and `|cmd'
630    as a pipe to command `cmd'.  Returns resultant FILE on success,
631    NULL on failure.  If NULL is returned then errno is set to a
632    sensible value.  */
633 FILE *
634 fn_open (const char *fn, const char *mode)
635 {
636   assert (mode[0] == 'r' || mode[0] == 'w');
637
638   if (mode[0] == 'r' && (!strcmp (fn, "stdin") || !strcmp (fn, "-"))) 
639     return stdin;
640   else if (mode[0] == 'w' && (!strcmp (fn, "stdout") || !strcmp (fn, "-")))
641     return stdout;
642   else if (mode[0] == 'w' && !strcmp (fn, "stderr"))
643     return stderr;
644   
645 #ifdef unix
646   if (fn[0] == '|')
647     {
648       if (get_safer_mode ())
649         return safety_violation (fn);
650
651       return popen (&fn[1], mode);
652     }
653   else if (*fn && fn[strlen (fn) - 1] == '|')
654     {
655       char *s;
656       FILE *f;
657
658       if (get_safer_mode ())
659         return safety_violation (fn);
660       
661       s = local_alloc (strlen (fn));
662       memcpy (s, fn, strlen (fn) - 1);
663       s[strlen (fn) - 1] = 0;
664       
665       f = popen (s, mode);
666
667       local_free (s);
668
669       return f;
670     }
671   else
672 #endif
673     {
674       FILE *f = fopen (fn, mode);
675
676       if (f && mode[0] == 'w')
677         setvbuf (f, NULL, _IOLBF, 0);
678
679       return f;
680     }
681 }
682
683 /* Counterpart to fn_open that closes file F with name FN; returns 0
684    on success, EOF on failure.  If EOF is returned, errno is set to a
685    sensible value. */
686 int
687 fn_close (const char *fn, FILE *f)
688 {
689   if (!strcmp (fn, "-"))
690     return 0;
691 #ifdef unix
692   else if (fn[0] == '|' || (*fn && fn[strlen (fn) - 1] == '|'))
693     {
694       pclose (f);
695       return 0;
696     }
697 #endif
698   else
699     return fclose (f);
700 }
701
702 #ifdef unix
703 /* A file's identity. */
704 struct file_identity 
705 {
706   dev_t device;               /* Device number. */
707   ino_t inode;                /* Inode number. */
708 };
709
710 /* Returns a pointer to a dynamically allocated structure whose
711    value can be used to tell whether two files are actually the
712    same file.  Returns a null pointer if no information about the
713    file is available, perhaps because it does not exist.  The
714    caller is responsible for freeing the structure with
715    fn_free_identity() when finished. */  
716 struct file_identity *
717 fn_get_identity (const char *filename) 
718 {
719   struct stat s;
720
721   if (stat (filename, &s) == 0) 
722     {
723       struct file_identity *identity = xmalloc (sizeof *identity);
724       identity->device = s.st_dev;
725       identity->inode = s.st_ino;
726       return identity;
727     }
728   else
729     return NULL;
730 }
731
732 /* Frees IDENTITY obtained from fn_get_identity(). */
733 void
734 fn_free_identity (struct file_identity *identity) 
735 {
736   free (identity);
737 }
738
739 /* Compares A and B, returning a strcmp()-type result. */
740 int
741 fn_compare_file_identities (const struct file_identity *a,
742                             const struct file_identity *b) 
743 {
744   assert (a != NULL);
745   assert (b != NULL);
746   if (a->device != b->device)
747     return a->device < b->device ? -1 : 1;
748   else
749     return a->inode < b->inode ? -1 : a->inode > b->inode;
750 }
751 #else /* not unix */
752 /* A file's identity. */
753 struct file_identity 
754 {
755   char *normalized_filename;  /* File's normalized name. */
756 };
757
758 /* Returns a pointer to a dynamically allocated structure whose
759    value can be used to tell whether two files are actually the
760    same file.  Returns a null pointer if no information about the
761    file is available, perhaps because it does not exist.  The
762    caller is responsible for freeing the structure with
763    fn_free_identity() when finished. */  
764 struct file_identity *
765 fn_get_identity (const char *filename) 
766 {
767   struct file_identity *identity = xmalloc (sizeof *identity);
768   identity->normalized_filename = fn_normalize (filename);
769   return identity;
770 }
771
772 /* Frees IDENTITY obtained from fn_get_identity(). */
773 void
774 fn_free_identity (struct file_identity *identity) 
775 {
776   if (identity != NULL) 
777     {
778       free (identity->normalized_filename);
779       free (identity);
780     }
781 }
782
783 /* Compares A and B, returning a strcmp()-type result. */
784 int
785 fn_compare_file_identities (const struct file_identity *a,
786                             const struct file_identity *b) 
787 {
788   return strcmp (a->normalized_filename, b->normalized_filename);
789 }
790 #endif /* not unix */