Add scratch file handles.
[pspp-builds.git] / src / filename.c
index 5cbbc9eab2d6ec2bbb83dc586205cd69a61d16f1..0c085776f5b889aefc23dfc9fb8174273bcc759e 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
 
-/* AIX requires this to be the first thing in the file.  */
 #include <config.h>
-#if __GNUC__
-#define alloca __builtin_alloca
-#else
-#if HAVE_ALLOCA_H
-#include <alloca.h>
-#else
-#ifdef _AIX
-#pragma alloca
-#else
-#ifndef alloca                 /* predefined by HP cc +Olibcalls */
-char *alloca ();
-#endif
-#endif
-#endif
-#endif
-
-#include <assert.h>
+#include "error.h"
+#include "filename.h"
+#include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
 #include <errno.h>
 #include "alloc.h"
 #include "error.h"
-#include "filename.h"
 #include "settings.h"
 #include "str.h"
 #include "version.h"
+#include "xreadlink.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
 
 #include "debug-print.h"
 
 /* PORTME: Everything in this file is system dependent. */
 
-#if unix
+#ifdef unix
 #include <pwd.h>
 #if HAVE_UNISTD_H
 #include <unistd.h>
 #endif
-#include "stat.h"
+#include <sys/stat.h>
+#include "stat-macros.h"
 #endif
 
-#if __WIN32__
+#ifdef __WIN32__
 #define NOGDI
 #define NOUSER
 #define NONLS
@@ -92,20 +81,20 @@ fn_interp_vars (const char *input, const char *(*getenv) (const char *))
   if (NULL == strchr (input, '$'))
     return xstrdup (input);
 
-  ds_init (NULL, &output, strlen (input));
+  ds_init (&output, strlen (input));
 
   for (;;)
     switch (*input)
       {
       case '\0':
-       return ds_value (&output);
+       return ds_c_str (&output);
        
       case '$':
        input++;
 
        if (*input == '$')
          {
-           ds_putchar (&output, '$');
+           ds_putc (&output, '$');
            input++;
          }
        else
@@ -131,22 +120,22 @@ fn_interp_vars (const char *input, const char *(*getenv) (const char *))
 
            while (*input && *input != stop
                   && (stop || isalpha ((unsigned char) *input)))
-             ds_putchar (&output, *input++);
+             ds_putc (&output, *input++);
            
-           value = getenv (ds_value (&output) + start);
+           value = getenv (ds_c_str (&output) + start);
            ds_truncate (&output, start);
-           ds_concat (&output, value);
+           ds_puts (&output, value);
 
            if (stop && *input == stop)
              input++;
          }
 
       default:
-       ds_putchar (&output, *input++);
+       ds_putc (&output, *input++);
       }
 }
 
-#if unix
+#ifdef unix
 /* Expands csh tilde notation from the path INPUT into a malloc()'d
    returned string. */
 char *
@@ -157,13 +146,13 @@ fn_tilde_expand (const char *input)
 
   if (NULL == strchr (input, '~'))
     return xstrdup (input);
-  ds_init (NULL, &output, strlen (input));
+  ds_init (&output, strlen (input));
 
   ip = input;
 
   for (ip = input; *ip; )
     if (*ip != '~' || (ip != input && ip[-1] != PATH_DELIMITER))
-      ds_putchar (&output, *ip++);
+      ds_putc (&output, *ip++);
     else
       {
        static const char stop_set[3] = {DIR_SEPARATOR, PATH_DELIMITER, 0};
@@ -183,27 +172,27 @@ fn_tilde_expand (const char *input)
            pwd = getpwnam (username);
 
            if (!pwd || !pwd->pw_dir)
-             ds_putchar (&output, *ip++);
+             ds_putc (&output, *ip++);
            else
-             ds_concat (&output, pwd->pw_dir);
+             ds_puts (&output, pwd->pw_dir);
          }
        else
          {
            const char *home = fn_getenv ("HOME");
            if (!home)
-             ds_putchar (&output, *ip++);
+             ds_putc (&output, *ip++);
            else
-             ds_concat (&output, home);
+             ds_puts (&output, home);
          }
 
        ip = cp;
       }
 
-  return ds_value (&output);
+  return ds_c_str (&output);
 }
 #else /* !unix */
 char *
-fn_tilde_expand (char *input)
+fn_tilde_expand (const char *input)
 {
   return xstrdup (input);
 }
@@ -218,7 +207,7 @@ fn_tilde_expand (char *input)
    If PREPEND is non-NULL, then it is prepended to each filename;
    i.e., it looks like PREPEND/PATH_COMPONENT/NAME.  This is not done
    with absolute directories in the path. */
-#if unix || __MSDOS__ || __WIN32__
+#if defined (unix) || defined (__MSDOS__) || defined (__WIN32__)
 char *
 fn_search_path (const char *basename, const char *path, const char *prepend)
 {
@@ -236,7 +225,7 @@ fn_search_path (const char *basename, const char *path, const char *prepend)
   }
 
   msg (VM (4), _("Searching for `%s'..."), basename);
-  ds_init (NULL, &filename, 64);
+  ds_init (&filename, 64);
 
   for (;;)
     {
@@ -256,21 +245,21 @@ fn_search_path (const char *basename, const char *path, const char *prepend)
       ds_clear (&filename);
       if (prepend && !fn_absolute_p (bp))
        {
-         ds_concat (&filename, prepend);
-         ds_putchar (&filename, DIR_SEPARATOR);
+         ds_puts (&filename, prepend);
+         ds_putc (&filename, DIR_SEPARATOR);
        }
-      ds_concat_buffer (&filename, bp, ep - bp);
+      ds_concat (&filename, bp, ep - bp);
       if (ep - bp
-         && ds_value (&filename)[ds_length (&filename) - 1] != DIR_SEPARATOR)
-       ds_putchar (&filename, DIR_SEPARATOR);
-      ds_concat (&filename, basename);
+         && ds_c_str (&filename)[ds_length (&filename) - 1] != DIR_SEPARATOR)
+       ds_putc (&filename, DIR_SEPARATOR);
+      ds_puts (&filename, basename);
       
-      msg (VM (5), " - %s", ds_value (&filename));
-      if (fn_exists_p (ds_value (&filename)))
+      msg (VM (5), " - %s", ds_c_str (&filename));
+      if (fn_exists_p (ds_c_str (&filename)))
        {
-         msg (VM (4), _("Found `%s'."), ds_value (&filename));
+         msg (VM (4), _("Found `%s'."), ds_c_str (&filename));
          free (subst_path);
-         return ds_value (&filename);
+         return ds_c_str (&filename);
        }
 
       if (0 == *ep)
@@ -334,7 +323,7 @@ fn_prepend_dir (const char *file, const char *dir)
    existing file.  Returns a malloc()'d copy of the canonical name.
    This function must always succeed; if it needs to bail out then it
    should return xstrdup(FN1).  */
-#if unix
+#ifdef unix
 char *
 fn_normalize (const char *filename)
 {
@@ -444,7 +433,7 @@ fn_normalize (const char *filename)
        }
     }
 }
-#elif __WIN32__
+#elif defined (__WIN32__)
 char *
 fn_normalize (const char *fn1)
 {
@@ -519,7 +508,7 @@ fn_dirname (const char *filename)
   if (len == 1 && filename[0] == '/')
     p = filename + 1;
   else if (len && filename[len - 1] == DIR_SEPARATOR)
-    p = mm_find_reverse (filename, len - 1, filename + len - 1, 1);
+    p = buf_find_reverse (filename, len - 1, filename + len - 1, 1);
   else
     p = strrchr (filename, DIR_SEPARATOR);
   if (p == NULL)
@@ -541,7 +530,20 @@ fn_basename (const char *filename)
   abort ();
 }
 #endif
+
+/* Returns the extension part of FILENAME as a malloc()'d string.
+   If FILENAME does not have an extension, returns an empty
+   string. */
+char *
+fn_extension (const char *filename) 
+{
+  const char *extension = strrchr (filename, '.');
+  if (extension == NULL)
+    extension = "";
+  return xstrdup (extension);
+}
 \f
+#if unix
 /* Returns the current working directory, as a malloc()'d string.
    From libc.info. */
 char *
@@ -561,6 +563,22 @@ fn_get_cwd (void)
       buffer = xmalloc (size);
     }
 }
+#else
+char *
+fn_get_cwd (void)
+{
+  int size = 2;
+  char *buffer = xmalloc (size);
+  if ( buffer) 
+  {
+    buffer[0]='.';
+    buffer[1]='\0';
+  }
+
+  return buffer;
+     
+}
+#endif
 \f
 /* Find out information about files. */
 
@@ -568,13 +586,13 @@ fn_get_cwd (void)
 int
 fn_absolute_p (const char *name)
 {
-#if unix
+#ifdef unix
   if (name[0] == '/'
       || !strncmp (name, "./", 2)
       || !strncmp (name, "../", 3)
       || name[0] == '~')
     return 1;
-#elif __MSDOS__
+#elif defined (__MSDOS__)
   if (name[0] == '\\'
       || !strncmp (name, ".\\", 2)
       || !strncmp (name, "..\\", 3)
@@ -592,7 +610,7 @@ fn_special_p (const char *filename)
 {
   if (!strcmp (filename, "-") || !strcmp (filename, "stdin")
       || !strcmp (filename, "stdout") || !strcmp (filename, "stderr")
-#if unix
+#ifdef unix
       || filename[0] == '|'
       || (*filename && filename[strlen (filename) - 1] == '|')
 #endif
@@ -606,7 +624,7 @@ fn_special_p (const char *filename)
 int
 fn_exists_p (const char *name)
 {
-#if unix
+#ifdef unix
   struct stat temp;
 
   return stat (name, &temp) == 0;
@@ -619,40 +637,13 @@ fn_exists_p (const char *name)
 #endif
 }
 
-#if unix
-/* Stolen from libc.info but heavily modified, this is a wrapper
-   around readlink() that allows for arbitrary filename length. */
+/* Returns the symbolic link value for FILENAME as a dynamically
+   allocated buffer, or a null pointer on failure. */
 char *
 fn_readlink (const char *filename)
 {
-  int size = 128;
-
-  for (;;)
-    {
-      char *buffer = xmalloc (size);
-      int nchars  = readlink (filename, buffer, size);
-      if (nchars == -1)
-       {
-         free (buffer);
-         return NULL;
-       }
-
-      if (nchars < size - 1)
-       {
-         buffer[nchars] = 0;
-         return buffer;
-       }
-      free (buffer);
-      size *= 2;
-    }
-}
-#else /* Not UNIX. */
-char *
-fn_readlink (const char *filename)
-{
-  return NULL;
+  return xreadlink (filename, 32);
 }
-#endif /* Not UNIX. */
 \f
 /* Environment variables. */
 
@@ -709,10 +700,10 @@ fn_open (const char *fn, const char *mode)
   else if (mode[0] == 'w' && !strcmp (fn, "stderr"))
     return stderr;
   
-#if unix
+#ifdef unix
   if (fn[0] == '|')
     {
-      if (set_safer)
+      if (get_safer_mode ())
        return safety_violation (fn);
 
       return popen (&fn[1], mode);
@@ -722,7 +713,7 @@ fn_open (const char *fn, const char *mode)
       char *s;
       FILE *f;
 
-      if (set_safer)
+      if (get_safer_mode ())
        return safety_violation (fn);
       
       s = local_alloc (strlen (fn));
@@ -755,7 +746,7 @@ fn_close (const char *fn, FILE *f)
 {
   if (!strcmp (fn, "-"))
     return 0;
-#if unix
+#ifdef unix
   else if (fn[0] == '|' || (*fn && fn[strlen (fn) - 1] == '|'))
     {
       pclose (f);
@@ -871,3 +862,93 @@ fn_close_ext (struct file_ext *f)
     }
   return 1;
 }
+
+#ifdef unix
+/* A file's identity. */
+struct file_identity 
+  {
+    dev_t device;               /* Device number. */
+    ino_t inode;                /* Inode number. */
+  };
+
+/* Returns a pointer to a dynamically allocated structure whose
+   value can be used to tell whether two files are actually the
+   same file.  Returns a null pointer if no information about the
+   file is available, perhaps because it does not exist.  The
+   caller is responsible for freeing the structure with
+   fn_free_identity() when finished. */  
+struct file_identity *
+fn_get_identity (const char *filename) 
+{
+  struct stat s;
+
+  if (stat (filename, &s) == 0) 
+    {
+      struct file_identity *identity = xmalloc (sizeof *identity);
+      identity->device = s.st_dev;
+      identity->inode = s.st_ino;
+      return identity;
+    }
+  else
+    return NULL;
+}
+
+/* Frees IDENTITY obtained from fn_get_identity(). */
+void
+fn_free_identity (struct file_identity *identity) 
+{
+  free (identity);
+}
+
+/* Compares A and B, returning a strcmp()-type result. */
+int
+fn_compare_file_identities (const struct file_identity *a,
+                            const struct file_identity *b) 
+{
+  assert (a != NULL);
+  assert (b != NULL);
+  if (a->device != b->device)
+    return a->device < b->device ? -1 : 1;
+  else
+    return a->inode < b->inode ? -1 : a->inode > b->inode;
+}
+#else /* not unix */
+/* A file's identity. */
+struct file_identity 
+  {
+    char *normalized_filename;  /* File's normalized name. */
+  };
+
+/* Returns a pointer to a dynamically allocated structure whose
+   value can be used to tell whether two files are actually the
+   same file.  Returns a null pointer if no information about the
+   file is available, perhaps because it does not exist.  The
+   caller is responsible for freeing the structure with
+   fn_free_identity() when finished. */  
+struct file_identity *
+fn_get_identity (const char *filename) 
+{
+  struct file_identity *identity = xmalloc (sizeof *identity);
+  identity->normalized_filename = fn_normalize (filename);
+  return identity;
+}
+
+/* Frees IDENTITY obtained from fn_get_identity(). */
+void
+fn_free_identity (struct file_identity *identity) 
+{
+  if (identity != NULL) 
+    {
+      free (identity->normalized_filename);
+      free (identity);
+    }
+}
+
+/* Compares A and B, returning a strcmp()-type result. */
+int
+fn_compare_file_identities (const struct file_identity *a,
+                            const struct file_identity *b) 
+{
+  return strcmp (a->normalized_filename, b->normalized_filename);
+}
+#endif /* not unix */