# HG changeset patch # User jwe # Date 1148679742 0 # Node ID 5e41e06f6a78b98866b8baa661566312c0df940b # Parent b0d4ff99a0c5450a5d8692abff5894954e2ae65f [project @ 2006-05-26 21:41:32 by jwe] diff --git a/src/ChangeLog b/src/ChangeLog --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,38 @@ +2006-05-26 John W. Eaton + + * load-path.cc (genpath, maybe_add_path_elts, Fgenpath, Frehash, + Fpath, Fpathdef): Move here from defaults.cc. + * load-path.cc, load-path.h: New files. + * Makefile.in (INCLUDES, DIST_SRC): Add them to the lists. + * help.cc (make_name_list, simple_help): Use load_path instead of + octave_fcn_file_name_cache. + (Flookfor): Use load_path instead of Vload_path_dir_path. + * octave.cc (octave_main): Likewise. + * parse.y (load_fcn_from_file): Likewise. + * utils.cc (Ffile_in_loadpath, file_in_path, fcn_file_in_path, + oct_file_in_path): Likewise. + * variables.cc (symbol_exist, symbol_out_of_date): Likewise. + * DLD-FUNCTIONS/fftw_wisdom.cc (Ffftw_wisdom): Likewise. + * utils.cc (Ffind_first_of_in_loadpath): Delete. + * input.cc (octave_gets): Call load_path::update here. + * dirfns.cc (octave_change_to_directory): Likewise. + * defaults.cc (VLOADPATH, VDEFAULT_LOADPATH, Vload_path_dir_path, + maybe_add_or_del_packages, update_load_path_dir_path, + execute_default_pkg_add_files, set_load_path): Delete. + * defaults.h.in (Vload_path_dir_path, + execute_default_pkg_add_files, maybe_add_default_load_path, + set_load_path): Delete decls. + + * fn-cache.h, fn-cache.cc: Delete. + * Makefile.in (INCLUDES, DIST_SRC): Remove them from the lists. + + * input.cc (octave_gets): Only update Vlast_prompt_time if we are + interactive and actually printing a prompt. Don't print prompt if + reading startup files or command line files. Initialize + Vlast_prompt_time to 0. + + * pr-output.cc (set_format): Always include space for sign. + 2006-05-23 John W. Eaton * load-save.cc (Fsave): Use tellp instead of pubseekoff to @@ -179,7 +214,7 @@ (symbols_of_defaults): Delete DEFVARs and function. * pr-output.cc (set_output_prec_and_fw): Set Voutput_precision and - Voutput_max_field_width directly instad of calling + Voutput_max_field_width directly instead of calling bind_builtin_variable. * octave.cc (octave_main, maximum_braindamage): diff --git a/src/DLD-FUNCTIONS/__gnuplot_raw__.l b/src/DLD-FUNCTIONS/__gnuplot_raw__.l --- a/src/DLD-FUNCTIONS/__gnuplot_raw__.l +++ b/src/DLD-FUNCTIONS/__gnuplot_raw__.l @@ -1647,7 +1647,7 @@ DEFUN_DLD (__gnuplot_set__, args, , "-*- texinfo -*-\n\ -@deffn {{Loadable Function} __gnuplot_set__ options\n\ +@deffn {Loadable Function} __gnuplot_set__ options\n\ Set plotting options for gnuplot\n\ @end deffn") { @@ -1661,7 +1661,7 @@ DEFUN_DLD (__gnuplot_show__, args, , "-*- texinfo -*-\n\ -@deffn {{Loadable Function} __gnuplot_show__ options\n\ +@deffn {Loadable Function} __gnuplot_show__ options\n\ Show plotting options.\n\ @end deffn") { diff --git a/src/DLD-FUNCTIONS/dispatch.cc b/src/DLD-FUNCTIONS/dispatch.cc --- a/src/DLD-FUNCTIONS/dispatch.cc +++ b/src/DLD-FUNCTIONS/dispatch.cc @@ -537,22 +537,24 @@ %! dispatch('dispatch_x','length','string') %! assert(dispatch_x(3),3) %! assert(dispatch_x("a"),1) -%! pause(1); +%! sleep (2); %! system("echo 'function a=dispatch_x(a),++a;'>dispatch_x.m"); +%! rehash(); %! assert(dispatch_x(3),4) %! assert(dispatch_x("a"),1) %!test -%! system("rm dispatch_x.m"); +%! unlink("dispatch_x.m"); %!test # replace dispatch m-file %! system("echo 'function a=dispatch_y(a)'>dispatch_y.m"); %! dispatch('hello','dispatch_y','complex scalar') %! assert(hello(3i),3i) -%! pause(1); +%! sleep (2); %! system("echo 'function a=dispatch_y(a),++a;'>dispatch_y.m"); +%! rehash(); %! assert(hello(3i),1+3i) %!test -%! system("rm dispatch_y.m"); +%! unlink("dispatch_y.m"); FIXME add tests for preservation of mark_as_command status. diff --git a/src/DLD-FUNCTIONS/fftw_wisdom.cc b/src/DLD-FUNCTIONS/fftw_wisdom.cc --- a/src/DLD-FUNCTIONS/fftw_wisdom.cc +++ b/src/DLD-FUNCTIONS/fftw_wisdom.cc @@ -37,6 +37,7 @@ #include "file-ops.h" #include "gripes.h" #include "lo-mappers.h" +#include "load-path.h" #include "oct-env.h" #include "oct-obj.h" #include "sighandlers.h" @@ -111,7 +112,7 @@ std::string str = args(0).string_value (); std::string wisdom = octave_env::make_absolute - (Vload_path_dir_path.find_first_of (str), octave_env::getcwd ()); + (load_path::find_file (str), octave_env::getcwd ()); // FIXME -- should probably protect FILE* resources with // auto_ptr or similar... diff --git a/src/Makefile.in b/src/Makefile.in --- a/src/Makefile.in +++ b/src/Makefile.in @@ -95,8 +95,8 @@ INCLUDES := Cell.h base-list.h c-file-ptr-stream.h comment-list.h \ defun-dld.h defun-int.h defun.h dirfns.h dynamic-ld.h \ - error.h file-io.h fn-cache.h gripes.h help.h input.h \ - lex.h load-save.h ls-hdf5.h ls-mat-ascii.h ls-mat4.h \ + error.h file-io.h gripes.h help.h input.h \ + lex.h load-path.h load-save.h ls-hdf5.h ls-mat-ascii.h ls-mat4.h \ ls-mat5.h ls-oct-ascii.h ls-oct-binary.h ls-utils.h \ oct-errno.h oct-fstrm.h oct-hist.h oct-iostrm.h oct-map.h oct-obj.h \ oct-prcstrm.h oct-procbuf.h oct-stdstrm.h oct-stream.h zfstream.h \ @@ -163,8 +163,8 @@ DIST_SRC := Cell.cc bitfcns.cc c-file-ptr-stream.cc comment-list.cc \ cutils.c data.cc debug.cc defaults.cc defun.cc dirfns.cc \ - dynamic-ld.cc error.cc file-io.cc fn-cache.cc gripes.cc \ - help.cc input.cc lex.l load-save.cc ls-hdf5.cc \ + dynamic-ld.cc error.cc file-io.cc gripes.cc \ + help.cc input.cc lex.l load-path.cc load-save.cc ls-hdf5.cc \ ls-mat-ascii.cc ls-mat4.cc ls-mat5.cc ls-oct-ascii.cc \ ls-oct-binary.cc ls-utils.cc main.c mappers.cc matherr.c \ oct-fstrm.cc oct-hist.cc oct-iostrm.cc oct-map.cc \ diff --git a/src/defaults.cc b/src/defaults.cc --- a/src/defaults.cc +++ b/src/defaults.cc @@ -50,6 +50,7 @@ #include "gripes.h" #include "help.h" #include "input.h" +#include "load-path.h" #include "oct-obj.h" #include "ov.h" #include "parse.h" @@ -85,16 +86,6 @@ // (--exec-path path) static std::string VEXEC_PATH; -// Load path specified on command line. -// (--path path; -p path) -static std::string VLOADPATH; - -// The default load path with OCTAVE_HOME appropriately substituted. -static std::string VDEFAULT_LOADPATH; - -// And the cached directory path corresponding to Vload_path. -dir_path Vload_path_dir_path; - // Name of the editor to be invoked by the edit_history command. std::string VEDITOR; @@ -106,83 +97,6 @@ // Name of the FFTW wisdom program. std::string Vfftw_wisdom_program; -// Each element of A and B should be directory names. For each -// element of A not in the list B, execute SCRIPT_FILE in that -// directory if it exists. - -static void -maybe_add_or_del_packages (const string_vector& a, - const string_vector& b, - const std::string& script_file) -{ - if (! octave_interpreter_ready) - return; - - unwind_protect::begin_frame ("maybe_add_or_del_packages"); - - unwind_protect_bool (input_from_startup_file); - - input_from_startup_file = true; - - octave_idx_type a_len = a.length (); - octave_idx_type b_len = b.length (); - - for (octave_idx_type i = 0; i < a_len; i++) - { - std::string a_dir = a[i]; - - bool found = false; - - for (octave_idx_type j = 0; j < b_len; j++) - { - if (b[j] == a_dir) - { - found = true; - break; - } - } - - if (! found) - { - std::string file = a_dir + file_ops::dir_sep_str + script_file; - - file_stat fs = file_stat (file); - - if (fs.exists ()) - source_file (file); - - if (error_state) - return; - } - } - - unwind_protect::run_frame ("maybe_add_or_del_packages"); -} - -static void -update_load_path_dir_path (void) -{ - string_vector old_dirs = Vload_path_dir_path.all_directories (); - - Vload_path_dir_path = dir_path (VLOADPATH, ""); - - string_vector new_dirs = Vload_path_dir_path.all_directories (); - - maybe_add_or_del_packages (old_dirs, new_dirs, "PKG_DEL"); - - if (! error_state) - maybe_add_or_del_packages (new_dirs, old_dirs, "PKG_ADD"); -} - -void -execute_default_pkg_add_files (void) -{ - string_vector old_dirs; - string_vector new_dirs = Vload_path_dir_path.all_directories (); - - maybe_add_or_del_packages (new_dirs, old_dirs, "PKG_ADD"); -} - static std::string subst_octave_home (const std::string& s) { @@ -337,81 +251,6 @@ octave_env::putenv ("PATH", VEXEC_PATH); } -static std::string -genpath (const std::string& dirname) -{ - std::string retval; - - std::string full_dirname = file_ops::tilde_expand (dirname); - - dir_entry dir (full_dirname); - - if (dir) - { - retval = dirname; - - string_vector dirlist = dir.read (); - - octave_idx_type len = dirlist.length (); - - for (octave_idx_type i = 0; i < len; i++) - { - std::string elt = dirlist[i]; - - if (elt != "." && elt != ".." && elt != "private") - { - std::string nm = full_dirname + file_ops::dir_sep_str + elt; - - file_stat fs (nm); - - if (fs && fs.is_dir ()) - retval += dir_path::path_sep_str + genpath (nm); - } - } - } - - return retval; -} - -static void -maybe_add_path_elts (std::string& pathvar, const std::string& dir) -{ - std::string tpath = genpath (dir); - - if (! tpath.empty ()) - pathvar += dir_path::path_sep_str + tpath; -} - -void -set_load_path (const std::string& path) -{ - VDEFAULT_LOADPATH = ":"; - - maybe_add_path_elts (VDEFAULT_LOADPATH, Vlocal_ver_oct_file_dir); - maybe_add_path_elts (VDEFAULT_LOADPATH, Vlocal_api_oct_file_dir); - maybe_add_path_elts (VDEFAULT_LOADPATH, Vlocal_oct_file_dir); - maybe_add_path_elts (VDEFAULT_LOADPATH, Vlocal_ver_fcn_file_dir); - maybe_add_path_elts (VDEFAULT_LOADPATH, Vlocal_api_fcn_file_dir); - maybe_add_path_elts (VDEFAULT_LOADPATH, Vlocal_fcn_file_dir); - maybe_add_path_elts (VDEFAULT_LOADPATH, Voct_file_dir); - maybe_add_path_elts (VDEFAULT_LOADPATH, Vfcn_file_dir); - - std::string tpath = path; - - if (tpath.empty ()) - tpath = octave_env::getenv ("OCTAVE_LOADPATH"); - - VLOADPATH = "."; - - if (! tpath.empty ()) - VLOADPATH += dir_path::path_sep_str + tpath; - - if (VDEFAULT_LOADPATH != ":") - VLOADPATH += VDEFAULT_LOADPATH; - - update_load_path_dir_path (); -} - void set_image_path (const std::string& path) { @@ -425,7 +264,10 @@ if (! tpath.empty ()) VIMAGE_PATH += dir_path::path_sep_str + tpath; - maybe_add_path_elts (VIMAGE_PATH, Vimage_dir); + tpath = genpath (Vimage_dir, ""); + + if (! tpath.empty ()) + VIMAGE_PATH += dir_path::path_sep_str + tpath; } static void @@ -535,8 +377,6 @@ set_exec_path (); - set_load_path (); - set_image_path (); set_default_info_file (); @@ -552,42 +392,6 @@ set_site_defaults_file (); } -DEFUN (genpath, args, , - "-*- texinfo -*-\n\ -@deftypefn {Built-in Function} {} genpath (@var{dir})\n\ -Return a path constructed from @var{dir} and all its subdiretories.\n\ -@end deftypefn") -{ - octave_value retval; - - if (args.length () == 1) - { - std::string dirname = args(0).string_value (); - - if (! error_state) - retval = genpath (dirname); - else - error ("genpath: expecting argument to be a character string"); - } - else - print_usage (); - - return retval; -} - -DEFUN (rehash, , , - "-*- texinfo -*-\n\ -@deftypefn {Built-in Function} {} rehash ()\n\ -Reinitialize Octave's @code{LOADPATH} directory cache.\n\ -@end deftypefn") -{ - octave_value_list retval; - - Vload_path_dir_path.rehash (); - - return retval; -} - DEFUN (EDITOR, args, nargout, "-*- texinfo -*-\n\ @deftypefn {Built-in Function} {@var{val} =} EDITOR ()\n\ @@ -654,91 +458,6 @@ return SET_NONEMPTY_INTERNAL_STRING_VARIABLE (IMAGE_PATH); } -DEFUN (path, args, nargout, - "-*- texinfo -*-\n\ -@deftypefn {Function File} {} path (@dots{})\n\ -Modify or display Octave's @code{LOADPATH}.\n\ -\n\ -If @var{nargin} and @var{nargout} are zero, display the elements of\n\ -Octave's @code{LOADPATH} in an easy to read format.\n\ -\n\ -If @var{nargin} is zero and nargout is greater than zero, return the\n\ -current value of @code{LOADPATH}.\n\ -\n\ -If @var{nargin} is greater than zero, concatenate the arguments,\n\ -separating them with @code{pathsep()}. Set the internal search path\n\ -to the result and return it.\n\ -\n\ -No checks are made for duplicate elements.\n\ -@seealso{addpath, rmpath, genpath, pathdef, savepath, pathsep}\n\ -@end deftypefn") -{ - octave_value retval; - - int argc = args.length () + 1; - - string_vector argv = args.make_argv ("path"); - - if (! error_state) - { - if (argc > 1) - { - std::string path = argv[1]; - - for (int i = 2; i < argc; i++) - path += dir_path::path_sep_str; - - size_t plen = path.length (); - - if (! ((plen == 1 && path[0] == ':') - || (plen > 1 - && path.substr (0, 2) == ("." + dir_path::path_sep_str)))) - path = "." + dir_path::path_sep_str + path; - - VLOADPATH = path; - - // By resetting the last prompt time variable, we will force - // checks for out of date symbols even if the change to - // LOADPATH and subsequent function calls happen between - // prompts. - - // FIXME -- maybe we should rename - // Vlast_prompt_time_stamp since the new usage doesn't really - // fit with the current name? - - Vlast_prompt_time.stamp (); - - update_load_path_dir_path (); - } - - if (nargout > 0) - retval = VLOADPATH; - else if (argc == 1 && nargout == 0) - { - octave_stdout << "\nOctave's search path contains the following directories:\n\n"; - - string_vector sv = Vload_path_dir_path.all_directories (); - - sv.list_in_columns (octave_stdout); - - octave_stdout << "\n"; - } - } - - return retval; -} - -DEFUN (pathdef, , , - "-*- texinfo -*-\n\ -@deftypefn {Built-in Function} {@var{val} =} DEFAULT_LOADPATH ()\n\ -Return the default list of directories in which to search for function\n\ -files.\n\ -@seealso{LOADPATH}\n\ -@end deftypefn") -{ - return octave_value (VDEFAULT_LOADPATH); -} - DEFUN (OCTAVE_HOME, args, , "-*- texinfo -*-\n\ @deftypefn {Built-in Function} {} OCTAVE_HOME ()\n\ diff --git a/src/defaults.h.in b/src/defaults.h.in --- a/src/defaults.h.in +++ b/src/defaults.h.in @@ -180,9 +180,6 @@ extern std::string Vimage_dir; -// And the cached directory path corresponding to Vload_path. -extern dir_path Vload_path_dir_path; - // Name of the editor to be invoked by the edit_history command. extern std::string VEDITOR; @@ -192,14 +189,9 @@ // Name of the FFTW wisdom program. extern std::string Vfftw_wisdom_program; -extern void execute_default_pkg_add_files (void); - -extern std::string maybe_add_default_load_path (const std::string& pathstring); - extern void install_defaults (void); extern void set_exec_path (const std::string& path = std::string ()); -extern void set_load_path (const std::string& path = std::string ()); extern void set_image_path (const std::string& path = std::string ()); #endif diff --git a/src/dirfns.cc b/src/dirfns.cc --- a/src/dirfns.cc +++ b/src/dirfns.cc @@ -55,6 +55,7 @@ #include "error.h" #include "gripes.h" #include "input.h" +#include "load-path.h" #include "oct-obj.h" #include "pager.h" #include "procstream.h" @@ -68,19 +69,18 @@ // directory tree. static bool Vconfirm_recursive_rmdir = true; -// FIXME -- changing the plotter directory should be handled -// by registering a function for octave_env::chdir to call so that -// this function can be eliminated. - static int octave_change_to_directory (const std::string& newdir) { int cd_ok = octave_env::chdir (newdir); if (cd_ok) - // FIXME -- this should be handled as a list of functions - // to call so users can add their own chdir handlers. - /* do_external_plotter_cd (newdir) */; + { + // FIXME -- should this be handled as a list of functions + // to call so users can add their own chdir handlers? + + load_path::update (); + } else { using namespace std; @@ -681,8 +681,8 @@ } DEFUN (filesep, args, , - "-*- texinfo -*-\n\ -@detypefn {Built-in Function} {} filesep ()\n\ + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {} filesep ()\n\ Return the system-dependent character used to separate directory names.\n\ @seealso{pathsep, dir, ls}\n\ @end deftypefn") diff --git a/src/fn-cache.cc b/src/fn-cache.cc --- a/src/fn-cache.cc +++ b/src/fn-cache.cc @@ -49,7 +49,7 @@ { bool something_changed = false; - dir_path p = path.empty () ? Vload_path_dir_path : dir_path (path); + dir_path p /* = path.empty () ? Vload_path_dir_path : dir_path (path) */; string_vector dirs = p.all_directories (); @@ -112,7 +112,7 @@ int total_len = 0; - dir_path p = path.empty () ? Vload_path_dir_path : dir_path (path); + dir_path p /* = path.empty () ? Vload_path_dir_path : dir_path (path) */; string_vector dirs = p.all_directories (); diff --git a/src/help.cc b/src/help.cc --- a/src/help.cc +++ b/src/help.cc @@ -54,6 +54,7 @@ #include "gripes.h" #include "help.h" #include "input.h" +#include "load-path.h" #include "oct-obj.h" #include "ov-usr-fcn.h" #include "pager.h" @@ -437,9 +438,9 @@ yesno = \"yes\"\n\ \n\ switch yesno\n\ - case {\"Yes\" \"yes\" \"YES\" \"y\" \"Y\"}\n\ + case @{\"Yes\" \"yes\" \"YES\" \"y\" \"Y\"@}\n\ value = 1;\n\ - case {\"No\" \"no\" \"NO\" \"n\" \"N\"}\n\ + case @{\"No\" \"no\" \"NO\" \"n\" \"N\"@}\n\ value = 0;\n\ otherwise\n\ error (\"invalid value\");\n\ @@ -564,7 +565,7 @@ lcl = curr_sym_tab->name_list (); int lcl_len = lcl.length (); - string_vector ffl = octave_fcn_file_name_cache::list_no_suffix (); + string_vector ffl = load_path::fcn_names (); int ffl_len = ffl.length (); string_vector afl = autoloaded_functions (); @@ -684,26 +685,7 @@ // Also need to search octave_path for script files. - string_vector dirs = Vload_path_dir_path.all_directories (); - - int len = dirs.length (); - - for (int i = 0; i < len; i++) - { - string_vector names = octave_fcn_file_name_cache::list (dirs[i]); - - if (! names.empty ()) - { - std::string dir - = octave_env::make_absolute (dirs[i], octave_env::getcwd ()); - - octave_stdout << "\n*** function files in " << dir << ":\n\n"; - - names.qsort (); - - names.list_in_columns (octave_stdout); - } - } + load_path::display (octave_stdout); string_vector autoloaded = autoloaded_functions (); @@ -1881,13 +1863,13 @@ } } - string_vector dirs = Vload_path_dir_path.all_directories (); + string_vector dirs = load_path::dirs (); int len = dirs.length (); for (int i = 0; i < len; i++) { - names = octave_fcn_file_name_cache::list (dirs[i]); + names = load_path::files (dirs[i]); if (! names.empty ()) { @@ -1912,19 +1894,18 @@ if (!sr) { // Check if this version is first in the path - string_vector tmp (2); - tmp(0) = name + ".oct"; - tmp(1) = name + ".m"; - std::string file_name = - Vload_path_dir_path.find_first_of (tmp); - if (file_name == dirs[i] + tmp(0) - || file_name == dirs[i] + tmp(1)) + std::string file_name = load_path::find_fcn (name); + + std::string dir = dirs[i] + file_ops::dir_sep_str; + + if (file_name == dir + name + ".oct" + || file_name == dir + name + ".m") { bool symbol_found; std::string h; - if (file_name == dirs[i] + tmp(0)) + if (file_name == dir + name + ".oct") { // oct-file. Must load to get help sr = lookup_by_name (name, false); @@ -1970,7 +1951,8 @@ } // Check if this function has autoloaded functions attached to it - std::string file_name = Vload_path_dir_path.find_first_of (names(j)); + std::string file_name = load_path::find_fcn (name); + string_vector autoload_fcns = reverse_lookup_autoload (file_name); if (! autoload_fcns.empty ()) diff --git a/src/input.cc b/src/input.cc --- a/src/input.cc +++ b/src/input.cc @@ -54,6 +54,7 @@ #include "gripes.h" #include "help.h" #include "input.h" +#include "load-path.h" #include "oct-map.h" #include "oct-hist.h" #include "toplev.h" @@ -90,7 +91,7 @@ int Vecho_executing_commands = ECHO_OFF; // The time we last printed a prompt. -octave_time Vlast_prompt_time; +octave_time Vlast_prompt_time = 0.0; // Character to append after successful command-line completion attempts. static char Vcompletion_append_char = ' '; @@ -212,10 +213,11 @@ std::string retval; - Vlast_prompt_time.stamp (); - if ((interactive || forced_interactive) - && (! (reading_fcn_file || reading_script_file))) + && (! (reading_fcn_file + || reading_script_file + || input_from_startup_file + || input_from_command_line_file))) { std::string ps = (promptflag > 0) ? VPS1 : VPS2; @@ -227,7 +229,14 @@ octave_diary << prompt; + Vlast_prompt_time.stamp (); + retval = gnu_readline (prompt); + + // There is no need to update the load_path cache if there is no + // user input. + if (! retval.empty ()) + load_path::update (); } else retval = gnu_readline (""); diff --git a/src/load-path.cc b/src/load-path.cc new file mode 100644 --- /dev/null +++ b/src/load-path.cc @@ -0,0 +1,1425 @@ +/* + +Copyright (C) 2006 John W. Eaton + +This file is part of Octave. + +Octave is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +Octave is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with Octave; see the file COPYING. If not, write to the Free +Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "dir-ops.h" +#include "file-ops.h" +#include "file-stat.h" +#include "oct-env.h" +#include "pathsearch.h" + +#include "defaults.h" +#include "defun.h" +#include "input.h" +#include "load-path.h" +#include "pager.h" +#include "parse.h" +#include "toplev.h" +#include "unwind-prot.h" +#include "utils.h" + +load_path *load_path::instance = 0; +load_path::hook_function_ptr load_path::add_hook = execute_pkg_add; +load_path::hook_function_ptr load_path::remove_hook = execute_pkg_del; +std::string load_path::command_line_path; + +static std::string Vsystem_path; + +void +load_path::dir_info::update (void) +{ + if (is_relative) + initialize (); + else + { + file_stat fs (dir_name); + + if (fs) + { + if (fs.mtime () != dir_mtime) + initialize (); + } + else + { + std::string msg = fs.error (); + warning ("load_path: %s: %s", dir_name.c_str (), msg.c_str ()); + } + } +} + +void +load_path::dir_info::initialize (void) +{ + is_relative = ! octave_env::absolute_pathname (dir_name); + + file_stat fs (dir_name); + + if (fs) + { + dir_mtime = fs.mtime (); + + bool has_private_subdir = get_file_list (dir_name); + + if (! error_state) + { + if (has_private_subdir) + { + std::string pdn = dir_name + file_ops::dir_sep_str + "private"; + + get_private_function_map (pdn); + } + } + } + else + { + std::string msg = fs.error (); + warning ("load_path: %s: %s", dir_name.c_str (), msg.c_str ()); + } +} + +bool +load_path::dir_info::get_file_list (const std::string& d) +{ + bool has_private_subdir = false; + + dir_entry dir (d); + + if (dir) + { + string_vector flist = dir.read (); + + octave_idx_type len = flist.length (); + + all_files.resize (len); + fcn_files.resize (len); + + octave_idx_type all_files_count = 0; + octave_idx_type fcn_files_count = 0; + + for (octave_idx_type i = 0; i < len; i++) + { + std::string fname = flist[i]; + + std::string full_name = d + file_ops::dir_sep_str + fname; + + file_stat fs (full_name); + + if (fs) + { + if (fs.is_dir ()) + { + if (! has_private_subdir && fname == "private") + has_private_subdir = true; + } + else + { + all_files[all_files_count++] = fname; + + size_t pos = fname.rfind ('.'); + + if (pos != NPOS) + { + std::string ext = fname.substr (pos); + + if (ext == ".m" || ext == ".oct") + { + std::string base = fname.substr (0, pos); + + if (valid_identifier (base)) + fcn_files[fcn_files_count++] = fname; + } + } + } + } + } + + all_files.resize (all_files_count); + fcn_files.resize (fcn_files_count); + } + else + { + std::string msg = dir.error (); + warning ("load_path: %s: %s", d.c_str (), msg.c_str ()); + } + + return has_private_subdir; +} + +void +load_path::dir_info::get_private_function_map (const std::string& d) +{ + dir_entry dir (d); + + if (dir) + { + string_vector flist = dir.read (); + + octave_idx_type len = flist.length (); + + for (octave_idx_type i = 0; i < len; i++) + { + std::string fname = flist[i]; + + std::string ext; + std::string base = fname; + + size_t pos = fname.rfind ('.'); + + if (pos != NPOS) + { + base = fname.substr (0, pos); + ext = fname.substr (pos); + + if (valid_identifier (base)) + { + int t = 0; + + if (ext == ".m") + t = load_path::M_FILE; + else if (ext == ".oct") + t = load_path::OCT_FILE; + + private_function_map[base] |= t; + } + } + } + } + else + { + std::string msg = dir.error (); + warning ("load_path: %s: %s", d.c_str (), msg.c_str ()); + } +} + +bool +load_path::instance_ok (void) +{ + bool retval = true; + + if (! instance) + instance = new load_path (); + + if (! instance) + { + ::error ("unable to create load path object!"); + + retval = false; + } + + return retval; +} + +load_path::const_dir_info_list_iterator +load_path::find_dir_info (const std::string& dir) const +{ + const_dir_info_list_iterator retval = dir_info_list.begin (); + + while (retval != dir_info_list.end ()) + { + if (retval->dir_name == dir) + break; + + retval++; + } + + return retval; +} + +load_path::dir_info_list_iterator +load_path::find_dir_info (const std::string& dir) +{ + dir_info_list_iterator retval = dir_info_list.begin (); + + while (retval != dir_info_list.end ()) + { + if (retval->dir_name == dir) + break; + + retval++; + } + + return retval; +} + +bool +load_path::contains (const std::string& dir) const +{ + return find_dir_info (dir) != dir_info_list.end (); +} + +void +load_path::move (dir_info_list_iterator i, bool at_end) +{ + if (dir_info_list.size () > 1) + { + dir_info di = *i; + + dir_info_list.erase (i); + + if (at_end) + dir_info_list.push_back (di); + else + dir_info_list.push_front (di); + + std::string dir = di.dir_name; + + string_vector fcn_files = di.fcn_files; + + octave_idx_type len = fcn_files.length (); + + for (octave_idx_type k = 0; k < len; k++) + { + std::string fname = fcn_files[k]; + + std::string ext; + std::string base = fname; + + size_t pos = fname.rfind ('.'); + + if (pos != NPOS) + { + base = fname.substr (0, pos); + ext = fname.substr (pos); + } + + std::list& file_info_list = fcn_map[base]; + + if (file_info_list.size () == 1) + continue; + else + { + for (std::list::iterator p = file_info_list.begin (); + p != file_info_list.end (); + p++) + { + if (p->dir_name == dir) + { + file_info& fi = *p; + + file_info_list.erase (p); + + if (at_end) + file_info_list.push_back (fi); + else + file_info_list.push_front (fi); + + break; + } + } + } + } + } +} + +static void +maybe_add_path_elts (std::string& path, const std::string& dir) +{ + std::string tpath = genpath (dir); + + if (! tpath.empty ()) + path += dir_path::path_sep_str + tpath; +} + +void +load_path::do_initialize (void) +{ + Vsystem_path = ":"; + + maybe_add_path_elts (Vsystem_path, Vlocal_ver_oct_file_dir); + maybe_add_path_elts (Vsystem_path, Vlocal_api_oct_file_dir); + maybe_add_path_elts (Vsystem_path, Vlocal_oct_file_dir); + maybe_add_path_elts (Vsystem_path, Vlocal_ver_fcn_file_dir); + maybe_add_path_elts (Vsystem_path, Vlocal_api_fcn_file_dir); + maybe_add_path_elts (Vsystem_path, Vlocal_fcn_file_dir); + maybe_add_path_elts (Vsystem_path, Voct_file_dir); + maybe_add_path_elts (Vsystem_path, Vfcn_file_dir); + + std::string tpath = load_path::command_line_path; + + if (tpath.empty ()) + tpath = octave_env::getenv ("OCTAVE_LOADPATH"); + + std::string xpath = "."; + + if (! tpath.empty ()) + xpath += dir_path::path_sep_str + tpath; + + if (Vsystem_path != ":") + xpath += Vsystem_path; + + do_set (xpath + ":::"); +} + +void +load_path::do_clear (void) +{ + dir_info_list.clear (); + fcn_map.clear (); +} + +static std::list +split_path (const std::string& p) +{ + std::list retval; + + size_t beg = 0; + size_t end = p.find (dir_path::path_sep_char); + + size_t len = p.length (); + + while (end != NPOS) + { + std::string elt = p.substr (beg, end-beg); + + if (! elt.empty ()) + retval.push_back (elt); + + beg = end + 1; + + if (beg == len) + break; + + end = p.find (dir_path::path_sep_char, beg); + } + + std::string elt = p.substr (beg); + + if (! elt.empty ()) + retval.push_back (elt); + + return retval; +} + +void +load_path::do_set (const std::string& p) +{ + do_clear (); + + std::list elts = split_path (p); + + // Temporarily disable add hook. + + unwind_protect_ptr (add_hook); + + add_hook = 0; + + for (std::list::const_iterator i = elts.begin (); + i != elts.end (); + i++) + do_append (*i); + + // Restore add hook and execute for all newly added directories. + + unwind_protect::run (); + + for (dir_info_list_iterator i = dir_info_list.begin (); + i != dir_info_list.end (); + i++) + { + if (add_hook) + add_hook (i->dir_name); + } +} + +void +load_path::do_append (const std::string& dir) +{ + if (! dir.empty ()) + { + dir_info_list_iterator i = find_dir_info (dir); + + if (i != dir_info_list.end ()) + move (i, true); + else + { + dir_info di (dir); + + if (! error_state) + { + dir_info_list.push_back (di); + + add_to_fcn_map (di, true); + + if (add_hook) + add_hook (dir); + } + } + } +} + +void +load_path::do_prepend (const std::string& dir) +{ + if (! dir.empty ()) + { + dir_info_list_iterator i = find_dir_info (dir); + + if (i != dir_info_list.end ()) + move (i, false); + else + { + dir_info di (dir); + + if (! error_state) + { + dir_info_list.push_front (di); + + add_to_fcn_map (di, false); + + if (add_hook) + add_hook (dir); + } + } + + // FIXME -- is there a better way to do this? + + i = find_dir_info ("."); + + if (i != dir_info_list.end ()) + move (i, false); + else + panic_impossible (); + } +} + +bool +load_path::do_remove (const std::string& dir) +{ + bool retval = false; + + if (! dir.empty ()) + { + if (dir == ".") + warning ("rmpath: can't remove \".\" from path"); + + dir_info_list_iterator i = find_dir_info (dir); + + if (i != dir_info_list.end ()) + { + retval = true; + + string_vector fcn_files = i->fcn_files; + + dir_info_list.erase (i); + + octave_idx_type len = fcn_files.length (); + + for (octave_idx_type k = 0; k < len; k++) + { + std::string fname = fcn_files[k]; + + std::string ext; + std::string base = fname; + + size_t pos = fname.rfind ('.'); + + if (pos != NPOS) + { + base = fname.substr (0, pos); + ext = fname.substr (pos); + } + + std::list& file_info_list = fcn_map[base]; + + for (std::list::iterator p = file_info_list.begin (); + p != file_info_list.end (); + p++) + { + if (p->dir_name == dir) + { + file_info_list.erase (p); + + if (file_info_list.empty ()) + fcn_map.erase (fname); + + break; + } + } + } + + if (remove_hook) + remove_hook (dir); + } + } + + return retval; +} + +void +load_path::do_update (void) const +{ + // I don't see a better way to do this because we need to + // preserve the correct directory ordering for new files that + // have appeared. + + fcn_map.clear (); + + for (dir_info_list_iterator p = dir_info_list.begin (); + p != dir_info_list.end (); + p++) + { + dir_info& di = *p; + + di.update (); + + add_to_fcn_map (di, true); + } +} + +std::string +load_path::do_find_fcn (const std::string& fcn, int type) const +{ + std::string retval; + + update (); + + const_fcn_map_iterator p = fcn_map.find (fcn); + + if (p != fcn_map.end ()) + { + const std::list& file_info_list = p->second; + + for (const_file_info_list_iterator i = file_info_list.begin (); + i != file_info_list.end (); + i++) + { + const file_info& fi = *i; + + int t = fi.types; + + retval = fi.dir_name + file_ops::dir_sep_str + fcn; + + if (type == load_path::OCT_FILE) + { + if ((type & t) == load_path::OCT_FILE) + { + retval += ".oct"; + break; + } + } + else if (type == load_path::M_FILE) + { + if ((type & t) == load_path::M_FILE) + { + retval += ".m"; + break; + } + } + else if (type == (load_path::M_FILE | load_path::OCT_FILE)) + { + if (t & load_path::OCT_FILE) + { + retval += ".oct"; + break; + } + else if (t & load_path::M_FILE) + { + retval += ".m"; + break; + } + } + else + error ("load_path::do_find_fcn: %s: invalid type code = %d", + fcn.c_str (), type); + } + } + + return retval; +} + +std::string +load_path::do_find_file (const std::string& file) const +{ + std::string retval; + + if (octave_env::absolute_pathname (file)) + { + file_stat fs (file); + + if (fs.exists ()) + return file; + } + + std::string dir_name; + + for (const_dir_info_list_iterator p = dir_info_list.begin (); + p != dir_info_list.end (); + p++) + { + string_vector all_files = p->all_files; + + octave_idx_type len = all_files.length (); + + for (octave_idx_type i = 0; i < len; i++) + { + if (all_files[i] == file) + { + dir_name = p->dir_name; + break; + } + } + } + + if (! dir_name.empty ()) + retval = dir_name + file_ops::dir_sep_str + file; + + return retval; +} + +std::string +load_path::do_find_first_of (const string_vector& flist) const +{ + std::string retval; + + std::string dir_name; + std::string file_name; + + octave_idx_type flen = flist.length (); + octave_idx_type rel_flen = 0; + + string_vector rel_flist (flen); + + for (octave_idx_type i = 0; i < flen; i++) + { + if (octave_env::absolute_pathname (flist[i])) + { + file_stat fs (flist[i]); + + if (fs.exists ()) + return flist[i]; + } + else + rel_flist[rel_flen++] = flist[i]; + } + + rel_flist.resize (rel_flen); + + for (const_dir_info_list_iterator p = dir_info_list.begin (); + p != dir_info_list.end (); + p++) + { + string_vector all_files = p->all_files; + + octave_idx_type len = all_files.length (); + + for (octave_idx_type i = 0; i < len; i++) + { + + for (octave_idx_type j = 0; j < rel_flen; j++) + { + if (all_files[i] == rel_flist[j]) + { + dir_name = p->dir_name; + file_name = rel_flist[j]; + break; + } + } + } + } + + if (! dir_name.empty ()) + retval = dir_name + file_ops::dir_sep_str + file_name; + + return retval; +} + +string_vector +load_path::do_find_all_first_of (const string_vector& flist) const +{ + std::list retlist; + + std::string dir_name; + std::string file_name; + + octave_idx_type flen = flist.length (); + octave_idx_type rel_flen = 0; + + string_vector rel_flist (flen); + + for (octave_idx_type i = 0; i < flen; i++) + { + if (octave_env::absolute_pathname (flist[i])) + { + file_stat fs (flist[i]); + + if (fs.exists ()) + retlist.push_back (flist[i]); + } + else + rel_flist[rel_flen++] = flist[i]; + } + + rel_flist.resize (rel_flen); + + for (const_dir_info_list_iterator p = dir_info_list.begin (); + p != dir_info_list.end (); + p++) + { + string_vector all_files = p->all_files; + + octave_idx_type len = all_files.length (); + + for (octave_idx_type i = 0; i < len; i++) + { + for (octave_idx_type j = 0; j < rel_flen; j++) + { + if (all_files[i] == rel_flist[j]) + retlist.push_back + (p->dir_name + file_ops::dir_sep_str + rel_flist[j]); + } + } + } + + size_t retsize = retlist.size (); + + string_vector retval (retsize); + + for (size_t i = 0; i < retsize; i++) + { + retval[i] = retlist.front (); + + retlist.pop_front (); + } + + return retval; +} + +string_vector +load_path::do_dirs (void) const +{ + size_t len = dir_info_list.size (); + + string_vector retval (len); + + octave_idx_type k = 0; + + for (const_dir_info_list_iterator i = dir_info_list.begin (); + i != dir_info_list.end (); + i++) + retval[k++] = i->dir_name; + + return retval; +} + +std::list +load_path::do_dir_list (void) const +{ + std::list retval; + + for (const_dir_info_list_iterator i = dir_info_list.begin (); + i != dir_info_list.end (); + i++) + retval.push_back (i->dir_name); + + return retval; +} + +string_vector +load_path::do_files (const std::string& dir) const +{ + string_vector retval; + + const_dir_info_list_iterator i = find_dir_info (dir); + + if (i != dir_info_list.end ()) + retval = i->fcn_files; + + return retval; +} + +string_vector +load_path::do_fcn_names (void) const +{ + size_t len = fcn_map.size (); + + string_vector retval (len); + + octave_idx_type count = 0; + + for (const_fcn_map_iterator p = fcn_map.begin (); + p != fcn_map.end (); + p++) + retval[count++] = p->first; + + return retval; +} + +std::string +load_path::do_path (void) const +{ + std::string xpath; + + string_vector xdirs = load_path::dirs (); + + octave_idx_type len = xdirs.length (); + + if (len > 0) + xpath = xdirs[0]; + + for (octave_idx_type i = 1; i < len; i++) + xpath += dir_path::path_sep_str + xdirs[i]; + + return xpath; +} + +void +load_path::do_display (std::ostream& os) const +{ + for (const_dir_info_list_iterator i = dir_info_list.begin (); + i != dir_info_list.end (); + i++) + { + string_vector fcn_files = i->fcn_files; + + if (! fcn_files.empty ()) + { + os << "\n*** function files in " << i->dir_name << ":\n\n"; + + fcn_files.list_in_columns (os); + } + +#if defined (DEBUG_LOAD_PATH) + + const std::map& private_function_map + = i->private_function_map; + + if (private_function_map.size () > 0) + { + os << "private:\n"; + + for (std::map::const_iterator p = private_function_map.begin (); + p != private_function_map.end (); + p++) + { + os << " " << p->first << " ("; + + bool printed_type = false; + + int types = p->second; + + if (types & load_path::OCT_FILE) + { + os << "oct"; + printed_type = true; + } + + if (types & load_path::M_FILE) + { + if (printed_type) + os << "|"; + os << "m"; + printed_type = true; + } + + os << ")\n"; + } + + os << "\n"; + } +#endif + } + +#if defined (DEBUG_LOAD_PATH) + + for (const_fcn_map_iterator i = fcn_map.begin (); + i != fcn_map.end (); + i++) + { + os << i->first << ":\n"; + + const std::list& file_info_list = i->second; + + for (const_file_info_list_iterator p = file_info_list.begin (); + p != file_info_list.end (); + p++) + { + os << " " << p->dir_name << " ("; + + bool printed_type = false; + + if (p->types & load_path::OCT_FILE) + { + os << "oct"; + printed_type = true; + } + + if (p->types & load_path::M_FILE) + { + if (printed_type) + os << "|"; + os << "m"; + printed_type = true; + } + + os << ")\n"; + } + } + + os << "\n"; + +#endif +} + +void +load_path::add_to_fcn_map (const dir_info& di, bool at_end) const +{ + std::string dir_name = di.dir_name; + + string_vector fcn_files = di.fcn_files; + + octave_idx_type len = fcn_files.length (); + + for (octave_idx_type i = 0; i < len; i++) + { + std::string fname = fcn_files[i]; + + std::string ext; + std::string base = fname; + + size_t pos = fname.rfind ('.'); + + if (pos != NPOS) + { + base = fname.substr (0, pos); + ext = fname.substr (pos); + } + + std::list& file_info_list = fcn_map[base]; + + file_info_list_iterator p = file_info_list.begin (); + + while (p != file_info_list.end ()) + { + if (p->dir_name == dir_name) + break; + + p++; + } + + int t = 0; + if (ext == ".m") + t = load_path::M_FILE; + else if (ext == ".oct") + t = load_path::OCT_FILE; + + if (p == file_info_list.end ()) + { + file_info fi (dir_name, t); + + if (at_end) + file_info_list.push_back (fi); + else + file_info_list.push_front (fi); + } + else + { + file_info& fi = *p; + + fi.types |= t; + } + } +} + +std::string +genpath (const std::string& dirname, const string_vector& skip) +{ + std::string retval; + + std::string full_dirname = file_ops::tilde_expand (dirname); + + dir_entry dir (full_dirname); + + if (dir) + { + retval = dirname; + + string_vector dirlist = dir.read (); + + octave_idx_type len = dirlist.length (); + + for (octave_idx_type i = 0; i < len; i++) + { + std::string elt = dirlist[i]; + + // FIXME -- the caller should be able to specify the list of + // directories to skip in addition to "." and "..". + + bool skip_p = (elt == "." || elt == ".."); + + if (! skip_p) + { + for (octave_idx_type j = 0; j < skip.length (); j++) + { + skip_p = (elt == skip[j]); + if (skip_p) + break; + } + + if (! skip_p) + { + std::string nm = full_dirname + file_ops::dir_sep_str + elt; + + file_stat fs (nm); + + if (fs && fs.is_dir ()) + retval += dir_path::path_sep_str + genpath (nm); + } + } + } + } + + return retval; +} + +static void +execute_pkg_add_or_del (const std::string& dir, + const std::string& script_file) +{ + if (! octave_interpreter_ready) + return; + + unwind_protect::begin_frame ("execute_pkg_add_or_del"); + + unwind_protect_bool (input_from_startup_file); + + input_from_startup_file = true; + + std::string file = dir + file_ops::dir_sep_str + script_file; + + file_stat fs = file_stat (file); + + if (fs.exists ()) + source_file (file); + + unwind_protect::run_frame ("execute_pkg_add_or_del"); +} + +void +execute_pkg_add (const std::string& dir) +{ + execute_pkg_add_or_del (dir, "PKG_ADD"); +} + +void +execute_pkg_del (const std::string& dir) +{ + execute_pkg_add_or_del (dir, "PKG_DEL"); +} + +DEFUN (genpath, args, , + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {} genpath (@var{dir})\n\ +Return a path constructed from @var{dir} and all its subdiretories.\n\ +@end deftypefn") +{ + octave_value retval; + + if (args.length () == 1) + { + std::string dirname = args(0).string_value (); + + if (! error_state) + retval = genpath (dirname); + else + error ("genpath: expecting argument to be a character string"); + } + else + print_usage (); + + return retval; +} + +DEFUN (rehash, , , + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {} rehash ()\n\ +Reinitialize Octave's @code{LOADPATH} directory cache.\n\ +@end deftypefn") +{ + octave_value_list retval; + + load_path::update (); + + // FIXME -- maybe we should rename this variable since it is being + // used for more than keeping track of the prompt time. + + // This will force updated functions to be found. + Vlast_prompt_time.stamp (); + + return retval; +} + +DEFUN (pathdef, , , + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {@var{val} =} pathdef ()\n\ +Return the default list of directories in which to search for function\n\ +files.\n\ +@seealso{path, addpath, rmpath, genpath, savepath, pathsep}\n\ +@end deftypefn") +{ + return octave_value (Vsystem_path); +} + +DEFUN (path, args, nargout, + "-*- texinfo -*-\n\ +@deftypefn {Function File} {} path (@dots{})\n\ +Modify or display Octave's @code{LOADPATH}.\n\ +\n\ +If @var{nargin} and @var{nargout} are zero, display the elements of\n\ +Octave's @code{LOADPATH} in an easy to read format.\n\ +\n\ +If @var{nargin} is zero and nargout is greater than zero, return the\n\ +current value of @code{LOADPATH}.\n\ +\n\ +If @var{nargin} is greater than zero, concatenate the arguments,\n\ +separating them with @code{pathsep()}. Set the internal search path\n\ +to the result and return it.\n\ +\n\ +No checks are made for duplicate elements.\n\ +@seealso{addpath, rmpath, genpath, pathdef, savepath, pathsep}\n\ +@end deftypefn") +{ + octave_value retval; + + int argc = args.length () + 1; + + string_vector argv = args.make_argv ("path"); + + if (! error_state) + { + if (argc > 1) + { + std::string path = argv[1]; + + for (int i = 2; i < argc; i++) + path += dir_path::path_sep_str; + + size_t plen = path.length (); + + if (! ((plen == 1 && path[0] == ':') + || (plen > 1 + && path.substr (0, 2) == ("." + dir_path::path_sep_str)))) + path = "." + dir_path::path_sep_str + path; + + load_path::set (path); + } + + if (nargout > 0) + retval = load_path::path (); + else if (argc == 1 && nargout == 0) + { + octave_stdout << "\nOctave's search path contains the following directories:\n\n"; + + string_vector dirs = load_path::dirs (); + + dirs.list_in_columns (octave_stdout); + + octave_stdout << "\n"; + } + } + + return retval; +} + +DEFCMD (addpath, args, nargout, + "-*- texinfo -*-\n\ +@deftypefn {Function File} {} addpath (@var{dir1}, @dots{})\n\ +@deftypefnx {Function File} {} addpath (@var{dir1}, @dots{}, @var{option})\n\ +Add @var{dir1}, @dots{} to the current function search path. If\n\ +@var{option} is @samp{\"-begin\"} or 0 (the default), prepend the\n\ +directory name to the current path. If @var{option} is @samp{\"-end\"}\n\ +or 1, append the directory name to the current path.\n\ +Directories added to the path must exist.\n\ +@seealso{path, rmpath, genpath, pathdef, savepath, pathsep}\n\ +@end deftypefn") +{ + octave_value retval; + + // Originally written by Bill Denney and Etienne Grossman. Heavily + // modified and translated to C++ by jwe. + + if (nargout > 0) + retval = load_path::path (); + + int nargin = args.length (); + + if (nargin > 0) + { + bool append = false; + + octave_value option_arg = args(nargin-1); + + if (option_arg.is_string ()) + { + std::string option = option_arg.string_value (); + + if (option == "-end") + { + append = true; + nargin--; + } + else if (option == "-begin") + nargin--; + } + else if (option_arg.is_numeric_type ()) + { + int val = option_arg.int_value (); + + if (! error_state) + { + if (val == 0) + append = false; + else if (val == 1) + append = true; + else + { + error ("addpath: expecting final argument to be 1 or 0"); + return retval; + } + } + else + { + error ("addpath: expecting final argument to be 1 or 0"); + return retval; + } + } + + std::list xpath = load_path::dir_list (); + + // Strip "." for now. Calling path to set the path will restore it. + + xpath.remove ("."); + + for (int i = 0; i < nargin; i++) + { + std::string arg = args(i).string_value (); + + if (! error_state) + { + std::list dir_elts = split_path (arg); + + for (std::list::const_iterator p = dir_elts.begin (); + p != dir_elts.end (); + p++) + { + std::string dir = *p; + + //dir = regexprep (dir_elts{j}, "//+", "/"); + //dir = regexprep (dir, "/$", ""); + + if (dir == "." && append) + warning ("addpath: \".\" is always first in the path"); + + file_stat fs (dir); + + if (fs) + { + if (fs.is_dir ()) + { + if (append) + load_path::append (dir); + else + load_path::prepend (dir); + } + else + warning ("addpath: %s: not a directory", dir.c_str ()); + } + else + { + std::string msg = fs.error (); + warning ("addpath: %s: %s", dir.c_str (), msg.c_str ()); + } + } + } + else + error ("addpath: expecting all args to be character strings"); + } + } + else + print_usage (); + + return retval; +} + +DEFCMD (rmpath, args, nargout, + "-*- texinfo -*-\n\ +@deftypefn {Function File} {} rmpath (@var{dir1}, @dots{})\n\ +Remove @var{dir1}, @dots{} from the current function search path.\n\ +\n\ +@seealso{path, addpath, genpath, pathdef, savepath, pathsep}\n\ +@end deftypefn") +{ + // Originally by Etienne Grossmann. Heavily modified and translated + // to C++ by jwe. + + octave_value retval; + + if (nargout > 0) + retval = load_path::path (); + + int nargin = args.length (); + + if (nargin > 0) + { + std::list xpath = load_path::dir_list (); + + for (int i = 0; i < nargin; i++) + { + std::string arg = args(i).string_value (); + + if (! error_state) + { + std::list dir_elts = split_path (arg); + + for (std::list::const_iterator p = dir_elts.begin (); + p != dir_elts.end (); + p++) + { + std::string dir = *p; + + //dir = regexprep (dir_elts{j}, "//+", "/"); + //dir = regexprep (dir, "/$", ""); + + if (! load_path::remove (dir)) + warning ("rmpath: %s: not found", dir.c_str ()); + } + } + else + error ("addpath: expecting all args to be character strings"); + } + } + else + print_usage (); + + return retval; +} + +/* +;;; Local Variables: *** +;;; mode: C++ *** +;;; End: *** +*/ diff --git a/src/load-path.h b/src/load-path.h new file mode 100644 --- /dev/null +++ b/src/load-path.h @@ -0,0 +1,337 @@ +/* + +Copyright (C) 2006 John W. Eaton + +This file is part of Octave. + +Octave is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +Octave is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with Octave; see the file COPYING. If not, write to the Free +Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + +*/ + +#if !defined (octave_load_path_h) +#define octave_load_path_h 1 + +#include +#include +#include +#include + +#include "str-vec.h" + +class +load_path +{ +protected: + + load_path (void) : dir_info_list (), fcn_map () { } + +public: + + typedef void (*hook_function_ptr) (const std::string& dir); + + ~load_path (void) { } + + static void initialize (void) + { + if (instance_ok ()) + instance->do_initialize (); + } + + static void clear (void) + { + if (instance_ok ()) + instance->do_clear (); + } + + static void set (const std::string& p) + { + if (instance_ok ()) + instance->do_set (p); + } + + static void append (const std::string& dir) + { + if (instance_ok ()) + instance->do_append (dir); + } + + static void prepend (const std::string& dir) + { + if (instance_ok ()) + instance->do_prepend (dir); + } + + static bool remove (const std::string& dir) + { + return instance_ok () ? instance->do_remove (dir) : false; + } + + static void update (void) + { + if (instance_ok ()) + instance->do_update (); + } + + static std::string find_fcn (const std::string& fcn) + { + return instance_ok () + ? instance->do_find_fcn (fcn) : std::string (); + } + + static std::string find_fcn_file (const std::string& fcn) + { + return instance_ok () ? + instance->do_find_fcn (fcn, M_FILE) : std::string (); + } + + static std::string find_oct_file (const std::string& fcn) + { + return instance_ok () ? + instance->do_find_fcn (fcn, OCT_FILE) : std::string (); + } + + static std::string find_file (const std::string& file) + { + return instance_ok () + ? instance->do_find_file (file) : std::string (); + } + + static std::string find_first_of (const string_vector& files) + { + return instance_ok () ? + instance->do_find_first_of (files) : std::string (); + } + + static string_vector find_all_first_of (const string_vector& files) + { + return instance_ok () ? + instance->do_find_all_first_of (files) : string_vector (); + } + + static string_vector dirs (void) + { + return instance_ok () ? instance->do_dirs () : string_vector (); + } + + static std::list dir_list (void) + { + return instance_ok () + ? instance->do_dir_list () : std::list (); + } + + static string_vector files (const std::string& dir) + { + return instance_ok () ? instance->do_files (dir) : string_vector (); + } + + static string_vector fcn_names (void) + { + return instance_ok () ? instance->do_fcn_names () : string_vector (); + } + + static std::string path (void) + { + return instance_ok () ? instance->do_path () : std::string (); + } + + static void display (std::ostream& os) + { + if (instance_ok ()) + instance->do_display (os); + } + + static void set_add_hook (hook_function_ptr f) { add_hook = f; } + + static void set_remove_hook (hook_function_ptr f) { remove_hook = f; } + + static void set_command_line_path (const std::string& p) + { + command_line_path = p; + } + +private: + + static const int M_FILE = 1; + static const int OCT_FILE = 2; + + class dir_info + { + public: + + dir_info (const std::string& d) : dir_name (d) { initialize (); } + + dir_info (const dir_info& di) + : dir_name (di.dir_name), is_relative (di.is_relative), + dir_mtime (di.dir_mtime), all_files (di.all_files), + fcn_files (di.fcn_files), + private_function_map (di.private_function_map) { } + + ~dir_info (void) { } + + dir_info& operator = (const dir_info& di) + { + if (&di != this) + { + dir_name = di.dir_name; + is_relative = di.is_relative; + dir_mtime = di.dir_mtime; + all_files = di.all_files; + fcn_files = di.fcn_files; + private_function_map = di.private_function_map; + } + + return *this; + } + + void update (void); + + std::string dir_name; + bool is_relative; + octave_time dir_mtime; + string_vector all_files; + string_vector fcn_files; + std::map private_function_map; + + private: + + void initialize (void); + + bool get_file_list (const std::string& d); + + void get_private_function_map (const std::string& d); + }; + + class file_info + { + public: + + file_info (const std::string& d, int t) : dir_name (d), types (t) { } + + file_info (const file_info& fi) + : dir_name (fi.dir_name), types (fi.types) { } + + ~file_info (void) { } + + file_info& operator = (const file_info& fi) + { + if (&fi != this) + { + dir_name = fi.dir_name; + types = fi.types; + } + + return *this; + } + + std::string dir_name; + int types; + }; + + // We maintain two ways of looking at the same information. + // + // First, a list of directories and the set of "public" files and + // private files (those found in the special "private" subdirectory) + // in each directory. + // + // Second, a map from file names (the union of all "public" files for all + // directories, but without filename exteinsions) to a list of + // corresponding information (directory name and file types). This + // way, we can quickly find shadowed file names and look up all + // overloaded functions (in the "@" directories used to implement + // classes). + + mutable std::list dir_info_list; + + mutable std::map > fcn_map; + + static load_path *instance; + + static hook_function_ptr add_hook; + + static hook_function_ptr remove_hook; + + static std::string command_line_path; + + static bool instance_ok (void); + + typedef std::list::const_iterator const_dir_info_list_iterator; + typedef std::list::iterator dir_info_list_iterator; + + typedef std::map >::const_iterator const_fcn_map_iterator; + typedef std::map >::iterator fcn_map_iterator; + + typedef std::list::const_iterator const_file_info_list_iterator; + typedef std::list::iterator file_info_list_iterator; + + const_dir_info_list_iterator find_dir_info (const std::string& dir) const; + dir_info_list_iterator find_dir_info (const std::string& dir); + + bool contains (const std::string& dir) const; + + void move (std::list::iterator i, bool at_end); + + void do_initialize (void); + + void do_clear (void); + + void do_set (const std::string& p); + + void do_append (const std::string& dir); + + void do_prepend (const std::string& dir); + + bool do_remove (const std::string& dir); + + void do_update (void) const; + + std::string do_find_fcn (const std::string& fcn, + int type = M_FILE | OCT_FILE) const; + + std::string do_find_file (const std::string& file) const; + + std::string do_find_first_of (const string_vector& files) const; + + string_vector do_find_all_first_of (const string_vector& files) const; + + string_vector do_dirs (void) const; + + std::list do_dir_list (void) const; + + string_vector do_files (const std::string& dir) const; + + string_vector do_fcn_names (void) const; + + std::string do_path (void) const; + + void do_display (std::ostream& os) const; + + void add_to_fcn_map (const dir_info& di, bool at_end) const; +}; + +extern std::string +genpath (const std::string& dir, const string_vector& skip = "private"); + +extern void execute_pkg_add (const std::string& dir); +extern void execute_pkg_del (const std::string& dir); + +#endif + +/* +;;; Local Variables: *** +;;; mode: C++ *** +;;; page-delimiter: "^/\\*" *** +;;; End: *** +*/ diff --git a/src/octave.cc b/src/octave.cc --- a/src/octave.cc +++ b/src/octave.cc @@ -58,6 +58,7 @@ #include "file-io.h" #include "input.h" #include "lex.h" +#include "load-path.h" #include "octave.h" #include "oct-hist.h" #include "oct-map.h" @@ -578,7 +579,7 @@ case 'p': if (args.optarg ()) - set_load_path (args.optarg ()); + load_path::set_command_line_path (args.optarg ()); break; case 'q': @@ -675,7 +676,7 @@ initialize_version_info (); - execute_default_pkg_add_files (); + load_path::initialize (); execute_startup_files (); diff --git a/src/parse.y b/src/parse.y --- a/src/parse.y +++ b/src/parse.y @@ -60,6 +60,7 @@ #include "error.h" #include "input.h" #include "lex.h" +#include "load-path.h" #include "oct-hist.h" #include "oct-map.h" #include "ov-fcn-handle.h" @@ -3350,7 +3351,7 @@ am_iter p = autoload_map.find (nm); if (p != autoload_map.end ()) - retval = octave_env::make_absolute (Vload_path_dir_path.find (p->second), + retval = octave_env::make_absolute (load_path::find_file (p->second), octave_env::getcwd ()); return retval; @@ -3413,13 +3414,8 @@ exec_script = true; } else - { - names[0] = nm + ".oct"; - names[1] = nm + ".m"; - - file = octave_env::make_absolute (Vload_path_dir_path.find_first_of (names), - octave_env::getcwd ()); - } + file = octave_env::make_absolute + (load_path::find_fcn (nm), octave_env::getcwd ()); } int len = file.length (); diff --git a/src/pr-output.cc b/src/pr-output.cc --- a/src/pr-output.cc +++ b/src/pr-output.cc @@ -318,7 +318,7 @@ } else if (inf_or_nan || int_only) { - fw = digits; + fw = 1 + digits; if (inf_or_nan && fw < 4) fw = 4; rd = fw; @@ -338,7 +338,7 @@ digits = -digits + 1; } - fw = ld + 1 + rd; + fw = 1 + ld + 1 + rd; if (inf_or_nan && fw < 4) fw = 4; } @@ -478,7 +478,7 @@ ld = ld_max > ld_min ? ld_max : ld_min; rd = rd_max > rd_min ? rd_max : rd_min; - fw = ld + 1 + rd; + fw = 1 + ld + 1 + rd; if (inf_or_nan && fw < 4) fw = 4; } @@ -626,7 +626,7 @@ ld = ld_max > ld_min ? ld_max : ld_min; rd = rd_max > rd_min ? rd_max : rd_min; - i_fw = r_fw = ld + 1 + rd; + i_fw = r_fw = 1 + ld + 1 + rd; if (inf_or_nan && i_fw < 4) i_fw = r_fw = 4; } @@ -806,7 +806,7 @@ ld = ld_max > ld_min ? ld_max : ld_min; rd = rd_max > rd_min ? rd_max : rd_min; - i_fw = r_fw = ld + 1 + rd; + i_fw = r_fw = 1 + ld + 1 + rd; if (inf_or_nan && i_fw < 4) i_fw = r_fw = 4; } @@ -981,7 +981,7 @@ ld = ld_max > ld_min ? ld_max : ld_min; rd = rd_max > rd_min ? rd_max : rd_min; - fw = sign + ld + 1 + rd; + fw = sign + 1 + ld + 1 + rd; } if (! (bank_format || hex_format || bit_format) diff --git a/src/utils.cc b/src/utils.cc --- a/src/utils.cc +++ b/src/utils.cc @@ -58,6 +58,7 @@ #include "error.h" #include "gripes.h" #include "input.h" +#include "load-path.h" #include "oct-errno.h" #include "oct-hist.h" #include "oct-obj.h" @@ -295,8 +296,7 @@ if (nargin == 1) { std::string fname = octave_env::make_absolute - (Vload_path_dir_path.find_first_of (names), - octave_env::getcwd ()); + (load_path::find_first_of (names), octave_env::getcwd ()); if (fname.empty ()) retval = Matrix (); @@ -308,7 +308,7 @@ std::string opt = args(1).string_value (); if (! error_state && opt == "all") - retval = Cell (make_absolute (Vload_path_dir_path.find_all_first_of (names))); + retval = Cell (make_absolute (load_path::find_all_first_of (names))); else error ("file_in_loadpath: invalid option"); } @@ -399,8 +399,10 @@ if (! suffix.empty ()) nm.append (suffix); - return octave_env::make_absolute (Vload_path_dir_path.find (nm), - octave_env::getcwd ()); + return std::string (); + + return octave_env::make_absolute + (load_path::find_file (nm), octave_env::getcwd ()); } // See if there is an function file in the path. If so, return the @@ -415,10 +417,17 @@ if (len > 0) { - if (len > 2 && name [len - 2] == '.' && name [len - 1] == 'm') - retval = file_in_path (name, ""); + if (octave_env::absolute_pathname (name)) + { + file_stat fs (name); + + if (fs.exists ()) + retval = name; + } + else if (len > 2 && name [len - 2] == '.' && name [len - 1] == 'm') + retval = load_path::find_fcn_file (name.substr (0, len-2)); else - retval = file_in_path (name, ".m"); + retval = load_path::find_fcn_file (name); } return retval; @@ -436,11 +445,18 @@ if (len > 0) { - if (len > 4 && name [len - 4] == '.' && name [len - 3] == 'o' - && name [len - 2] == 'c' && name [len - 1] == 't') - retval = file_in_path (name, ""); + if (octave_env::absolute_pathname (name)) + { + file_stat fs (name); + + if (fs.exists ()) + retval = name; + } + else if (len > 4 && name [len - 4] == '.' && name [len - 3] == 'o' + && name [len - 2] == 'c' && name [len - 1] == 't') + retval = load_path::find_oct_file (name.substr (0, len-4)); else - retval = file_in_path (name, ".oct"); + retval = load_path::find_oct_file (name); } return retval; @@ -657,23 +673,6 @@ return retval; } -DEFUN (find_first_of_in_loadpath, args, , "") -{ - octave_value retval; - - if (args.length () == 1) - { - string_vector names = args(0).all_strings (); - - if (! error_state) - retval = Vload_path_dir_path.find_all_first_of (names); - } - else - print_usage (); - - return retval; -} - DEFUNX ("errno", Ferrno, args, , "-*- texinfo -*-\n\ @deftypefn {Built-in Function} {@var{err} =} errno ()\n\ diff --git a/src/variables.cc b/src/variables.cc --- a/src/variables.cc +++ b/src/variables.cc @@ -46,6 +46,7 @@ #include "help.h" #include "input.h" #include "lex.h" +#include "load-path.h" #include "oct-map.h" #include "oct-obj.h" #include "ov.h" @@ -812,22 +813,15 @@ std::string file_name = lookup_autoload (name); if (file_name.empty ()) - { - string_vector names (2); - - names(0) = name + ".oct"; - names(1) = name + ".m"; - - file_name = Vload_path_dir_path.find_first_of (names); - } + file_name = load_path::find_fcn (name); size_t len = file_name.length (); - if (! file_name.empty ()) + if (len > 0) { if (type == "any" || type == "file") { - if (file_name.substr (len-4) == ".oct") + if (len > 4 && file_name.substr (len-4) == ".oct") retval = 3; else retval = 2; @@ -1031,12 +1025,8 @@ if (file.empty ()) { - names[0] = nm + ".oct"; - names[1] = nm + ".m"; - file = octave_env::make_absolute - (Vload_path_dir_path.find_first_of (names), - octave_env::getcwd ()); + (load_path::find_fcn (nm), octave_env::getcwd ()); } }