file-name: Generalize fn_interp_vars().
[pspp-builds.git] / src / data / file-name.c
index 14afd60e53be5f86e01963d6f0a9aa08927e5de3..cf58b6db1a24a27cb0f38b7d8b79417e62be4ef5 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -63,13 +63,13 @@ fn_init (void)
 /* Functions for performing operations on file names. */
 
 
-/* Substitutes $variables in SRC, putting the result in DST,
-   properly handling the case where SRC is a substring of DST.
-   Variables are as defined by GETENV. Supports $var and ${var}
-   syntaxes; $$ substitutes as $. */
+/* Copies from SRC to DST, calling INSERT_VARIABLE to handle each
+   instance of $var or ${var} in SRC.  $$ is replaced by $. */
 void
-fn_interp_vars (struct substring src, const char *(*getenv) (const char *),
-                struct string *dst_)
+fn_interp_vars (struct substring src,
+                void (*insert_variable) (const char *var,
+                                         struct string *dst, void *aux),
+                void *aux, struct string *dst_)
 {
   struct string dst = DS_EMPTY_INITIALIZER;
   int c;
@@ -84,8 +84,7 @@ fn_interp_vars (struct substring src, const char *(*getenv) (const char *),
         else
           {
             struct substring var_name;
-            size_t start;
-            const char *value;
+            char *var;
 
             if (ss_match_char (&src, '('))
               ss_get_until (&src, ')', &var_name);
@@ -95,12 +94,9 @@ fn_interp_vars (struct substring src, const char *(*getenv) (const char *),
               ss_get_chars (&src, MAX (1, ss_span (src, ss_cstr (CC_ALNUM))),
                             &var_name);
 
-            start = ds_length (&dst);
-            ds_put_substring (&dst, var_name);
-            value = getenv (ds_cstr (&dst) + start);
-            ds_truncate (&dst, start);
-
-            ds_put_cstr (&dst, value);
+            var = ss_xstrdup (var_name);
+            insert_variable (var, &dst, aux);
+            free (var);
           }
       }
 
@@ -108,6 +104,14 @@ fn_interp_vars (struct substring src, const char *(*getenv) (const char *),
   ds_destroy (&dst);
 }
 
+static void
+insert_env_var (const char *var, struct string *dst, void *aux UNUSED)
+{
+  const char *value = fn_getenv (var);
+  if (value != NULL)
+    ds_put_cstr (dst, value);
+}
+
 /* Searches for a configuration file with name NAME in the path
    given by PATH, which is environment-interpolated.
    Directories in PATH are delimited by ':'.  Returns the
@@ -126,7 +130,7 @@ fn_search_path (const char *base_name, const char *path_)
 
   /* Interpolate environment variables. */
   ds_init_cstr (&path, path_);
-  fn_interp_vars (ds_ss (&path), fn_getenv, &path);
+  fn_interp_vars (ds_ss (&path), insert_env_var, NULL, &path);
 
   verbose_msg (2, _("searching for \"%s\" in path \"%s\""),
                base_name, ds_cstr (&path));
@@ -138,6 +142,7 @@ fn_search_path (const char *base_name, const char *path_)
       if (!ds_is_empty (&file) && !ISSLASH (ds_last (&file)))
        ds_put_char (&file, '/');
       ds_put_cstr (&file, base_name);
+      ds_relocate (&file);
 
       /* Check whether file exists. */
       if (fn_exists (ds_cstr (&file)))
@@ -447,37 +452,56 @@ fn_compare_file_identities (const struct file_identity *a,
 unsigned int
 fn_hash_identity (const struct file_identity *identity)
 {
-  unsigned int hash = identity->device ^ identity->inode;
+  unsigned int hash = hash_int (identity->device, identity->inode);
   if (identity->name != NULL)
-    hash ^= hsh_hash_string (identity->name);
+    hash = hash_string (identity->name, hash);
   return hash;
 }
 
-
-
-#ifdef WINDOWS32
+#ifdef WIN32
 
 /* Apparently windoze users like to see output dumped into their home directory,
    not the current directory (!) */
 const char *
 default_output_path (void)
 {
-  static const char *home_dir = NULL;
+  static char *path = NULL;
 
-  /* Windows NT defines HOMEDRIVE and HOMEPATH.  But give preference
-     to HOME, because the user can change HOME.  */
-  if (home_dir == NULL)
+  if ( path == NULL)
     {
-      const char *home_drive = getenv ("HOMEDRIVE");
-      const char *home_path = getenv ("HOMEPATH");
+      /* Windows NT defines HOMEDRIVE and HOMEPATH.  But give preference
+        to HOME, because the user can change HOME.  */
+
+      const char *home_dir = getenv ("HOME");
+      int i;
 
-      if (home_drive != NULL && home_path != NULL)
-       home_dir = xasprintf ("%s%s%c",
-                             home_drive, home_path, DIRECTORY_SEPARATOR);
+      if (home_dir == NULL)
+       {
+         const char *home_drive = getenv ("HOMEDRIVE");
+         const char *home_path = getenv ("HOMEPATH");
+
+         if (home_drive != NULL && home_path != NULL)
+           home_dir = xasprintf ("%s%s",
+                                 home_drive, home_path);
+       }
+
+      if (home_dir == NULL)
+       home_dir = "c:/users/default"; /* poor default */
+
+      /* Copy home_dir into path.  Add a slash at the end but
+         only if there isn't already one there, because Windows
+         treats // specially. */
+      if (home_dir[0] == '\0'
+          || strchr ("/\\", home_dir[strlen (home_dir) - 1]) == NULL)
+        path = xasprintf ("%s%c", home_dir, '/');
       else
-       home_dir = "c:/users/default/"; /* poor default */
+        path = xstrdup (home_dir);
+
+      for(i = 0; i < strlen (path); i++)
+       if (path[i] == '\\') path[i] = '/';
     }
-  return home_dir;
+
+  return path;
 }
 
 #else