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