Logo Search packages:      
Sourcecode: octave2.0 version File versions

help.cc

/*

Copyright (C) 1996 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 "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 "pager.h"
#include "pathsearch.h"
#include "pt-const.h"
#include "pt-exp.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"

// 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;

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.

string_vector
names (help_list *lst, int& count)
{
  string_vector retval;

  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;
}

help_list *
operator_help (void)
{
  return operators;
}

help_list *
keyword_help (void)
{
  return keywords;
}

static void
additional_help_message (ostream& os)
{
  if (! Vsuppress_verbose_help_message)
    os << "\n\
Additional help for builtin 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";

  if (! Vsuppress_verbose_help_message)
    os << "\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";
}

void
print_usage (const string& nm, int just_usage)
{
  symbol_record *sym_rec = global_sym_tab->lookup (nm, 0, 0);
  if (sym_rec)
    {
      string h = sym_rec->help ();

      if (h.length () > 0)
      {
        octave_stdout << "\n*** " << nm << ":\n\n"
          << h << "\n";

        if (! just_usage)
          additional_help_message (octave_stdout);
      }
    }
  else
    warning ("no usage message found for `%s'", nm.c_str ());
}

static void
display_names_from_help_list (ostream& os, help_list *list,
                        const char *desc)
{
  int count = 0;
  string_vector symbols = names (list, count);
  if (! symbols.empty ())
    {
      os << "\n*** " << desc << ":\n\n";
      symbols.list_in_columns (os);
    }
}

static string
print_symbol_type (ostream& os, symbol_record *sym_rec,
               const string& name, int print)
{
  string retval;

  if (sym_rec->is_user_function ())
    {
      tree_fvc *defn = sym_rec->def ();

      string fn = defn->fcn_file_name ();

      if (! fn.empty ())
      {
        string ff = fcn_file_in_path (fn);

        ff = ff.length () > 0 ? ff : fn;

        if (print)
          os << name
             << " is the function defined from: "
             << ff << "\n";
        else
          retval = ff;
      }
      else
      {
        if (print)
          os << name << " is a user-defined function\n";
        else
          retval = "user-defined function";
      }
    }
  else if (sym_rec->is_text_function ())
    {
      if (print)
      os << name << " is a builtin text-function\n";
      else
      retval = "builtin text-function";
    }
  else if (sym_rec->is_builtin_function ())
    {
      if (print)
      os << name << " is a builtin function\n";
      else
      retval = "builtin function";
    }
  else if (sym_rec->is_user_variable ())
    {
      if (print)
      os << name << " is a user-defined variable\n";
      else
      retval = "user-defined variable";
    }
  else if (sym_rec->is_builtin_variable ())
    {
      if (print)
      os << name << " is a builtin variable\n";
      else
      retval = "builtin variable";
    }
  else
    {
      if (print)
      os << "which: `" << name << "' has unknown type\n";
      else
      retval = "unknown type";
    }

  return retval;
}

static void
display_symtab_names (ostream& os, const string_vector& names,
                  int /* count */, const string& desc)
{
  if (! names.empty ())
    {
      os << "\n*** " << desc << ":\n\n";
      names.list_in_columns (os);
    }
}

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");

#ifdef LIST_SYMBOLS
#undef LIST_SYMBOLS
#endif
#define LIST_SYMBOLS(type, msg) \
  do \
    { \
      int count; \
      string_vector names = global_sym_tab->list (count, 0, 0, 1, type); \
      display_symtab_names (octave_stdout, names, count, msg); \
    } \
  while (0)

  // XXX FIXME XXX -- is this distinction needed?

  LIST_SYMBOLS (symbol_def::TEXT_FUNCTION,
            "text functions (these names are also reserved)");

  LIST_SYMBOLS (symbol_def::MAPPER_FUNCTION, "mapper functions");

  LIST_SYMBOLS (symbol_def::BUILTIN_FUNCTION, "general functions");

  LIST_SYMBOLS (symbol_def::BUILTIN_VARIABLE, "builtin variables");

  // 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 ())
      {
        octave_stdout << "\n*** function files in "
                  << make_absolute (dirs[i], Vcurrent_directory)
                  << ":\n\n";

        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 ());
            }
          }
      }
    }
}

int
help_from_list (ostream& os, const help_list *list,
            const string& nm, int usage)
{
  char *name;
  while ((name = list->name) != 0)
    {
      if (strcmp (name, nm.c_str ()) == 0)
      {
        if (usage)
          os << "\nusage: ";
        else
          {
            os << "\n*** " << nm << ":\n\n";
          }

        os << list->help << "\n";

        return 1;
      }
      list++;
    }
  return 0;
}

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;

      symbol_record *sym_rec = lookup_by_name (argv[i], 0);

      if (sym_rec && sym_rec->is_defined ())
      {
        string h = sym_rec->help ();

        if (h.length () > 0)
          {
            print_symbol_type (octave_stdout, sym_rec, argv[i], 1);
            octave_stdout << "\n" << h << "\n";
            continue;
          }
      }

      string path = fcn_file_in_path (argv[i]);

      string h = get_help_from_file (path);

      if (! h.empty ())
      {
        octave_stdout << argv[i] << " is the file: "
          << path << "\n\n" << h << "\n";

        continue;
      }

      octave_stdout << "\nhelp: sorry, `" << argv[i]
                << "' is not documented\n"; 
    }

  additional_help_message (octave_stdout);
}

DEFUN_TEXT (help, args, ,
  "help [-i] [topic ...]\n\
\n\
print cryptic yet witty messages")
{
  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].length () > 0 && argv[1] == "-i")
      {
        help_from_info (argv, 2, argc);
      }
      else
      {
        builtin_help (argc, argv);
      }
    }

  return retval;
}

DEFUN_TEXT (type, args, nargout,
  "type NAME ...]\n\
\n\
display the definition of each NAME that refers to a function")
{
  octave_value_list retval;

  begin_unwind_frame ("Ftype");

  int argc = args.length () + 1;

  string_vector argv = args.make_argv ("type");

  if (error_state)
    return retval;

  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)
      {
        print_usage ("type");
        return retval;
      }

      ostrstream output_buf;

      for (int i = idx; i < argc; i++)
      {
        string id = argv[i];
        string elts;

        if (id[id.length () - 1] != '.')
          {
            size_t pos = id.find ('.');

            if (pos != NPOS)
            {
              elts = id.substr (pos+1);
              id = id.substr (0, pos);
            }
          }

        symbol_record *sym_rec = lookup_by_name (id, 0);

        if (sym_rec && sym_rec->is_defined ())
          {
            if (sym_rec->is_user_function ())
            {
              tree_fvc *defn = sym_rec->def ();

              string fn = defn->fcn_file_name ();
              string ff = fcn_file_in_path (fn);

              if (pr_orig_txt && ! ff.empty ())
                {
                  ifstream fs (ff.c_str (), ios::in);

                  if (fs)
                  {
                    if (nargout == 0 && ! quiet)
                      output_buf << argv[i]
                               << " is the function defined from: "
                               << ff << "\n\n";

                    char ch;

                    while (fs.get (ch))
                      output_buf << ch;
                  }
                  else
                  output_buf << "unable to open `" << ff
                           << "' for reading!\n";
                }
              else
                {
                  if (nargout == 0 && ! quiet)
                  output_buf << argv[i]
                           << " is a user-defined function:\n\n";

                  tree_print_code tpc (output_buf, "", pr_orig_txt);

                  defn->accept (tpc);
                }
            }

            // XXX FIXME XXX -- this code should be shared with
            // Fwhich.

            else if (sym_rec->is_text_function ())
            output_buf << argv[i] << " is a builtin text-function\n";
            else if (sym_rec->is_builtin_function ())
            output_buf << argv[i] << " is a builtin function\n";
            else if (sym_rec->is_user_variable ()
                   || sym_rec->is_builtin_variable ())
            {
              tree_fvc *defn = sym_rec->def ();

              assert (defn && defn->is_constant ());

              tree_constant *tmp = (tree_constant *) defn;

              int var_ok = 1;
              if (tmp && tmp->is_map ())
                {
                  if (! elts.empty ())
                  {
                    octave_value ult =
                      tmp->lookup_map_element (elts, 0, 1);

                    if (! ult.is_defined ())
                      var_ok = 0;                   
                  }
                }

              if (nargout == 0 && ! quiet)
                {
                  if (var_ok)
                  {
                    output_buf << argv[i];
                    if (sym_rec->is_user_variable ())
                      output_buf << " is a user-defined variable\n";
                    else
                      output_buf << " is a built-in variable\n";
                  }
                  else
                  {
                    if (! elts.empty ())
                      output_buf << "type: structure `" << id
                        << "' has no member `" << elts << "'\n";
                    else
                      output_buf << "type: `" << id
                        << "' has unknown type!\n";
                  }
                }
              if (! tmp->is_map ())
                {
                  tree_print_code tpc (output_buf, "", pr_orig_txt);

                  tmp->accept (tpc);

                  if (nargout == 0)
                  output_buf << "\n";
                }
            }
            else
            output_buf << "type: `" << argv[i] << "' has unknown type!\n";
          }
        else
          {
            string ff = fcn_file_in_path (argv[i]);

            if (pr_orig_txt && ! ff.empty ())
            {
              ifstream fs (ff.c_str (), ios::in);

              if (fs)
                {
                  if (nargout == 0 && ! quiet)
                  output_buf << argv[i] << " is the script file: "
                           << ff << "\n\n";

                  char ch;

                  while (fs.get (ch))
                  output_buf << ch;
                }
              else
                output_buf << "unable to open `" << ff
                         << "' for reading!\n";
            }
            else
            output_buf << "type: `" << argv[i] << "' undefined\n";
          }     
      }

      output_buf << ends;

      char *s = output_buf.str ();

      if (nargout == 0)
      octave_stdout << s;
      else
      retval = s;

      delete [] s;
    }
  else
    print_usage ("type");

  return retval;
}

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;

  int argc = args.length () + 1;

  string_vector argv = args.make_argv ("which");

  if (error_state)
    return retval;

  if (argc > 1)
    {
      if (nargout > 0)
      retval.resize (argc-1, Matrix ());

      for (int i = 1; i < argc; i++)
      {
        symbol_record *sym_rec = lookup_by_name (argv[i], 0);

        if (sym_rec && sym_rec->is_defined ())
          {
            int print = (nargout == 0);
            string tmp = print_symbol_type (octave_stdout, sym_rec,
                                    argv[i], print);
            if (! print)
            retval(i) = tmp;
          }
        else
          {
            string path = fcn_file_in_path (argv[i]);

            if (! path.empty ())
            {
              if (nargout == 0)
                octave_stdout << "which: `" << argv[i]
                          << "' is the script file: "
                          << path << "\n";
              else
                retval(i) = path;
            }
            else
            {
              if (nargout == 0)
                octave_stdout << "which: `" << argv[i]
                          << "' is undefined\n";
              else
                retval(i) = "undefined";
            }
          }
      }
    }
  else
    print_usage ("which");

  return retval;
}

static int
suppress_verbose_help_message (void)
{
  Vsuppress_verbose_help_message
    = check_preference ("suppress_verbose_help_message");

  return 0;
}

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;
}

void
symbols_of_help (void)
{
  DEFVAR (INFO_FILE, Vinfo_file, 0, info_file,
    "name of the Octave info file");

  DEFVAR (INFO_PROGRAM, Vinfo_prog, 0, info_prog,
    "name of the Octave info reader");

  DEFVAR (suppress_verbose_help_message, 0.0, 0, suppress_verbose_help_message,
    "suppress printing of message pointing to additional help in the\n\
help and usage functions");
}

/*
;;; Local Variables: ***
;;; mode: C++ ***
;;; End: ***
*/

Generated by  Doxygen 1.6.0   Back to index