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