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