1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
3 Written by Ben Pfaff <blp@gnu.org>.
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.
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.
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
21 #include <libpspp/message.h>
27 #include <libpspp/alloc.h>
29 #include <libpspp/message.h>
30 #include <libpspp/str.h>
32 #include <libpspp/version.h>
33 #include "xreadlink.h"
36 #define _(msgid) gettext (msgid)
38 /* PORTME: Everything in this file is system dependent. */
44 #include "stat-macros.h"
51 #include <win32/windows.h>
60 const char *config_path;
65 config_path = fn_getenv_default ("STAT_CONFIG_PATH", default_config_path);
68 /* Functions for performing operations on filenames. */
71 /* Substitutes $variables as defined by GETENV into TARGET.
72 TARGET must be a string containing the text for which substitution
74 Supports $var and ${var} syntaxes;
78 fn_interp_vars (struct string *target,
79 const char *(*getenv) (const char *))
86 input = xmalloc(ds_length(target) + 1);
89 strcpy(input, ds_c_str(target));
91 if (NULL == strchr (ds_c_str(target), '$'))
108 ds_putc (target, '$');
117 start = ds_length (target);
132 while (*s && *s != stop
133 && (stop || isalpha ((unsigned char) *s)))
135 ds_putc (target, *s++);
138 value = getenv (ds_c_str (target) + start);
139 ds_truncate (target, start);
140 ds_puts (target, value);
142 if (stop && *s == stop)
148 ds_putc (target, *s++);
158 /* Expands csh tilde notation from the path INPUT into a malloc()'d
161 fn_tilde_expand (const char *input)
164 struct string output;
166 if (NULL == strchr (input, '~'))
167 return xstrdup (input);
168 ds_init (&output, strlen (input));
172 for (ip = input; *ip; )
173 if (*ip != '~' || (ip != input && ip[-1] != PATH_DELIMITER))
174 ds_putc (&output, *ip++);
177 static const char stop_set[3] = {DIR_SEPARATOR, PATH_DELIMITER, 0};
182 cp = ip + strcspn (ip, stop_set);
189 strncpy (username, ip, cp - ip + 1);
191 pwd = getpwnam (username);
193 if (!pwd || !pwd->pw_dir)
194 ds_putc (&output, *ip++);
196 ds_puts (&output, pwd->pw_dir);
200 const char *home = fn_getenv ("HOME");
202 ds_putc (&output, *ip++);
204 ds_puts (&output, home);
210 return ds_c_str (&output);
214 fn_tilde_expand (const char *input)
216 return xstrdup (input);
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
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. */
230 fn_search_path (const char *base_name, const char *path_, const char *prefix)
233 struct string dir = DS_INITIALIZER;
234 struct string file = DS_INITIALIZER;
238 if (fn_absolute_p (base_name))
239 return fn_tilde_expand (base_name);
241 /* Interpolate environment variables and do tilde-expansion. */
242 ds_create (&path, path_);
243 fn_interp_vars (&path, fn_getenv);
245 tmp_str = fn_tilde_expand (ds_c_str (&path));
246 ds_assign_c_str (&path, tmp_str);
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))
253 /* Construct file name. */
255 if (prefix != NULL && !fn_absolute_p (ds_c_str (&dir)))
257 ds_puts (&file, prefix);
258 ds_putc (&file, DIR_SEPARATOR);
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);
265 /* Check whether file exists. */
266 if (fn_exists_p (ds_c_str (&file)))
268 verbose_msg (2, _("...found \"%s\""), ds_c_str (&file));
271 return ds_c_str (&file);
276 verbose_msg (2, _("...not found"));
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). */
290 fn_normalize (const char *filename)
293 char *fn1, *fn2, *dest;
296 if (fn_special_p (filename))
297 return xstrdup (filename);
299 fn1 = fn_tilde_expand (filename);
301 /* Follow symbolic links. */
305 fn1 = fn_readlink (fn1);
314 maxlen = strlen (fn1) * 2;
317 dest = fn2 = xmalloc (maxlen + 1);
320 if (*src == DIR_SEPARATOR)
325 while (getcwd (dest, maxlen - (dest - fn2)) == NULL && errno == ERANGE)
328 dest = fn2 = xrealloc (fn2, maxlen + 1);
337 dest = strchr (fn2, '\0');
338 if (dest - fn2 >= maxlen)
340 int ofs = dest - fn2;
342 fn2 = xrealloc (fn2, maxlen + 1);
345 if (dest[-1] != DIR_SEPARATOR)
346 *dest++ = DIR_SEPARATOR;
356 if (c == DIR_SEPARATOR || c == 0)
358 /* remove `./', `../' from directory */
359 if (dest[-1] == '.' && dest[-2] == DIR_SEPARATOR)
361 else if (dest[-1] == '.' && dest[-2] == '.' && dest[-3] == DIR_SEPARATOR)
366 while (dest[-1] != DIR_SEPARATOR)
369 else if (dest[-1] != DIR_SEPARATOR) /* remove extra slashes */
374 if (dest[-1] == DIR_SEPARATOR && dest > fn2 + 1)
379 return xrealloc (fn2, strlen (fn2) + 1);
387 if (dest - fn2 >= maxlen)
389 int ofs = dest - fn2;
391 fn2 = xrealloc (fn2, maxlen + 1);
398 #elif defined (__WIN32__)
400 fn_normalize (const char *fn1)
406 /* Don't change special filenames. */
407 if (is_special_filename (filename))
408 return xstrdup (filename);
410 /* First find the required buffer length. */
411 len = GetFullPathName (fn1, 0, NULL, NULL);
418 /* Then make a buffer that big. */
420 success = GetFullPathName (fn1, len, fn2, NULL);
421 if (success >= len || success == 0)
431 fn_normalize (const char *fn1)
433 char *fn2 = _fullpath (NULL, fn1, 0);
437 for (cp = fn2; *cp; cp++)
438 *cp = toupper ((unsigned char) (*cp));
441 return xstrdup (fn1);
445 fn_normalize (const char *fn1)
447 char *fn2 = xmalloc (1024);
449 fn2 = xrealloc (fn2, strlen (fn2) + 1);
452 #else /* not Lose32, Unix, or DJGPP */
454 fn_normalize (const char *fn)
458 #endif /* not Lose32, Unix, or DJGPP */
460 /* Returns the directory part of FILENAME, as a malloc()'d
463 fn_dirname (const char *filename)
469 len = strlen (filename);
470 if (len == 1 && filename[0] == '/')
472 else if (len && filename[len - 1] == DIR_SEPARATOR)
473 p = buf_find_reverse (filename, len - 1, filename + len - 1, 1);
475 p = strrchr (filename, DIR_SEPARATOR);
479 s = xmalloc (p - filename + 1);
480 memcpy (s, filename, p - filename);
486 /* Returns the basename part of FILENAME as a malloc()'d string. */
489 fn_basename (const char *filename)
491 /* Not used, not implemented. */
496 /* Returns the extension part of FILENAME as a malloc()'d string.
497 If FILENAME does not have an extension, returns an empty
500 fn_extension (const char *filename)
502 const char *extension = strrchr (filename, '.');
503 if (extension == NULL)
505 return xstrdup (extension);
509 /* Returns the current working directory, as a malloc()'d string.
515 char *buffer = xmalloc (size);
519 char *value = getcwd (buffer, size);
525 buffer = xmalloc (size);
533 char *buffer = xmalloc (size);
545 /* Find out information about files. */
547 /* Returns nonzero iff NAME specifies an absolute filename. */
549 fn_absolute_p (const char *name)
553 || !strncmp (name, "./", 2)
554 || !strncmp (name, "../", 3)
557 #elif defined (__MSDOS__)
559 || !strncmp (name, ".\\", 2)
560 || !strncmp (name, "..\\", 3)
561 || (name[0] && name[1] == ':'))
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. */
571 fn_special_p (const char *filename)
573 if (!strcmp (filename, "-") || !strcmp (filename, "stdin")
574 || !strcmp (filename, "stdout") || !strcmp (filename, "stderr")
576 || filename[0] == '|'
577 || (*filename && filename[strlen (filename) - 1] == '|')
585 /* Returns nonzero if file with name NAME exists. */
587 fn_exists_p (const char *name)
592 return stat (name, &temp) == 0;
594 FILE *f = fopen (name, "r");
602 /* Returns the symbolic link value for FILENAME as a dynamically
603 allocated buffer, or a null pointer on failure. */
605 fn_readlink (const char *filename)
607 return xreadlink (filename, 32);
610 /* Environment variables. */
612 /* Simulates $VER and $ARCH environment variables. */
614 fn_getenv (const char *s)
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);
624 /* Returns getenv(KEY) if that's non-NULL; else returns DEF. */
626 fn_getenv_default (const char *key, const char *def)
628 const char *value = getenv (key);
629 return value ? value : def;
632 /* Basic file handling. */
634 /* Used for giving an error message on a set_safer security
637 safety_violation (const char *fn)
639 msg (SE, _("Not opening pipe file `%s' because SAFER option set."), fn);
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. */
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
654 fn_open (const char *fn, const char *mode)
656 assert (mode[0] == 'r' || mode[0] == 'w');
658 if (mode[0] == 'r' && (!strcmp (fn, "stdin") || !strcmp (fn, "-")))
660 else if (mode[0] == 'w' && (!strcmp (fn, "stdout") || !strcmp (fn, "-")))
662 else if (mode[0] == 'w' && !strcmp (fn, "stderr"))
668 if (get_safer_mode ())
669 return safety_violation (fn);
671 return popen (&fn[1], mode);
673 else if (*fn && fn[strlen (fn) - 1] == '|')
678 if (get_safer_mode ())
679 return safety_violation (fn);
681 s = local_alloc (strlen (fn));
682 memcpy (s, fn, strlen (fn) - 1);
683 s[strlen (fn) - 1] = 0;
694 FILE *f = fopen (fn, mode);
696 if (f && mode[0] == 'w')
697 setvbuf (f, NULL, _IOLBF, 0);
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
707 fn_close (const char *fn, FILE *f)
709 if (!strcmp (fn, "-"))
712 else if (fn[0] == '|' || (*fn && fn[strlen (fn) - 1] == '|'))
723 /* A file's identity. */
726 dev_t device; /* Device number. */
727 ino_t inode; /* Inode number. */
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)
741 if (stat (filename, &s) == 0)
743 struct file_identity *identity = xmalloc (sizeof *identity);
744 identity->device = s.st_dev;
745 identity->inode = s.st_ino;
752 /* Frees IDENTITY obtained from fn_get_identity(). */
754 fn_free_identity (struct file_identity *identity)
759 /* Compares A and B, returning a strcmp()-type result. */
761 fn_compare_file_identities (const struct file_identity *a,
762 const struct file_identity *b)
766 if (a->device != b->device)
767 return a->device < b->device ? -1 : 1;
769 return a->inode < b->inode ? -1 : a->inode > b->inode;
772 /* A file's identity. */
775 char *normalized_filename; /* File's normalized name. */
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)
787 struct file_identity *identity = xmalloc (sizeof *identity);
788 identity->normalized_filename = fn_normalize (filename);
792 /* Frees IDENTITY obtained from fn_get_identity(). */
794 fn_free_identity (struct file_identity *identity)
796 if (identity != NULL)
798 free (identity->normalized_filename);
803 /* Compares A and B, returning a strcmp()-type result. */
805 fn_compare_file_identities (const struct file_identity *a,
806 const struct file_identity *b)
808 return strcmp (a->normalized_filename, b->normalized_filename);
810 #endif /* not unix */