1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000 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., 59 Temple Place - Suite 330, Boston, MA
32 #include "debug-print.h"
34 /* PORTME: Everything in this file is system dependent. */
48 #include <win32/windows.h>
57 const char *config_path;
62 config_path = fn_getenv_default ("STAT_CONFIG_PATH", default_config_path);
65 /* Functions for performing operations on filenames. */
67 /* Substitutes $variables as defined by GETENV into INPUT and returns
68 a copy of the resultant string. Supports $var and ${var} syntaxes;
69 $$ substitutes as $. */
71 fn_interp_vars (const char *input, const char *(*getenv) (const char *))
75 if (NULL == strchr (input, '$'))
76 return xstrdup (input);
78 ds_init (&output, strlen (input));
84 return ds_c_str (&output);
91 ds_putc (&output, '$');
100 start = ds_length (&output);
107 else if (*input == '{')
115 while (*input && *input != stop
116 && (stop || isalpha ((unsigned char) *input)))
117 ds_putc (&output, *input++);
119 value = getenv (ds_c_str (&output) + start);
120 ds_truncate (&output, start);
121 ds_puts (&output, value);
123 if (stop && *input == stop)
128 ds_putc (&output, *input++);
133 /* Expands csh tilde notation from the path INPUT into a malloc()'d
136 fn_tilde_expand (const char *input)
139 struct string output;
141 if (NULL == strchr (input, '~'))
142 return xstrdup (input);
143 ds_init (&output, strlen (input));
147 for (ip = input; *ip; )
148 if (*ip != '~' || (ip != input && ip[-1] != PATH_DELIMITER))
149 ds_putc (&output, *ip++);
152 static const char stop_set[3] = {DIR_SEPARATOR, PATH_DELIMITER, 0};
157 cp = ip + strcspn (ip, stop_set);
164 strncpy (username, ip, cp - ip + 1);
166 pwd = getpwnam (username);
168 if (!pwd || !pwd->pw_dir)
169 ds_putc (&output, *ip++);
171 ds_puts (&output, pwd->pw_dir);
175 const char *home = fn_getenv ("HOME");
177 ds_putc (&output, *ip++);
179 ds_puts (&output, home);
185 return ds_c_str (&output);
189 fn_tilde_expand (const char *input)
191 return xstrdup (input);
195 /* Searches for a configuration file with name NAME in the path given
196 by PATH, which is tilde- and environment-interpolated. Directories
197 in PATH are delimited by PATH_DELIMITER, defined in <pref.h>.
198 Returns the malloc'd full name of the first file found, or NULL if
201 If PREPEND is non-NULL, then it is prepended to each filename;
202 i.e., it looks like PREPEND/PATH_COMPONENT/NAME. This is not done
203 with absolute directories in the path. */
204 #if defined (unix) || defined (__MSDOS__) || defined (__WIN32__)
206 fn_search_path (const char *basename, const char *path, const char *prepend)
209 struct string filename;
212 if (fn_absolute_p (basename))
213 return fn_tilde_expand (basename);
216 char *temp = fn_interp_vars (path, fn_getenv);
217 bp = subst_path = fn_tilde_expand (temp);
221 msg (VM (4), _("Searching for `%s'..."), basename);
222 ds_init (&filename, 64);
229 msg (VM (4), _("Search unsuccessful!"));
230 ds_destroy (&filename);
235 for (ep = bp; *ep && *ep != PATH_DELIMITER; ep++)
238 /* Paste together PREPEND/PATH/BASENAME. */
239 ds_clear (&filename);
240 if (prepend && !fn_absolute_p (bp))
242 ds_puts (&filename, prepend);
243 ds_putc (&filename, DIR_SEPARATOR);
245 ds_concat (&filename, bp, ep - bp);
247 && ds_c_str (&filename)[ds_length (&filename) - 1] != DIR_SEPARATOR)
248 ds_putc (&filename, DIR_SEPARATOR);
249 ds_puts (&filename, basename);
251 msg (VM (5), " - %s", ds_c_str (&filename));
252 if (fn_exists_p (ds_c_str (&filename)))
254 msg (VM (4), _("Found `%s'."), ds_c_str (&filename));
256 return ds_c_str (&filename);
261 msg (VM (4), _("Search unsuccessful!"));
263 ds_destroy (&filename);
269 #else /* not unix, msdog, lose32 */
271 fn_search_path (const char *basename, const char *path, const char *prepend)
273 size_t size = strlen (path) + 1 + strlen (basename) + 1;
278 size += strlen (prepend) + 1;
279 string = xmalloc (size);
284 cp = stpcpy (cp, prepend);
285 *cp++ = DIR_SEPARATOR;
287 cp = stpcpy (cp, path);
288 *cp++ = DIR_SEPARATOR;
289 strcpy (cp, basename);
293 #endif /* not unix, msdog, lose32 */
295 /* Prepends directory DIR to filename FILE and returns a malloc()'d
298 fn_prepend_dir (const char *file, const char *dir)
303 if (fn_absolute_p (file))
304 return xstrdup (file);
306 temp = xmalloc (strlen (file) + 1 + strlen (dir) + 1);
307 cp = stpcpy (temp, dir);
308 if (cp != temp && cp[-1] != DIR_SEPARATOR)
309 *cp++ = DIR_SEPARATOR;
310 cp = stpcpy (cp, file);
315 /* fn_normalize(): This very OS-dependent routine canonicalizes
316 filename FN1. The filename should not need to be the name of an
317 existing file. Returns a malloc()'d copy of the canonical name.
318 This function must always succeed; if it needs to bail out then it
319 should return xstrdup(FN1). */
322 fn_normalize (const char *filename)
325 char *fn1, *fn2, *dest;
328 if (fn_special_p (filename))
329 return xstrdup (filename);
331 fn1 = fn_tilde_expand (filename);
333 /* Follow symbolic links. */
337 fn1 = fn_readlink (fn1);
346 maxlen = strlen (fn1) * 2;
349 dest = fn2 = xmalloc (maxlen + 1);
352 if (*src == DIR_SEPARATOR)
357 while (getcwd (dest, maxlen - (dest - fn2)) == NULL && errno == ERANGE)
360 dest = fn2 = xrealloc (fn2, maxlen + 1);
369 dest = strchr (fn2, '\0');
370 if (dest - fn2 >= maxlen)
372 int ofs = dest - fn2;
374 fn2 = xrealloc (fn2, maxlen + 1);
377 if (dest[-1] != DIR_SEPARATOR)
378 *dest++ = DIR_SEPARATOR;
388 if (c == DIR_SEPARATOR || c == 0)
390 /* remove `./', `../' from directory */
391 if (dest[-1] == '.' && dest[-2] == DIR_SEPARATOR)
393 else if (dest[-1] == '.' && dest[-2] == '.' && dest[-3] == DIR_SEPARATOR)
398 while (dest[-1] != DIR_SEPARATOR)
401 else if (dest[-1] != DIR_SEPARATOR) /* remove extra slashes */
406 if (dest[-1] == DIR_SEPARATOR && dest > fn2 + 1)
411 return xrealloc (fn2, strlen (fn2) + 1);
419 if (dest - fn2 >= maxlen)
421 int ofs = dest - fn2;
423 fn2 = xrealloc (fn2, maxlen + 1);
430 #elif defined (__WIN32__)
432 fn_normalize (const char *fn1)
438 /* Don't change special filenames. */
439 if (is_special_filename (filename))
440 return xstrdup (filename);
442 /* First find the required buffer length. */
443 len = GetFullPathName (fn1, 0, NULL, NULL);
450 /* Then make a buffer that big. */
452 success = GetFullPathName (fn1, len, fn2, NULL);
453 if (success >= len || success == 0)
463 fn_normalize (const char *fn1)
465 char *fn2 = _fullpath (NULL, fn1, 0);
469 for (cp = fn2; *cp; cp++)
470 *cp = toupper ((unsigned char) (*cp));
473 return xstrdup (fn1);
477 fn_normalize (const char *fn1)
479 char *fn2 = xmalloc (1024);
481 fn2 = xrealloc (fn2, strlen (fn2) + 1);
484 #else /* not Lose32, Unix, or DJGPP */
486 fn_normalize (const char *fn)
490 #endif /* not Lose32, Unix, or DJGPP */
492 /* Returns the directory part of FILENAME, as a malloc()'d
495 fn_dirname (const char *filename)
501 len = strlen (filename);
502 if (len == 1 && filename[0] == '/')
504 else if (len && filename[len - 1] == DIR_SEPARATOR)
505 p = mm_find_reverse (filename, len - 1, filename + len - 1, 1);
507 p = strrchr (filename, DIR_SEPARATOR);
511 s = xmalloc (p - filename + 1);
512 memcpy (s, filename, p - filename);
518 /* Returns the basename part of FILENAME as a malloc()'d string. */
521 fn_basename (const char *filename)
523 /* Not used, not implemented. */
529 /* Returns the current working directory, as a malloc()'d string.
535 char *buffer = xmalloc (size);
539 char *value = getcwd (buffer, size);
545 buffer = xmalloc (size);
553 char *buffer = xmalloc (size);
565 /* Find out information about files. */
567 /* Returns nonzero iff NAME specifies an absolute filename. */
569 fn_absolute_p (const char *name)
573 || !strncmp (name, "./", 2)
574 || !strncmp (name, "../", 3)
577 #elif defined (__MSDOS__)
579 || !strncmp (name, ".\\", 2)
580 || !strncmp (name, "..\\", 3)
581 || (name[0] && name[1] == ':'))
588 /* Returns 1 if the filename specified is a virtual file that doesn't
589 really exist on disk, 0 if it's a real filename. */
591 fn_special_p (const char *filename)
593 if (!strcmp (filename, "-") || !strcmp (filename, "stdin")
594 || !strcmp (filename, "stdout") || !strcmp (filename, "stderr")
596 || filename[0] == '|'
597 || (*filename && filename[strlen (filename) - 1] == '|')
605 /* Returns nonzero if file with name NAME exists. */
607 fn_exists_p (const char *name)
612 return stat (name, &temp) == 0;
614 FILE *f = fopen (name, "r");
623 /* Stolen from libc.info but heavily modified, this is a wrapper
624 around readlink() that allows for arbitrary filename length. */
626 fn_readlink (const char *filename)
632 char *buffer = xmalloc (size);
633 int nchars = readlink (filename, buffer, size);
640 if (nchars < size - 1)
649 #else /* Not UNIX. */
651 fn_readlink (const char *filename)
655 #endif /* Not UNIX. */
657 /* Environment variables. */
659 /* Simulates $VER and $ARCH environment variables. */
661 fn_getenv (const char *s)
663 if (!strcmp (s, "VER"))
664 return fn_getenv_default ("STAT_VER", bare_version);
665 else if (!strcmp (s, "ARCH"))
666 return fn_getenv_default ("STAT_ARCH", host_system);
671 /* Returns getenv(KEY) if that's non-NULL; else returns DEF. */
673 fn_getenv_default (const char *key, const char *def)
675 const char *value = getenv (key);
676 return value ? value : def;
679 /* Basic file handling. */
681 /* Used for giving an error message on a set_safer security
684 safety_violation (const char *fn)
686 msg (SE, _("Not opening pipe file `%s' because SAFER option set."), fn);
691 /* As a general comment on the following routines, a `sensible value'
692 for errno includes 0 if there is no associated system error. The
693 routines will only set errno to 0 if there is an error in a
694 callback that sets errno to 0; they themselves won't. */
696 /* File open routine that understands `-' as stdin/stdout and `|cmd'
697 as a pipe to command `cmd'. Returns resultant FILE on success,
698 NULL on failure. If NULL is returned then errno is set to a
701 fn_open (const char *fn, const char *mode)
703 assert (mode[0] == 'r' || mode[0] == 'w');
705 if (mode[0] == 'r' && (!strcmp (fn, "stdin") || !strcmp (fn, "-")))
707 else if (mode[0] == 'w' && (!strcmp (fn, "stdout") || !strcmp (fn, "-")))
709 else if (mode[0] == 'w' && !strcmp (fn, "stderr"))
716 return safety_violation (fn);
718 return popen (&fn[1], mode);
720 else if (*fn && fn[strlen (fn) - 1] == '|')
726 return safety_violation (fn);
728 s = local_alloc (strlen (fn));
729 memcpy (s, fn, strlen (fn) - 1);
730 s[strlen (fn) - 1] = 0;
741 FILE *f = fopen (fn, mode);
743 if (f && mode[0] == 'w')
744 setvbuf (f, NULL, _IOLBF, 0);
750 /* Counterpart to fn_open that closes file F with name FN; returns 0
751 on success, EOF on failure. If EOF is returned, errno is set to a
754 fn_close (const char *fn, FILE *f)
756 if (!strcmp (fn, "-"))
759 else if (fn[0] == '|' || (*fn && fn[strlen (fn) - 1] == '|'))
769 /* More extensive file handling. */
771 /* File open routine that extends fn_open(). Opens or reopens a
772 file according to the contents of file_ext F. Returns nonzero on
773 success. If 0 is returned, errno is set to a sensible value. */
775 fn_open_ext (struct file_ext *f)
779 p = strstr (f->filename, "%d");
782 char *s = local_alloc (strlen (f->filename) + INT_DIGITS - 1);
785 memcpy (s, f->filename, p - f->filename);
786 cp = spprintf (&s[p - f->filename], "%d", *f->sequence_no);
794 if (f->preclose (f) == 0)
797 if (EOF == fn_close (f->filename, f->file) || error)
811 f->file = fn_open (s, f->mode);
814 if (f->file && f->postopen)
815 if (f->postopen (f) == 0)
818 fn_close (f->filename, f->file);
824 return (f->file != NULL);
830 f->file = fn_open (f->filename, f->mode);
832 if (f->file && f->postopen)
833 if (f->postopen (f) == 0)
836 fn_close (f->filename, f->file);
842 return (f->file != NULL);
846 /* Properly closes the file associated with file_ext F, if any.
847 Return nonzero on success. If zero is returned, errno is set to a
850 fn_close_ext (struct file_ext *f)
857 if (f->preclose (f) == 0)
860 if (EOF == fn_close (f->filename, f->file) || error)
876 /* A file's identity. */
879 dev_t device; /* Device number. */
880 ino_t inode; /* Inode number. */
883 /* Returns a pointer to a dynamically allocated structure whose
884 value can be used to tell whether two files are actually the
885 same file. Returns a null pointer if no information about the
886 file is available, perhaps because it does not exist. The
887 caller is responsible for freeing the structure with
888 fn_free_identity() when finished. */
889 struct file_identity *
890 fn_get_identity (const char *filename)
894 if (stat (filename, &s) == 0)
896 struct file_identity *identity = xmalloc (sizeof *identity);
897 identity->device = s.st_dev;
898 identity->inode = s.st_ino;
905 /* Frees IDENTITY obtained from fn_get_identity(). */
907 fn_free_identity (struct file_identity *identity)
912 /* Compares A and B, returning a strcmp()-type result. */
914 fn_compare_file_identities (const struct file_identity *a,
915 const struct file_identity *b)
919 if (a->device != b->device)
920 return a->device < b->device ? -1 : 1;
922 return a->inode < b->inode ? -1 : a->inode > b->inode;
925 /* A file's identity. */
928 char *normalized_filename; /* File's normalized name. */
931 /* Returns a pointer to a dynamically allocated structure whose
932 value can be used to tell whether two files are actually the
933 same file. Returns a null pointer if no information about the
934 file is available, perhaps because it does not exist. The
935 caller is responsible for freeing the structure with
936 fn_free_identity() when finished. */
937 struct file_identity *
938 fn_get_identity (const char *filename)
940 struct file_identity *identity = xmalloc (sizeof *identity);
941 identity->normalized_filename = fn_normalize (filename);
945 /* Frees IDENTITY obtained from fn_get_identity(). */
947 fn_free_identity (struct file_identity *identity)
949 if (identity != NULL)
951 free (identity->normalized_filename);
956 /* Compares A and B, returning a strcmp()-type result. */
958 fn_compare_file_identities (const struct file_identity *a,
959 const struct file_identity *b)
961 return strcmp (a->normalized_filename, b->normalized_filename);
963 #endif /* not unix */