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 #include <libpspp/debug-print.h>
40 /* PORTME: Everything in this file is system dependent. */
46 #include "stat-macros.h"
53 #include <win32/windows.h>
62 const char *config_path;
67 config_path = fn_getenv_default ("STAT_CONFIG_PATH", default_config_path);
70 /* Functions for performing operations on filenames. */
73 /* Substitutes $variables as defined by GETENV into TARGET.
74 TARGET must be a string containing the text for which substitution
76 Supports $var and ${var} syntaxes;
80 fn_interp_vars (struct string *target,
81 const char *(*getenv) (const char *))
85 char *input = xmalloc(ds_length(target) + 1);
88 strcpy(input, ds_c_str(target));
90 if (NULL == strchr (ds_c_str(target), '$'))
107 ds_putc (target, '$');
116 start = ds_length (target);
131 while (*s && *s != stop
132 && (stop || isalpha ((unsigned char) *s)))
134 ds_putc (target, *s++);
137 value = getenv (ds_c_str (target) + start);
138 ds_truncate (target, start);
139 ds_puts (target, value);
141 if (stop && *s == stop)
146 ds_putc (target, *s++);
156 /* Expands csh tilde notation from the path INPUT into a malloc()'d
159 fn_tilde_expand (const char *input)
162 struct string output;
164 if (NULL == strchr (input, '~'))
165 return xstrdup (input);
166 ds_init (&output, strlen (input));
170 for (ip = input; *ip; )
171 if (*ip != '~' || (ip != input && ip[-1] != PATH_DELIMITER))
172 ds_putc (&output, *ip++);
175 static const char stop_set[3] = {DIR_SEPARATOR, PATH_DELIMITER, 0};
180 cp = ip + strcspn (ip, stop_set);
187 strncpy (username, ip, cp - ip + 1);
189 pwd = getpwnam (username);
191 if (!pwd || !pwd->pw_dir)
192 ds_putc (&output, *ip++);
194 ds_puts (&output, pwd->pw_dir);
198 const char *home = fn_getenv ("HOME");
200 ds_putc (&output, *ip++);
202 ds_puts (&output, home);
208 return ds_c_str (&output);
212 fn_tilde_expand (const char *input)
214 return xstrdup (input);
218 /* Searches for a configuration file with name NAME in the path given
219 by PATH, which is tilde- and environment-interpolated. Directories
220 in PATH are delimited by PATH_DELIMITER, defined in <pref.h>.
221 Returns the malloc'd full name of the first file found, or NULL if
224 If PREPEND is non-NULL, then it is prepended to each filename;
225 i.e., it looks like PREPEND/PATH_COMPONENT/NAME. This is not done
226 with absolute directories in the path. */
227 #if defined (unix) || defined (__MSDOS__) || defined (__WIN32__)
229 fn_search_path (const char *basename, const char *path, const char *prepend)
232 struct string filename;
235 if (fn_absolute_p (basename))
236 return fn_tilde_expand (basename);
240 ds_create(&temp, path);
242 fn_interp_vars(&temp, fn_getenv);
244 bp = subst_path = fn_tilde_expand (ds_c_str(&temp));
249 msg (VM (4), _("Searching for `%s'..."), basename);
250 ds_init (&filename, 64);
257 msg (VM (4), _("Search unsuccessful!"));
258 ds_destroy (&filename);
263 for (ep = bp; *ep && *ep != PATH_DELIMITER; ep++)
266 /* Paste together PREPEND/PATH/BASENAME. */
267 ds_clear (&filename);
268 if (prepend && !fn_absolute_p (bp))
270 ds_puts (&filename, prepend);
271 ds_putc (&filename, DIR_SEPARATOR);
273 ds_concat (&filename, bp, ep - bp);
275 && ds_c_str (&filename)[ds_length (&filename) - 1] != DIR_SEPARATOR)
276 ds_putc (&filename, DIR_SEPARATOR);
277 ds_puts (&filename, basename);
279 msg (VM (5), " - %s", ds_c_str (&filename));
280 if (fn_exists_p (ds_c_str (&filename)))
282 msg (VM (4), _("Found `%s'."), ds_c_str (&filename));
284 return ds_c_str (&filename);
289 msg (VM (4), _("Search unsuccessful!"));
291 ds_destroy (&filename);
297 #else /* not unix, msdog, lose32 */
299 fn_search_path (const char *basename, const char *path, const char *prepend)
301 size_t size = strlen (path) + 1 + strlen (basename) + 1;
306 size += strlen (prepend) + 1;
307 string = xmalloc (size);
312 cp = stpcpy (cp, prepend);
313 *cp++ = DIR_SEPARATOR;
315 cp = stpcpy (cp, path);
316 *cp++ = DIR_SEPARATOR;
317 strcpy (cp, basename);
321 #endif /* not unix, msdog, lose32 */
323 /* Prepends directory DIR to filename FILE and returns a malloc()'d
326 fn_prepend_dir (const char *file, const char *dir)
331 if (fn_absolute_p (file))
332 return xstrdup (file);
334 temp = xmalloc (strlen (file) + 1 + strlen (dir) + 1);
335 cp = stpcpy (temp, dir);
336 if (cp != temp && cp[-1] != DIR_SEPARATOR)
337 *cp++ = DIR_SEPARATOR;
338 cp = stpcpy (cp, file);
343 /* fn_normalize(): This very OS-dependent routine canonicalizes
344 filename FN1. The filename should not need to be the name of an
345 existing file. Returns a malloc()'d copy of the canonical name.
346 This function must always succeed; if it needs to bail out then it
347 should return xstrdup(FN1). */
350 fn_normalize (const char *filename)
353 char *fn1, *fn2, *dest;
356 if (fn_special_p (filename))
357 return xstrdup (filename);
359 fn1 = fn_tilde_expand (filename);
361 /* Follow symbolic links. */
365 fn1 = fn_readlink (fn1);
374 maxlen = strlen (fn1) * 2;
377 dest = fn2 = xmalloc (maxlen + 1);
380 if (*src == DIR_SEPARATOR)
385 while (getcwd (dest, maxlen - (dest - fn2)) == NULL && errno == ERANGE)
388 dest = fn2 = xrealloc (fn2, maxlen + 1);
397 dest = strchr (fn2, '\0');
398 if (dest - fn2 >= maxlen)
400 int ofs = dest - fn2;
402 fn2 = xrealloc (fn2, maxlen + 1);
405 if (dest[-1] != DIR_SEPARATOR)
406 *dest++ = DIR_SEPARATOR;
416 if (c == DIR_SEPARATOR || c == 0)
418 /* remove `./', `../' from directory */
419 if (dest[-1] == '.' && dest[-2] == DIR_SEPARATOR)
421 else if (dest[-1] == '.' && dest[-2] == '.' && dest[-3] == DIR_SEPARATOR)
426 while (dest[-1] != DIR_SEPARATOR)
429 else if (dest[-1] != DIR_SEPARATOR) /* remove extra slashes */
434 if (dest[-1] == DIR_SEPARATOR && dest > fn2 + 1)
439 return xrealloc (fn2, strlen (fn2) + 1);
447 if (dest - fn2 >= maxlen)
449 int ofs = dest - fn2;
451 fn2 = xrealloc (fn2, maxlen + 1);
458 #elif defined (__WIN32__)
460 fn_normalize (const char *fn1)
466 /* Don't change special filenames. */
467 if (is_special_filename (filename))
468 return xstrdup (filename);
470 /* First find the required buffer length. */
471 len = GetFullPathName (fn1, 0, NULL, NULL);
478 /* Then make a buffer that big. */
480 success = GetFullPathName (fn1, len, fn2, NULL);
481 if (success >= len || success == 0)
491 fn_normalize (const char *fn1)
493 char *fn2 = _fullpath (NULL, fn1, 0);
497 for (cp = fn2; *cp; cp++)
498 *cp = toupper ((unsigned char) (*cp));
501 return xstrdup (fn1);
505 fn_normalize (const char *fn1)
507 char *fn2 = xmalloc (1024);
509 fn2 = xrealloc (fn2, strlen (fn2) + 1);
512 #else /* not Lose32, Unix, or DJGPP */
514 fn_normalize (const char *fn)
518 #endif /* not Lose32, Unix, or DJGPP */
520 /* Returns the directory part of FILENAME, as a malloc()'d
523 fn_dirname (const char *filename)
529 len = strlen (filename);
530 if (len == 1 && filename[0] == '/')
532 else if (len && filename[len - 1] == DIR_SEPARATOR)
533 p = buf_find_reverse (filename, len - 1, filename + len - 1, 1);
535 p = strrchr (filename, DIR_SEPARATOR);
539 s = xmalloc (p - filename + 1);
540 memcpy (s, filename, p - filename);
546 /* Returns the basename part of FILENAME as a malloc()'d string. */
549 fn_basename (const char *filename)
551 /* Not used, not implemented. */
556 /* Returns the extension part of FILENAME as a malloc()'d string.
557 If FILENAME does not have an extension, returns an empty
560 fn_extension (const char *filename)
562 const char *extension = strrchr (filename, '.');
563 if (extension == NULL)
565 return xstrdup (extension);
569 /* Returns the current working directory, as a malloc()'d string.
575 char *buffer = xmalloc (size);
579 char *value = getcwd (buffer, size);
585 buffer = xmalloc (size);
593 char *buffer = xmalloc (size);
605 /* Find out information about files. */
607 /* Returns nonzero iff NAME specifies an absolute filename. */
609 fn_absolute_p (const char *name)
613 || !strncmp (name, "./", 2)
614 || !strncmp (name, "../", 3)
617 #elif defined (__MSDOS__)
619 || !strncmp (name, ".\\", 2)
620 || !strncmp (name, "..\\", 3)
621 || (name[0] && name[1] == ':'))
628 /* Returns 1 if the filename specified is a virtual file that doesn't
629 really exist on disk, 0 if it's a real filename. */
631 fn_special_p (const char *filename)
633 if (!strcmp (filename, "-") || !strcmp (filename, "stdin")
634 || !strcmp (filename, "stdout") || !strcmp (filename, "stderr")
636 || filename[0] == '|'
637 || (*filename && filename[strlen (filename) - 1] == '|')
645 /* Returns nonzero if file with name NAME exists. */
647 fn_exists_p (const char *name)
652 return stat (name, &temp) == 0;
654 FILE *f = fopen (name, "r");
662 /* Returns the symbolic link value for FILENAME as a dynamically
663 allocated buffer, or a null pointer on failure. */
665 fn_readlink (const char *filename)
667 return xreadlink (filename, 32);
670 /* Environment variables. */
672 /* Simulates $VER and $ARCH environment variables. */
674 fn_getenv (const char *s)
676 if (!strcmp (s, "VER"))
677 return fn_getenv_default ("STAT_VER", bare_version);
678 else if (!strcmp (s, "ARCH"))
679 return fn_getenv_default ("STAT_ARCH", host_system);
684 /* Returns getenv(KEY) if that's non-NULL; else returns DEF. */
686 fn_getenv_default (const char *key, const char *def)
688 const char *value = getenv (key);
689 return value ? value : def;
692 /* Basic file handling. */
694 /* Used for giving an error message on a set_safer security
697 safety_violation (const char *fn)
699 msg (SE, _("Not opening pipe file `%s' because SAFER option set."), fn);
704 /* As a general comment on the following routines, a `sensible value'
705 for errno includes 0 if there is no associated system error. The
706 routines will only set errno to 0 if there is an error in a
707 callback that sets errno to 0; they themselves won't. */
709 /* File open routine that understands `-' as stdin/stdout and `|cmd'
710 as a pipe to command `cmd'. Returns resultant FILE on success,
711 NULL on failure. If NULL is returned then errno is set to a
714 fn_open (const char *fn, const char *mode)
716 assert (mode[0] == 'r' || mode[0] == 'w');
718 if (mode[0] == 'r' && (!strcmp (fn, "stdin") || !strcmp (fn, "-")))
720 else if (mode[0] == 'w' && (!strcmp (fn, "stdout") || !strcmp (fn, "-")))
722 else if (mode[0] == 'w' && !strcmp (fn, "stderr"))
728 if (get_safer_mode ())
729 return safety_violation (fn);
731 return popen (&fn[1], mode);
733 else if (*fn && fn[strlen (fn) - 1] == '|')
738 if (get_safer_mode ())
739 return safety_violation (fn);
741 s = local_alloc (strlen (fn));
742 memcpy (s, fn, strlen (fn) - 1);
743 s[strlen (fn) - 1] = 0;
754 FILE *f = fopen (fn, mode);
756 if (f && mode[0] == 'w')
757 setvbuf (f, NULL, _IOLBF, 0);
763 /* Counterpart to fn_open that closes file F with name FN; returns 0
764 on success, EOF on failure. If EOF is returned, errno is set to a
767 fn_close (const char *fn, FILE *f)
769 if (!strcmp (fn, "-"))
772 else if (fn[0] == '|' || (*fn && fn[strlen (fn) - 1] == '|'))
782 /* More extensive file handling. */
784 /* File open routine that extends fn_open(). Opens or reopens a
785 file according to the contents of file_ext F. Returns nonzero on
786 success. If 0 is returned, errno is set to a sensible value. */
788 fn_open_ext (struct file_ext *f)
792 p = strstr (f->filename, "%d");
795 char *s = local_alloc (strlen (f->filename)
796 + INT_STRLEN_BOUND (int) - 1);
799 memcpy (s, f->filename, p - f->filename);
800 cp = spprintf (&s[p - f->filename], "%d", *f->sequence_no);
808 if (f->preclose (f) == 0)
811 if (EOF == fn_close (f->filename, f->file) || error)
825 f->file = fn_open (s, f->mode);
828 if (f->file && f->postopen)
829 if (f->postopen (f) == 0)
832 fn_close (f->filename, f->file);
838 return (f->file != NULL);
844 f->file = fn_open (f->filename, f->mode);
846 if (f->file && f->postopen)
847 if (f->postopen (f) == 0)
850 fn_close (f->filename, f->file);
856 return (f->file != NULL);
860 /* Properly closes the file associated with file_ext F, if any.
861 Return nonzero on success. If zero is returned, errno is set to a
864 fn_close_ext (struct file_ext *f)
871 if (f->preclose (f) == 0)
874 if (EOF == fn_close (f->filename, f->file) || error)
890 /* A file's identity. */
893 dev_t device; /* Device number. */
894 ino_t inode; /* Inode number. */
897 /* Returns a pointer to a dynamically allocated structure whose
898 value can be used to tell whether two files are actually the
899 same file. Returns a null pointer if no information about the
900 file is available, perhaps because it does not exist. The
901 caller is responsible for freeing the structure with
902 fn_free_identity() when finished. */
903 struct file_identity *
904 fn_get_identity (const char *filename)
908 if (stat (filename, &s) == 0)
910 struct file_identity *identity = xmalloc (sizeof *identity);
911 identity->device = s.st_dev;
912 identity->inode = s.st_ino;
919 /* Frees IDENTITY obtained from fn_get_identity(). */
921 fn_free_identity (struct file_identity *identity)
926 /* Compares A and B, returning a strcmp()-type result. */
928 fn_compare_file_identities (const struct file_identity *a,
929 const struct file_identity *b)
933 if (a->device != b->device)
934 return a->device < b->device ? -1 : 1;
936 return a->inode < b->inode ? -1 : a->inode > b->inode;
939 /* A file's identity. */
942 char *normalized_filename; /* File's normalized name. */
945 /* Returns a pointer to a dynamically allocated structure whose
946 value can be used to tell whether two files are actually the
947 same file. Returns a null pointer if no information about the
948 file is available, perhaps because it does not exist. The
949 caller is responsible for freeing the structure with
950 fn_free_identity() when finished. */
951 struct file_identity *
952 fn_get_identity (const char *filename)
954 struct file_identity *identity = xmalloc (sizeof *identity);
955 identity->normalized_filename = fn_normalize (filename);
959 /* Frees IDENTITY obtained from fn_get_identity(). */
961 fn_free_identity (struct file_identity *identity)
963 if (identity != NULL)
965 free (identity->normalized_filename);
970 /* Compares A and B, returning a strcmp()-type result. */
972 fn_compare_file_identities (const struct file_identity *a,
973 const struct file_identity *b)
975 return strcmp (a->normalized_filename, b->normalized_filename);
977 #endif /* not unix */