Mercurial > hg > octave-nkf
view src/help.cc @ 3355:c4983fc7318f
[project @ 1999-11-18 05:20:50 by jwe]
author | jwe |
---|---|
date | Thu, 18 Nov 1999 05:20:52 +0000 |
parents | 15cddaacbc2d |
children | d2e12e998a78 |
line wrap: on
line source
/* Copyright (C) 1996, 1997 John W. Eaton This file is part of Octave. Octave is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. Octave is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Octave; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include <config.h> #endif #include <csignal> #include <cstdlib> #include <cstring> #include <string> #include <iostream.h> #include <fstream.h> #include <strstream.h> #ifdef HAVE_UNISTD_H #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif #include <unistd.h> #endif #include "cmd-edit.h" #include "file-ops.h" #include "oct-env.h" #include "str-vec.h" #include <defaults.h> #include "defun.h" #include "dirfns.h" #include "error.h" #include "fn-cache.h" #include "gripes.h" #include "help.h" #include "input.h" #include "oct-obj.h" #include "ov-usr-fcn.h" #include "pager.h" #include "parse.h" #include "pathsearch.h" #include "procstream.h" #include "pt-pr-code.h" #include "sighandlers.h" #include "symtab.h" #include "syswait.h" #include "toplev.h" #include "unwind-prot.h" #include "utils.h" #include "variables.h" #include "version.h" // Name of the info file specified on command line. // (--info-file file) string Vinfo_file; // Name of the info reader we'd like to use. // (--info-program program) string Vinfo_prog; // If TRUE, don't print additional help message in help and usage // functions. static bool Vsuppress_verbose_help_message; // XXX FIXME XXX -- maybe this should use string instead of char*. struct help_list { const char *name; const char *help; }; static help_list operators[] = { { "!", "Logical not operator. See also `~'.\n", }, { "!=", "Logical not equals operator. See also `~' and `<>'.\n", }, { "\"", "String delimiter.\n", }, { "#", "Begin comment character. See also `%'.", }, { "%", "Begin comment charcter. See also `#'.", }, { "&", "Logical and operator. See also `&&'.", }, { "&&", "Logical and operator. See also `&'.", }, { "'", "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.", }, { ")", "Array index or function argument delimiter.", }, { "*", "Multiplication operator. See also `.*'", }, { "**", "Power operator. See also `^', `.**', and `.^'", }, { "+", "Addition operator.", }, { "++", "Increment operator. As in C, may be applied as a prefix or postfix operator.", }, { ",", "Array index, function argument, or command separator.", }, { "-", "Subtraction or unary negation operator.", }, { "--", "Decrement operator. As in C, may be applied as a prefix or postfix operator.", }, { ".'", "Matrix transpose operator. For complex matrices, computes the\n\ transpose, *not* the complex conjugate transpose. See also `''.", }, { ".*", "Element by element multiplication operator. See also `*'.", }, { ".**", "Element by element power operator. See also `**', `^', and `.^'.", }, { "./", "Element by element division operator. See also `/' and `\\'.", }, { ".^", "Element by element power operator. See also `**', `^', and `.^'.", }, { "/", "Right division. See also `\\' and `./'.", }, { ":", "Select entire rows or columns of matrices.", }, { ";", "Array row or command separator. See also `,'.", }, { "<", "Less than operator.", }, { "<=", "Less than or equals operator.", }, { "<>", "Logical not equals operator. See also `!=' and `~='.", }, { "=", "Assignment operator.", }, { "==", "Equality test operator.", }, { ">", "Greater than operator.", }, { ">=", "Greater than or equals operator.", }, { "[", "Return list delimiter. See also `]'.", }, { "\\", "Left division operator. See also `/' and `./'.", }, { "]", "Return list delimiter. See also `['.", }, { "^", "Power operator. See also `**', `.^', and `.**.'", }, { "|", "Logical or operator. See also `||'.", }, { "||", "Logical or operator. See also `|'.", }, { "~", "Logical not operator. See also `!' and `~'.", }, { "~=", "Logical not equals operator. See also `<>' and `!='.", }, { 0, 0, }, }; static help_list keywords[] = { { "all_va_args", "Pass all unnamed arguments to another function call.", }, { "break", "Exit the innermost enclosing while or for loop.", }, { "catch", "begin the cleanup part of a try-catch block", }, { "continue", "Jump to the end of the innermost enclosing while or for loop.", }, { "else", "Alternate action for an if block.", }, { "elseif", "Alternate conditional test for an if block.", }, { "end", "Mark the end of any for, if, while, or function block.", }, { "end_try_catch", "Mark the end of an try-catch block.", }, { "end_unwind_protect", "Mark the end of an unwind_protect block.", }, { "endfor", "Mark the end of a for loop.", }, { "endfunction", "Mark the end of a function.", }, { "endif", "Mark the end of an if block.", }, { "endwhile", "Mark the end of a while loop.", }, { "for", "Begin a for loop.", }, { "function", "Begin a function body.", }, { "global", "Declare variables to have global scope.", }, { "gplot", "Produce 2-D plots using gnuplot-like command syntax.", }, { "gsplot", "Produce 3-D plots using gnuplot-like command syntax.", }, { "if", "Begin an if block.", }, { "return", "Return from a function.", }, { "try", "Begin a try-catch block.", }, { "unwind_protect", "Begin an unwind_protect block.", }, { "unwind_protect_cleanup", "Begin the cleanup section of an unwind_protect block.", }, { "while", "Begin a while loop.", }, { 0, 0, }, }; // Return a copy of the operator or keyword names. static string_vector names (help_list *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++; } } return retval; } static help_list * operator_help (void) { return operators; } static help_list * keyword_help (void) { return keywords; } // It's not likely that this does the right thing now. XXX FIXME XXX string_vector make_name_list (void) { string_vector key = names (keyword_help ()); int key_len = key.length (); string_vector glb = global_sym_tab->name_list (); int glb_len = glb.length (); string_vector top = top_level_sym_tab->name_list (); int top_len = top.length (); string_vector lcl; if (top_level_sym_tab != curr_sym_tab) lcl = curr_sym_tab->name_list (); int lcl_len = lcl.length (); string_vector ffl = octave_fcn_file_name_cache::list_no_suffix (); int ffl_len = ffl.length (); int total_len = key_len + glb_len + top_len + lcl_len + ffl_len; string_vector list (total_len); // Put all the symbols in one big list. int j = 0; int i = 0; for (i = 0; i < key_len; i++) list[j++] = key[i]; for (i = 0; i < glb_len; i++) list[j++] = glb[i]; for (i = 0; i < top_len; i++) list[j++] = top[i]; for (i = 0; i < lcl_len; i++) list[j++] = lcl[i]; for (i = 0; i < ffl_len; i++) list[j++] = ffl[i]; return list; } void additional_help_message (ostream& os) { if (! Vsuppress_verbose_help_message) os << "\n\ Additional help for built-in functions, operators, and variables\n\ is available in the on-line version of the manual. Use the command\n\ `help -i <topic>' to search the manual index.\n\ \n\ Help and information about Octave is also available on the WWW\n\ at http://www.che.wisc.edu/octave/octave.html and via the\n\ help-octave@bevo.che.wisc.edu mailing list.\n"; } // XXX FIXME XXX -- this needs a major overhaul to cope with new // symbol table stuff. static void display_names_from_help_list (ostream& os, help_list *list, const char *desc) { string_vector symbols = names (list); if (! symbols.empty ()) { os << "\n*** " << desc << ":\n\n"; symbols.qsort (); symbols.list_in_columns (os); } } static void display_symtab_names (ostream& os, const string_vector& names, const string& desc) { if (! names.empty ()) { os << "\n*** " << desc << ":\n\n"; names.list_in_columns (os); } } #ifdef LIST_SYMBOLS #undef LIST_SYMBOLS #endif #define LIST_SYMBOLS(type, msg) \ do \ { \ string_vector names \ = global_sym_tab->name_list (string_vector (), true, type); \ display_symtab_names (octave_stdout, names, msg); \ } \ while (0) static void simple_help (void) { 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"); // XXX FIXME XXX -- is this distinction needed? LIST_SYMBOLS (symbol_record::BUILTIN_CONSTANT, "built-in constants"); LIST_SYMBOLS (symbol_record::BUILTIN_VARIABLE, "built-in variables"); LIST_SYMBOLS (symbol_record::TEXT_FUNCTION, "text functions (these names are also reserved)"); LIST_SYMBOLS (symbol_record::MAPPER_FUNCTION, "mapper functions"); LIST_SYMBOLS (symbol_record::BUILTIN_FUNCTION, "general functions"); // Also need to list variables and currently compiled functions from // the symbol table, if there are any. // Also need to search octave_path for script files. string_vector dirs = Vload_path_dir_path.all_directories (); int len = dirs.length (); for (int i = 0; i < len; i++) { string_vector names = octave_fcn_file_name_cache::list (dirs[i]); if (! names.empty ()) { string dir = octave_env::make_absolute (dirs[i], octave_env::getcwd ()); octave_stdout << "\n*** function files in " << dir << ":\n\n"; names.qsort (); names.list_in_columns (octave_stdout); } } } static int try_info (const string& nm) { int status = 0; static char *cmd_str = 0; delete [] cmd_str; cmd_str = 0; ostrstream cmd_buf; cmd_buf << Vinfo_prog << " --file " << Vinfo_file; string directory_name = Vinfo_file; size_t pos = directory_name.rfind ('/'); if (pos != NPOS) { directory_name.resize (pos + 1); cmd_buf << " --directory " << directory_name; } if (nm.length () > 0) cmd_buf << " --index-search " << nm; cmd_buf << ends; cmd_str = cmd_buf.str (); volatile octave_interrupt_handler old_interrupt_handler = octave_ignore_interrupts (); status = system (cmd_str); octave_set_interrupt_handler (old_interrupt_handler); if (WIFEXITED (status)) status = WEXITSTATUS (status); else status = 127; return status; } static void help_from_info (const string_vector& argv, int idx, int argc) { if (idx == argc) try_info (string ()); else { for (int i = idx; i < argc; i++) { int status = try_info (argv[i]); if (status) { if (status == 127) { error ("help: unable to find info"); error ("help: you need info 2.18 or later (texinfo 3.12)"); break; } else { message ("help", "sorry, `%s' is not indexed in the manual", argv[i].c_str ()); } } } } } static bool looks_like_texinfo (const string& msg, size_t& p1) { p1 = msg.find ('\n'); string t = msg.substr (0, p1); if (p1 == NPOS) p1 = 0; size_t p2 = t.find ("-*- texinfo -*-"); return (p2 != NPOS); } void display_help_text (ostream& os, const string& msg) { // Look for "-*- texinfo -*-" in first line of help message. If it // is present, use makeinfo to format the rest of the message before // sending it to the output stream. Otherwise, just print the // message. size_t pos; if (looks_like_texinfo (msg, pos)) { string tmp_file_name = file_ops::tempnam ("", ""); int cols = command_editor::terminal_cols (); if (cols > 16) cols--; if (cols > 64) cols -= 7; if (cols > 80) cols = 72; ostrstream buf; buf << "sed 's/^[#%]+ *//' | makeinfo" << " -D \"VERSION " << OCTAVE_VERSION << "\"" << " -D \"OCTAVEHOME " << OCTAVE_PREFIX << "\"" << " -D \"TARGETHOSTTYPE " << CANONICAL_HOST_TYPE << "\"" << " --fill-column " << cols << " --no-warn" << " --no-validate" << " --no-headers" << " --force" << " --output " << tmp_file_name << " > /dev/null 2>&1" << ends; char *cmd = buf.str (); oprocstream filter (cmd); delete [] cmd; if (filter) { filter << msg.substr (pos+1); filter.close (); ifstream tmp_file (tmp_file_name.c_str ()); int c; while ((c = tmp_file.get ()) != EOF) os << (char) c; tmp_file.close (); file_ops::unlink (tmp_file_name); } else os << msg; } else os << msg; } static bool help_from_list (ostream& os, const help_list *list, const string& nm, int usage) { const char *name; while ((name = list->name) != 0) { if (strcmp (name, nm.c_str ()) == 0) { if (usage) os << "\nusage: "; else { os << "\n*** " << nm << ":\n\n"; } display_help_text (os, list->help); os << "\n"; return true; } list++; } return false; } static bool help_from_symbol_table (ostream& os, const string& nm) { bool retval = false; symbol_record *sym_rec = lookup_by_name (nm, 0); if (sym_rec && sym_rec->is_defined ()) { string h = sym_rec->help (); if (h.length () > 0) { sym_rec->which (os); os << "\n"; display_help_text (os, h); os << "\n"; retval = true; } } return retval; } static bool help_from_file (ostream& os, const string& nm) { bool retval = false; string path = fcn_file_in_path (nm); string h = get_help_from_file (path); if (! h.empty ()) { os << nm << " is the file: " << path << "\n\n"; display_help_text (os, h); os << "\n"; retval = true; } return retval; } static void builtin_help (int argc, const string_vector& argv) { help_list *op_help_list = operator_help (); help_list *kw_help_list = keyword_help (); for (int i = 1; i < argc; i++) { if (help_from_list (octave_stdout, op_help_list, argv[i], 0)) continue; if (help_from_list (octave_stdout, kw_help_list, argv[i], 0)) continue; if (help_from_symbol_table (octave_stdout, argv[i])) continue; if (help_from_file (octave_stdout, argv[i])) continue; octave_stdout << "\nhelp: sorry, `" << argv[i] << "' is not documented\n"; } additional_help_message (octave_stdout); } DEFUN_TEXT (help, args, , "-*- texinfo -*-\n\ @deffn {Command} help\n\ Octave's @code{help} command can be used to print brief usage-style\n\ messages, or to display information directly from an on-line version of\n\ the printed manual, using the GNU Info browser. If invoked without any\n\ arguments, @code{help} prints a list of all the available operators,\n\ functions, and built-in variables. If the first argument is @code{-i},\n\ the @code{help} command searches the index of the on-line version of\n\ this manual for the given topics.\n\ \n\ For example, the command @kbd{help help} prints a short message\n\ describing the @code{help} command, and @kbd{help -i help} starts the\n\ GNU Info browser at this node in the on-line version of the manual.\n\ \n\ Once the GNU Info browser is running, help for using it is available\n\ using the command @kbd{C-h}.\n\ @end deffn") { octave_value_list retval; int argc = args.length () + 1; string_vector argv = args.make_argv ("help"); if (error_state) return retval; if (argc == 1) simple_help (); else { if (argv[1] == "-i") help_from_info (argv, 2, argc); else builtin_help (argc, argv); } return retval; } static void do_type (ostream& os, const string& name, bool pr_type_info, bool quiet, bool pr_orig_txt) { symbol_record *sym_rec = lookup_by_name (name, 0); if (sym_rec && sym_rec->is_defined ()) { if (sym_rec->is_user_function ()) { octave_value tmp = sym_rec->def (); octave_function *defn = tmp.function_value (); string fn = defn ? defn->fcn_file_name () : string (); if (pr_orig_txt && ! fn.empty ()) { ifstream fs (fn.c_str (), ios::in); if (fs) { if (pr_type_info && ! quiet) os << name << " is the function defined from: " << fn << "\n\n"; char ch; while (fs.get (ch)) os << ch; } else os << "unable to open `" << fn << "' for reading!\n"; } else { if (pr_type_info && ! quiet) os << name << " is a user-defined function:\n\n"; tree_print_code tpc (os, "", pr_orig_txt); defn->accept (tpc); } } // XXX FIXME XXX -- this code should be shared with // Fwhich. else if (sym_rec->is_text_function ()) os << name << " is a built-in text-function\n"; else if (sym_rec->is_builtin_function ()) os << name << " is a built-in function\n"; else if (sym_rec->is_user_variable () || sym_rec->is_builtin_variable () || sym_rec->is_builtin_constant ()) { octave_value defn = sym_rec->def (); int var_ok = 1; if (! error_state) { if (pr_type_info && ! quiet) { if (var_ok) { os << name; if (sym_rec->is_user_variable ()) os << " is a user-defined variable\n"; else if (sym_rec->is_builtin_variable ()) os << " is a built-in variable\n"; else if (sym_rec->is_builtin_constant ()) os << " is a built-in constant\n"; else panic_impossible (); } else os << "type: `" << name << "' has unknown type!\n"; } defn.print_raw (os, true); if (pr_type_info) os << "\n"; } } else error ("type: `%s' has unknown type!", name.c_str ()); } else { string ff = fcn_file_in_path (name); if (! ff.empty ()) { ifstream fs (ff.c_str (), ios::in); if (fs) { if (pr_type_info && ! quiet) os << name << " is the script file: " << ff << "\n\n"; char ch; while (fs.get (ch)) os << ch; } else os << "unable to open `" << ff << "' for reading!\n"; } else error ("type: `%s' undefined", name.c_str ()); } } DEFUN_TEXT (type, args, nargout, "type NAME\n\ \n\ display the definition of each NAME that refers to a function") { octave_value_list retval; unwind_protect::begin_frame ("Ftype"); int argc = args.length () + 1; string_vector argv = args.make_argv ("type"); if (! error_state) { if (argc > 1) { // XXX FIXME XXX -- 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) { ostrstream output_buf; for (int i = idx; i < argc; i++) { 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) { output_buf << ends; char *s = output_buf.str (); retval = s; delete [] s; } } else print_usage ("type"); } else print_usage ("type"); } abort: return retval; } static string do_which (const string& name) { string retval; symbol_record *sym_rec = lookup_by_name (name, 0); if (sym_rec && sym_rec->is_defined ()) retval = sym_rec->which (); else { string path = fcn_file_in_path (name); if (! path.empty ()) retval = path; else retval = "undefined"; } return retval; } static void do_which (ostream& os, const string& name) { symbol_record *sym_rec = lookup_by_name (name, 0); if (sym_rec && sym_rec->is_defined ()) sym_rec->which (os); else { string path = fcn_file_in_path (name); if (! path.empty ()) os << "which: `" << name << "' is the script file\n" << path << "\n"; else os << "which: `" << name << "' is undefined\n"; } } DEFUN_TEXT (which, args, nargout, "which NAME ...\n\ \n\ display the type of each NAME. If NAME is defined from an function\n\ file, print the full name of the file.") { octave_value_list retval; string_vector argv = args.make_argv ("which"); if (! error_state) { int argc = argv.length (); if (nargout > 0) retval.resize (argc-1, Matrix ()); if (argc > 1) { for (int i = 1; i < argc; i++) { string id = argv[i]; if (nargout == 0) do_which (octave_stdout, id); else retval(i-1) = do_which (id); } } else print_usage (argv[0]); } return retval; } static int info_file (void) { int status = 0; string s = builtin_string_variable ("INFO_FILE"); if (s.empty ()) { gripe_invalid_value_specified ("INFO_FILE"); status = -1; } else Vinfo_file = s; return status; } static int info_prog (void) { int status = 0; string s = builtin_string_variable ("INFO_PROGRAM"); if (s.empty ()) { gripe_invalid_value_specified ("INFO_PROGRAM"); status = -1; } else Vinfo_prog = s; return status; } static int suppress_verbose_help_message (void) { Vsuppress_verbose_help_message = check_preference ("suppress_verbose_help_message"); return 0; } void symbols_of_help (void) { DEFVAR (INFO_FILE, Vinfo_file, info_file, "-*- texinfo -*-\n\ @defvr {Built-in Variable} INFO_FILE\n\ The variable @code{INFO_FILE} names the location of the Octave info file.\n\ The default value is @code{\"@var{octave-home}/info/octave.info\"}, where\n\ @var{octave-home} is the directory where all of Octave is installed.\n\ @end defvr"); DEFVAR (INFO_PROGRAM, Vinfo_prog, info_prog, "-*- texinfo -*-\n\ @defvr {Built-in Variable} INFO_FILE\n\ The variable @code{INFO_FILE} names the location of the Octave info file.\n\ The default value is @code{\"@var{octave-home}/info/octave.info\"}, where\n\ @var{octave-home} is the directory where all of Octave is installed.\n\ @end defvr"); DEFVAR (suppress_verbose_help_message, 0.0, suppress_verbose_help_message, "-*- texinfo -*-\n\ @defvr {Built-in Variable} suppress_verbose_help_message\n\ If the value of @code{suppress_verbose_help_message} is nonzero, Octave\n\ will not add additional help information to the end of the output from\n\ the @code{help} command and usage messages for built-in commands.\n\ @end defvr"); } /* ;;; Local Variables: *** ;;; mode: C++ *** ;;; End: *** */