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