# HG changeset patch # User Soren Hauberg # Date 1232666572 18000 # Node ID f134925a1cfa5efa485f1ea95b82a00182f61bf7 # Parent 83b8c739d62612c96809a612511e8af2116626ed m-file implementation of help system diff --git a/scripts/ChangeLog b/scripts/ChangeLog --- a/scripts/ChangeLog +++ b/scripts/ChangeLog @@ -1,3 +1,27 @@ +2009-01-22 John W. Eaton + + * 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 + + * 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 * optimization/fsolve.m: Undo the last change. diff --git a/scripts/Makefile.in b/scripts/Makefile.in --- a/scripts/Makefile.in +++ b/scripts/Makefile.in @@ -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 diff --git a/scripts/configure.in b/scripts/configure.in --- a/scripts/configure.in +++ b/scripts/configure.in @@ -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 \ diff --git a/scripts/help/Makefile.in b/scripts/help/Makefile.in new file mode 100644 --- /dev/null +++ b/scripts/help/Makefile.in @@ -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 +# . + +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 diff --git a/scripts/help/__additional_help_message__.m b/scripts/help/__additional_help_message__.m new file mode 100644 --- /dev/null +++ b/scripts/help/__additional_help_message__.m @@ -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 +## . + +## -*- 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 ' 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 diff --git a/scripts/help/__strip_html_tags__.m b/scripts/help/__strip_html_tags__.m new file mode 100644 --- /dev/null +++ b/scripts/help/__strip_html_tags__.m @@ -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 +## . + +## -*- 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 diff --git a/scripts/miscellaneous/doc.m b/scripts/help/doc.m rename from scripts/miscellaneous/doc.m rename to scripts/help/doc.m --- a/scripts/miscellaneous/doc.m +++ b/scripts/help/doc.m @@ -1,4 +1,4 @@ -## Copyright (C) 2005, 2006, 2007 Soren Hauberg +## Copyright (C) 2005, 2006, 2007, 2009 Søren Hauberg ## ## This file is part of Octave. ## diff --git a/scripts/help/gen_doc_cache.m b/scripts/help/gen_doc_cache.m new file mode 100644 --- /dev/null +++ b/scripts/help/gen_doc_cache.m @@ -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 +## . + +## -*- 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 + diff --git a/scripts/help/get_first_help_sentence.m b/scripts/help/get_first_help_sentence.m new file mode 100644 --- /dev/null +++ b/scripts/help/get_first_help_sentence.m @@ -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 +## . + +## -*- 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 + diff --git a/scripts/help/help.m b/scripts/help/help.m new file mode 100644 --- /dev/null +++ b/scripts/help/help.m @@ -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 +## . + +## -*- 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 + diff --git a/scripts/help/lookfor.m b/scripts/help/lookfor.m new file mode 100644 --- /dev/null +++ b/scripts/help/lookfor.m @@ -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 +## . + +## -*- 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 + diff --git a/scripts/help/makeinfo.m b/scripts/help/makeinfo.m new file mode 100644 --- /dev/null +++ b/scripts/help/makeinfo.m @@ -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 +## . + +## -*- 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 diff --git a/scripts/help/print_usage.m b/scripts/help/print_usage.m new file mode 100644 --- /dev/null +++ b/scripts/help/print_usage.m @@ -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 +## . + +## -*- 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 + diff --git a/scripts/help/type.m b/scripts/help/type.m new file mode 100644 --- /dev/null +++ b/scripts/help/type.m @@ -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 +## . + +## -*- 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 + diff --git a/scripts/help/which.m b/scripts/help/which.m new file mode 100644 --- /dev/null +++ b/scripts/help/which.m @@ -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 +## . + +## -*- 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 diff --git a/scripts/miscellaneous/Makefile.in b/scripts/miscellaneous/Makefile.in --- a/scripts/miscellaneous/Makefile.in +++ b/scripts/miscellaneous/Makefile.in @@ -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 \ diff --git a/scripts/pkg/pkg.m b/scripts/pkg/pkg.m --- a/scripts/pkg/pkg.m +++ b/scripts/pkg/pkg.m @@ -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"}; diff --git a/src/ChangeLog b/src/ChangeLog --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,31 @@ +2009-01-22 John W. Eaton + + * 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 + + * 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 * toplev.cc (octave_config_info): Check OCTAVEUSE_OS_X_API instead diff --git a/src/defun-int.h b/src/defun-int.h --- a/src/defun-int.h +++ b/src/defun-int.h @@ -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); diff --git a/src/defun.cc b/src/defun.cc --- a/src/defun.cc +++ b/src/defun.cc @@ -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 diff --git a/src/help.cc b/src/help.cc --- a/src/help.cc +++ b/src/help.cc @@ -86,258 +86,259 @@ // functions. static bool Vsuppress_verbose_help_message = false; -// FIXME -- maybe this should use string instead of char*. +#include -struct help_list -{ - const char *name; - const char *help; -}; +typedef std::map map_type; +typedef map_type::value_type pair_type; +typedef map_type::const_iterator map_iter; -static help_list operators[] = +template +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 ' 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& 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 (" 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 " - 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 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::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; } diff --git a/src/help.h b/src/help.h --- a/src/help.h +++ b/src/help.h @@ -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.