changeset 19369:bb8d3f17248d

Overhaul savepath.m, pathdef.m, matlabroot.m. Fix pathdef which has been broken since at least 3.2.4 (nobody noticed). Use project specific .octaverc for savepath, pathdef before other choices. * matlabroot.m: Remove stray extra newline before start of BIST tests. * scripts/path/module.mk: Add getsavepath.m to build system * scripts/path/private/getsavepath.m: New function abstracts common code used by both pathdef and savepath to read an rc file. * pathdef.m: Redo docstring. Validate number of input arguments. Look for project-specific .octaverc file ahead of other rc files. Use private function getsavepath to read in rc file. Return pathsep() separated list of directories (this was broken). Add one lame BIST test. * savepath.m: Redo docstring. Use project-specific .octaverc if it exists. Use private function getsavepath to read in rc file. Replace for loops around fprintf with single calls to fprintf and a comma-separated-list of arguments. Use Octave syntax, rather than for loops, to convert cell array of structs to cellstr of a single struct field value. Turn off backtrace warning temporarily when alerting the user about where the savepath information has been saved. Use ostrsplit and strcat rather than regexpi for performance. Add BIST test.
author Rik <rik@octave.org>
date Thu, 20 Nov 2014 08:08:35 -0800
parents bcec6f9d1596
children 3a905b642408
files scripts/path/matlabroot.m scripts/path/module.mk scripts/path/pathdef.m scripts/path/private/getsavepath.m scripts/path/savepath.m
diffstat 5 files changed, 200 insertions(+), 155 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/path/matlabroot.m	Tue Nov 18 12:13:06 2014 +0000
+++ b/scripts/path/matlabroot.m	Thu Nov 20 08:08:35 2014 -0800
@@ -32,6 +32,5 @@
 endfunction
 
 
-
 %!assert (matlabroot (), OCTAVE_HOME ())
 
--- a/scripts/path/module.mk	Tue Nov 18 12:13:06 2014 +0000
+++ b/scripts/path/module.mk	Thu Nov 20 08:08:35 2014 -0800
@@ -1,9 +1,13 @@
 FCN_FILE_DIRS += path
 
+path_PRIVATE_FCN_FILES = \
+  path/private/getsavepath.m
+
 path_FCN_FILES = \
   path/matlabroot.m \
   path/pathdef.m \
-  path/savepath.m
+  path/savepath.m \
+  $(path_PRIVATE_FCN_FILES)
 
 FCN_FILES += $(path_FCN_FILES)
 
--- a/scripts/path/pathdef.m	Tue Nov 18 12:13:06 2014 +0000
+++ b/scripts/path/pathdef.m	Thu Nov 20 08:08:35 2014 -0800
@@ -20,108 +20,82 @@
 ## -*- texinfo -*-
 ## @deftypefn {Function File} {@var{val} =} pathdef ()
 ## Return the default path for Octave.
-## The path information is extracted from one of three sources.
+##
+## The path information is extracted from one of four sources.
 ## The possible sources, in order of preference, are:
 ##
 ## @enumerate
+## @item @file{.octaverc}
+##
 ## @item @file{~/.octaverc}
 ##
-## @item @file{<octave-home>/@dots{}/<version>/m/startup/octaverc}
+## @item @file{<OCTAVE_HOME>/@dots{}/<version>/m/startup/octaverc}
 ##
-## @item Octave's path prior to changes by any octaverc.
+## @item Octave's path prior to changes by any octaverc file.
 ## @end enumerate
 ## @seealso{path, addpath, rmpath, genpath, savepath}
 ## @end deftypefn
 
 function val = pathdef ()
 
-  ## Locate the site octaverc file.
-  pathdir = octave_config_info ("localstartupfiledir");
-  site_octaverc = fullfile (pathdir, "octaverc");
+  if (nargin > 0)
+    print_usage ();
+  endif
+
+  ## Locate any project-specific .octaverc file.
+  proj_octaverc = fullfile (pwd, ".octaverc");
+  if (exist (proj_octaverc, "file"))
+    proj_path = __extractpath__ (proj_octaverc);
+    if (! isempty (proj_path))
+      val = proj_path;
+      return;
+    endif
+  endif
 
   ## Locate the user's ~/.octaverc file.
   user_octaverc = fullfile ("~", ".octaverc");
-
-  ## Extract the specified paths from the site and user octavercs.
-  site_path = __extractpath__ (site_octaverc);
   if (exist (user_octaverc, "file"))
     user_path = __extractpath__ (user_octaverc);
-  else
-    user_path = "";
+    if (! isempty (user_path))
+      val = user_path;
+      return;
+    endif
+  endif
+
+  ## No user octaverc file, locate the site octaverc file.
+  pathdir = octave_config_info ("localstartupfiledir");
+  site_octaverc = fullfile (pathdir, "octaverc");
+  site_path = __extractpath__ (site_octaverc);
+  if (! isempty (site_path))
+    val = site_path;
+    return;
   endif
 
-  ## A path definition in the user rcfile has precedence over the site rcfile.
-  if (! isempty (user_path))
-    val = user_path;
-  elseif (! isempty (site_path))
-    val = site_path;
+  ## No project, user, or site octaverc file.  Use Octave's default.
+  val = __pathorig__ ();
+
+endfunction
+
+## Extact the path information from the script/function @var{file}, created by
+## @file{savepath.m}.  If successful, @code{__extractpath__} returns the path
+## specified in @var{file}.
+
+## Author: Ben Abbott <bpabbott@mac.com>
+
+function path = __extractpath__ (savefile)
+
+  [filelines, startline, endline] = getsavepath (savefile);
+  if (startline > 0)
+    tmp = regexprep (filelines(startline+1:endline-1),
+                     "^.*path \\('([^\']+)'.*$", "$1");
+    path = strjoin (tmp, ":");
   else
-    val = __pathorig__ ();
+    path = "";
   endif
 
 endfunction
 
-## Extact the path information from the script/function @var{file},
-## created by @file{savepath.m}.  If @var{file} is omitted,
-## @file{~/.octaverc} is used.  If successful, @code{__extractpath__}
-## returns the path specified in @var{file}.
-
-## Author: Ben Abbott <bpabbott@mac.com>
-
-function specifiedpath = __extractpath__ (savefile)
-
-  ## The majority of this code was borrowed from savepath.m.
-  ## FIXME: is there some way to share the common parts instead of duplicating?
-  ## ANSWER: Yes.  Create a private directory and extract this section of code
-  ##         and place it there in a new function only visible by pathdef() and
-  ##         savepath().
-  beginstring = "## Begin savepath auto-created section, do not edit";
-  endstring   = "## End savepath auto-created section";
-
-  if (nargin == 0)
-    savefile = tilde_expand ("~/.octaverc");
-  endif
 
-  ## Parse the file if it exists to see if we should replace a section
-  ## or create a section.
-  startline = endline = 0;
-  filelines = {};
-  if (exist (savefile) == 2)
-    [fid, msg] = fopen (savefile, "rt");
-    if (fid < 0)
-      error ("__extractpath__: could not open savefile, %s: %s", savefile, msg);
-    endif
-    unwind_protect
-      linenum = 0;
-      while (ischar (line = fgetl (fid)))
-        filelines{++linenum} = line;
-        ## find the first and last lines if they exist in the file
-        if (strcmp (line, beginstring))
-          startline = linenum;
-        elseif (strcmp (line, endstring))
-          endline = linenum;
-        endif
-      endwhile
-    unwind_protect_cleanup
-      closeread = fclose (fid);
-      if (closeread < 0)
-        error ("__extractpath__: could not close savefile after reading, %s",
-               savefile);
-      endif
-    end_unwind_protect
-  endif
+## FIXME: Need some better BIST tests
+%!assert (ischar (pathdef ()))
 
-  ## Extract the path specifiation.
-  if (startline > endline || (startline > 0 && endline == 0))
-    error ("__extractpath__: unable to parse file, %s", savefile);
-  elseif (startline > 0)
-    ## Undo doubling of single quote characters performed by savepath.
-    specifiedpath = strrep (regexprep (cstrcat (filelines(startline:endline){:}),
-                                       " *path *\\('(.*)'\\); *", "$1"),
-                            "''", "'");
-  else
-    specifiedpath = "";
-  endif
-
-endfunction
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/path/private/getsavepath.m	Thu Nov 20 08:08:35 2014 -0800
@@ -0,0 +1,53 @@
+## Copyright (C) 2014 Rik Wehbring
+##
+## 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 3 of the License, 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, see
+## <http://www.gnu.org/licenses/>.
+
+function [filelines, startline, endline] = getsavepath (file)
+
+  beginstring = "## Begin savepath auto-created section, do not edit";
+  endstring   = "## End savepath auto-created section";
+
+  ## Read in the file while checking for errors along the way.
+  startline = endline = 0;
+  filelines = {};
+  if (exist (file) == 2)
+    [fid, msg] = fopen (file, "rt");
+    if (fid < 0)
+      error ("getsavepath: could not open file, %s: %s", file, msg);
+    endif
+    linenum = 0;
+    while (ischar (line = fgetl (fid)))
+      filelines{++linenum} = line;
+      ## Find the first and last lines if they exist in the file.
+      if (strcmp (line, beginstring))
+        startline = linenum;
+      elseif (strcmp (line, endstring))
+        endline = linenum;
+      endif
+    endwhile
+    if (fclose (fid) < 0)
+      error ("getsavepath: could not close file after reading, %s", file);
+    endif
+  endif
+
+  ## Verify the file was correctly formatted. 
+  if (startline > endline || (startline > 0 && endline == 0))
+    error ("getsavepath: unable to parse file, %s", file);
+  endif
+
+endfunction
+
--- a/scripts/path/savepath.m	Tue Nov 18 12:13:06 2014 +0000
+++ b/scripts/path/savepath.m	Thu Nov 20 08:08:35 2014 -0800
@@ -22,8 +22,20 @@
 ## @deftypefnx {Function File} {@var{status} =} savepath (@dots{})
 ## Save the unique portion of the current function search path that is
 ## not set during Octave's initialization process to @var{file}.
-## If @var{file} is omitted, @file{~/.octaverc} is used.  If successful,
-## @code{savepath} returns 0.
+##
+## If @var{file} is omitted, Octave looks in the current directory for a
+## project-specific @file{.octaverc} file in which to save the path
+## information.  If no such file is present then the user's configuration file
+## @file{~/.octaverc} is used.
+##
+## If successful, @code{savepath} returns 0.
+##
+## The @code{savepath} function makes it simple to customize a user's
+## configuration file to restore the working paths necessary for a particular
+## instance of Octave.  Assuming no filename is specified, Octave will
+## automatically restore the saved directory paths from the appropriate
+## @file{.octaverc} file when starting up.  If a filename has been specified
+## then the paths may be restored manually by calling @code{source @var{file}}.
 ## @seealso{path, addpath, rmpath, genpath, pathdef}
 ## @end deftypefn
 
@@ -31,54 +43,27 @@
 
 function retval = savepath (file)
 
-  ret = 1;
-
   beginstring = "## Begin savepath auto-created section, do not edit";
   endstring   = "## End savepath auto-created section";
 
+  ## Use project-specific or user's .octaverc when no file specified 
   if (nargin == 0)
-    file = fullfile ("~", ".octaverc");
+    file = fullfile (pwd, ".octaverc");
+    if (! exist (file, "file"))
+      file = fullfile ("~", ".octaverc");
+    endif
   endif
 
-  ## parse the file if it exists to see if we should replace an
-  ## existing section or create a new section
-  startline = endline = 0;
-  filelines = {};
-  if (exist (file) == 2)
-    [fid, msg] = fopen (file, "rt");
-    if (fid < 0)
-      error ("savepath: could not open file, %s: %s", file, msg);
-    endif
-    unwind_protect
-      linenum = 0;
-      while (ischar (line = fgetl (fid)))
-        filelines{++linenum} = line;
-        ## find the first and last lines if they exist in the file
-        if (strcmp (line, beginstring))
-          startline = linenum;
-        elseif (strcmp (line, endstring))
-          endline = linenum;
-        endif
-      endwhile
-    unwind_protect_cleanup
-      closeread = fclose (fid);
-      if (closeread < 0)
-        error ("savepath: could not close file after reading, %s", file);
-      endif
-    end_unwind_protect
-  endif
+  ## Read in the file
+  [filelines, startline, endline] = getsavepath (file);
 
-  if (startline > endline || (startline > 0 && endline == 0))
-    error ("savepath: unable to parse file, %s", file);
-  endif
-
-  ## put the current savepath lines into the file
+  ## Determine where the savepath lines are placed in the file.
   if (isempty (filelines)
       || (startline == 1 && endline == length (filelines)))
-    ## savepath is the entire file
+    ## savepath is the entire file.
     pre = post = {};
   elseif (endline == 0)
-    ## drop the savepath statements at the end of the file
+    ## Drop the savepath statements at the end of the file.
     pre = filelines;
     post = {};
   elseif (startline == 1)
@@ -88,50 +73,51 @@
     pre = filelines(1:startline-1);
     post = {};
   else
-    ## insert in the middle
+    ## Insert in the middle.
     pre = filelines(1:startline-1);
     post = filelines(endline+1:end);
   endif
 
-  ## write the results
+  ## Write the results.
   [fid, msg] = fopen (file, "wt");
   if (fid < 0)
     error ("savepath: unable to open file for writing, %s, %s", file, msg);
   endif
   unwind_protect
-    for i = 1:length (pre)
-      fprintf (fid, "%s\n", pre{i});
-    endfor
+    fprintf (fid, "%s\n", pre{:});
 
     ## Remove the portion of the path defined via the command line
     ## and/or the environment.
     workingpath = parsepath (path);
-    command_line_path = parsepath (command_line_path ());
+    cmd_line_path = parsepath (command_line_path ());
     octave_path = parsepath (getenv ("OCTAVE_PATH"));
-    pathdef = pathdef ();
-    if (isempty (pathdef))
-      ## This occurs when running octave via run-octave. In this instance
+    default_path = pathdef ();
+    if (isempty (default_path))
+      ## This occurs when running octave via run-octave.  In this instance
       ## the entire path is specified via the command line and pathdef()
       ## is empty.
       [~, n] = setdiff (workingpath, octave_path);
-      default_path = command_line_path;
+      default_path = cmd_line_path;
     else
-      [~, n] = setdiff (workingpath, union (command_line_path, octave_path));
-      default_path = parsepath (pathdef);
+      [~, n] = setdiff (workingpath, union (cmd_line_path, octave_path));
+      default_path = parsepath (default_path);
     endif
     ## This is the path we'd like to preserve when octave is run.
-    path_to_preserve = workingpath (sort (n));
+    path_to_preserve = workingpath(sort (n));
 
-    ## Determine the path to Octave's user and sytem wide pkgs.
+    ## Determine the path to Octave's user and system wide packages.
     [pkg_user, pkg_system] = pkg ("list");
-    pkg_user_path = cell (1, numel (pkg_user));
-    pkg_system_path = cell (1, numel (pkg_system));
-    for n = 1:numel (pkg_user)
-      pkg_user_path{n} = pkg_user{n}.archprefix;
-    endfor
-    for n = 1:numel (pkg_system)
-      pkg_system_path{n} = pkg_system{n}.archprefix;
-    endfor
+    ## Conversion from cell array of structs to cellstr of archprefixes 
+    if (isempty (pkg_user))
+      pkg_user_path = {};
+    else
+      pkg_user_path = {[pkg_user{:}].archprefix};
+    endif
+    if (isempty (pkg_system))
+      pkg_system_path = {};
+    else
+      pkg_system_path = {[pkg_system{:}].archprefix};
+    endif
     pkg_path = union (pkg_user_path, pkg_system_path);
 
     ## Rely on Octave's initialization to include the pkg path elements.
@@ -140,16 +126,16 @@
       path_to_preserve = path_to_preserve(sort (n));
     endif
 
-    ## Split the path to be saved into two groups. Those path elements that
+    ## Split the path to be saved into two groups.  Those path elements that
     ## belong at the beginning and those at the end.
     if (! isempty (default_path))
       n1 = find (strcmp (default_path{1}, path_to_preserve));
       n2 = find (strcmp (default_path{end}, path_to_preserve));
-      n_middle = round (0.5*(n1+n2));
+      n_middle = round ((n1+n2)/2);
       [~, n] = setdiff (path_to_preserve, default_path);
       path_to_save = path_to_preserve(sort (n));
       ## Remove pwd
-      path_to_save = path_to_save(! strcmp (path_to_save, ["." pathsep]));
+      path_to_save(strcmp (path_to_save, ["." pathsep])) = [];
       n = ones (size (path_to_save));
       for m = 1:numel (path_to_save)
         n(m) = find (strcmp (path_to_save{m}, path_to_preserve));
@@ -179,28 +165,57 @@
     endif
     fprintf (fid, "%s\n", endstring);
 
-    for i = 1:length (post)
-      fprintf (fid, "%s\n", post{i});
-    endfor
+    fprintf (fid, "%s\n", post{:});
   unwind_protect_cleanup
-    closeread = fclose (fid);
-    if (closeread < 0)
+    status = fclose (fid);
+    if (status < 0)
       error ("savepath: could not close savefile after writing, %s", file);
     elseif (nargin == 0)
+      warning ("off", "backtrace", "local");
       warning ("savepath: current path saved to %s", file);
     endif
   end_unwind_protect
 
-  ret = 0;
-
   if (nargout > 0)
-    retval = ret;
+    retval = 0;
   endif
 
 endfunction
 
+## Convert single string of paths to cell array of paths
 function path_elements = parsepath (p)
-  pat = sprintf ('([^%s]+[%s$])', pathsep, pathsep);
-  path_elements = regexpi (strcat (p, pathsep), pat, "match");
+  path_elements = strcat (ostrsplit (p, pathsep), pathsep);
 endfunction
 
+
+%!test
+%! fname = tempname ();
+%! status = savepath (fname);
+%! assert (status == 0);
+%! old_dir = pwd;
+%! unwind_protect
+%!   cd (P_tmpdir);
+%!   if (exist (fullfile (pwd, ".octaverc")))
+%!     unlink (".octaverc");
+%!   endif
+%!   ## Create blank .octaverc file
+%!   fid = fopen (".octaverc", "wt"); 
+%!   assert (fid >= 0);
+%!   fclose (fid);
+%!   ## Save path into local .octaverc file
+%!   status = savepath ();
+%!   assert (status == 0);
+%!   ## Compare old and new versions
+%!   fid = fopen (fname, "rb");
+%!   assert (fid >= 0);
+%!   orig_data = fread (fid);
+%!   fclose (fid);
+%!   fid = fopen (".octaverc", "rb");
+%!   assert (fid >= 0);
+%!   new_data = fread (fid);
+%!   fclose (fid);
+%!   assert (orig_data, new_data);
+%! unwind_protect_cleanup
+%!   cd (old_dir);
+%! end_unwind_protect 
+