# HG changeset patch # User jwe # Date 865589654 0 # Node ID 443851377f3faf7cabaed25a111bf0ef4b4debd6 # Parent cf74b80972127b06df12312dd5e552dd61e377d3 [project @ 1997-06-06 09:29:28 by jwe] diff --git a/liboctave/ChangeLog b/liboctave/ChangeLog --- a/liboctave/ChangeLog +++ b/liboctave/ChangeLog @@ -1,3 +1,11 @@ +Fri Jun 6 04:27:40 1997 John W. Eaton + + * file-ops.cc (file_ops::tilde_expand): Steal more code from bash + to do better job expanding tildes. + + * str-vec.cc (string_vector::string_vector (const char * const *): + Use temporary variable to compute length. + Thu Jun 5 01:44:43 1997 John W. Eaton * Makefile.in: Make building of static library optional. diff --git a/liboctave/file-ops.cc b/liboctave/file-ops.cc --- a/liboctave/file-ops.cc +++ b/liboctave/file-ops.cc @@ -29,6 +29,8 @@ #include #include +#include + #ifdef HAVE_SYS_TYPES_H #include #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 , 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 , 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. diff --git a/liboctave/file-ops.h b/liboctave/file-ops.h --- a/liboctave/file-ops.h +++ b/liboctave/file-ops.h @@ -25,12 +25,12 @@ #include -class string_vector; - #ifdef HAVE_SYS_TYPES_H #include #endif +#include "str-vec.h" + struct file_ops { @@ -49,6 +49,16 @@ static string tempnam (const string&, const string&); static string tempnam (const string&, const string&, string&); + typedef string (*tilde_expansion_hook) (const string&); + + static tilde_expansion_hook tilde_expansion_preexpansion_hook; + + static tilde_expansion_hook tilde_expansion_failure_hook; + + static string_vector tilde_additional_prefixes; + + static string_vector tilde_additional_suffixes; + static string tilde_expand (const string&); static string_vector tilde_expand (const string_vector&); diff --git a/liboctave/str-vec.cc b/liboctave/str-vec.cc --- a/liboctave/str-vec.cc +++ b/liboctave/str-vec.cc @@ -47,7 +47,9 @@ { int n = 0; - while (*s++) + const char * const *t = s; + + while (*t++) n++; resize (n); diff --git a/src/ChangeLog b/src/ChangeLog --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,13 @@ +Fri Jun 6 04:30:57 1997 John W. Eaton + + * utils.cc (search_path_for_file): New arg, do_tilde_expansion. + If TRUE, perform tilde expansion on path before searching. + (file_in_path): Call search_path_for_file with do_tilde_expansion + set to false, since we've already performed tilde expansion on the + load path. + + * defaults.cc (loadpath): Perform tilde expansion here. + Thu Jun 5 01:42:39 1997 John W. Eaton * Makefile.in: Make building of static library optional. diff --git a/src/defaults.cc b/src/defaults.cc --- a/src/defaults.cc +++ b/src/defaults.cc @@ -42,6 +42,7 @@ #include #include "defun.h" #include "error.h" +#include "file-ops.h" #include "gripes.h" #include "help.h" #include "ov.h" @@ -384,7 +385,7 @@ status = -1; } else - Vload_path = maybe_add_default_load_path (s); + Vload_path = file_ops::tilde_expand (maybe_add_default_load_path (s)); return status; } diff --git a/src/utils.cc b/src/utils.cc --- a/src/utils.cc +++ b/src/utils.cc @@ -57,14 +57,16 @@ #include "SLStack.h" +#include "dir-ops.h" +#include "file-ops.h" #include "file-stat.h" #include "oct-cmplx.h" #include "oct-env.h" +#include "pathsearch.h" #include "str-vec.h" #include #include "defun.h" -#include "dir-ops.h" #include "dirfns.h" #include "error.h" #include "gripes.h" @@ -72,7 +74,6 @@ #include "oct-hist.h" #include "oct-obj.h" #include "pager.h" -#include "pathsearch.h" #include "sysdep.h" #include "toplev.h" #include "unwind-prot.h" @@ -227,9 +228,12 @@ // See if the given file is in the path. string -search_path_for_file (const string& path, const string& name) +search_path_for_file (const string& path, const string& name, + bool do_tilde_expansion) { - dir_path p (path); + string tmp_path = do_tilde_expansion ? file_ops::tilde_expand (path) : path; + + dir_path p (tmp_path); return octave_env::make_absolute (p.find (name), octave_env::getcwd ()); } @@ -269,7 +273,7 @@ if (! suffix.empty ()) nm.append (suffix); - return search_path_for_file (Vload_path, nm); + return search_path_for_file (Vload_path, nm, false); } // See if there is an function file in the path. If so, return the diff --git a/src/utils.h b/src/utils.h --- a/src/utils.h +++ b/src/utils.h @@ -33,7 +33,7 @@ class octave_value_list; class string_vector; -extern string search_path_for_file (const string&, const string&); +extern string search_path_for_file (const string&, const string&, bool = true); extern string file_in_path (const string&, const string&); extern string fcn_file_in_path (const string&); extern string oct_file_in_path (const string&);