changeset 8575:f134925a1cfa

m-file implementation of help system
author Soren Hauberg <soren@hauberg.org>
date Thu, 22 Jan 2009 18:22:52 -0500
parents 83b8c739d626
children 6f0e539b3fc3
files scripts/ChangeLog scripts/Makefile.in scripts/configure.in scripts/help/Makefile.in scripts/help/__additional_help_message__.m scripts/help/__strip_html_tags__.m scripts/help/doc.m scripts/help/gen_doc_cache.m scripts/help/get_first_help_sentence.m scripts/help/help.m scripts/help/lookfor.m scripts/help/makeinfo.m scripts/help/print_usage.m scripts/help/type.m scripts/help/which.m scripts/miscellaneous/Makefile.in scripts/miscellaneous/doc.m scripts/pkg/pkg.m src/ChangeLog src/defun-int.h src/defun.cc src/help.cc src/help.h
diffstat 23 files changed, 1761 insertions(+), 1778 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/ChangeLog	Thu Jan 22 13:59:33 2009 -0500
+++ b/scripts/ChangeLog	Thu Jan 22 18:22:52 2009 -0500
@@ -1,3 +1,27 @@
+2009-01-22  John W. Eaton  <jwe@octave.org>
+
+	* help/which.m: New function.
+	* help/Makefile.in (SOURCES): Add it to the list.
+
+	* help/help.m: Also display location of the file before the help text.
+	* help/print_usage: Also display additional help text.
+	* help/__additional_help_message__.m: Return message instead of
+	displaying it.
+
+2009-01-22  Søren Hauberg  <hauberg@gmail.com>
+
+	* help: New directory.
+	* configure.in (AC_CONFIG_FILES): Add help/Makefile to the list.
+	* Makefile.in (SUBDIRS): Add it to the list.
+	* help/__additional_help_message__.m, help/__strip_html_tags__.m,
+	help/gen_doc_cache.m, help/get_first_help_sentence.m, help/help.m,
+	help/lookfor.m, help/makeinfo.m, help/print_usage.m, help/type.m:
+	New functions.
+	* help/Makefile.in (SOURCES): Add them to the list.
+	* help/doc.m: Move here from miscellaneous/doc.m.
+	* miscellaneous/Makefile.in (SOURCES): Remove doc.m from the list.
+	* miscellaneous/pkg.m: Generate documentation cache during install.
+
 2009-01-22  Jaroslav Hajek  <highegg@gmail.com>
 
 	* optimization/fsolve.m: Undo the last change.
--- a/scripts/Makefile.in	Thu Jan 22 13:59:33 2009 -0500
+++ b/scripts/Makefile.in	Thu Jan 22 18:22:52 2009 -0500
@@ -42,7 +42,7 @@
 	configure.in configure mkinstalldirs mkdoc mkpkgadd gethelp.cc \
 	skip-autoheader move-if-change) DOCSTRINGS
 
-SUBDIRS = audio deprecated elfun general geometry image io \
+SUBDIRS = audio deprecated elfun general geometry help image io \
 	linear-algebra miscellaneous optimization path pkg plot polynomial \
 	set signal sparse specfun special-matrix startup \
 	statistics strings testfun time
--- a/scripts/configure.in	Thu Jan 22 13:59:33 2009 -0500
+++ b/scripts/configure.in	Thu Jan 22 18:22:52 2009 -0500
@@ -29,7 +29,7 @@
 AC_PROG_INSTALL
 
 AC_CONFIG_FILES([Makefile audio/Makefile deprecated/Makefile elfun/Makefile \
-	  general/Makefile geometry/Makefile image/Makefile \
+	  general/Makefile geometry/Makefile help/Makefile image/Makefile \
 	  io/Makefile linear-algebra/Makefile miscellaneous/Makefile \
 	  optimization/Makefile path/Makefile pkg/Makefile plot/Makefile \
 	  polynomial/Makefile set/Makefile \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/help/Makefile.in	Thu Jan 22 18:22:52 2009 -0500
@@ -0,0 +1,94 @@
+# Makefile for octave's scripts/help directory
+#
+# Copyright (C) 1995, 1996, 1997, 2002, 2005, 2006, 2007 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 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/>.
+
+TOPDIR = ../..
+
+script_sub_dir = help
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+
+include $(TOPDIR)/Makeconf
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+
+SOURCES = \
+  __additional_help_message__.m \
+  __strip_html_tags__.m \
+  doc.m \
+  gen_doc_cache.m \
+  get_first_help_sentence.m \
+  help.m \
+  lookfor.m \
+  makeinfo.m \
+  print_usage.m \
+  type.m \
+  which.m
+
+DISTFILES = $(addprefix $(srcdir)/, Makefile.in $(SOURCES))
+
+FCN_FILES = $(addprefix $(srcdir)/, $(SOURCES))
+FCN_FILES_NO_DIR = $(notdir $(FCN_FILES))
+
+all: PKG_ADD
+.PHONY: all
+
+install install-strip:
+	$(do-script-install)
+.PHONY: install install-strip
+
+uninstall:
+	$(do-script-uninstall)
+.PHONY: uninstall
+
+clean:
+.PHONY: clean
+
+PKG_ADD: $(FCN_FILES)
+	@echo "making PKG_ADD"
+	@$(do-mkpkgadd)
+
+tags: $(SOURCES)
+	ctags $(SOURCES)
+
+TAGS: $(SOURCES)
+	etags $(SOURCES)
+
+mostlyclean: clean
+.PHONY: mostlyclean
+
+distclean: clean
+	rm -f Makefile PKG_ADD
+.PHONY: distclean
+
+maintainer-clean: distclean
+	rm -f tags TAGS
+.PHONY: maintainer-clean
+
+dist:
+	ln $(DISTFILES) ../../`cat ../../.fname`/scripts/help
+.PHONY: dist
+
+check-m-sources:
+	@$(do-check-m-sources)
+.PHONY: check-m-sources
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/help/__additional_help_message__.m	Thu Jan 22 18:22:52 2009 -0500
@@ -0,0 +1,40 @@
+## Copyright (C) 2009 Søren Hauberg
+##
+## This program is free software; you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or (at
+## your option) any later version.
+##
+## This program 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 this program; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} __additional_help_message__ ()
+## Return additional help message.
+##
+## This is an internal function and should not be used directly.
+## @seealso{suppress_verbose_help_message}
+## @end deftypefn
+
+function msg = __additional_help_message__ ()
+
+  if (suppress_verbose_help_message ())
+    msg = "";
+  else
+    msg = "\
+Additional help for built-in functions and operators is\n\
+available in the on-line version of the manual.  Use the command\n\
+`doc <topic>' to search the manual index.\n\
+\n\
+Help and information about Octave is also available on the WWW\n\
+at http://www.octave.org and via the help@octave.org\n\
+mailing list.\n";
+  endif
+
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/help/__strip_html_tags__.m	Thu Jan 22 18:22:52 2009 -0500
@@ -0,0 +1,80 @@
+## Copyright (C) 2009 Søren Hauberg
+##
+## This program is free software; you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or (at
+## your option) any later version.
+##
+## This program 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 this program; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {[@var{text}, @var{status}] =} __strip_html_tags__ (@var{html_text})
+## This function removes html tags from a text. This is used as a simple
+## html-to-text function.
+##
+## This is an internal function and should not be used directly.
+## @end deftypefn
+
+function [text, status] = __strip_html_tags__ (html_text)
+  start = find (html_text == "<");
+  stop  = find (html_text == ">");
+  if (length (start) == length (stop))
+    text = html_text;
+    for n = length(start):-1:1
+      text (start (n):stop (n)) = [];
+    endfor
+    text = strip_superfluous_endlines (text);
+    status = 0;
+  else
+    warning ("help: invalid HTML data");
+    warning ("Raw HTML source follows...");
+    disp (html_text);
+    text = "";
+    status = 1;
+  endif
+endfunction
+
+## This function removes end-lines (\n) that makes printing look bad
+function text = strip_superfluous_endlines (text)
+  ## Find groups of end-lines
+  els = find (text == "\n");
+  dels = diff (els);
+  groups = [els(1), 1]; # list containing [start, length] of each group
+  for k = 1:length (dels)
+    if (dels (k) == 1)
+      groups (end, 2) ++;
+    else
+      groups (end+1, 1:2) = [els(k+1), 1];
+    endif
+  endfor
+
+  keep = true (size (text));
+
+  ## Remove end-lines in the beginning
+  if (groups (1, 1) == 1)
+    keep (1:groups (1, 2)) = false;
+  endif
+  
+  ## Remove end-lines from the end
+  if (sum (groups (end, :)) - 1 == length (text))
+    keep (groups (end, 1):end) = false;
+  endif
+  
+  ## Remove groups of end-lines with more than 3 end-lines next to each other
+  idx = find (groups (:, 2) >= 3);
+  for k = 1:length (idx)
+    start = groups (idx (k), 1);
+    stop = start + groups (idx (k), 2) - 1;
+    keep (start+2:stop) = false;
+  endfor
+  
+  ## Actually remove the elements
+  text = text (keep);
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/help/doc.m	Thu Jan 22 18:22:52 2009 -0500
@@ -0,0 +1,113 @@
+## Copyright (C) 2005, 2006, 2007, 2009 Søren Hauberg
+## 
+## 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Command} doc @var{function_name}
+## Display documentation for the function @var{function_name}
+## directly from an on-line version of
+## the printed manual, using the GNU Info browser.  If invoked without
+## any arguments, the manual is shown from the beginning.
+##
+## For example, the command @kbd{doc rand} starts the GNU Info browser
+## at this node in the on-line version of the manual.
+##
+## Once the GNU Info browser is running, help for using it is available
+## using the command @kbd{C-h}.
+## @seealso{help}
+## @end deftypefn
+
+## Author: Soren Hauberg <soren@hauberg.org>
+## Adapted-by: jwe
+
+## PKG_ADD: mark_as_command doc
+
+function retval = doc (fname)
+
+  if (nargin == 0 || nargin == 1)
+
+    ftype = 0;
+
+    if (nargin == 1)
+      ## Get the directory where the function lives.
+      ## FIXME -- maybe we should have a better way of doing this.
+
+      if (ischar (fname))
+	ftype = exist (fname);
+      else
+	error ("doc: expecting argument to be a character string");
+      endif
+    else
+      fname = "";
+    endif
+
+    if (ftype == 2 || ftype == 3)
+      ffile = which (fname);
+    else
+      ffile = "";
+    endif
+
+    if (isempty (ffile))
+      info_dir = octave_config_info ("infodir");
+    else
+      info_dir = fileparts (ffile);
+    endif
+
+    ## Determine if a file called doc.info exist in the same 
+    ## directory as the function.
+
+    info_file_name = fullfile (info_dir, "doc.info");
+
+    [stat_info, err] = stat (info_file_name);
+
+    if (err < 0)
+      info_file_name = info_file ();
+    endif
+
+    ## FIXME -- don't change the order of the arguments below because
+    ## the info-emacs-info script currently expects --directory DIR as
+    ## the third and fourth arguments.  Someone should fix that.
+
+    cmd = sprintf ("\"%s\" --file \"%s\" --directory \"%s\"",
+		   info_program (), info_file_name, info_dir);
+
+    have_fname = ! isempty (fname);
+
+    if (have_fname)
+      status = system (sprintf ("%s --index-search %s", cmd, fname));
+    endif
+
+    if (! (have_fname && status == 0))
+      status = system (cmd);
+      if (status == 127)
+	warning ("unable to find info program `%s'", info_program ());
+      endif
+    endif
+
+    if (nargout > 0)
+      retval = status;
+    endif
+
+  else
+    print_usage ();
+  endif
+
+endfunction
+
+%!test if exist( info_file ()) != 2
+%!       error ("Info file %s does not exist!", info_file ());
+%!     endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/help/gen_doc_cache.m	Thu Jan 22 18:22:52 2009 -0500
@@ -0,0 +1,151 @@
+## Copyright (C) 2009 Søren Hauberg
+##
+## This program is free software; you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or (at
+## your option) any later version.
+##
+## This program 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 this program; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} gen_doc_cache ()
+## @deftypefnx{Function File} gen_doc_cache (@var{directory})
+## Generate documentation caches for all functions in a given directory.
+##
+## A documentation cache is generated for all functions in @var{directory}. The
+## resulting cache is saved in the file @code{help_cache.mat} in @var{directory}.
+## The cache is used to speed up @code{lookfor}.
+## If no directory is given, all directories in the current path is traversed.
+##
+## @seealso{lookfor, path}
+## @end deftypefn
+
+function gen_doc_cache (p = path ())
+  if (!ischar (p))
+    print_usage ();
+  endif
+  
+  ## Generate caches for all directories in path
+  idx = find (p == pathsep ());
+  prev_idx = 1;
+  for n = 1:length (idx)
+    f = p (prev_idx:idx (n)-1);
+    gen_doc_cache_in_dir (f);
+    prev_idx = idx (n) + 1;
+  endfor
+    
+  ## Generate cache for keywords, operators, and builtins if we're handling the
+  ## entire path
+  if (nargin == 0)
+    gen_builtin_cache ();
+  endif
+endfunction
+
+function [text, first_sentence, status] = handle_function (f, text, format)
+  first_sentence = "";
+  ## Skip functions that start with __ as these shouldn't be searched by lookfor
+  if (length (f) > 2 && all (f (1:2) == "_"))
+    status = 1;
+    return;
+  endif
+
+  ## Take action depending on help text format
+  switch (lower (format))
+    case "plain text"
+      status = 0;
+    case "texinfo"
+      [text, status] = makeinfo (text, "plain text");
+    case "html"
+      [text, status] = strip_html_tags (text);
+    otherwise
+      status = 1;
+  endswitch
+    
+  ## Did we get the help text?
+  if (status != 0 || isempty (text))
+    warning ("gen_doc_cache: unusable help text in '%s'. Ignoring function.", f);
+    return;
+  endif
+
+  ## Get first sentence of help text
+  first_sentence = get_first_help_sentence (f);
+endfunction
+
+function cache = create_cache (list)
+  cache = {};
+  
+  ## For each function:
+  for n = 1:length (list)
+    f = list {n};
+    
+    ## Get help text
+    [text, format] = get_help_text (f);
+    
+    [text, first_sentence, status] = handle_function (f, text, format);
+
+    ## Did we get the help text?
+    if (status != 0)
+      continue;
+    endif
+    
+    ## Store the help text
+    cache (1, end+1) = f;
+    cache (2, end) = text;
+    cache (3, end) = first_sentence;
+  endfor
+endfunction
+
+function gen_doc_cache_in_dir (directory)
+  ## If 'directory' is not in the current path, add it so we search it
+  dir_in_path = false;
+  p = path ();
+  idx = find (p == pathsep ());
+  prev_idx = 1;
+  for n = 1:length (idx)
+    f = p (prev_idx:idx (n)-1);
+    if (strcmp (f, directory))
+      dir_in_path = true;
+      break;
+    endif
+    prev_idx = idx (n) + 1;
+  endfor
+  
+  if (!dir_in_path)
+    addpath (directory);
+  endif
+
+  ## Get list of functions in directory and create cache
+  list = __list_functions__ (directory);
+  cache = create_cache (list);
+  
+  ## Write the cache
+  fn = fullfile (directory, "help_cache.mat");
+  save ("-binary", fn, "cache"); # FIXME: Should we zip it ?
+  
+  if (!dir_in_path)
+    rmpath (directory);
+  endif
+endfunction
+
+function gen_builtin_cache ()
+  operators = __operators__ ();
+  keywords = __keywords__ ();
+  builtins = __builtins__ ();
+  list = {operators{:}, keywords{:}, builtins{:}};
+
+  cache = create_cache (list);
+  
+  ## Write the cache
+  ## FIXME: Where should we store this cache?
+  ## FIXME: if we change it -- update 'lookfor'
+  fn = fullfile (octave_config_info.datadir, "builtin_cache.mat"); 
+  save ("-binary", fn, "cache"); # FIXME: Should we zip it ?
+endfunction
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/help/get_first_help_sentence.m	Thu Jan 22 18:22:52 2009 -0500
@@ -0,0 +1,149 @@
+## Copyright (C) 2009 Søren Hauberg
+##
+## This program is free software; you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or (at
+## your option) any later version.
+##
+## This program 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 this program; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {[@var{retval}, @var{status}] =} get_first_help_sentence (@var{name}, @var{max_len})
+## Return the first sentence of a function help text.
+##
+## The function reads the first sentence of the help text of the function
+## @var{name}. The first sentence is defined as the text after the function
+## declaration until either the first period (".") or the first appearance of
+## two consecutive end-lines ("\n\n"). The text is truncated to a maximum length
+## of @var{max_len}, which defaults to 80.
+##
+## The optional output argument @var{status} returns the status reported by
+## @code{makeinfo}. If only one output argument is requested, and @var{status}
+## is non-zero, a warning is displayed.
+##
+## As an example, the first sentence of this help text is
+##
+## @example
+## get_first_help_sentence ("get_first_help_sentence")
+## @print{} ans = Return the first sentence of a function help text.
+## @end example
+## @end deftypefn
+
+function [retval, status] = get_first_help_sentence (name, max_len = 80)
+  ## Check input
+  if (nargin == 0)
+    error ("get_first_help_sentence: not enough input arguments");
+  endif
+  
+  if (!ischar (name))
+    error ("get_first_help_sentence: first input must be a string");
+  endif
+  
+  if (!isnumeric (max_len) || max_len <= 0 || max_len != round (max_len))
+    error ("get_first_help_sentence: second input must be positive integer");
+  endif
+
+  ## First, we get the raw help text
+  [help_text, format] = get_help_text (name);
+  
+  ## Then, we take action depending on the format
+  switch (lower (format))
+    case "plain text"
+      [retval, status] = first_sentence_plain_text (help_text, max_len);
+    case "texinfo"
+      [retval, status] = first_sentence_texinfo (help_text, max_len);
+    case "html"
+      [retval, status] = first_sentence_html (help_text, max_len);
+    case "not found"
+      error ("get_first_help_sentence: `%s' not found\n", name);
+    otherwise
+      error ("get_first_help_sentence: internal error: unsupported help text format: '%s'\n", format);
+  endswitch
+
+  if (nargout <= 1 && status != 0)
+    warning ("get_first_help_sentence: couldn't run makeinfo on '%s'", name);
+  endif
+endfunction
+
+## This function extracts the first sentence from a plain text help text
+function [retval, status] = first_sentence_plain_text (help_text, max_len)
+  ## Extract first line by searching for a period or a double line-end.
+  period_idx = find (help_text == ".", 1);
+  line_end_idx = strfind (help_text, "\n\n");
+  retval = help_text (1:min ([period_idx(:); line_end_idx(:); max_len; length(help_text)]));
+  status = 0;
+endfunction
+
+## This function extracts the first sentence from a Texinfo help text.
+## The function works by removing @def* from the texinfo text. After this, we
+## render the text to plain text using makeinfo, and then extract the first line.
+function [retval, status] = first_sentence_texinfo (help_text, max_len)
+  ## Lines ending with "@\n" are continuation lines, so they should be concatenated
+  ## with the following line.
+  help_text = strrep (help_text, "@\n", " ");
+  
+  ## Find, and remove, lines that start with @def. This should remove things
+  ## such as @deftypefn, @deftypefnx, @defvar, etc.
+  keep = true (size (help_text));
+  def_idx = strfind (help_text, "@def");
+  if (!isempty (def_idx))
+    endl_idx = find (help_text == "\n");
+    for k = 1:length (def_idx)
+      endl = endl_idx (find (endl_idx > def_idx (k), 1));
+      if (isempty (endl))
+        keep (def_idx (k):end) = false;
+      else
+        keep (def_idx (k):endl) = false;
+      endif
+    endfor
+  
+    ## Remove the @end ... that corresponds to the @def we removed above
+    def1 = def_idx (1);
+    space_idx = find (help_text == " ");
+    space_idx = space_idx (find (space_idx > def1, 1));
+    bracket_idx = find (help_text == "{" | help_text == "}");
+    bracket_idx = bracket_idx (find (bracket_idx > def1, 1));
+    if (isempty (space_idx) && isempty (bracket_idx))
+      error ("get_first_help_sentence: couldn't parse texinfo");
+    endif
+    sep_idx = min (space_idx, bracket_idx);
+    def_type = help_text (def1+1:sep_idx-1);
+
+    end_idx = strfind (help_text, sprintf ("@end %s", def_type));
+    if (isempty (end_idx))
+      error ("get_first_help_sentence: couldn't parse texinfo");
+    endif
+    endl = endl_idx (find (endl_idx > end_idx, 1));
+    if (isempty (endl))
+      keep (end_idx:end) = false;
+    else
+      keep (end_idx:endl) = false;
+    endif
+    
+    help_text = help_text (keep);
+  endif
+  
+  ## Run makeinfo to generate plain text
+  [help_text, status] = makeinfo (help_text, "plain text");
+  
+  ## Extract first line with plain text method.
+  retval = first_sentence_plain_text (help_text, max_len);
+endfunction
+
+## This function extracts the first sentence from a html help text.
+## The function simply removes the tags and treats the text as plain text.
+function [retval, status] = first_sentence_html (help_text, max_len)
+  ## Strip tags
+  [help_text, status] = strip_html_tags (help_text);
+  
+  ## Extract first line with plain text method.
+  retval = first_sentence_plain_text (help_text, max_len);
+endfunction
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/help/help.m	Thu Jan 22 18:22:52 2009 -0500
@@ -0,0 +1,82 @@
+## Copyright (C) 2009 Søren Hauberg
+##
+## This program is free software; you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or (at
+## your option) any later version.
+##
+## This program 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 this program; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn {Command} help @var{name}
+## Display the help text for @var{name}.
+## If invoked without any arguments, @code{help} prints a list
+## of all the available operators and functions.
+## 
+## For example, the command @kbd{help help} prints a short message
+## describing the @code{help} command.
+## 
+## The help command can give you information about operators, but not the
+## comma and semicolons that are used as command separators.  To get help
+## for those, you must type @kbd{help comma} or @kbd{help semicolon}.
+## @seealso{doc, which, lookfor}
+## @end deftypefn
+
+## PKG_ADD: mark_as_command help
+
+function help (name)
+  if (nargin == 0)
+    disp ("Help is available for the topics listed below.");
+    disp ("");
+    
+    disp ("*** operators:");
+    operators = __operators__ ();
+    disp (list_in_columns (operators (:, 1)));
+
+    disp ("*** reserved words:");
+    keywords = __keywords__ ();
+    disp (list_in_columns (keywords (:, 1)));
+
+    disp ("*** available functions:");
+    functions = __list_functions__ ();
+    disp (list_in_columns (functions));
+    
+  elseif (nargin == 1 && ischar (name))
+    ## Get help text
+    [text, format] = get_help_text (name);
+    
+    ## Take action depending on help text format
+    switch (lower (format))
+      case "plain text"
+        status = 0;
+      case "texinfo"
+        [text, status] = makeinfo (text, "plain text");
+      case "html"
+        [text, status] = strip_html_tags (text);
+      case "not found"
+        error ("help: `%s' not found\n", name);
+      otherwise
+        error ("help: internal error: unsupported help text format: '%s'\n", format);
+    endswitch
+    
+    ## Print text
+    if (status != 0)
+      warning ("makeinfo: Texinfo formatting filter exited abnormally");
+      warning ("makeinfo: raw Texinfo source of help text follows...\n");
+    endif
+
+    which (name);
+    printf ("\n%s\n%s", text, __additional_help_message__ ());
+    
+  else
+    error ("help: invalid input\n");
+  endif
+endfunction
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/help/lookfor.m	Thu Jan 22 18:22:52 2009 -0500
@@ -0,0 +1,162 @@
+## Copyright (C) 2009 Søren Hauberg
+##
+## This program is free software; you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or (at
+## your option) any later version.
+##
+## This program 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 this program; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn {Command} lookfor @var{str}
+## @deftypefnx {Command} lookfor -all @var{str}
+## @deftypefnx {Function} {[@var{fun}, @var{helpstring}] = } lookfor (@var{str})
+## @deftypefnx {Function} {[@var{fun}, @var{helpstring}] = } lookfor ('-all', @var{str})
+## Search for the string @var{str} in all of the functions found in the
+## function search path.  By default @code{lookfor} searches for @var{str}
+## in the first sentence of the help string of each function found. The entire
+## help string of each function found in the path can be searched if
+## the '-all' argument is supplied. All searches are case insensitive.
+## 
+## Called with no output arguments, @code{lookfor} prints the list of matching
+## functions to the terminal. Otherwise the output arguments @var{fun} and
+## @var{helpstring} define the matching functions and the first sentence of
+## each of their help strings.
+## 
+## Note that the ability of @code{lookfor} to correctly identify the first
+## sentence of the help of the functions is dependent on the format of the
+## functions help. All of the functions in Octave itself will correctly
+## find the first sentence, but the same can not be guaranteed for other
+## functions. Therefore the use of the '-all' argument might be necessary
+## to find related functions that are not part of Octave.
+## @seealso{help, which}
+## @end deftypefn
+
+## PKG_ADD: mark_as_command lookfor
+
+function [out_fun, out_help_text] = lookfor (str, extra)
+  if (strcmpi (str, "-all"))
+    ## The difference between using '-all' and not, is which part of the caches
+    ## we search. The cache is organised such that its first column contains
+    ## the function name, its second column contains the full help text, and its
+    ## third column contains the first sentence of the help text.
+    str = extra;
+    search_type = 2; # when using caches, search its second column
+  else
+    search_type = 3; # when using caches, search its third column
+  endif
+  str = lower (str);
+
+  ## Search operators, keywords, and built-ins
+  cache_file = fullfile (octave_config_info.datadir, "builtin_cache.mat");
+  if (exist (cache_file, "file"))
+    [fun, help_text] = search_cache (str, cache_file, search_type);
+  else
+    fun = help_text = {};
+  endif
+  
+  ## Search functions in path
+  p = path ();
+  idx = find (p == pathsep ());
+  prev_idx = 1;
+  for n = 1:length (idx)
+    f = p (prev_idx:idx (n)-1);
+    cache_file = fullfile (f, "help_cache.mat");
+    if (exist (cache_file, "file"))
+      ## We have a cache. Read it and search it!
+      [funs, hts] = search_cache (str, cache_file, search_type);
+      fun (end+1:end+length (funs)) = funs;
+      help_text (end+1:end+length (hts)) = hts;
+    else
+      ## We don't have a cache. Search files
+      funs_in_f = __list_functions__ (f);
+      for m = 1:length (funs_in_f)
+        fn = funs_in_f {m};
+        
+        ## Skip files that start with __
+        if (length (fn) > 2 && strcmp (fn (1:2), "__"))
+          continue;
+        endif
+        
+        ## Extract first sentence
+        try
+          first_sentence = get_first_help_sentence (fn);
+          status = 0;
+        catch
+          status = 1;
+        end_try_catch
+
+        if (search_type == 2) # search entire help text
+          [text, format] = get_help_text (fn);
+    
+          ## Take action depending on help text format
+          switch (lower (format))
+            case "plain text"
+              status = 0;
+            case "texinfo"
+              [text, status] = makeinfo (text, "plain text");
+            case "html"
+              [text, status] = strip_html_tags (text);
+            otherwise
+              status = 1;
+          endswitch
+
+        elseif (status == 0) # only search the first sentence of the help text
+          text = first_sentence;
+        endif
+        
+        ## Search the help text, if we can
+        if (status == 0 && !isempty (strfind (text, str)))
+          fun (end+1) = fn;
+          help_text (end+1) = first_sentence;
+        endif
+      endfor
+    endif
+    prev_idx = idx (n) + 1;
+  endfor
+  
+  if (nargout == 0)
+    ## Print the results (FIXME: improve this to make it look better.
+    indent = 20;
+    term_width = terminal_size() (2);
+    desc_width = term_width - indent - 2;
+    indent_space = repmat (" ", 1, indent);
+    for k = 1:length (fun)
+      f = fun {k};
+      f (end+1:indent) = " ";
+      printf (f);
+      desc = strtrim (strrep (help_text {k}, "\n", " "));
+      ldesc = length (desc);
+      printf ("%s\n", desc (1:min (desc_width, ldesc)));
+      for start = desc_width+1:desc_width:ldesc
+        stop = min (start + desc_width, ldesc);
+        printf ("%s%s\n", indent_space, strtrim (desc (start:stop)));
+      endfor
+    endfor
+
+  else
+    ## Return the results instead of displaying them
+    out_fun = fun;
+    out_help_text = help_text;
+  endif
+endfunction
+
+function [funs, help_texts] = search_cache (str, cache_file, search_type)
+  load (cache_file);
+  if (! isempty(cache))
+    tmp = strfind (cache (search_type, :), str);
+    cache_idx = find (!cellfun ("isempty", tmp));
+    funs = cache (1, cache_idx);
+    help_texts = cache (3, cache_idx);
+  else
+    funs = help_texts = {};
+  endif
+endfunction
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/help/makeinfo.m	Thu Jan 22 18:22:52 2009 -0500
@@ -0,0 +1,160 @@
+## Copyright (C) 2009 Søren Hauberg
+##
+## This program is free software; you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or (at
+## your option) any later version.
+##
+## This program 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 this program; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {[@var{retval}, @var{status}] =} makeinfo (@var{text}, @
+## @var{output_type})
+## @deftypefnx{Function File} {[@var{retval}, @var{status}] =} makeinfo (@var{text}, @
+## @var{output_type}, @var{see_also})
+## Run @code{makeinfo} on a given text.
+##
+## The string @var{text} is run through the @code{makeinfo} program to generate
+## output in various formats. This string must contain valid Texinfo formatted
+## text.
+##
+## The @var{output_type} selects the format of the output. This can be either
+## @t{"html"}, @t{"texinfo"}, or @t{"plain text"}. By default this is
+## @t{"plain text"}. If @var{output_type} is @t{"texinfo"}, the @t{@@seealso}
+## macro is expanded, but otherwise the text is unaltered.
+##
+## If the optional argument @var{see_also} is present, it is used to expand the
+## Octave specific @t{@@seealso} macro. This argument must be a function handle,
+## that accepts a cell array of strings as input argument (each elements of the
+## array corresponds to the arguments to the @t{@@seealso} macro), and return
+## the expanded string. If this argument is not given, the @t{@@seealso} macro
+## will be expanded to the text
+##
+## @example
+## See also: arg1, arg2@, ...
+## @end example
+##
+## @noindent
+## for @t{"plain text"} output, and
+##
+## @example
+## See also: @@ref@{arg1@}, @@ref@{arg2@}, ...
+## @end example
+##
+## @noindent
+## otherwise.
+##
+## The optional output argument @var{status} contains the exit status of the
+## @code{makeinfo} program as returned by @code{system}.
+## @end deftypefn
+
+function [retval, status] = makeinfo (text, output_type = "plain text", see_also = [])
+  ## Check input
+  if (nargin == 0)
+    print_usage ();
+  endif
+  
+  if (!ischar (text))
+    error ("makeinfo: first input argument must be a string");
+  endif
+  
+  if (!ischar (output_type))
+    error ("makeinfo: second input argument must be a string");
+  endif
+  
+  ## Define the @seealso macro
+  if (isempty (see_also))
+    if (strcmpi (output_type, "plain text"))
+      see_also = @simple_see_also;
+    else
+      see_also = @simple_see_also_with_refs;
+    endif
+  endif
+  
+  if (!isa (see_also, "function_handle"))
+    error ("makeinfo: third input argument must be the empty matrix, or a function handle");
+  endif
+  
+  ## It seems like makeinfo sometimes gets angry if the character on a line is
+  ## a space, so we remove these.
+  text = strrep (text, "\n ", "\n");
+  
+  ## Handle @seealso macro
+  SEE_ALSO = "@seealso";
+  start = strfind (text, SEE_ALSO);
+  if (!isempty (start))
+    if (start == 1 || (text (start-1) != "@"))
+      bracket_start = find (text (start:end) == "{", 1);
+      stop = find (text (start:end) == "}", 1);
+      if (!isempty (stop) && !isempty (bracket_start))
+        stop += start - 1;
+        bracket_start += start - 1;
+      else
+        bracket_start = start + length (SEE_ALSO);
+        stop = find (text (start:end) == "\n", 1);
+        if (isempty (stop))
+          stop = length (text);
+        else
+          stop += start - 1;
+        endif
+      endif
+      see_also_args = text (bracket_start+1:(stop-1));
+      see_also_args = strtrim (cellstr (split (see_also_args, ",")));
+      expanded = see_also (see_also_args);
+      text = strcat (text (1:start-1), expanded, text (stop+1:end));
+    endif
+  endif
+  
+  if (strcmpi (output_type, "texinfo"))
+    status = 0;
+    retval = text;
+    return;
+  endif
+  
+  ## Create the final TeXinfo input string
+  text = sprintf ("\\input texinfo\n\n%s\n\n@bye\n", text);
+  
+  unwind_protect
+    ## Write Texinfo to tmp file
+    [fid, name, msg] = mkstemp ("octave_help_XXXXXX", true);
+    fwrite (fid, text);
+    fclose (fid);
+
+    ## Take action depending on output type
+    switch (lower (output_type))
+      case "plain text"
+         cmd = sprintf ("%s --no-headers --no-warn --force --no-validate %s",
+                        makeinfo_program (), name);
+      case "html"
+         cmd = sprintf ("%s --no-headers --html --no-warn --no-validate --force %s",
+                        makeinfo_program (), name);
+      otherwise
+        error ("makeinfo: unsupported output type: '%s'", output_type);
+    endswitch
+  
+    ## Call makeinfo
+    [status, retval] = system (cmd);
+   
+  unwind_protect_cleanup
+    if (exist (name, "file"))
+      delete (name);
+    endif
+  end_unwind_protect
+endfunction
+
+function expanded = simple_see_also (args)
+  expanded = strcat ("\nSee also:", sprintf (" %s,", args {:}));
+  expanded = strcat (expanded (1:end-1), "\n\n");
+endfunction
+
+function expanded = simple_see_also_with_refs (args)
+  expanded = strcat ("\nSee also:", sprintf (" @ref{%s},", args {:}));
+  expanded = strcat (expanded (1:end-1), "\n\n");
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/help/print_usage.m	Thu Jan 22 18:22:52 2009 -0500
@@ -0,0 +1,112 @@
+## Copyright (C) 2009 Søren Hauberg
+##
+## This program is free software; you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or (at
+## your option) any later version.
+##
+## This program 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 this program; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {} print_usage ()
+## @deftypefnx{Function File} {} print_usage (@var{name})
+## Print the usage message for a function.  When called with no input arguments
+## the @code{print_usage} function displays the usage message of the currently
+## executing function.
+## @seealso{help}
+## @end deftypefn
+
+function print_usage (name)
+  ## Handle input
+  if (nargin == 0)
+    ## Determine the name of the calling function
+    x = dbstack ();
+    if (numel (x) > 1)
+      name = x (2).name;
+    else
+      error ("print_usage: invalid function\n");
+    endif
+  elseif (!ischar (name))
+    error ("print_usage: input argument must be a string");
+  endif
+  
+  ## Do the actual work
+  [text, format] = get_help_text (name);
+  max_len = 80;
+  switch (lower (format))
+    case "plain text"
+      [usage_string, status] = get_usage_plain_text (text, max_len);
+    case "texinfo"
+      [usage_string, status] = get_usage_texinfo (text, max_len);
+    case "html"
+      [usage_string, status] = get_usage_html (text, max_len);
+    case "not found"
+      error ("print_usage: `%s' not found\n", name);
+    otherwise
+      error ("print_usage: internal error: unsupported help text format: '%s'\n", format);
+  endswitch
+  
+  ## Raise the final error
+  if (status != 0)
+    warning ("makeinfo: Texinfo formatting filter exited abnormally");
+    warning ("makeinfo: raw Texinfo source of help text follows...\n");
+  endif
+
+  error ("Invalid call to %s.  Correct usage is:\n\n%s\n%s",
+	 name, usage_string, __additional_help_message__ ());
+endfunction
+
+function [retval, status] = get_usage_plain_text (help_text, max_len)
+  ## Extract first line by searching for a double line-end.
+  line_end_idx = strfind (help_text, "\n\n");
+  retval = help_text (1:min ([line_end_idx , max_len, length(help_text)]));
+  status = 0;
+endfunction
+
+function [retval, status] = get_usage_texinfo (help_text, max_len)
+  ## Lines ending with "@\n" are continuation lines, so they should be
+  ## concatenated with the following line.
+  help_text = strrep (help_text, "@\n", " ");
+  
+  ## Find, and keep, lines that start with @def or @end def. This should include things
+  ## such as @deftypefn, @deftypefnx, @defvar, etc. and their corresponding @end's
+  def_idx = strfind (help_text, "@def");
+  if (!isempty (def_idx))
+    buffer = "";
+    endl_idx = find (help_text == "\n");
+    for k = 1:length (def_idx)
+      endl = endl_idx (find (endl_idx > def_idx (k), 1));
+      if (isempty (endl))
+        buffer = strcat (buffer, help_text (def_idx (k):end), "\n");
+      else
+        buffer = strcat (buffer, help_text (def_idx (k):endl));
+      endif
+    endfor
+    
+    end_def_idx = strfind (help_text, "@end def");
+    if (!isempty (end_def_idx))
+      buffer = strcat (buffer, help_text (end_def_idx:end));
+    endif
+  else
+    [retval, status] = get_usage_plain_text (help_text, max_len);
+  endif
+
+  ## Run makeinfo to generate plain text
+  [retval, status] = makeinfo (buffer, "plain text");
+endfunction
+
+function [retval, status] = get_usage_html (help_text, max_len)
+  ## Strip tags
+  [help_text, status] = strip_html_tags (help_text);
+  
+  ## Extract first line with plain text method.
+  retval = get_usage_plain_text (help_text, max_len);
+endfunction
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/help/type.m	Thu Jan 22 18:22:52 2009 -0500
@@ -0,0 +1,101 @@
+## Copyright (C) 2009 Søren Hauberg
+##
+## This program is free software; you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or (at
+## your option) any later version.
+##
+## This program 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 this program; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn {Command} type options name @dots{}
+## Display the definition of each @var{name} that refers to a function.
+## 
+## Normally also displays whether each @var{name} is user-defined or built-in;
+## the @code{-q} option suppresses this behaviour.
+##
+## If an output argument is requested nothing is displayed. Instead a cell array
+## of strings is returned, where each element corresponds to the definition of
+## each requested function.
+## @end deftypefn
+
+## PKG_ADD: mark_as_command type
+
+function retval = type (varargin)
+  ## Parse input
+  if (nargin == 0)
+    error ("type: not enough input arguments");
+  endif
+
+  if (!iscellstr (varargin))
+    error ("type: input arguments must be strings");
+  endif
+    
+  quiet = false;
+  idx = strcmpi (varargin, "-q") | strcmpi (varargin, "-quiet");
+  if (any (idx))
+    quiet = true;
+    varargin (idx) = [];
+  endif
+
+  text = cell (size (varargin));
+  for n = 1:length (varargin)
+    text {n} = do_type (varargin {n}, quiet);
+    if (nargout == 0)
+      disp (text {n});
+    endif
+  endfor
+  
+  ## Should we return the text or print if
+  if (nargout > 0)
+    retval = text;
+  endif
+endfunction
+
+function text = do_type (name, quiet)
+  ## Find function and get its code
+  text = "";
+  e = exist (name);
+  if (e == 2)
+    ## m-file or ordinary file
+    file = which (name);
+    if (isempty (file))
+      ## 'name' is an ordinary file, and not a function name.
+      ## FIXME: Should we just print it anyway?
+      error ("type: `%s' undefined\n", name);
+    endif
+    
+    ## Read the file
+    fid = fopen (file, "r");
+    if (fid < 0)
+      error ("type: couldn't open `%s' for reading", file);
+    endif
+    contents = char (fread (fid).');
+    fclose (fid);
+    
+    if (quiet)
+      text = contents;
+    else
+      text = sprintf ("%s is the user-defined function defined from: %s\n\n%s",
+                      name, file, contents);
+    endif    
+  elseif (e == 3)
+    text = sprintf ("%s is a dynamically-linked function", name);
+  elseif (e == 5)
+    text = sprintf ("%s is a built-in function", name);
+  elseif (any (strcmp (__operators__ (), name)))
+    text = sprintf ("%s is an operator", name);
+  elseif (any (strcmp (__keywords__ (), name)))
+    text = sprintf ("%s is a keyword", name);
+  else
+    error ("type: `%s' undefined\n", name);
+  endif
+endfunction
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/help/which.m	Thu Jan 22 18:22:52 2009 -0500
@@ -0,0 +1,48 @@
+## Copyright (C) 2009 John W. Eaton
+##
+## This program is free software; you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or (at
+## your option) any later version.
+##
+## This program 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 this program; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deffn {Command} which name @dots{}
+## Display the type of each @var{name}.  If @var{name} is defined from a
+## function file, the full name of the file is also displayed.
+## @seealso{help, lookfor}
+## @end deffn
+
+## PKG_ADD: mark_as_command which
+
+function varargout = which (varargin)
+
+  if (nargin > 0 && iscellstr (varargin))
+    m = __which__ (varargin{:});
+
+    if (nargout == 0)
+      for i = 1:nargin
+	if (isempty (m(i).file))
+	  printf ("`%s' is a %s function\n",
+		  m(i).name, m(i).type);
+	else
+	  printf ("`%s' is a %s from the file %s\n",
+		  m(i).name, m(i).type, m(i).file);
+	endif
+      endfor
+    else
+      varargout = {m.file};
+    endif
+  else
+    print_usage ();
+  endif
+
+endfunction
--- a/scripts/miscellaneous/Makefile.in	Thu Jan 22 13:59:33 2009 -0500
+++ b/scripts/miscellaneous/Makefile.in	Thu Jan 22 18:22:52 2009 -0500
@@ -35,7 +35,7 @@
 
 SOURCES = ans.m bincoeff.m bug_report.m bunzip2.m cast.m comma.m \
   compare_versions.m computer.m copyfile.m debug.m \
-  delete.m dir.m doc.m dos.m dump_prefs.m edit.m \
+  delete.m dir.m dos.m dump_prefs.m edit.m \
   fileattrib.m fileparts.m flops.m fullfile.m getfield.m gunzip.m gzip.m \
   info.m inputname.m intwarning.m ismac.m ispc.m isunix.m license.m \
   list_primes.m ls.m ls_command.m menu.m mex.m mexext.m mkoctfile.m \
--- a/scripts/miscellaneous/doc.m	Thu Jan 22 13:59:33 2009 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,113 +0,0 @@
-## Copyright (C) 2005, 2006, 2007 Soren Hauberg
-## 
-## 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/>.
-
-## -*- texinfo -*-
-## @deftypefn {Command} doc @var{function_name}
-## Display documentation for the function @var{function_name}
-## directly from an on-line version of
-## the printed manual, using the GNU Info browser.  If invoked without
-## any arguments, the manual is shown from the beginning.
-##
-## For example, the command @kbd{doc rand} starts the GNU Info browser
-## at this node in the on-line version of the manual.
-##
-## Once the GNU Info browser is running, help for using it is available
-## using the command @kbd{C-h}.
-## @seealso{help}
-## @end deftypefn
-
-## Author: Soren Hauberg <soren@hauberg.org>
-## Adapted-by: jwe
-
-## PKG_ADD: mark_as_command doc
-
-function retval = doc (fname)
-
-  if (nargin == 0 || nargin == 1)
-
-    ftype = 0;
-
-    if (nargin == 1)
-      ## Get the directory where the function lives.
-      ## FIXME -- maybe we should have a better way of doing this.
-
-      if (ischar (fname))
-	ftype = exist (fname);
-      else
-	error ("doc: expecting argument to be a character string");
-      endif
-    else
-      fname = "";
-    endif
-
-    if (ftype == 2 || ftype == 3)
-      ffile = which (fname);
-    else
-      ffile = "";
-    endif
-
-    if (isempty (ffile))
-      info_dir = octave_config_info ("infodir");
-    else
-      info_dir = fileparts (ffile);
-    endif
-
-    ## Determine if a file called doc.info exist in the same 
-    ## directory as the function.
-
-    info_file_name = fullfile (info_dir, "doc.info");
-
-    [stat_info, err] = stat (info_file_name);
-
-    if (err < 0)
-      info_file_name = info_file ();
-    endif
-
-    ## FIXME -- don't change the order of the arguments below because
-    ## the info-emacs-info script currently expects --directory DIR as
-    ## the third and fourth arguments.  Someone should fix that.
-
-    cmd = sprintf ("\"%s\" --file \"%s\" --directory \"%s\"",
-		   info_program (), info_file_name, info_dir);
-
-    have_fname = ! isempty (fname);
-
-    if (have_fname)
-      status = system (sprintf ("%s --index-search %s", cmd, fname));
-    endif
-
-    if (! (have_fname && status == 0))
-      status = system (cmd);
-      if (status == 127)
-	warning ("unable to find info program `%s'", info_program ());
-      endif
-    endif
-
-    if (nargout > 0)
-      retval = status;
-    endif
-
-  else
-    print_usage ();
-  endif
-
-endfunction
-
-%!test if exist( info_file ()) != 2
-%!       error ("Info file %s does not exist!", info_file ());
-%!     endif
--- a/scripts/pkg/pkg.m	Thu Jan 22 13:59:33 2009 -0500
+++ b/scripts/pkg/pkg.m	Thu Jan 22 18:22:52 2009 -0500
@@ -743,7 +743,8 @@
       copy_files (desc, pdir, global_install);
       create_pkgadddel (desc, pdir, "PKG_ADD", global_install);
       create_pkgadddel (desc, pdir, "PKG_DEL", global_install);
-      finish_installation (desc, pdir, global_install)
+      finish_installation (desc, pdir, global_install);
+      generate_lookfor_cache (desc);
     endfor
   catch
     ## Something went wrong, delete tmpdirs.
@@ -1585,6 +1586,10 @@
   endif
 endfunction
 
+function generate_lookfor_cache (desc)
+  gen_doc_cache (genpath (desc.dir));
+endfunction
+
 ## Make sure the package contains the essential files.
 function verify_directory (dir)
   needed_files = {"COPYING", "DESCRIPTION"};
--- a/src/ChangeLog	Thu Jan 22 13:59:33 2009 -0500
+++ b/src/ChangeLog	Thu Jan 22 18:22:52 2009 -0500
@@ -1,3 +1,31 @@
+2009-01-22  John W. Eaton  <jwe@octave.org>
+
+	* help.cc (do_which (std::ostream&, const std::string&), Fwhich):
+	Delete.
+	(do_which (const std::string&, std::string&), F__which__):
+	New functions.
+	* do_which (const std::string&):
+	Call do_which (const std::string&, std::string&) to do the work.
+
+2009-01-22  Søren Hauberg  <hauberg@gmail.com>
+
+	* defun-int.h (print_usage): No longer mark as deprecated.
+	* defun.cc (print_usage): Simply call feval to execute print_usage.m.
+	* help.cc (additional_help_message, display_names_from_help_list,
+	display_symtab_names, simple_help, try_info, help_from_info,
+	display_help_text, display_usage_text, raw_help_from_list,
+	help_from_list, help_from_symbol_table, help_from_file,
+	builtin_help, Fhelp, display_file, do_type, Ftype,
+	first_help_sentence, print_lookfor, Flookfor): Delete.
+	(looks_like_html, raw_help_from_map, raw_help, do_get_help_text,
+	F__operators__, F__keywords__, F__builtins__,
+	file_is_in_dir, F__list_functions__): New functions.
+	(pair_type, map_iter): New typedefs.
+	(operators, keywords): Use pair_type for elements of list.
+	(names): Use map for lists.
+	* help.h (display_help_text, display_usage_text,
+	additional_help_message): Delete decls.
+
 2009-01-22  John W. Eaton  <jwe@octave.org>
 
 	* toplev.cc (octave_config_info): Check OCTAVEUSE_OS_X_API instead
--- a/src/defun-int.h	Thu Jan 22 13:59:33 2009 -0500
+++ b/src/defun-int.h	Thu Jan 22 18:22:52 2009 -0500
@@ -34,7 +34,7 @@
 class octave_value;
 
 extern OCTINTERP_API void print_usage (void);
-extern OCTINTERP_API void print_usage (const std::string&) GCC_ATTR_DEPRECATED;
+extern OCTINTERP_API void print_usage (const std::string&);
 
 extern OCTINTERP_API void check_version (const std::string& version, const std::string& fcn);
 
--- a/src/defun.cc	Thu Jan 22 13:59:33 2009 -0500
+++ b/src/defun.cc	Thu Jan 22 18:22:52 2009 -0500
@@ -44,72 +44,23 @@
 #include "symtab.h"
 #include "toplev.h"
 #include "variables.h"
+#include "parse.h"
 
 // Print the usage part of the doc string of FCN (user-defined or DEFUN).
-
-static void
-print_usage (octave_function *fcn)
+void
+print_usage (void)
 {
-  if (fcn)
-    {
-      std::string nm = fcn->name ();
-
-      std::string doc = fcn->doc_string ();
-
-      if (doc.length () > 0)
-	{
-	  std::ostringstream buf;
-
-	  buf << "\nInvalid call to " << nm << ".  Correct usage is:\n\n";
-
-	  display_usage_text (buf, doc);
-
-	  buf << "\n";
-
-	  additional_help_message (buf);
-
-	  defun_usage_message (buf.str ());
-	}
-      else
-	error ("no usage message found for `%s'", nm.c_str ());
-    }
+  const octave_function *cur = octave_call_stack::current ();
+  if (cur)
+    print_usage (cur->name ());
   else
     error ("print_usage: invalid function");
 }
 
-// Print the usage part of the doc string of the current function
-// (user-defined or DEFUN).
-
 void
-print_usage (void)
-{
-  print_usage (octave_call_stack::current ());
-}
-
-// Deprecated.
-void
-print_usage (const std::string&)
+print_usage (const std::string& name)
 {
-  print_usage ();
-}
-
-DEFUN (print_usage, args, ,
-  "-*- texinfo -*-\n\
-@deftypefn {Loadable Function} {} print_usage ()\n\
-Print the usage message for the currently executing function.  The\n\
-@code{print_usage} function is only intended to work inside a\n\
-user-defined function.\n\
-@seealso{help}\n\
-@end deftypefn")
-{
-  octave_value retval;
-
-  if (args.length () == 0)
-    print_usage (octave_call_stack::caller_user_code ());
-  else
-    print_usage ();
-
-  return retval;
+  feval ("print_usage", octave_value (name), 0);
 }
 
 void
--- a/src/help.cc	Thu Jan 22 13:59:33 2009 -0500
+++ b/src/help.cc	Thu Jan 22 18:22:52 2009 -0500
@@ -86,258 +86,259 @@
 // functions.
 static bool Vsuppress_verbose_help_message = false;
 
-// FIXME -- maybe this should use string instead of char*.
+#include <map>
 
-struct help_list
-{
-  const char *name;
-  const char *help;
-};
+typedef std::map<std::string, std::string> map_type;
+typedef map_type::value_type pair_type;
+typedef map_type::const_iterator map_iter;
 
-static help_list operators[] =
+template<typename T, std::size_t z>
+std::size_t
+size (T const (&)[z])
 {
-  { "!",
-    "Logical not operator.  See also `~'.\n", },
+  return z;
+}
 
-  { "!=",
-    "Logical not equals operator.  See also `~' and `<>'.\n", },
+// FIXME -- The descriptions could easily be in texinfo -- should they?
+const static pair_type operators[] =
+{
+  pair_type ("!",
+    "Logical not operator.  See also `~'.\n"),
 
-  { "\"",
-    "String delimiter.\n", },
+  pair_type ("!=",
+    "Logical not equals operator.  See also `~='.\n"),
 
-  { "#",
-    "Begin comment character.  See also `%'.", },
+  pair_type ("\"",
+    "String delimiter.\n"),
+
+  pair_type ("#",
+    "Begin comment character.  See also `%'."),
 
-  { "%",
-    "Begin comment charcter.  See also `#'.", },
+  pair_type ("%",
+    "Begin comment charcter.  See also `#'."),
 
-  { "&",
-    "Logical and operator.  See also `&&'.", },
+  pair_type ("&",
+    "Element by element logical and operator.  See also `&&'."),
 
-  { "&&",
-    "Logical and operator.  See also `&'.", },
+  pair_type ("&&",
+    "Logical and operator (with short-circuit evaluation).  See also `&'."),
 
-  { "'",
+  pair_type ("'",
     "Matrix transpose operator.  For complex matrices, computes the\n\
 complex conjugate (Hermitian) transpose.  See also `.''\n\
 \n\
 The single quote character may also be used to delimit strings, but\n\
 it is better to use the double quote character, since that is never\n\
-ambiguous", },
-
-  { "(",
-    "Array index or function argument delimiter.", },
+ambiguous"),
 
-  { ")",
-    "Array index or function argument delimiter.", },
+  pair_type ("(",
+    "Array index or function argument delimiter."),
 
-  { "*",
-    "Multiplication operator.  See also `.*'", },
+  pair_type (")",
+    "Array index or function argument delimiter."),
 
-  { "**",
-    "Power operator.  See also `^', `.**', and `.^'", },
+  pair_type ("*",
+    "Multiplication operator.  See also `.*'"),
 
-  { "+",
-    "Addition operator.", },
+  pair_type ("**",
+    "Power operator.  See also `^', `.**', and `.^'"),
 
-  { "++",
+  pair_type ("+",
+    "Addition operator."),
+
+  pair_type ("++",
     "Increment operator.  As in C, may be applied as a prefix or postfix\n\
-operator.", },
-
-  { ",",
-    "Array index, function argument, or command separator.", },
+operator."),
 
-  { "-",
-    "Subtraction or unary negation operator.", },
+  pair_type (",",
+    "Array index, function argument, or command separator."),
 
-  { "--",
+  pair_type ("-",
+    "Subtraction or unary negation operator."),
+
+  pair_type ("--",
     "Decrement operator.  As in C, may be applied as a prefix or postfix\n\
-operator.", },
-
-  { ".'",
-    "Matrix transpose operator.  For complex matrices, computes the\n\
-transpose, *not* the complex conjugate transpose.  See also `''.", },
+operator."),
 
-  { ".*",
-    "Element by element multiplication operator.  See also `*'.", },
+  pair_type (".'",
+    "Matrix transpose operator.  For complex matrices, computes the\n\
+transpose, *not* the complex conjugate transpose.  See also `''."),
 
-  { ".**",
-    "Element by element power operator.  See also `**', `^', and `.^'.", },
+  pair_type (".*",
+    "Element by element multiplication operator.  See also `*'."),
 
-  { "./",
-    "Element by element division operator.  See also `/' and `\\'.", },
+  pair_type (".**",
+    "Element by element power operator.  See also `**', `^', and `.^'."),
 
-  { ".^",
-    "Element by element power operator.  See also `**', `^', and `.^'.", },
+  pair_type ("./",
+    "Element by element division operator.  See also `/' and `\\'."),
 
-  { "/",
-    "Right division.  See also `\\' and `./'.", },
+  pair_type (".^",
+    "Element by element power operator.  See also `**', `^', and `.^'."),
 
-  { ":",
-    "Select entire rows or columns of matrices.", },
+  pair_type ("/",
+    "Right division.  See also `\\' and `./'."),
 
-  { ";",
-    "Array row or command separator.  See also `,'.", },
+  pair_type (":",
+    "Select entire rows or columns of matrices."),
 
-  { "<",
-    "Less than operator.", },
+  pair_type (";",
+    "Array row or command separator.  See also `,'."),
 
-  { "<=",
-    "Less than or equals operator.", },
+  pair_type ("<",
+    "Less than operator."),
 
-  { "<>",
-    "Logical not equals operator.  See also `!=' and `~='.", },
+  pair_type ("<=",
+    "Less than or equals operator."),
 
-  { "=",
-    "Assignment operator.", },
+  pair_type ("=",
+    "Assignment operator."),
 
-  { "==",
-    "Equality test operator.", },
+  pair_type ("==",
+    "Equality test operator."),
 
-  { ">",
-    "Greater than operator.", },
+  pair_type (">",
+    "Greater than operator."),
 
-  { ">=",
-    "Greater than or equals operator.", },
+  pair_type (">=",
+    "Greater than or equals operator."),
 
-  { "[",
-    "Return list delimiter.  See also `]'.", },
+  pair_type ("[",
+    "Return list delimiter.  See also `]'."),
 
-  { "\\",
-    "Left division operator.  See also `/' and `./'.", },
+  pair_type ("\\",
+    "Left division operator.  See also `/' and `./'."),
 
-  { "]",
-    "Return list delimiter.  See also `['.", },
+  pair_type ("]",
+    "Return list delimiter.  See also `['."),
 
-  { "^",
-    "Power operator.  See also `**', `.^', and `.**.'", },
+  pair_type ("^",
+    "Power operator.  See also `**', `.^', and `.**.'"),
 
-  { "|",
-    "Logical or operator.  See also `||'.", },
+  pair_type ("|",
+    "Element by element logical or operator.  See also `||'."),
 
-  { "||",
-    "Logical or operator.  See also `|'.", },
+  pair_type ("||",
+    "Logical or operator (with short-circuit evaluation).  See also `|'."),
 
-  { "~",
-    "Logical not operator.  See also `!' and `~'.", },
+  pair_type ("~",
+    "Logical not operator.  See also `!' and `~'."),
 
-  { "~=",
-    "Logical not equals operator.  See also `<>' and `!='.", },
-
-  { 0, 0, },
+  pair_type ("~=",
+    "Logical not equals operator.  See also `!='."),
 };
 
-static help_list keywords[] =
+const static pair_type keywords[] =
 {
-  { "break",
+  pair_type ("break",
     "-*- texinfo -*-\n\
 @deffn Keyword break\n\
 Exit the innermost enclosing do, while or for loop.\n\
 @seealso{do, while, for, continue}\n\
-@end deffn", },
+@end deffn"),
 
-  { "case",
+  pair_type ("case",
     "-*- texinfo -*-\n\
 @deffn Keyword case @{@var{value}@}\n\
 A case statement in an switch. Octave cases are exclusive and do not\n\
 fall-through as do C-language cases. A switch statement must have at least\n\
 one case.  See @code{switch} for an example.\n\
 @seealso{switch}\n\
-@end deffn", },
+@end deffn"),
 
-  { "catch",
+  pair_type ("catch",
     "-*- texinfo -*-\n\
 @deffn Keyword catch\n\
 Begin the cleanup part of a try-catch block.\n\
 @seealso{try}\n\
-@end deffn", },
+@end deffn"),
 
-  { "continue",
+  pair_type ("continue",
     "-*- texinfo -*-\n\
 @deffn Keyword continue\n\
 Jump to the end of the innermost enclosing do, while or for loop.\n\
 @seealso{do, while, for, break}\n\
-@end deffn", },
+@end deffn"),
 
-  { "do",
+  pair_type ("do",
     "-*- texinfo -*-\n\
 @deffn Keyword do\n\
 Begin a do-until loop. This differs from a do-while loop in that the\n\
 body of the loop is executed at least once.\n\
 @seealso{while}\n\
-@end deffn", },
+@end deffn"),
 
-  { "else",
+  pair_type ("else",
     "-*- texinfo -*-\n\
 @deffn Keyword else\n\
 Alternate action for an if block.  See @code{if} for an example.\n\
 @seealso{if}\n\
-@end deffn", },
+@end deffn"),
 
-  { "elseif",
+  pair_type ("elseif",
     "-*- texinfo -*-\n\
 @deffn Keyword elseif (@var{condition})\n\
 Alternate conditional test for an if block.  See @code{if} for an example.\n\
 @seealso{if}\n\
-@end deffn", },
+@end deffn"),
 
-  { "end",
+  pair_type ("end",
     "-*- texinfo -*-\n\
 @deffn Keyword end\n\
 Mark the end of any @code{for}, @code{if}, @code{do}, @code{while}, or @code{function} block.\n\
 @seealso{for, if, do, while, function}\n\
-@end deffn", },
+@end deffn"),
 
-  { "end_try_catch",
+  pair_type ("end_try_catch",
     "-*- texinfo -*-\n\
 @deffn Keyword end_try_catch\n\
 Mark the end of an @code{try-catch} block.\n\
 @seealso{try, catch}\n\
-@end deffn", }, 
+@end deffn"), 
 
-  { "end_unwind_protect",
+  pair_type ("end_unwind_protect",
     "-*- texinfo -*-\n\
 @deffn Keyword end_unwind_protect\n\
 Mark the end of an unwind_protect block.\n\
 @seealso{unwind_protect}\n\
-@end deffn", }, 
+@end deffn"), 
 
-  { "endfor",
+  pair_type ("endfor",
     "-*- texinfo -*-\n\
 @deffn Keyword endfor\n\
 Mark the end of a for loop.  See @code{for} for an example.\n\
 @seealso{for}\n\
-@end deffn", },
+@end deffn"),
 
-  { "endfunction",
+  pair_type ("endfunction",
     "-*- texinfo -*-\n\
 @deffn Keyword endfunction\n\
 Mark the end of a function.\n\
 @seealso{function}\n\
-@end deffn", },
+@end deffn"),
 
-  { "endif",
+  pair_type ("endif",
     "-*- texinfo -*-\n\
 @deffn Keyword endif\n\
 Mark the end of an if block.  See @code{if} for an example.\n\
 @seealso{if}\n\
-@end deffn", },
+@end deffn"),
 
-  { "endswitch",
+  pair_type ("endswitch",
     "-*- texinfo -*-\n\
 @deffn Keyword endswitch\n\
 Mark the end of a switch block.  See @code{switch} for an example.\n\
 @seealso{switch}\n\
-@end deffn", },
+@end deffn"),
 
-  { "endwhile",
+  pair_type ("endwhile",
     "-*- texinfo -*-\n\
 @deffn Keyword endwhile\n\
 Mark the end of a while loop.  See @code{while} for an example.\n\
 @seealso{do, while}\n\
-@end deffn", },
+@end deffn"),
 
-  { "for",
+  pair_type ("for",
     "-*- texinfo -*-\n\
 @deffn Keyword for @var{i} = @var{range}\n\
 Begin a for loop.\n\
@@ -347,9 +348,9 @@
 endfor\n\
 @end example\n\
 @seealso{do, while}\n\
-@end deffn", },
+@end deffn"),
 
-  { "function",
+  pair_type ("function",
     "-*- texinfo -*-\n\
 @deffn Keyword function @var{outputs} = function (@var{input}, ...)\n\
 @deffnx Keyword function {} function (@var{input}, ...)\n\
@@ -357,9 +358,9 @@
 Begin a function body with @var{outputs} as results and @var{inputs} as\n\
 parameters.\n\
 @seealso{return}\n\
-@end deffn", },
+@end deffn"),
 
-  { "global",
+  pair_type ("global",
     "-*- texinfo -*-\n\
 @deffn Keyword global\n\
 Declare variables to have global scope.\n\
@@ -370,9 +371,9 @@
 endif\n\
 @end example\n\
 @seealso{persistent}\n\
-@end deffn", },
+@end deffn"),
 
-  { "if",
+  pair_type ("if",
     "-*- texinfo -*-\n\
 @deffn Keyword if (@var{cond}) @dots{} endif\n\
 @deffnx Keyword if (@var{cond}) @dots{} else @dots{} endif\n\
@@ -390,16 +391,16 @@
 endif\n\
 @end example\n\
 @seealso{switch}\n\
-@end deffn", },
+@end deffn"),
 
-  { "otherwise",
+  pair_type ("otherwise",
     "-*- texinfo -*-\n\
 @deffn Keyword otherwise\n\
 The default statement in a switch block (similar to else in an if block).\n\
 @seealso{switch}\n\
-@end deffn", },
+@end deffn"),
 
-  { "persistent",
+  pair_type ("persistent",
     "-*- texinfo -*-\n\
 @deffn Keyword persistent @var{var}\n\
 Declare variables as persistent.  A variable that has been declared\n\
@@ -408,30 +409,30 @@
 variables and global variables is that persistent variables are local in \n\
 scope to a particular function and are not visible elsewhere.\n\
 @seealso{global}\n\
-@end deffn", },
+@end deffn"),
 
-  { "replot",
+  pair_type ("replot",
     "-*- texinfo -*-\n\
 @deffn Keyword replot\n\
 Replot a graphic.\n\
 @seealso{plot}\n\
-@end deffn", },
+@end deffn"),
 
-  { "return",
+  pair_type ("return",
     "-*- texinfo -*-\n\
 @deffn Keyword return\n\
 Return from a function.\n\
 @seealso{function}\n\
-@end deffn", },
+@end deffn"),
 
-  { "static",
+  pair_type ("static",
     "-*- texinfo -*-\n\
 @deffn Keyword static\n\
 This function has been deprecated in favor of persistent.\n\
 @seealso{persistent}\n\
-@end deffn", },
+@end deffn"),
 
-  { "switch",
+  pair_type ("switch",
     "-*- texinfo -*-\n\
 @deffn Keyword switch @var{statement}\n\
 Begin a switch block.\n\
@@ -448,9 +449,9 @@
 endswitch\n\
 @end example\n\
 @seealso{if, case, otherwise}\n\
-@end deffn", },
+@end deffn"),
 
-  { "try",
+  pair_type ("try",
     "-*- texinfo -*-\n\
 @deffn Keyword try\n\
 Begin a try-catch block.\n\
@@ -460,16 +461,16 @@
 recommended to use the lasterr function to re-throw the error after cleanup\n\
 is completed).\n\
 @seealso{catch,unwind_protect}\n\
-@end deffn", }, 
+@end deffn"), 
 
-  { "until",
+  pair_type ("until",
     "-*- texinfo -*-\n\
 @deffn Keyword until\n\
 End a do-until loop.\n\
 @seealso{do}\n\
-@end deffn", },
+@end deffn"),
 
-  { "unwind_protect",
+  pair_type ("unwind_protect",
     "-*- texinfo -*-\n\
 @deffn Keyword unwind_protect\n\
 Begin an unwind_protect block.\n\
@@ -481,112 +482,82 @@
 unwind_protect_cleanup will be run with or without an error in the\n\
 unwind_protect block).\n\
 @seealso{unwind_protect_cleanup,try}\n\
-@end deffn", }, 
+@end deffn"), 
 
-  { "unwind_protect_cleanup",
+  pair_type ("unwind_protect_cleanup",
     "-*- texinfo -*-\n\
 @deffn Keyword unwind_protect_cleanup\n\
 Begin the cleanup section of an unwind_protect block.\n\
 @seealso{unwind_protect}\n\
-@end deffn", }, 
+@end deffn"), 
 
-  { "varargin",
+  pair_type ("varargin",
     "-*- texinfo -*-\n\
 @deffn Keyword varargin\n\
 Pass an arbitrary number of arguments into a function.\n\
 @seealso{varargout, nargin, nargout}\n\
-@end deffn", },
+@end deffn"),
 
-  { "varargout",
+  pair_type ("varargout",
     "-*- texinfo -*-\n\
 @deffn Keyword varargout\n\
 Pass an arbitrary number of arguments out of a function.\n\
 @seealso{varargin, nargin, nargout}\n\
-@end deffn", },
+@end deffn"),
 
-  { "while",
+  pair_type ("while",
     "-*- texinfo -*-\n\
 @deffn Keyword while\n\
 Begin a while loop.\n\
 @seealso{do}\n\
-@end deffn", },
-
-  { 0, 0, },
+@end deffn"),
 };
 
 // Return a copy of the operator or keyword names.
-
 static string_vector
-names (help_list *lst)
+names (const map_type& lst)
 {
-  string_vector retval;
-
-  int count = 0;
-  help_list *ptr = lst;
-  while (ptr->name)
-    {
-      count++;
-      ptr++;
-    }
-
-  if (count > 0)
-    {
-      retval.resize (count);
-
-      ptr = lst;
-      for (int i = 0; i < count; i++)
-	{
-	  retval[i] = ptr->name;
-	  ptr++;
-	}
-    }
-
+  string_vector retval (lst.size ());
+  int j = 0;
+  for (map_iter iter = lst.begin (); iter != lst.end (); iter ++)
+    retval [j++] = iter->first;
   return retval;
 }
 
-static help_list *
-operator_help (void)
-{
-  return operators;
-}
+const static map_type operators_map (operators, operators + size (operators));
+const static map_type keywords_map (keywords, keywords + size (keywords));
+const static string_vector keyword_names = names (keywords_map);
 
-static help_list *
-keyword_help (void)
-{
-  return keywords;
-}
-
-// It's not likely that this does the right thing now.  FIXME
+// FIXME -- It's not likely that this does the right thing now.
 
 string_vector
 make_name_list (void)
 {
-  string_vector key = names (keyword_help ());
-  int key_len = key.length ();
+  const int key_len = keyword_names.length ();
 
-  string_vector bif = symbol_table::built_in_function_names ();
-  int bif_len = bif.length ();
+  const string_vector bif = symbol_table::built_in_function_names ();
+  const int bif_len = bif.length ();
 
   // FIXME -- is this really necessary here?
-  string_vector glb = symbol_table::global_variable_names ();
-  int glb_len = glb.length ();
+  const string_vector glb = symbol_table::global_variable_names ();
+  const int glb_len = glb.length ();
 
   // FIXME -- is this really necessary here?
-  string_vector top = symbol_table::top_level_variable_names ();
-  int top_len = top.length ();
+  const string_vector top = symbol_table::top_level_variable_names ();
+  const int top_len = top.length ();
 
   string_vector lcl;
   if (! symbol_table::at_top_level ())
     lcl = symbol_table::variable_names ();
-  int lcl_len = lcl.length ();
+  const int lcl_len = lcl.length ();
 
-  string_vector ffl = load_path::fcn_names ();
-  int ffl_len = ffl.length ();
+  const string_vector ffl = load_path::fcn_names ();
+  const int ffl_len = ffl.length ();
 
-  string_vector afl = autoloaded_functions ();
-  int afl_len = afl.length ();
+  const string_vector afl = autoloaded_functions ();
+  const int afl_len = afl.length ();
 
-  int total_len = key_len + bif_len + glb_len + top_len + lcl_len
+  const int total_len = key_len + bif_len + glb_len + top_len + lcl_len
     + ffl_len + afl_len;
 
   string_vector list (total_len);
@@ -596,7 +567,7 @@
   int j = 0;
   int i = 0;
   for (i = 0; i < key_len; i++)
-    list[j++] = key[i];
+    list[j++] = keyword_names[i];
 
   for (i = 0; i < bif_len; i++)
     list[j++] = bif[i];
@@ -619,121 +590,14 @@
   return list;
 }
 
-void
-additional_help_message (std::ostream& os)
-{
-  if (! Vsuppress_verbose_help_message)
-    os << "\
-Additional help for built-in functions and operators is\n\
-available in the on-line version of the manual.  Use the command\n\
-`doc <topic>' to search the manual index.\n\
-\n\
-Help and information about Octave is also available on the WWW\n\
-at http://www.octave.org and via the help@octave.org\n\
-mailing list.\n"; 
-}
-
-// FIXME -- this needs a major overhaul to cope with new
-// symbol table stuff.
-
-static void
-display_names_from_help_list (std::ostream& os, help_list *list,
-			      const char *desc)
-{
-  string_vector symbols = names (list);
-
-  if (! symbols.empty ())
-    {
-      os << "\n*** " << desc << ":\n\n";
-
-      symbols.sort ();
-
-      symbols.list_in_columns (os);
-    }
-}
-
-static void
-display_symtab_names (std::ostream& os, const std::list<std::string>& names,
-		      const std::string& desc)
-{
-  if (! names.empty ())
-    {
-      os << "\n*** " << desc << ":\n\n";
-
-      string_vector sv (names);
-
-      sv.list_in_columns (os);
-    }
-}
-
-static void
-simple_help (void)
+static bool
+looks_like_html (const std::string& msg)
 {
-  octave_stdout << "Help is available for the topics listed below.\n";
-
-  additional_help_message (octave_stdout);
-
-  display_names_from_help_list (octave_stdout, operator_help (),
-				"operators");
-
-  display_names_from_help_list (octave_stdout, keyword_help (),
-				"reserved words");
-
-  display_symtab_names (octave_stdout,
-			symbol_table::built_in_function_names (),
-			"built-in functions");
-
-  // FIXME -- list functions defined on command line?
-
-  load_path::display (octave_stdout);
-
-  string_vector autoloaded = autoloaded_functions ();
-
-  if (! autoloaded.empty ())
-    {
-      octave_stdout << "\n*** autoloaded functions:\n\n";
-
-      autoloaded.sort ();
-
-      autoloaded.list_in_columns (octave_stdout);
-    }
-}
-
-static int
-try_info (const std::string& nm)
-{
-  int retval = -1;
-
-  warning ("please use `doc' instead of `help -i'");
-
-  octave_value_list args;
-  args(0) = nm;
-  octave_value_list result = feval ("doc", args, 1);
-
-  if (result.length () > 0)
-    retval = result(0).int_value ();
-
-  return retval;
-}
-
-static void
-help_from_info (const string_vector& argv, int idx, int argc)
-{
-  if (idx == argc)
-    try_info (std::string ());
-  else
-    {
-      for (int i = idx; i < argc; i++)
-	{
-	  int status = try_info (argv[i]);
-
-	  if (status == 127)
-	    break;
-	  else if (status != 0)
-	    message ("help", "`%s' is not indexed in the manual",
-		     argv[i].c_str ());
-	}
-    }
+  const size_t p1 = msg.find ('\n');
+  std::string t = msg.substr (0, p1);
+  const size_t p2 = t.find ("<html"); // FIXME: this comparison should be case-insensitive
+  
+   return (p2 != std::string::npos);
 }
 
 static bool
@@ -751,187 +615,6 @@
   return (p2 != std::string::npos);
 }
 
-void
-display_help_text (std::ostream& os, const std::string& msg)
-{
-  // Look for "-*- texinfo -*-" in first line of help message.  If it
-  // is present, use makeinfo to format the rest of the message before
-  // sending it to the output stream.  Otherwise, just print the
-  // message.
-
-  size_t pos;
-
-  if (looks_like_texinfo (msg, pos))
-    {
-      os.flush ();
-
-      std::string tmp_file_name = file_ops::tempnam ("", "");
-
-      int cols = command_editor::terminal_cols ();
-
-      if (cols > 16)
-	cols--;
-
-      if (cols > 64)
-	cols -= 7;
-
-      if (cols > 80)
-	cols = 72;
-
-      std::ostringstream buf;
-
-      // Use double quotes to quote the sed patterns for Windows.
-
-      buf << "sed -e \"s/^[#%][#%]* *//\" -e \"s/^ *@/@/\" | "
-	  << "\"" << Vmakeinfo_program << "\""
-	  << " -D \"VERSION " << OCTAVE_VERSION << "\""
-	  << " -D \"OCTAVEHOME " << OCTAVE_PREFIX << "\""
-	  << " -D \"TARGETHOSTTYPE " << OCTAVE_CANONICAL_HOST_TYPE << "\""
-	  << " --fill-column " << cols
-	  << " --no-warn"
-	  << " --no-validate"
-	  << " --no-headers"
-	  << " --force"
-	  << " --output \"" << tmp_file_name << "\"";
-
-      oprocstream filter (buf.str ());
-
-      if (filter && filter.is_open ())
-	{
-	  filter << "@macro seealso {args}\n"
-		 << "@sp 1\n"
-		 << "@noindent\n"
-		 << "See also: \\args\\.\n"
-                 << "@end macro\n";
-
-	  filter << msg.substr (pos+1) << std::endl;
-
-	  int status = filter.close ();
-
-	  std::ifstream tmp_file (tmp_file_name.c_str ());
-
-	  if (WIFEXITED (status) && WEXITSTATUS (status) == 0)
-	    {
-	      int c;
-	      while ((c = tmp_file.get ()) != EOF)
-		os << (char) c;
-
-	      tmp_file.close ();
-	    }
-	  else
-	    {
-	      warning ("help: Texinfo formatting filter exited abnormally");
-	      warning ("help: raw Texinfo source of help text follows...");
-	      warning ("help:\n\n%s\n\n", msg.c_str ());
-	    }
-
-	  file_ops::unlink (tmp_file_name);
-	}
-      else
-	os << msg;
-    }
-  else
-    os << msg;
-}
-
-void
-display_usage_text (std::ostream& os, const std::string& msg)
-{
-  std::string filtered_msg = msg;
-
-  size_t pos;
-
-  if (looks_like_texinfo (msg, pos))
-    {
-      std::ostringstream buf;
-
-      buf << "-*- texinfo -*-\n";
-
-      bool found_def = false;
-
-      size_t msg_len = msg.length ();
-
-      while (pos < msg_len)
-	{
-	  size_t new_pos = msg.find_first_of ('\n', pos);
-
-	  if (new_pos == std::string::npos)
-	    new_pos = msg_len-1;
-
-	  std::string line = msg.substr (pos, new_pos-pos+1);
-
-	  if (line.substr (0, 4) == "@def"
-	      || line.substr (0, 8) == "@end def")
-	    {
-	      found_def = true;
-	      buf << line;
-	    }
-
-	  pos = new_pos + 1;
-	}
-
-      if (found_def)
-	filtered_msg = buf.str ();
-    }
-
-  display_help_text (os, filtered_msg);
-}
-
-static bool
-raw_help_from_list (const help_list *list, const std::string& nm, 
-		    std::string& h, bool& symbol_found)
-{
-  bool retval = false;
-
-  const char *name;
-
-  while ((name = list->name) != 0)
-    {
-      if (strcmp (name, nm.c_str ()) == 0)
-	{
-	  symbol_found = true;
-
-	  h = list->help;
-
-	  if (h.length () > 0)
-	    retval = true;
-
-	  break;
-	}
-      list++;
-    }
-
-  return retval;;
-}
-
-static bool
-help_from_list (std::ostream& os, const help_list *list,
-		const std::string& nm, int usage, bool& symbol_found)
-{
-  bool retval = false;
-
-  std::string h;
-
-  if (raw_help_from_list (list, nm, h, symbol_found))
-    {
-      if (h.length () > 0)
-	{
-	  if (usage)
-	    os << "\nusage: ";
-	  else
-	    os << "\n*** " << nm << ":\n\n";
-
-	  display_help_text (os, h);
-
-	  os << "\n";
-
-	  retval = true;
-	}
-    }
-
-  return retval;
-}
-
 static bool
 raw_help_from_symbol_table (const std::string& nm, std::string& h, 
 			    std::string& w, bool& symbol_found)
@@ -967,45 +650,6 @@
 }
 
 static bool
-help_from_symbol_table (std::ostream& os, const std::string& nm,
-			bool& symbol_found)
-{
-  bool retval = false;
-
-  std::string h;
-  std::string w;
-
-  if (raw_help_from_symbol_table (nm, h, w, symbol_found))
-    {
-      if (h.length () > 0)
-	{
-	  std::string dispatch_help = symbol_table::help_for_dispatch (nm);
-
-	  if (! dispatch_help.empty ())
-	    {
-	      size_t pos = 0;
-
-	      std::string pfx = looks_like_texinfo (h, pos)
-		? std::string ("\n\n@noindent\n") : std::string ("\n\n");
-
-	      h += pfx + dispatch_help;
-	    }
-
-	  display_help_text (os, h);
-
-	  if (w.length () > 0 && ! Vsuppress_verbose_help_message)
-	    os << w << "\n";
-
-	  os << "\n";
-
-	  retval = true;
-	}
-    }
-
-  return retval;
-}
-
-static bool
 raw_help_from_file (const std::string& nm, std::string& h, 
 		    std::string& file, bool& symbol_found)
 {
@@ -1032,334 +676,134 @@
 }
 
 static bool
-help_from_file (std::ostream& os, const std::string& nm, bool& symbol_found)
+raw_help_from_map (const std::string& nm, std::string& h, 
+		   const map_type& map, bool& symbol_found)
 {
-  bool retval = false;
-
-  std::string h;
-  std::string file;
-
-  if (raw_help_from_file (nm, h, file, symbol_found))
-    {
-      if (h.length () > 0)
-	{
-	  // Strip extension
-	  size_t l = file.length ();
-	  if (l > 2 && file.substr (l-2) == ".m")
-	    {
-	      std::string tmp = file.substr (0, l - 2);
-
-	      if (file_stat (tmp + ".oct"))
-		file = tmp + ".oct";
-	      else if (file_stat (tmp + ".mex"))
-		file = tmp + ".mex";
-	    }
-
-	  os << nm << " is the file " << file << "\n\n";
-
-	  display_help_text (os, h);
-
-	  os << "\n";
-
-	  retval = true;
-	}
-    }
-
-  return retval;
+  map_iter idx = map.find (nm);
+  symbol_found = (idx != map.end ());
+  h = (symbol_found) ? idx->second : "";
+  return symbol_found;
 }
 
 std::string
-raw_help (const std::string& nm, bool &symbol_found)
+raw_help (const std::string& nm, bool& symbol_found)
 {
   std::string h;
   std::string w;
   std::string f;
 
-  (raw_help_from_list (operator_help (), nm, h, symbol_found)
-   || raw_help_from_list (keyword_help (), nm, h, symbol_found)
-   || raw_help_from_symbol_table (nm, h, w, symbol_found)
-   || raw_help_from_file (nm, h, f, symbol_found));
+  (raw_help_from_symbol_table (nm, h, w, symbol_found)
+   || raw_help_from_file (nm, h, f, symbol_found)
+   || raw_help_from_map (nm, h, operators_map, symbol_found)
+   || raw_help_from_map (nm, h, keywords_map, symbol_found));
 
   return h;
 }
 
 static void
-builtin_help (int argc, const string_vector& argv)
+do_get_help_text (const std::string name, std::string& text,
+		  std::string& format)
 {
-  help_list *op_help_list = operator_help ();
-  help_list *kw_help_list = keyword_help ();
-
-  for (int i = 1; i < argc; i++)
+  bool symbol_found = false;
+  text = raw_help (name, symbol_found);
+  
+  format = "Not found";
+  if (symbol_found)
     {
-      bool symbol_found = false;
-
-      if (help_from_list (octave_stdout, op_help_list, argv[i], 0,
-			  symbol_found))
-	continue;
-
-      if (help_from_list (octave_stdout, kw_help_list, argv[i], 0,
-			  symbol_found))
-	continue;
-
-      if (help_from_symbol_table (octave_stdout, argv[i], symbol_found))
-	continue;
-
-      if (error_state)
-	{
-	  octave_stdout << "\n";
-	  error_state = 0;
-	  continue;
-	}
-
-      if (help_from_file (octave_stdout, argv[i], symbol_found))
-	continue;
-
-      if (error_state)
-	{
-	  octave_stdout << "\n";
-	  error_state = 0;
-	  continue;
-	}
-
-      if (symbol_found)
-	octave_stdout << "\nhelp: `" << argv[i]
-		      << "' is not documented\n"; 
+      size_t idx = -1;
+      if (looks_like_texinfo (text, idx))
+        {
+          format = "texinfo";
+          text.erase (0, idx);
+        }
+      else if (looks_like_html (text))
+        {
+          format = "html";
+        }
       else
-	octave_stdout << "\nhelp: `" << argv[i]
-		      << "' not found\n"; 
+        {
+          format = "plain text";
+        }
     }
-  
-  additional_help_message (octave_stdout);
 }
 
-DEFCMD (help, args, ,
-  "-*- texinfo -*-\n\
-@deffn {Command} help @var{name}\n\
-Display the help text for @var{name}.\n\
-If invoked without any arguments, @code{help} prints a list\n\
-of all the available operators and functions.\n\
+DEFUN (get_help_text, args, , "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {[@var{text}, @var{format}] =} get_help_text (@var{name})\n\
+Returns the help text of a given function.\n\
 \n\
-For example, the command @kbd{help help} prints a short message\n\
-describing the @code{help} command.\n\
+This function returns the raw help text @var{text} and an indication of\n\
+its format for the function @var{name}. The format indication @var{format}\n\
+is a string that can be either @t{\"texinfo\"}, @t{\"html\"}, or\n\
+@t{\"plain text\"}.\n\
 \n\
-The help command can give you information about operators, but not the\n\
-comma and semicolons that are used as command separators.  To get help\n\
-for those, you must type @kbd{help comma} or @kbd{help semicolon}.\n\
-@seealso{doc, which, lookfor}\n\
-@end deffn")
+To convert the help text to other formats, use the @code{makeinfo} function.\n\
+\n\
+@seealso{makeinfo}\n\
+@end deftypefn\n")
 {
   octave_value_list retval;
 
-  int argc = args.length () + 1;
+  if (args.length () == 1)
+    {
+      const std::string name = args (0).string_value ();
 
-  string_vector argv = args.make_argv ("help");
-
-  if (error_state)
-    return retval;
+      if (! error_state)
+	{
+	  std::string text;
+	  std::string format;
 
-  if (argc == 1)
-    simple_help ();
+	  do_get_help_text (name, text, format);
+  
+	  retval(1) = format;
+	  retval(0) = text;
+	}
+      else
+	error ("get_help_text: invalid input");
+    }
   else
-    {
-      if (argv[1] == "-i")
-	help_from_info (argv, 2, argc);
-      else
-	builtin_help (argc, argv);
-    }
+    print_usage ();
 
   return retval;
 }
 
-static void
-display_file (std::ostream& os, const std::string& name,
-	      const std::string& fname, const std::string& type,
-	      bool pr_type_info, bool quiet)
-{
-  std::ifstream fs (fname.c_str (), std::ios::in);
-
-  if (fs)
-    {
-      if (pr_type_info && ! quiet)
-	os << name << " is the " << type << " defined from the file\n"
-	   << fname << ":\n\n";
-
-      char ch;
-
-      while (fs.get (ch))
-	os << ch;
-    }
-  else
-    os << "unable to open `" << fname << "' for reading!\n";
-}
-
-static void
-do_type (std::ostream& os, const std::string& name, bool pr_type_info,
-	 bool quiet, bool pr_orig_txt)
+DEFUN (__operators__, , , "-*- texinfo -*-\n\
+@deftypefn {Function File} __operators__ ()\n\
+Return a cell array of strings containing the names of all operators.\n\
+\n\
+This is an internal function and should not be used directly.\n\
+@end deftypefn\n")
 {
-  // FIXME -- should we bother with variables here (earlier versions
-  // of Octave displayed them)?
-
-  octave_value val = symbol_table::varval (name);
-
-  if (val.is_defined ())
-    {
-      if (pr_type_info && ! quiet)
-	os << name << " is a variable\n";
-
-      val.print_raw (os, pr_orig_txt);
-
-      if (pr_type_info)
-	os << "\n";
-    }
-  else
-    {
-      val = symbol_table::find_function (name);
-
-      if (val.is_defined ())
-	{
-	  octave_function *fcn = val.function_value ();
-
-	  if (fcn)
-	    {
-	      std::string fn = fcn->fcn_file_name ();
-
-	      if (fcn->is_builtin_function ())
-		os << name << " is a built-in function" << std::endl;
-	      else if (fcn->is_dld_function () || fcn->is_mex_function ())
-		os << name
-		  << " is a dyanmically loaded function from the file\n"
-		   << fn << std::endl;
-	      else if (pr_orig_txt && ! fn.empty ())
-		display_file (os, name, fn,
-			      val.is_user_script () ? "script" : "function",
-			      pr_type_info, quiet);
-	      else
-		{
-		  if (pr_type_info && ! quiet)
-		    {
-		      os << name;
-
-		      if (fcn->is_user_function ())
-			{
-			  if (fn.empty ())
-			    os << " is a command-line function:\n\n";
-			  else
-			    os << " is a "
-			       << (val.is_user_script ()
-				   ? std::string ("script")
-				   : std::string ("function"))
-			       << " defined from the file\n"
-			       << fn << ":\n\n";
-			}
-		    }
-
-		  tree_print_code tpc (os, "", pr_orig_txt);
-
-		  fcn->accept (tpc);
-		}
-	    }
-	}
-    }
+  return octave_value (Cell (names (operators_map)));
 }
 
-DEFCMD (type, args, nargout,
-  "-*- texinfo -*-\n\
+DEFUN (__keywords__, , , "-*- texinfo -*-\n\
+@deftypefn {Function File} __keywords__ ()\n\
+Return a cell array of strings containing the names of all keywords.\n\
 \n\
-@deffn {Command} type options name @dots{}\n\
-Display the definition of each @var{name} that refers to a function.\n\
-\n\
-Normally also displays whether each @var{name} is user-defined or built-in;\n\
-the @code{-q} option suppresses this behaviour.\n\
-@end deffn")
+This is an internal function and should not be used directly.\n\
+@end deftypefn\n")
 {
-  octave_value retval;
-
-  int argc = args.length () + 1;
-
-  string_vector argv = args.make_argv ("type");
-
-  if (! error_state)
-    {
-      if (argc > 1)
-	{
-	  // FIXME -- we should really use getopt ()
-
-	  bool quiet = false;
-	  bool pr_orig_txt = true;
-
-	  int idx;
-
-	  for (idx = 1; idx < argc; idx++)
-	    {
-	      if (argv[idx] == "-q" || argv[idx] == "-quiet")
-		quiet = true;
-	      else if (argv[idx] == "-t" || argv[idx] == "-transformed")
-		pr_orig_txt = false;
-	      else
-		break;
-	    }
-
-	  if (idx < argc)
-	    {
-	      std::ostringstream output_buf;
-
-	      for (int i = idx; i < argc; i++)
-		{
-		  std::string id = argv[i];
-
-		  if (nargout == 0)
-		    do_type (octave_stdout, id, true, quiet, pr_orig_txt);
-		  else
-		    do_type (output_buf, id, false, quiet, pr_orig_txt);
-
-		  if (error_state)
-		    goto abort;
-		}
-
-	      if (nargout != 0)
-		retval = output_buf.str ();
-	    }
-	  else
-	    print_usage ();
-	}
-      else
-	print_usage ();
-    }
-
- abort:
-
-  return retval;
+  return octave_value (Cell (names (keywords_map)));
 }
 
-std::string
-do_which (const std::string& name)
+DEFUN (__builtins__, , , "-*- texinfo -*-\n\
+@deftypefn {Function File} __builtins__ ()\n\
+Return a cell array of strings containing the names of all builtin functions.\n\
+\n\
+This is an internal function and should not be used directly.\n\
+@end deftypefn\n")
 {
-  std::string retval;
-
-  octave_value val = symbol_table::find_function (name);
-
-  if (val.is_defined ())
-    {
-      octave_function *fcn = val.function_value ();
+  const string_vector bif = symbol_table::built_in_function_names ();
 
-      if (fcn)
-	{
-	  std::string fn = fcn->fcn_file_name ();
-
-	  retval = fn.empty ()
-	    ? (fcn->is_user_function ()
-	       ? "command-line function" : "built-in function")
-	    : fn;
-	}
-    }
-
-  return retval;
+  return octave_value (Cell (bif));
 }
 
-static void
-do_which (std::ostream& os, const std::string& name)
+static std::string
+do_which (const std::string& name, std::string& type)
 {
-  std::string desc;
+  std::string file;
+
+  type = std::string ();
 
   octave_value val = symbol_table::find_function (name);
 
@@ -1369,35 +813,43 @@
 
       if (fcn)
 	{
-	  desc = fcn->fcn_file_name ();
+	  file = fcn->fcn_file_name ();
 
-	  if (desc.empty ())
+	  if (file.empty ())
 	    {
 	      if (fcn->is_user_function ())
-		desc = "is a command-line function";
+		type = "command-line function";
 	      else
-		desc = "is a built-in function";
+		type = "built-in function";
 	    }
 	  else
-	    desc = "is the "
-	      + (val.is_user_script ()
-		 ? std::string ("script") : std::string ("function"))
-	      + " from the file " + desc;
+	    type = val.is_user_script ()
+	      ? std::string ("script") : std::string ("function");
 	}
-      
-      os << "which: `" << name << "' " << desc << std::endl;
     }
+
+  return file;
 }
 
-DEFCMD (which, args, nargout,
+std::string
+do_which (const std::string& name)
+{
+  std::string retval;
+
+  std::string type;
+
+  retval = do_which (name, type);
+
+  return retval;
+}
+
+DEFUN (__which__, args, ,
   "-*- texinfo -*-\n\
-@deffn {Command} which name @dots{}\n\
-Display the type of each @var{name}.  If @var{name} is defined from a\n\
-function file, the full name of the file is also displayed.\n\
-@seealso{help, lookfor}\n\
-@end deffn")
+@deftypefn {Built-in Function} {} __which__ (@var{name}, @dots{})\n\
+Undocumented internal function.\n\
+@end deftypefn")
 {
-  octave_value_list retval;
+  octave_value retval;
 
   string_vector argv = args.make_argv ("which");
 
@@ -1405,20 +857,32 @@
     {
       int argc = argv.length ();
 
-      if (nargout > 0)
-	retval.resize (argc-1, Matrix ());
-
       if (argc > 1)
 	{
+	  Octave_map m (dim_vector (1, argc-1));
+
+	  Cell names (1, argc-1);
+	  Cell files (1, argc-1);
+	  Cell types (1, argc-1);
+
 	  for (int i = 1; i < argc; i++)
 	    {
-	      std::string id = argv[i];
+	      std::string name = argv[i];
+
+	      std::string type;
+
+	      std::string file = do_which (name, type);
 
-	      if (nargout == 0)
-		do_which (octave_stdout, id);
-	      else
-		retval(i-1) = do_which (id);
+	      names(i-1) = name;
+	      files(i-1) = file;
+	      types(i-1) = type;
 	    }
+
+	  m.assign ("name", names);
+	  m.assign ("file", files);
+	  m.assign ("type", types);
+
+	  retval = m;
 	}
       else
 	print_usage ();
@@ -1427,757 +891,95 @@
   return retval;
 }
 
-// FIXME 
-// This function attempts to find the first sentence of a help string, though
-// given that the user can create the help in an arbitrary format, your
-// success might vary.. it works much better with help string formated in
-// texinfo. Using regex might make this function much simpler.
-
-std::string 
-first_help_sentence (const std::string& h, bool short_sentence = true)
+// FIXME -- Are we sure this function always does the right thing?
+inline bool
+file_is_in_dir (const std::string filename, const std::string dir)
 {
-  std::string retval;
-
-  size_t pos = 0;
-
-  if (looks_like_texinfo (h, pos))
-    { 
-     // Get the parsed help string.
-      pos = 0;
-      std::ostringstream os;
-      display_help_text (os, h);
-      std::string h2 = os.str ();
-
-      while (1)
-	{
-	  // Skip leading whitespace and get new line
-	  pos = h2.find_first_not_of ("\n\t ", pos);
-
-	  if (pos == std::string::npos)
-	    break;
-
-	  size_t new_pos = h2.find_first_of ('\n', pos);
-	  std::string line = h2.substr (pos, new_pos-pos);
-
-	  // Skip lines starting in "-"
-	  if (line.find_first_of ('-') == 0)
-	    {
-	      pos = new_pos + 1;
-	      continue;
-	    }
+  if (filename.find (dir) == 0)
+    {
+      const int dir_len = dir.size ();
+      const int filename_len = filename.size ();
+      const int max_allowed_seps = file_ops::is_dir_sep (dir [dir_len-1]) ? 0 : 1;
+      
+      int num_seps = 0;
+      for (int i = dir_len; i < filename_len; i++)
+        if (file_ops::is_dir_sep (filename [i]))
+          num_seps ++;
+      
+      return (num_seps <= max_allowed_seps);
+    }
+  else
+    return false;
+}
 
-	  break;
-	}
-
-      if (pos == std::string::npos)
-	return retval;
-
-      // At start of real text. Get first line with the sentence
-      size_t new_pos = h2.find_first_of ('\n', pos);
-      std::string line = h2.substr (pos, new_pos-pos);
-      size_t dot_pos;
-
-      while ((dot_pos = line.find_first_of ('.')) == std::string::npos)
-	{
-	  // Trim trailing blanks on line
-	  line.substr (0, line.find_last_not_of ("\n\t ") + 1);
-
-	  // Append next line
-	  size_t tmp_pos = h2.find_first_not_of ("\n\t ", new_pos + 1);
-	  if (tmp_pos == std::string::npos || h2.substr (tmp_pos, 1) == "\n")
-	    break;
+DEFUN (__list_functions__, args, , "-*- texinfo -*-\n\
+@deftypefn {Function File} {@var{retval} =} __list_functions__ ()\n\
+@deftypefnx{Function File} {@var{retval} =} __list_functions__ (@var{directory})\n\
+Return the functions available in a given directory.\n\
+\n\
+The function returns a cell array of strings containing the names of all\n\
+functions available in said directory. If no directory is given, the current\n\
+path is searched.\n\
+\n\
+This is an internal function and should not be used directly.\n\
+@seealso{path}\n\
+@end deftypefn\n")
+{
+  octave_value_list retval;
 
-	  new_pos = h2.find_first_of ('\n', tmp_pos);
-	  std::string next = h2.substr (tmp_pos, new_pos-tmp_pos);
-
-	  if (short_sentence)
-	    {
-	      if ((tmp_pos = next.find_first_of ('.')) != std::string::npos)
-		{
-		  line = line + " " + next;
-		  dot_pos = line.find_first_of ('.');
-		}
-	      break;
-	    }
-	  else
-	    line = line + " " + next;
-	}
-
-      if (dot_pos == std::string::npos)
-	retval = line;
-      else
-	retval = line.substr (0, dot_pos + 1);
+  // Get list of functions
+  const string_vector ffl = load_path::fcn_names ();
+  const int ffl_len = ffl.length ();
+  const string_vector afl = autoloaded_functions ();
+  const int afl_len = afl.length ();
+  
+  if (args.length () == 0)
+    {
+      Cell C (ffl_len + afl_len, 1);
+      int j = 0;
+      for (int i = 0; i < ffl_len; i++)
+        C (j++, 0) = octave_value (ffl [i]);
+      for (int i = 0; i < afl_len; i++)
+        C (j++, 0) = octave_value (afl [i]);
+            
+      retval.append (octave_value (C));
     }
   else
     {
-      std::string _upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-      std::string _lower = "abcdefghijklmnopqrstuvwxyz";
-      std::string _alpha = _upper + _lower + "_";
-      std::string _alphanum = _alpha + "1234567890";
-      pos = 0;
-
-      while (1)
-	{
-	  // Skip leading whitespace and get new line
-	  pos = h.find_first_not_of ("\n\t ", pos);
-
-	  if (pos == std::string::npos)
-	    break;
-
-	  size_t new_pos = h.find_first_of ('\n', pos);
-	  std::string line = h.substr (pos, new_pos-pos);
-
-	  // Make a lower case copy to simplify some tests
-	  std::string lower = line;
-	  std::transform (lower.begin (), lower.end (), lower.begin (), tolower);
-
-	  // Skip lines starting in "-" or "Usage"
-	  if (lower.find_first_of ('-') == 0
-	      || lower.substr (0, 5) == "usage")
-	    {
-	      pos = (new_pos == std::string::npos ? std::string::npos : new_pos + 1);
-	      continue;
-	    }
-
-	  size_t line_pos = 0;
-	  size_t tmp_pos = 0;
-
-	  // chop " blah : "
-	  tmp_pos = line.find_first_not_of ("\t ", line.find_first_not_of 
-					     (_alphanum, line_pos));
-	  if (tmp_pos != std::string::npos && line.substr (tmp_pos, 1) == ":")
-	    line_pos = line.find_first_not_of ("\t ", tmp_pos + 1);
-
-	  if (line_pos == std::string::npos)
-	    {
-	      pos = (new_pos == std::string::npos ? std::string::npos : new_pos + 1);
-	      continue;
-	    }
-
-	  // chop " function "
-	  if (lower.substr (line_pos, 8) == "function")
-	    line_pos =  line.find_first_not_of ("\t ", line_pos + 8);
-	  
-	  if (line_pos == std::string::npos)
-	    {
-	      pos = (new_pos == std::string::npos ? std::string::npos : new_pos + 1);
-	      continue;
-	    }
-
-	  // chop " [a,b] = "
-	  if (line.substr (line_pos, 1) == "[")
-	    {
-	      tmp_pos = line.find_first_not_of 
-		("\t ", line.find_first_of ("]", line_pos) + 1);
-
-	      if (tmp_pos != std::string::npos && line.substr (tmp_pos, 1) == "=")
-		line_pos = line.find_first_not_of ("\t ",tmp_pos + 1);
-	    }
-
-	  if (line_pos == std::string::npos)
-	    {
-	      pos = (new_pos == std::string::npos ? std::string::npos : new_pos + 1);
-	      continue;
-	    }
-
-	  // chop " a = "
-	  if (line.find_first_not_of (_alpha, line_pos) != line_pos)
-	    {
-	      tmp_pos = line.find_first_not_of ("\t ", line.find_first_not_of 
-						(_alphanum, line_pos));
-	      if (tmp_pos != std::string::npos && line.substr (tmp_pos, 1) == "=")
-		line_pos = line.find_first_not_of ("\t ", tmp_pos + 1);
-	    }
-
-	  if (line_pos == std::string::npos)
-	    {
-	      pos = new_pos + 1;
-	      continue;
-	    }
-
-	  // chop " f(x) "
-	  if (line.find_first_not_of (_alpha, line_pos) != line_pos)
-	    {
-	      tmp_pos = line.find_first_not_of ("\t ", line.find_first_not_of 
-						(_alphanum, line_pos));
-	      if (tmp_pos != std::string::npos && line.substr (tmp_pos, 1) == "(")
-		line_pos = line.find_first_not_of ("\t ", line.find_first_of 
-						   (")", tmp_pos) + 1);
-	    }
-
-	  if (line_pos == std::string::npos)
-	    {
-	      pos = (new_pos == std::string::npos ? std::string::npos : new_pos + 1);
-	      continue;
-	    }
-
-	  // chop " ; "
-	  if (line.substr (line_pos, 1) == ":"
-	      || line.substr (line_pos, 1) == ";")
-	    line_pos = line.find_first_not_of ("\t ", line_pos + 1);
-
-	  if (line_pos == std::string::npos)
-	    {
-	      pos = (new_pos == std::string::npos ? std::string::npos : new_pos + 1);
-	      continue;
-	    }
-
-	  // chop " BLAH "
-	  if (line.length () > line_pos + 2
-	      && line.find_first_of (_upper, line_pos) == line_pos
-	      && line.find_first_of (_upper, line_pos+1) == line_pos + 1)
-	    line_pos = line.find_first_not_of ("\t ", line.find_first_not_of 
-			(_upper + "0123456789_", line_pos));
-
-	  if (line_pos == std::string::npos)
-	    {
-	      pos = (new_pos == std::string::npos ? std::string::npos : new_pos + 1);
-	      continue;
-	    }
-
-	  // chop " blah --- "
-	  tmp_pos = line.find_first_not_of ("\t ", line.find_first_not_of 
-					     (_alphanum, line_pos));
-	  if (tmp_pos != std::string::npos && line.substr (tmp_pos, 1) == "-")
-	    {
-	      tmp_pos = line.find_first_not_of ("-", tmp_pos);
-	      if (line.substr (tmp_pos, 1) == " "
-		  || line.substr (tmp_pos, 1) == "\t")
-		line_pos = line.find_first_not_of ("\t ", tmp_pos);
-	    }
-
-	  if (line_pos == std::string::npos)
-	    {
-	      pos = (new_pos == std::string::npos ? std::string::npos : new_pos + 1);
-	      continue;
-	    }
-
-	  // chop " blah <TAB> "
-	  if (line.find_first_not_of (_alpha, line_pos) != line_pos)
-	    {
-	      tmp_pos = line.find_first_not_of (" ", line.find_first_not_of 
-						(_alphanum, line_pos));
-	      if (tmp_pos != std::string::npos && line.substr (tmp_pos, 1) == "\t")
-		line_pos = line.find_first_not_of ("\t ", line.find_first_of 
-						   (")", tmp_pos) + 1);
-	    }
-
-	  if (line_pos == std::string::npos)
-	    {
-	      pos = (new_pos == std::string::npos ? std::string::npos : new_pos + 1);
-	      continue;
-	    }
-
-	  // chop " blah  "
-	  if (line.find_first_not_of (_alpha, line_pos) != line_pos)
-	    {
-	      tmp_pos = line.find_first_not_of (_alphanum, line_pos);
-
-	      if (tmp_pos != std::string::npos
-		  && (line.substr (tmp_pos, 2) == "\t\t"
-		      || line.substr (tmp_pos, 2) == "\t "
-		      || line.substr (tmp_pos, 2) == " \t"
-		      || line.substr (tmp_pos, 2) == " "))
-		line_pos = line.find_first_not_of ("\t ", tmp_pos);
-	    }
-
-	  if (line_pos == std::string::npos)
-	    {
-	      pos = (new_pos == std::string::npos ? std::string::npos : new_pos + 1);
-	      continue;
-	    }
-
-	  // skip blah \n or \n blah
-	  // skip blank line
-	  // skip "# !/usr/bin/octave"
-	  if ((line.substr (line_pos , 2) == "or"
-	       && line.find_first_not_of ("\n\t ", line_pos + 2) == std::string::npos)
-	      || line.find_first_not_of ("\n\t ", line_pos) == std::string::npos
-	      || line.substr (line_pos, 2) == "!/")
-	    {
-	      pos = (new_pos == std::string::npos ? std::string::npos : new_pos + 1);
-	      continue;
-	    }
-
-	  // Got the start of first sentence, break.
-	  pos = pos + line_pos;
-	  break;
-	}
-
-      if (pos == std::string::npos)
-	return retval;
-
-      // At start of real text. Get first line with the sentence
-      size_t new_pos = h.find_first_of ('\n', pos);
-      std::string line = h.substr (pos, new_pos-pos);
-      size_t dot_pos;
-
-      while ((dot_pos = line.find_first_of ('.')) == std::string::npos)
-	{
-	  // Trim trailing blanks on line
-	  line = line.substr (0, line.find_last_not_of ("\n\t ") + 1);
-
-	  // Append next line
-	  size_t tmp_pos = h.find_first_not_of ("\t ", new_pos + 1);
-	  if (tmp_pos == std::string::npos || h.substr (tmp_pos, 1) == "\n")
-	    break;
-
-	  new_pos = h.find_first_of ('\n', tmp_pos);
-	  std::string next = h.substr (tmp_pos, new_pos-tmp_pos);
-
-	  if (short_sentence)
-	    {
-	      // Only add the next line if it terminates the sentence, then break
-	      if ((tmp_pos = next.find_first_of ('.')) != std::string::npos)
-		{
-		  line = line + " " + next;
-		  dot_pos = line.find_first_of ('.');
-		}
-	      break;
-	    }
-	  else
-	    line = line + " " + next;
-	}
-
-      if (dot_pos == std::string::npos)
-	retval = line;
+      // Get input
+      std::string dir = args (0).string_value ();
+      if (error_state)
+        error ("__list_functions__: input must be a string");
       else
-	retval = line.substr (0, dot_pos + 1);
-    }
-
-  return retval;
-}
-
-static void
-print_lookfor (const std::string& name, const std::string& line)
-{
-  const size_t deflen = 20;
-
-  size_t max_width = command_editor::terminal_cols () - deflen;
-  if (max_width < deflen)
-    max_width = deflen;
-
-  size_t name_len = name.length ();
-
-  size_t width = max_width;
-  if (name_len > deflen)
-    {
-      width = command_editor::terminal_cols () - name_len;
-      if (width < deflen)
-	width = deflen;
-    }
-
-  size_t pad_len = deflen > name_len ? deflen - name_len + 1 : 1;
-  octave_stdout << name << std::string (pad_len, ' ');
-
-  size_t pos = 0;
-
-  while (1)
-    {
-      size_t new_pos = line.find_first_of ("\n\t ", pos);
-      size_t end_pos = new_pos;
-
-      if (line.length () - pos < width)
-	new_pos = end_pos = std::string::npos;
-      else
-	while (new_pos != std::string::npos && new_pos - pos < width)
-	  {
-	    end_pos = new_pos;
-	    new_pos = line.find_first_of ("\n\t ", new_pos + 1);
-	  }
-
-      octave_stdout << line.substr (pos, end_pos-pos) << std::endl;
-		  
-      if (end_pos == std::string::npos)
-	break;
-
-      pos = end_pos + 1;
-      width = max_width;
-      octave_stdout << std::string (deflen + 1, ' ');
-    }
-}
-
-DEFCMD (lookfor, args, nargout, 
-  "-*- texinfo -*-\n\
-@deffn {Command} lookfor @var{str}\n\
-@deffnx {Command} lookfor -all @var{str}\n\
-@deffnx {Function} {[@var{fun}, @var{helpstring}] = } lookfor (@var{str})\n\
-@deffnx {Function} {[@var{fun}, @var{helpstring}] = } lookfor ('-all', @var{str})\n\
-Search for the string @var{str} in all of the functions found in the\n\
-function search path.  By default @code{lookfor} searches for @var{str}\n\
-in the first sentence of the help string of each function found. The entire\n\
-help string of each function found in the path can be searched if\n\
-the '-all' argument is supplied. All searches are case insensitive.\n\
-\n\
-Called with no output arguments, @code{lookfor} prints the list of matching\n\
-functions to the terminal. Otherwise the output arguments @var{fun} and\n\
-@var{helpstring} define the matching functions and the first sentence of\n\
-each of their help strings.\n\
-\n\
-Note that the ability of @code{lookfor} to correctly identify the first\n\
-sentence of the help of the functions is dependent on the format of the\n\
-functions help. All of the functions in Octave itself will correctly\n\
-find the first sentence, but the same cannot be guaranteed for other\n\
-functions. Therefore the use of the '-all' argument might be necessary\n\
-to find related functions that are not part of Octave.\n\
-@seealso{help, which}\n\
-@end deffn")
-{
-  octave_value_list retval;
-
-  int nargin = args.length ();
-  bool first_sentence_only = true;
-
-  if (nargin != 1 && nargin != 2)
-    {
-      print_usage ();
-      return retval;
-    }
-
-  string_vector ret[2];
-
-  std::string txt;
-
-  if (args(0).is_string ())
-    {
-      txt = args(0).string_value ();
+        {
+          dir = file_ops::canonicalize_file_name (dir);
+          
+          // FIXME -- This seems very inefficient. Is there a better way?
+          std::list<std::string> list;
+          for (int i = 0; i < ffl_len; i++)
+            {
+              const std::string filename = do_which (ffl [i]);
+              if (file_is_in_dir (filename, dir))
+                list.push_back (ffl [i]);
+            }
+          for (int i = 0; i < afl_len; i++)
+            {
+              const std::string filename = do_which (afl [i]);
+              if (file_is_in_dir (filename, dir))
+                list.push_back (afl [i]);
+            }
+            
+          Cell C (list.size (), 1);
+          int j = 0;
+          for (std::list<std::string>::const_iterator iter = list.begin ();
+               iter != list.end (); iter++)
+            {
+              C (j++, 0) = octave_value (*iter);
+            }
 
-      if (nargin == 2)
-	{
-	  if (args(1).is_string ())
-	    {
-	      std::string tmp = args(1).string_value ();
-
-	      if (txt.substr(0,1) == "-")
-		{
-		  txt = tmp;
-		  tmp = args(0).string_value ();
-		}
-
-	      if (tmp == "-all")
-		first_sentence_only = false;
-	      else
-		error ("lookfor: unrecognized option argument");
-	    }
-	  else
-	    error ("lookfor: arguments must be a string");
-	}
-    }
-  else
-    error ("lookfor: argument must be a string");
-
-  if (!error_state)
-    {
-      // All tests in lower case
-      std::transform (txt.begin (), txt.end (), txt.begin (), tolower);
-
-      help_list *ptr = keyword_help ();
-      while (ptr->name)
-	{
-	  std::string name = ptr->name;
-	  std::string h = ptr->help;
-
-	  if (name.find (txt) != std::string::npos)
-	    {
-	      if (nargout)
-		{
-		  ret[0].append (name);
-		  ret[1].append (first_help_sentence (h));
-		}
-	      else
-		print_lookfor (name, first_help_sentence (h));
-	    }
-	  else
-	    {
-	      std::string s;
-
-	      if (first_sentence_only)
-		s = first_help_sentence (h);
-	      else
-		s = h;
-	      
-	      std::transform (s.begin (), s.end (), s.begin (), tolower);
-
-	      if (s.length () > 0 && s.find (txt) != std::string::npos)
-		{
-		  if (nargout)
-		    {
-		      ret[0].append (name);
-		      ret[1].append (first_help_sentence (h));
-		    }
-		  else
-		    print_lookfor (name, first_help_sentence (h));
-		}
-	    }
-
-	  OCTAVE_QUIT;
-
-	  ptr++;
-	}
-
-      ptr = operator_help ();
-      while (ptr->name)
-	{
-	  std::string name = ptr->name;
-	  std::string h = ptr->help;
-
-	  if (name.find (txt) != std::string::npos)
-	    {
-	      if (nargout)
-		{
-		  ret[0].append (name);
-		  ret[1].append (first_help_sentence (h));
-		}
-	      else
-		print_lookfor (name, first_help_sentence (h));
-	    }
-	  else
-	    {
-	      std::string s;
-	      if (first_sentence_only)
-		s = first_help_sentence (h);
-	      else
-		s = h;
-	      
-	      std::transform (s.begin (), s.end (), s.begin (), tolower);
-
-	      if (s.length () > 0 && s.find (txt) != std::string::npos)
-		{
-		  if (nargout)
-		    {
-		      ret[0].append (name);
-		      ret[1].append (first_help_sentence (h));
-		    }
-		  else
-		    print_lookfor (name, first_help_sentence (h));
-		}
-	    }
-
-	  OCTAVE_QUIT;
-
-	  ptr++;
-	}
-
-      string_vector names;
-
-#ifdef OLD_SYMTAB
-      // Check the symbol record table
-      names = fbi_sym_tab->name_list (string_vector (), true);
-
-      for (octave_idx_type i = 0; i < names.length (); i++)
-	{
-	  std::string name = names (i);
-
-	  OCTAVE_QUIT;
-
-	  symbol_record *sr = lookup_by_name (name, 0);
-	  if (sr && sr->is_defined ()
-	      && sr->type_name () != "overloaded function")
-	    {
-	      std::string h = sr->help ();
-
-	      if (name.find (txt) != std::string::npos)
-		{
-		  if (nargout)
-		    {
-		      ret[0].append (name);
-		      ret[1].append (first_help_sentence (h));
-		    }
-		  else
-		    print_lookfor (name, first_help_sentence (h));
-		}
-	      else
-		{
-		  std::string s;
-
-		  if (first_sentence_only)
-		    s = first_help_sentence (h);
-		  else
-		    s = h;
-	      
-		  std::transform (s.begin (), s.end (), s.begin (), tolower);
-
-		  if (s.length () > 0 && s.find (txt) != std::string::npos)
-		    {
-		      if (nargout)
-			{
-			  ret[0].append (name);
-			  ret[1].append (first_help_sentence (h));
-			}
-		      else
-			print_lookfor (name, first_help_sentence (h));
-		    }
-		}
-	    }
-	}
-#endif
-
-      string_vector dirs = load_path::dirs ();
-
-      int len = dirs.length ();
-
-      for (int i = 0; i < len; i++)
-	{
-	  names = load_path::files (dirs[i]);
-
-	  if (! names.empty ())
-	    {
-	      for (int j = 0; j < names.length (); j++)
-		{
-		  std::string name = names (j);
-
-		  OCTAVE_QUIT;
-
-		  // Strip extension
-		  size_t l = name.length ();
-		  if (l > 4 && name.substr (l-4) == ".oct")
-		    name = name.substr (0, l - 4);
-		  else if (l > 2 && name.substr (l-2) == ".m")
-		    name = name.substr (0, l - 2);
-		  else
-		    continue;
-
-#ifdef OLD_SYMTAB
-		  // Check if already in symbol table
-		  symbol_record *sr = fbi_sym_tab->lookup (name);
-
-		  if (!sr)
-		    {
-		      // Check if this version is first in the path
-
-		      std::string file_name = load_path::find_fcn (name);
-		      
-		      std::string dir = dirs[i];
-
-		      if (! file_ops::is_dir_sep (dir[dir.length()-1]))
-			dir += 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 == dir + name + ".oct")
-			    {
-			      // oct-file. Must load to get help
-			      sr = lookup_by_name (name, false);
-
-			      if (sr && sr->is_defined ())
-				h = sr->help ();
-			    }
-			  else
-			    h = get_help_from_file (file_name, symbol_found);
-
-			  if (name.find (txt) != std::string::npos)
-			    {
-			      if (nargout)
-				{
-				  ret[0].append (name);
-				  ret[1].append (first_help_sentence (h));
-				}
-			      else
-				print_lookfor (name, first_help_sentence (h));
-			    }
-			  else
-			    {
-			      std::string s;
-			      if (first_sentence_only)
-				s = first_help_sentence (h);
-			      else
-				s = h;
-
-			      std::transform (s.begin (), s.end (), s.begin (), tolower);
-
-			      if (s.length () > 0 && s.find (txt) != std::string::npos)
-				{
-				  if (nargout)
-				    {
-				      ret[0].append (name);
-				      ret[1].append (first_help_sentence (h));
-				    }
-				  else
-				    print_lookfor (name, first_help_sentence (h));
-				}
-			    }
-			}
-		    }
-#endif
-
-		  // Check if this function has autoloaded functions attached to it
-		  std::string file_name = load_path::find_fcn (name);
-
-		  string_vector autoload_fcns = reverse_lookup_autoload (file_name);
-
-		  if (! autoload_fcns.empty ())
-		    {
-		      for (int k = 0; k < autoload_fcns.length (); k++)
-			{
-			  std::string aname = autoload_fcns (k);
-
-#ifdef OLD_SYMTAB
-			  // Check if already in symbol table
-			  sr = fbi_sym_tab->lookup (aname);
-
-			  if (!sr)
-			    {
-			      // Must load to get help
-			      sr = lookup_by_name (aname, false);
-
-			      std::string h;
-			      if (sr && sr->is_defined ())
-				h = sr->help ();
-
-			      if (aname.find (txt) != std::string::npos)
-				{
-				  if (nargout)
-				    {
-				      ret[0].append (aname);
-				      ret[1].append (first_help_sentence (h));
-				    }
-				  else
-				    print_lookfor (aname, first_help_sentence (h));
-				}
-			      else
-				{
-				  std::string s;
-				  if (first_sentence_only)
-				    s = first_help_sentence (h);
-				  else
-				    s = h;
-
-				  std::transform (s.begin (), s.end (), s.begin (), 
-					     tolower);
-
-				  if (s.length () > 0 && s.find (txt) != std::string::npos)
-				    {
-				      if (nargout)
-					{
-					  ret[0].append (aname);
-					  ret[1].append (first_help_sentence (h));
-					}
-				      else
-					print_lookfor (aname, first_help_sentence (h));
-				    }
-				}
-			    }
-#endif
-			}
-		    }
-		}
-	    }
-	}
-
-      if (nargout != 0)
-	{
-	  retval (1) = ret[1];
-	  retval (0) = ret[0];
-	}
-    }
-  else
-    {
-      error ("lookfor: argument must be a string");
-    }
+          retval.append (octave_value (C));
+        }
+    }  
 
   return retval;
 }
--- a/src/help.h	Thu Jan 22 13:59:33 2009 -0500
+++ b/src/help.h	Thu Jan 22 18:22:52 2009 -0500
@@ -31,12 +31,6 @@
 
 extern string_vector make_name_list (void);
 
-extern void display_help_text (std::ostream&, const std::string&);
-
-extern void display_usage_text (std::ostream&, const std::string&);
-
-extern void additional_help_message (std::ostream&);
-
 extern OCTINTERP_API std::string raw_help (const std::string&, bool&);
 
 // Name of the info file specified on command line.