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