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