498963525d80933ab003d5f4c173deb2e65bc4f4
[pspp-builds.git] / src / data / filename.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 "filename.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 given
208    by PATH, which is tilde- and environment-interpolated.  Directories
209    in PATH are delimited by PATH_DELIMITER, defined in <pref.h>.
210    Returns the malloc'd full name of the first file found, or NULL if
211    none is 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_absolute_p (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, PATH_DELIMITER_STRING, &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_absolute_p (ds_c_str (&dir)))
246         {
247           ds_puts (&file, prefix);
248           ds_putc (&file, DIR_SEPARATOR);
249         }
250       ds_puts (&file, ds_c_str (&dir));
251       if (ds_length (&dir) && ds_last (&file) != DIR_SEPARATOR)
252         ds_putc (&file, DIR_SEPARATOR);
253       ds_puts (&file, base_name);
254
255       /* Check whether file exists. */
256       if (fn_exists_p (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_special_p (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 == DIR_SEPARATOR)
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] != DIR_SEPARATOR)
336         *dest++ = DIR_SEPARATOR;
337     }
338
339   for (;;)
340     {
341       int c, f;
342
343       c = *src++;
344
345       f = 0;
346       if (c == DIR_SEPARATOR || c == 0)
347         {
348           /* remove `./', `../' from directory */
349           if (dest[-1] == '.' && dest[-2] == DIR_SEPARATOR)
350             dest--;
351           else if (dest[-1] == '.' && dest[-2] == '.' && dest[-3] == DIR_SEPARATOR)
352             {
353               dest -= 3;
354               if (dest == fn2)
355                 dest++;
356               while (dest[-1] != DIR_SEPARATOR)
357                 dest--;
358             }
359           else if (dest[-1] != DIR_SEPARATOR)   /* remove extra slashes */
360             f = 1;
361
362           if (c == 0)
363             {
364               if (dest[-1] == DIR_SEPARATOR && 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_dirname (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] == DIR_SEPARATOR)
463     p = buf_find_reverse (filename, len - 1, filename + len - 1, 1);
464   else
465     p = strrchr (filename, DIR_SEPARATOR);
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 basename part of FILENAME as a malloc()'d string. */
477 #if 0
478 char *
479 fn_basename (const char *filename)
480 {
481   /* Not used, not implemented. */
482   abort ();
483 }
484 #endif
485
486 /* Returns the extension part of FILENAME as a malloc()'d string.
487    If FILENAME does not have an extension, returns an empty
488    string. */
489 char *
490 fn_extension (const char *filename) 
491 {
492   const char *extension = strrchr (filename, '.');
493   if (extension == NULL)
494     extension = "";
495   return xstrdup (extension);
496 }
497 \f
498 #if unix
499 /* Returns the current working directory, as a malloc()'d string.
500    From libc.info. */
501 char *
502 fn_get_cwd (void)
503 {
504   int size = 100;
505   char *buffer = xmalloc (size);
506      
507   for (;;)
508     {
509       char *value = getcwd (buffer, size);
510       if (value != 0)
511         return buffer;
512
513       size *= 2;
514       free (buffer);
515       buffer = xmalloc (size);
516     }
517 }
518 #else
519 char *
520 fn_get_cwd (void)
521 {
522   int size = 2;
523   char *buffer = xmalloc (size);
524   if ( buffer) 
525     {
526       buffer[0]='.';
527       buffer[1]='\0';
528     }
529
530   return buffer;
531      
532 }
533 #endif
534 \f
535 /* Find out information about files. */
536
537 /* Returns nonzero iff NAME specifies an absolute filename. */
538 int
539 fn_absolute_p (const char *name)
540 {
541 #ifdef unix
542   if (name[0] == '/'
543       || !strncmp (name, "./", 2)
544       || !strncmp (name, "../", 3)
545       || name[0] == '~')
546     return 1;
547 #elif defined (__MSDOS__)
548   if (name[0] == '\\'
549       || !strncmp (name, ".\\", 2)
550       || !strncmp (name, "..\\", 3)
551       || (name[0] && name[1] == ':'))
552     return 1;
553 #endif
554   
555   return 0;
556 }
557   
558 /* Returns 1 if the filename specified is a virtual file that doesn't
559    really exist on disk, 0 if it's a real filename. */
560 int
561 fn_special_p (const char *filename)
562 {
563   if (!strcmp (filename, "-") || !strcmp (filename, "stdin")
564       || !strcmp (filename, "stdout") || !strcmp (filename, "stderr")
565 #ifdef unix
566       || filename[0] == '|'
567       || (*filename && filename[strlen (filename) - 1] == '|')
568 #endif
569       )
570     return 1;
571
572   return 0;
573 }
574
575 /* Returns nonzero if file with name NAME exists. */
576 int
577 fn_exists_p (const char *name)
578 {
579 #ifdef unix
580   struct stat temp;
581
582   return stat (name, &temp) == 0;
583 #else
584   FILE *f = fopen (name, "r");
585   if (!f)
586     return 0;
587   fclose (f);
588   return 1;
589 #endif
590 }
591
592 /* Returns the symbolic link value for FILENAME as a dynamically
593    allocated buffer, or a null pointer on failure. */
594 char *
595 fn_readlink (const char *filename)
596 {
597   return xreadlink (filename, 32);
598 }
599 \f
600 /* Environment variables. */
601
602 /* Simulates $VER and $ARCH environment variables. */
603 const char *
604 fn_getenv (const char *s)
605 {
606   if (!strcmp (s, "VER"))
607     return fn_getenv_default ("STAT_VER", bare_version);
608   else if (!strcmp (s, "ARCH"))
609     return fn_getenv_default ("STAT_ARCH", host_system);
610   else
611     return getenv (s);
612 }
613
614 /* Returns getenv(KEY) if that's non-NULL; else returns DEF. */
615 const char *
616 fn_getenv_default (const char *key, const char *def)
617 {
618   const char *value = getenv (key);
619   return value ? value : def;
620 }
621 \f
622 /* Basic file handling. */
623
624 /* Used for giving an error message on a set_safer security
625    violation. */
626 static FILE *
627 safety_violation (const char *fn)
628 {
629   msg (SE, _("Not opening pipe file `%s' because SAFER option set."), fn);
630   errno = EPERM;
631   return NULL;
632 }
633
634 /* As a general comment on the following routines, a `sensible value'
635    for errno includes 0 if there is no associated system error.  The
636    routines will only set errno to 0 if there is an error in a
637    callback that sets errno to 0; they themselves won't. */
638
639 /* File open routine that understands `-' as stdin/stdout and `|cmd'
640    as a pipe to command `cmd'.  Returns resultant FILE on success,
641    NULL on failure.  If NULL is returned then errno is set to a
642    sensible value.  */
643 FILE *
644 fn_open (const char *fn, const char *mode)
645 {
646   assert (mode[0] == 'r' || mode[0] == 'w');
647
648   if (mode[0] == 'r' && (!strcmp (fn, "stdin") || !strcmp (fn, "-"))) 
649     return stdin;
650   else if (mode[0] == 'w' && (!strcmp (fn, "stdout") || !strcmp (fn, "-")))
651     return stdout;
652   else if (mode[0] == 'w' && !strcmp (fn, "stderr"))
653     return stderr;
654   
655 #ifdef unix
656   if (fn[0] == '|')
657     {
658       if (get_safer_mode ())
659         return safety_violation (fn);
660
661       return popen (&fn[1], mode);
662     }
663   else if (*fn && fn[strlen (fn) - 1] == '|')
664     {
665       char *s;
666       FILE *f;
667
668       if (get_safer_mode ())
669         return safety_violation (fn);
670       
671       s = local_alloc (strlen (fn));
672       memcpy (s, fn, strlen (fn) - 1);
673       s[strlen (fn) - 1] = 0;
674       
675       f = popen (s, mode);
676
677       local_free (s);
678
679       return f;
680     }
681   else
682 #endif
683     {
684       FILE *f = fopen (fn, mode);
685
686       if (f && mode[0] == 'w')
687         setvbuf (f, NULL, _IOLBF, 0);
688
689       return f;
690     }
691 }
692
693 /* Counterpart to fn_open that closes file F with name FN; returns 0
694    on success, EOF on failure.  If EOF is returned, errno is set to a
695    sensible value. */
696 int
697 fn_close (const char *fn, FILE *f)
698 {
699   if (!strcmp (fn, "-"))
700     return 0;
701 #ifdef unix
702   else if (fn[0] == '|' || (*fn && fn[strlen (fn) - 1] == '|'))
703     {
704       pclose (f);
705       return 0;
706     }
707 #endif
708   else
709     return fclose (f);
710 }
711
712 #ifdef unix
713 /* A file's identity. */
714 struct file_identity 
715 {
716   dev_t device;               /* Device number. */
717   ino_t inode;                /* Inode number. */
718 };
719
720 /* Returns a pointer to a dynamically allocated structure whose
721    value can be used to tell whether two files are actually the
722    same file.  Returns a null pointer if no information about the
723    file is available, perhaps because it does not exist.  The
724    caller is responsible for freeing the structure with
725    fn_free_identity() when finished. */  
726 struct file_identity *
727 fn_get_identity (const char *filename) 
728 {
729   struct stat s;
730
731   if (stat (filename, &s) == 0) 
732     {
733       struct file_identity *identity = xmalloc (sizeof *identity);
734       identity->device = s.st_dev;
735       identity->inode = s.st_ino;
736       return identity;
737     }
738   else
739     return NULL;
740 }
741
742 /* Frees IDENTITY obtained from fn_get_identity(). */
743 void
744 fn_free_identity (struct file_identity *identity) 
745 {
746   free (identity);
747 }
748
749 /* Compares A and B, returning a strcmp()-type result. */
750 int
751 fn_compare_file_identities (const struct file_identity *a,
752                             const struct file_identity *b) 
753 {
754   assert (a != NULL);
755   assert (b != NULL);
756   if (a->device != b->device)
757     return a->device < b->device ? -1 : 1;
758   else
759     return a->inode < b->inode ? -1 : a->inode > b->inode;
760 }
761 #else /* not unix */
762 /* A file's identity. */
763 struct file_identity 
764 {
765   char *normalized_filename;  /* File's normalized name. */
766 };
767
768 /* Returns a pointer to a dynamically allocated structure whose
769    value can be used to tell whether two files are actually the
770    same file.  Returns a null pointer if no information about the
771    file is available, perhaps because it does not exist.  The
772    caller is responsible for freeing the structure with
773    fn_free_identity() when finished. */  
774 struct file_identity *
775 fn_get_identity (const char *filename) 
776 {
777   struct file_identity *identity = xmalloc (sizeof *identity);
778   identity->normalized_filename = fn_normalize (filename);
779   return identity;
780 }
781
782 /* Frees IDENTITY obtained from fn_get_identity(). */
783 void
784 fn_free_identity (struct file_identity *identity) 
785 {
786   if (identity != NULL) 
787     {
788       free (identity->normalized_filename);
789       free (identity);
790     }
791 }
792
793 /* Compares A and B, returning a strcmp()-type result. */
794 int
795 fn_compare_file_identities (const struct file_identity *a,
796                             const struct file_identity *b) 
797 {
798   return strcmp (a->normalized_filename, b->normalized_filename);
799 }
800 #endif /* not unix */