diff liboctave/file-ops.cc @ 3040:443851377f3f

[project @ 1997-06-06 09:29:28 by jwe]
author jwe
date Fri, 06 Jun 1997 09:34:14 +0000
parents cf676ff8b702
children 42975c59d2a0
line wrap: on
line diff
--- a/liboctave/file-ops.cc	Thu Jun 05 22:09:03 1997 +0000
+++ b/liboctave/file-ops.cc	Fri Jun 06 09:34:14 1997 +0000
@@ -29,6 +29,8 @@
 #include <cstdlib>
 #include <cstring>
 
+#include <iostream.h>
+
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
@@ -192,74 +194,250 @@
   return retval;
 }
 
-// If NAME has a leading ~ or ~user, Unix-style, expand it to the
-// user's home directory.  If no ~, or no <pwd.h>, just return NAME.
-
-// Mostly stolen from kpathsea.  Readline also has a more complicated
-// tilde-expand function, but we can probalby get by with something a
-// bit simpler.
+// The following tilde-expansion code was stolen and adapted from
+// readline.
 
 // XXX FIXME XXX
 #define DIR_SEP_CHAR '/'
 
+// The default value of tilde_additional_prefixes.  This is set to
+// whitespace preceding a tilde so that simple programs which do not
+// perform any word separation get desired behaviour.
+static const char *default_prefixes[] = { " ~", "\t~", ":~", 0 };
+
+// The default value of tilde_additional_suffixes.  This is set to
+// whitespace or newline so that simple programs which do not perform
+// any word separation get desired behaviour.
+static const char *default_suffixes[] = { " ", "\n", ":", 0 };
+
+// If non-null, this contains the address of a function that the
+// application wants called before trying the standard tilde
+// expansions.  The function is called with the text sans tilde, and
+// returns a malloc()'ed string which is the expansion, or a NULL
+// pointer if the expansion fails.
+file_ops::tilde_expansion_hook file_ops::tilde_expansion_preexpansion_hook = 0;
+
+// If non-null, this contains the address of a function to call if the
+// standard meaning for expanding a tilde fails.  The function is
+// called with the text (sans tilde, as in "foo"), and returns a
+// malloc()'ed string which is the expansion, or a NULL pointer if
+// there is no expansion.
+file_ops::tilde_expansion_hook file_ops::tilde_expansion_failure_hook = 0;
+
+// When non-null, this is a NULL terminated array of strings which are
+// duplicates for a tilde prefix.  Bash uses this to expand `=~' and
+// `:~'.
+string_vector file_ops::tilde_additional_prefixes = default_prefixes;
+
+// When non-null, this is a NULL terminated array of strings which
+// match the end of a username, instead of just "/".  Bash sets this
+// to `:' and `=~'.
+string_vector file_ops::tilde_additional_suffixes = default_suffixes;
+
+// Find the start of a tilde expansion in S, and return the index
+// of the tilde which starts the expansion.  Place the length of the
+// text which identified this tilde starter in LEN, excluding the
+// tilde itself.
+
+static size_t
+tilde_find_prefix (const string& s, size_t& len)
+{
+  len = 0;
+
+  size_t s_len = s.length ();
+
+  if (s_len == 0 || s[0] == '~')
+    return 0;
+
+  string_vector prefixes = file_ops::tilde_additional_prefixes;
+
+  if (! prefixes.empty ())
+    {
+      for (size_t i = 0; i < s_len; i++)
+	{
+	  for (int j = 0; j < prefixes.length (); j++)
+	    {
+	      size_t pfx_len = prefixes[j].length ();
+
+	      if (prefixes[j].compare (s.substr (i, pfx_len)) == 0)
+		{
+		  len = pfx_len - 1;
+		  return i + len;
+		}
+	    }
+	}
+    }
+
+  return s_len;
+}
+
+// Find the end of a tilde expansion in S, and return the index
+// of the character which ends the tilde definition.
+
+static size_t
+tilde_find_suffix (const string& s)
+{
+  size_t s_len = s.length ();
+
+  string_vector suffixes = file_ops::tilde_additional_suffixes;
+
+  size_t i = 0;
+
+  for ( ; i < s_len; i++)
+    {
+      if (s[i] == DIR_SEP_CHAR)
+	break;
+
+      if (! suffixes.empty ())
+	{
+	  for (int j = 0; j < suffixes.length (); j++)
+	    {
+	      size_t sfx_len = suffixes[j].length ();
+
+	      if (suffixes[j].compare (s.substr (i, sfx_len)) == 0)
+		return i;
+	    }
+	}
+    }
+
+  return i;
+}
+
+// Take FNAME and return the tilde prefix we want expanded.
+
+static string
+isolate_tilde_prefix (const string& fname)
+{
+  size_t f_len = fname.length ();
+
+  size_t len = 1;
+
+  while (len < f_len && fname[len] != DIR_SEP_CHAR)
+    len++;
+
+  return fname.substr (1, len);
+}
+
+// Do the work of tilde expansion on FILENAME.  FILENAME starts with a
+// tilde.
+
+static string
+tilde_expand_word (const string& filename)
+{
+  size_t f_len = filename.length ();
+
+  if (f_len == 0 || filename[0] != '~')
+    return filename;
+
+  // A leading `~/' or a bare `~' is *always* translated to the value
+  // of $HOME or the home directory of the current user, regardless of
+  // any preexpansion hook.
+
+  if (f_len == 1 || filename[1] == DIR_SEP_CHAR)
+    return octave_env::get_home_directory () + filename.substr (1);
+
+  string username = isolate_tilde_prefix (filename);
+
+  size_t user_len = username.length ();
+
+  string dirname;
+
+  if (file_ops::tilde_expansion_preexpansion_hook)
+    {
+      string expansion
+	= file_ops::tilde_expansion_preexpansion_hook (username);
+
+      if (! expansion.empty ())
+	{
+	  dirname = expansion + filename.substr (user_len);
+	  return dirname;
+	}
+    }
+
+  // No preexpansion hook, or the preexpansion hook failed.  Look in the
+  // password database.
+
+  octave_passwd pw = octave_passwd::getpwnam (username);
+
+  if (! pw)
+    {
+      // If the calling program has a special syntax for expanding tildes,
+      // and we couldn't find a standard expansion, then let them try.
+
+      if (file_ops::tilde_expansion_failure_hook)
+	{
+	  string expansion
+	    = file_ops::tilde_expansion_failure_hook (username);
+
+	  if (! expansion.empty ())
+	    dirname = expansion + filename.substr (user_len);
+	}
+
+      // If we don't have a failure hook, or if the failure hook did not
+      // expand the tilde, return a copy of what we were passed.
+
+      if (dirname.length () == 0)
+	dirname = filename;
+    }
+  else
+    dirname = pw.dir () + filename.substr (user_len);
+
+  return dirname;
+}
+
+// If NAME has a leading ~ or ~user, Unix-style, expand it to the
+// user's home directory.  If no ~, or no <pwd.h>, just return NAME.
+
 string
 file_ops::tilde_expand (const string& name)
 {
-  string expansion = name;
+  string result;
 
-  // If no leading tilde, do nothing.
+  size_t name_len = name.length ();
 
-  size_t beg = name.find_first_not_of (" \t");
+  // Scan through S expanding tildes as we come to them.
 
-  if (beg != NPOS && name[beg] == '~')
-    {
-      // If `~' or `~/', use the user's home directory.  If that is
-      // empty, just use ".".
+  size_t pos = 0;
 
-      // If `~user' or `~user/', look up user in the passwd database.
-
-      size_t len = name.length ();
+  while (1)
+    {
+      if (pos > name_len)
+	break;
 
-      if (beg == len-1 || name[beg+1] == DIR_SEP_CHAR)
-	{
-	  string home = octave_env::get_home_directory ();
+      size_t len;
+
+      // Make START point to the tilde which starts the expansion.
 
-	  if (home.empty ())
-	    home = ".";
-        
-	  expansion = name.substr (0, beg) + home;
+      size_t start = tilde_find_prefix (name.substr (pos), len);
+
+      result.append (name.substr (pos, start));
 
-	  if (beg < len)
-	    expansion.append (name.substr (beg+1));
-	}
-      else
-	{
-	  size_t end = name.find (DIR_SEP_CHAR, beg);
+      // Advance STRING to the starting tilde.
+
+      pos += start;
 
-	  size_t len = end;
+      // Make FINI be the index of one after the last character of the
+      // username.
 
-	  if (end != NPOS)
-	    len -= beg + 1;
+      size_t fini = tilde_find_suffix (name.substr (pos));
 
-	  string user = name.substr (beg+1, len);
+      // If both START and FINI are zero, we are all done.
 
-	  octave_passwd pw = octave_passwd::getpwnam (user);
+      if (! (start || fini))
+	break;
 
-	  // If no such user, just return the original string.
+      // Expand the entire tilde word, and copy it into RESULT.
 
-	  if (pw)
-	    {
-	      expansion = string (" ", beg) + pw.dir ();
+      string tilde_word = name.substr (pos, fini);
+
+      pos += fini;
 
-	      if (end != NPOS)
-		expansion.append (name.substr (end));
-	    }
-	  else
-	    expansion = name;
-	}
+      string expansion = tilde_expand_word (tilde_word);
+
+      result.append (expansion);
     }
 
-  return expansion;
+  return result;
 }
 
 // A vector version of the above.