checkin of 0.3.0
[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 #if __CHECKER__
377       memset (dest, 0, maxlen);
378 #endif
379       while (getcwd (dest, maxlen - (dest - fn2)) == NULL && errno == ERANGE)
380         {
381           maxlen *= 2;
382           dest = fn2 = xrealloc (fn2, maxlen + 1);
383 #if __CHECKER__
384           memset (dest, 0, maxlen);
385 #endif
386           errno = 0;
387         }
388       if (errno)
389         {
390           free (fn1);
391           free (fn2);
392           return NULL;
393         }
394       dest = strchr (fn2, '\0');
395       if (dest - fn2 >= maxlen)
396         {
397           int ofs = dest - fn2;
398           maxlen *= 2;
399           fn2 = xrealloc (fn2, maxlen + 1);
400           dest = fn2 + ofs;
401         }
402       if (dest[-1] != DIR_SEPARATOR)
403         *dest++ = DIR_SEPARATOR;
404     }
405
406   for (;;)
407     {
408       int c, f;
409
410       c = *src++;
411
412       f = 0;
413       if (c == DIR_SEPARATOR || c == 0)
414         {
415           /* remove `./', `../' from directory */
416           if (dest[-1] == '.' && dest[-2] == DIR_SEPARATOR)
417             dest--;
418           else if (dest[-1] == '.' && dest[-2] == '.' && dest[-3] == DIR_SEPARATOR)
419             {
420               dest -= 3;
421               if (dest == fn2)
422                 dest++;
423               while (dest[-1] != DIR_SEPARATOR)
424                 dest--;
425             }
426           else if (dest[-1] != DIR_SEPARATOR)   /* remove extra slashes */
427             f = 1;
428
429           if (c == 0)
430             {
431               if (dest[-1] == DIR_SEPARATOR && dest > fn2 + 1)
432                 dest--;
433               *dest = 0;
434               free (fn1);
435
436               return xrealloc (fn2, strlen (fn2) + 1);
437             }
438         }
439       else
440         f = 1;
441
442       if (f)
443         {
444           if (dest - fn2 >= maxlen)
445             {
446               int ofs = dest - fn2;
447               maxlen *= 2;
448               fn2 = xrealloc (fn2, maxlen + 1);
449               dest = fn2 + ofs;
450             }
451           *dest++ = c;
452         }
453     }
454 }
455 #elif __WIN32__
456 char *
457 fn_normalize (const char *fn1)
458 {
459   DWORD len;
460   DWORD success;
461   char *fn2;
462
463   /* Don't change special filenames. */
464   if (is_special_filename (filename))
465     return xstrdup (filename);
466
467   /* First find the required buffer length. */
468   len = GetFullPathName (fn1, 0, NULL, NULL);
469   if (!len)
470     {
471       fn2 = xstrdup (fn1);
472       return fn2;
473     }
474
475   /* Then make a buffer that big. */
476   fn2 = xmalloc (len);
477   success = GetFullPathName (fn1, len, fn2, NULL);
478   if (success >= len || success == 0)
479     {
480       free (fn2);
481       fn2 = xstrdup (fn1);
482       return fn2;
483     }
484   return fn2;
485 }
486 #elif __BORLANDC__
487 char *
488 fn_normalize (const char *fn1)
489 {
490   char *fn2 = _fullpath (NULL, fn1, 0);
491   if (fn2)
492     {
493       char *cp;
494       for (cp = fn2; *cp; cp++)
495         *cp = toupper ((unsigned char) (*cp));
496       return fn2;
497     }
498   return xstrdup (fn1);
499 }
500 #elif __DJGPP__
501 char *
502 fn_normalize (const char *fn1)
503 {
504   char *fn2 = xmalloc (1024);
505   _fixpath (fn1, fn2);
506   fn2 = xrealloc (fn2, strlen (fn2) + 1);
507   return fn2;
508 }
509 #else /* not Lose32, Unix, or DJGPP */
510 char *
511 fn_normalize (const char *fn)
512 {
513   return xstrdup (fn);
514 }
515 #endif /* not Lose32, Unix, or DJGPP */
516
517 /* Returns the directory part of FILENAME, as a malloc()'d
518    string. */
519 char *
520 fn_dirname (const char *filename)
521 {
522   const char *p;
523   char *s;
524   size_t len;
525
526   len = strlen (filename);
527   if (len == 1 && filename[0] == '/')
528     p = filename + 1;
529   else if (len && filename[len - 1] == DIR_SEPARATOR)
530     p = mm_find_reverse (filename, len - 1, filename + len - 1, 1);
531   else
532     p = strrchr (filename, DIR_SEPARATOR);
533   if (p == NULL)
534     p = filename;
535
536   s = xmalloc (p - filename + 1);
537   memcpy (s, filename, p - filename);
538   s[p - filename] = 0;
539
540   return s;
541 }
542
543 /* Returns the basename part of FILENAME as a malloc()'d string. */
544 #if 0
545 char *
546 fn_basename (const char *filename)
547 {
548   /* Not used, not implemented. */
549   abort ();
550 }
551 #endif
552 \f
553 /* Returns the current working directory, as a malloc()'d string.
554    From libc.info. */
555 char *
556 fn_get_cwd (void)
557 {
558   int size = 100;
559   char *buffer = xmalloc (size);
560      
561   for (;;)
562     {
563       char *value = getcwd (buffer, size);
564       if (value != 0)
565         return buffer;
566
567       size *= 2;
568       free (buffer);
569       buffer = xmalloc (size);
570     }
571 }
572 \f
573 /* Find out information about files. */
574
575 /* Returns nonzero iff NAME specifies an absolute filename. */
576 int
577 fn_absolute_p (const char *name)
578 {
579 #if unix
580   if (name[0] == '/'
581       || !strncmp (name, "./", 2)
582       || !strncmp (name, "../", 3)
583       || name[0] == '~')
584     return 1;
585 #elif __MSDOS__
586   if (name[0] == '\\'
587       || !strncmp (name, ".\\", 2)
588       || !strncmp (name, "..\\", 3)
589       || (name[0] && name[1] == ':'))
590     return 1;
591 #endif
592   
593   return 0;
594 }
595   
596 /* Returns 1 if the filename specified is a virtual file that doesn't
597    really exist on disk, 0 if it's a real filename. */
598 int
599 fn_special_p (const char *filename)
600 {
601   if (!strcmp (filename, "-") || !strcmp (filename, "stdin")
602       || !strcmp (filename, "stdout") || !strcmp (filename, "stderr")
603 #if unix
604       || filename[0] == '|'
605       || (*filename && filename[strlen (filename) - 1] == '|')
606 #endif
607       )
608     return 1;
609
610   return 0;
611 }
612
613 /* Returns nonzero if file with name NAME exists. */
614 int
615 fn_exists_p (const char *name)
616 {
617 #if unix
618   struct stat temp;
619
620   return stat (name, &temp) == 0;
621 #else
622   FILE *f = fopen (name, "r");
623   if (!f)
624     return 0;
625   fclose (f);
626   return 1;
627 #endif
628 }
629
630 #if unix
631 /* Stolen from libc.info but heavily modified, this is a wrapper
632    around readlink() that allows for arbitrary filename length. */
633 char *
634 fn_readlink (const char *filename)
635 {
636   int size = 128;
637
638   for (;;)
639     {
640       char *buffer = xmalloc (size);
641       int nchars  = readlink (filename, buffer, size);
642       if (nchars == -1)
643         {
644           free (buffer);
645           return NULL;
646         }
647
648       if (nchars < size - 1)
649         {
650           buffer[nchars] = 0;
651           return buffer;
652         }
653       free (buffer);
654       size *= 2;
655     }
656 }
657 #else /* Not UNIX. */
658 char *
659 fn_readlink (const char *filename)
660 {
661   return NULL;
662 }
663 #endif /* Not UNIX. */
664 \f
665 /* Environment variables. */
666
667 /* Simulates $VER and $ARCH environment variables. */
668 const char *
669 fn_getenv (const char *s)
670 {
671   if (!strcmp (s, "VER"))
672     return fn_getenv_default ("STAT_VER", bare_version);
673   else if (!strcmp (s, "ARCH"))
674     return fn_getenv_default ("STAT_ARCH", host_system);
675   else
676     return getenv (s);
677 }
678
679 /* Returns getenv(KEY) if that's non-NULL; else returns DEF. */
680 const char *
681 fn_getenv_default (const char *key, const char *def)
682 {
683   const char *value = getenv (key);
684   return value ? value : def;
685 }
686 \f
687 /* Basic file handling. */
688
689 /* Used for giving an error message on a set_safer security
690    violation. */
691 static FILE *
692 safety_violation (const char *fn)
693 {
694   msg (SE, _("Not opening pipe file `%s' because SAFER option set."), fn);
695   errno = EPERM;
696   return NULL;
697 }
698
699 /* As a general comment on the following routines, a `sensible value'
700    for errno includes 0 if there is no associated system error.  The
701    routines will only set errno to 0 if there is an error in a
702    callback that sets errno to 0; they themselves won't. */
703
704 /* File open routine that understands `-' as stdin/stdout and `|cmd'
705    as a pipe to command `cmd'.  Returns resultant FILE on success,
706    NULL on failure.  If NULL is returned then errno is set to a
707    sensible value.  */
708 FILE *
709 fn_open (const char *fn, const char *mode)
710 {
711   assert (mode[0] == 'r' || mode[0] == 'w');
712
713   if (mode[0] == 'r' && (!strcmp (fn, "stdin") || !strcmp (fn, "-"))) 
714     return stdin;
715   else if (mode[0] == 'w' && (!strcmp (fn, "stdout") || !strcmp (fn, "-")))
716     return stdout;
717   else if (mode[0] == 'w' && !strcmp (fn, "stderr"))
718     return stderr;
719   
720 #if unix
721   if (fn[0] == '|')
722     {
723       if (set_safer)
724         return safety_violation (fn);
725
726       return popen (&fn[1], mode);
727     }
728   else if (*fn && fn[strlen (fn) - 1] == '|')
729     {
730       char *s;
731       FILE *f;
732
733       if (set_safer)
734         return safety_violation (fn);
735       
736       s = local_alloc (strlen (fn));
737       memcpy (s, fn, strlen (fn) - 1);
738       s[strlen (fn) - 1] = 0;
739       
740       f = popen (s, mode);
741
742       local_free (s);
743
744       return f;
745     }
746   else
747 #endif
748     {
749       FILE *f = fopen (fn, mode);
750
751       if (f && mode[0] == 'w')
752         setvbuf (f, NULL, _IOLBF, 0);
753
754       return f;
755     }
756 }
757
758 /* Counterpart to fn_open that closes file F with name FN; returns 0
759    on success, EOF on failure.  If EOF is returned, errno is set to a
760    sensible value. */
761 int
762 fn_close (const char *fn, FILE *f)
763 {
764   if (!strcmp (fn, "-"))
765     return 0;
766 #if unix
767   else if (fn[0] == '|' || (*fn && fn[strlen (fn) - 1] == '|'))
768     {
769       pclose (f);
770       return 0;
771     }
772 #endif
773   else
774     return fclose (f);
775 }
776 \f
777 /* More extensive file handling. */
778
779 /* File open routine that extends fn_open().  Opens or reopens a
780    file according to the contents of file_ext F.  Returns nonzero on
781    success.  If 0 is returned, errno is set to a sensible value. */
782 int
783 fn_open_ext (struct file_ext *f)
784 {
785   char *p;
786
787   p = strstr (f->filename, "%d");
788   if (p)
789     {
790       char *s = local_alloc (strlen (f->filename) + INT_DIGITS - 1);
791       char *cp;
792
793       memcpy (s, f->filename, p - f->filename);
794       cp = spprintf (&s[p - f->filename], "%d", *f->sequence_no);
795       strcpy (cp, &p[2]);
796
797       if (f->file)
798         {
799           int error = 0;
800
801           if (f->preclose)
802             if (f->preclose (f) == 0)
803               error = errno;
804
805           if (EOF == fn_close (f->filename, f->file) || error)
806             {
807               f->file = NULL;
808               local_free (s);
809
810               if (error)
811                 errno = error;
812
813               return 0;
814             }
815
816           f->file = NULL;
817         }
818
819       f->file = fn_open (s, f->mode);
820       local_free (s);
821
822       if (f->file && f->postopen)
823         if (f->postopen (f) == 0)
824           {
825             int error = errno;
826             fn_close (f->filename, f->file);
827             errno = error;
828
829             return 0;
830           }
831
832       return (f->file != NULL);
833     }
834   else if (f->file)
835     return 1;
836   else
837     {
838       f->file = fn_open (f->filename, f->mode);
839
840       if (f->file && f->postopen)
841         if (f->postopen (f) == 0)
842           {
843             int error = errno;
844             fn_close (f->filename, f->file);
845             errno = error;
846
847             return 0;
848           }
849
850       return (f->file != NULL);
851     }
852 }
853
854 /* Properly closes the file associated with file_ext F, if any.
855    Return nonzero on success.  If zero is returned, errno is set to a
856    sensible value. */
857 int
858 fn_close_ext (struct file_ext *f)
859 {
860   if (f->file)
861     {
862       int error = 0;
863
864       if (f->preclose)
865         if (f->preclose (f) == 0)
866           error = errno;
867
868       if (EOF == fn_close (f->filename, f->file) || error)
869         {
870           f->file = NULL;
871
872           if (error)
873             errno = error;
874
875           return 0;
876         }
877
878       f->file = NULL;
879     }
880   return 1;
881 }