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