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