# HG changeset patch # User David Bateman # Date 1211141685 -7200 # Node ID 791231dac33344e930465319ec447f3c8ad18ed5 # Parent 97b7ba81e1c301c1e43e7a98a00c40891220ddc1 Add regexp matching to Fwho and Fclear diff --git a/liboctave/ChangeLog b/liboctave/ChangeLog --- a/liboctave/ChangeLog +++ b/liboctave/ChangeLog @@ -1,3 +1,10 @@ +2008-05-20 David Bateman + + * regex-match.cc, regex-match.h: New class for simple regular + expression matching + * Makefile.in (INCLUDES): Add regex-match.h here, and + (LIBOCTAVE_CXX_SOURCES): regex-match.cc here. + 2008-05-19 David Bateman * dSparse.cc: Replace some DGBCON with GPBCON where they are diff --git a/liboctave/Makefile.in b/liboctave/Makefile.in --- a/liboctave/Makefile.in +++ b/liboctave/Makefile.in @@ -85,7 +85,7 @@ oct-rl-hist.h oct-shlib.h oct-sort.h oct-spparms.h oct-syscalls.h \ oct-sparse.h oct-time.h oct-uname.h \ pathlen.h pathsearch.h prog-args.h \ - randgamma.h randmtzig.h randpoisson.h \ + randgamma.h randmtzig.h randpoisson.h regex-match.h \ so-array.h sparse-sort.h statdefs.h str-vec.h \ sparse-util.h sun-utils.h sysdir.h systime.h syswait.h \ $(MATRIX_INC) @@ -136,7 +136,8 @@ lo-utils.cc mach-info.cc oct-alloc.cc oct-env.cc \ oct-fftw.cc oct-group.cc oct-md5.cc oct-passwd.cc oct-rand.cc \ oct-shlib.cc oct-spparms.cc oct-syscalls.cc oct-time.cc oct-uname.cc \ - prog-args.cc so-array.cc sparse-sort.cc sparse-util.cc str-vec.cc \ + prog-args.cc regex-match.cc \ + so-array.cc sparse-sort.cc sparse-util.cc str-vec.cc \ $(TEMPLATE_SRC) \ $(TI_SRC) \ $(MATRIX_SRC) diff --git a/liboctave/regex-match.cc b/liboctave/regex-match.cc new file mode 100644 --- /dev/null +++ b/liboctave/regex-match.cc @@ -0,0 +1,153 @@ +/* + +Copyright (C) 2008 David Bateman + +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 +. + +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "regex-match.h" +#include "str-vec.h" + +regex_match& +regex_match::operator = (const regex_match& gm) +{ + if (this != &gm) + { +#if HAVE_REGEX + for (int i = 0; i < pat.length (); i++) + regfree (compiled +i); + delete [] compiled; +#endif + pat = gm.pat; + case_insen = gm.case_insen; + init (); + } + return *this; +} + +regex_match::~regex_match (void) +{ +#if HAVE_REGEX + for (int i = 0; i < pat.length (); i++) + regfree (compiled +i); + delete [] compiled; +#endif +} + + +void +regex_match::set_pattern (const std::string& p) +{ +#if HAVE_REGEX + for (int i = 0; i < pat.length (); i++) + regfree (compiled +i); + delete [] compiled; +#endif + pat = p; + init (); +} + +void +regex_match::set_pattern (const string_vector& p) +{ +#if HAVE_REGEX + for (int i = 0; i < pat.length (); i++) + regfree (compiled +i); + delete [] compiled; +#endif + pat = p; + init (); +} + +void +regex_match::init (void) +{ +#ifdef HAVE_REGEX + int npat = pat.length (); + int err; + int i; + + compiled = new regex_t [npat]; + + for (i = 0; i < npat; i++) + if (err = regcomp (compiled + i, pat(i).c_str (), + (REG_NOSUB | REG_EXTENDED | + (case_insen ? REG_ICASE : 0)))) + break; + + if (err) + { + int len = regerror(err, compiled + i, 0, 0); + OCTAVE_LOCAL_BUFFER (char, errmsg, len); + regerror(err, compiled + i, errmsg, len); + (*current_liboctave_error_handler) ("%s in pattern (%s)", errmsg, + pat(i).c_str()); + + for (int j = 0; j < i + 1; j++) + regfree(compiled + j); + } +#else + (*current_liboctave_error_handler) + ("regex not available in this version of Octave"); +#endif +} + +bool +regex_match::match (const std::string& s) +{ +#if HAVE_REGEX + int npat = pat.length (); + + const char *str = s.c_str (); + + for (int i = 0; i < npat; i++) + if (regexec(compiled + i, str, 0, 0, 0) == 0) + return true; +#endif + + return false; +} + +Array +regex_match::match (const string_vector& s) +{ + int n = s.length (); + + Array retval (n); + + for (int i = 0; i < n; i++) + retval(i) = match (s[i]); + + return retval; +} + + +/* +;;; Local Variables: *** +;;; mode: C++ *** +;;; End: *** +*/ + diff --git a/liboctave/regex-match.h b/liboctave/regex-match.h new file mode 100644 --- /dev/null +++ b/liboctave/regex-match.h @@ -0,0 +1,90 @@ +/* + +Copyright (C) 2008 David Bateman + +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 +. + +*/ + +#if !defined (octave_regex_match_h) +#define octave_regex_match_h 1 + +#include + +#if defined (HAVE_REGEX) +#if defined (__MINGW32__) +#define __restrict +#endif +#if defined (HAVE_SYS_TYPES_H) +#include +#endif +#include +#endif + +#include "Array.h" +#include "str-vec.h" + +class +OCTAVE_API +regex_match +{ +public: + + regex_match (const std::string& p, bool insen = false) + : pat (p), case_insen (insen){ init (); } + + regex_match (const string_vector& p = string_vector (), bool insen = false) + : pat (p), case_insen (insen) { init (); } + + regex_match (const regex_match& gm) + : pat (gm.pat), case_insen (gm.case_insen) { init (); } + + regex_match& operator = (const regex_match& gm); + + ~regex_match (void); + + void set_pattern (const std::string& p); + + void set_pattern (const string_vector& p); + + bool match (const std::string&); + + Array match (const string_vector&); + +private: + + void init (void); + + // Regex pattern(s). + string_vector pat; + + // Should match be case insensitive + bool case_insen; + +#if HAVE_REGEX + regex_t *compiled; +#endif + +}; + +#endif + +/* +;;; Local Variables: *** +;;; mode: C++ *** +;;; End: *** +*/ diff --git a/src/ChangeLog b/src/ChangeLog --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,28 @@ +2008-05-20 David Bateman + + * load-save.cc (do_load): Treat non verbose list_only output in + the same manner as Fwho. + * symtab.h (static void clear_variable_regexp (const + std::string&): New method. + (static std::list regexp (const std::string&)): Ditto. + (static std::list regexp_variables (const + std::string&)): Ditto. + (static std::list regexp_global_variables (const + std::string&)): Ditto, + (static std::list regexp_variables (const + string_vector&)): Ditto. + (void do_clear_variable_regexp (const std::string&)): Ditto. + (std::list do_regexp (const std::string&, bool) + const): Ditto. + (do_who): Accept the "-regexp" option. Use regexp versions of + symbol table functions. + (static inline bool name_match_any_pattern (const string_vector&, + int, int, bool): Add regexp argument, and use regexp matching if + true. + (do_clear_variables): Add regexp option and pass to + name_match_any_pattern. + (Fclear): Accept the -regexp option. + 2008-05-07 John W. Eaton * pt-arg-list.cc, pt-arg-list.h (tree_argument_list::dup): diff --git a/src/load-save.cc b/src/load-save.cc --- a/src/load-save.cc +++ b/src/load-save.cc @@ -334,6 +334,7 @@ Octave_map retstruct; std::ostringstream output_buf; + std::list symbol_names; octave_idx_type count = 0; @@ -411,9 +412,10 @@ << std::setiosflags (std::ios::right) << std::setw (7) << tc.rows () << std::setw (7) << tc.columns () - << " "; + << " " << name << "\n"; } - output_buf << name << "\n"; + else + symbol_names.push_back (name); } else { @@ -449,12 +451,28 @@ if (list_only && count) { - std::string msg = output_buf.str (); + if (verbose) + { + std::string msg = output_buf.str (); - if (nargout > 0) - retval = msg; + if (nargout > 0) + retval = msg; + else + octave_stdout << msg; + } else - octave_stdout << msg; + { + if (nargout > 0) + retval = Cell (string_vector (symbol_names)); + else + { + string_vector names (symbol_names); + + names.list_in_columns (octave_stdout); + + octave_stdout << "\n"; + } + } } else if (retstruct.nfields () != 0) retval = retstruct; diff --git a/src/symtab.h b/src/symtab.h --- a/src/symtab.h +++ b/src/symtab.h @@ -31,6 +31,7 @@ #include #include "glob-match.h" +#include "regex-match.h" class tree_argument_list; @@ -1337,6 +1338,14 @@ inst->do_clear_variable_pattern (pat); } + static void clear_variable_regexp (const std::string& pat) + { + symbol_table *inst = get_instance (xcurrent_scope); + + if (inst) + inst->do_clear_variable_regexp (pat); + } + static void clear_symbol_pattern (const std::string& pat) { // FIXME -- are we supposed to do both here? @@ -1527,6 +1536,13 @@ return inst ? inst->do_glob (pattern) : std::list (); } + static std::list regexp (const std::string& pattern) + { + symbol_table *inst = get_instance (xcurrent_scope); + + return inst ? inst->do_regexp (pattern) : std::list (); + } + static std::list glob_variables (const std::string& pattern) { symbol_table *inst = get_instance (xcurrent_scope); @@ -1534,6 +1550,13 @@ return inst ? inst->do_glob (pattern, true) : std::list (); } + static std::list regexp_variables (const std::string& pattern) + { + symbol_table *inst = get_instance (xcurrent_scope); + + return inst ? inst->do_regexp (pattern, true) : std::list (); + } + static std::list glob_global_variables (const std::string& pattern) { @@ -1556,6 +1579,28 @@ return retval; } + static std::list + regexp_global_variables (const std::string& pattern) + { + std::list retval; + + regex_match pat (pattern); + + for (global_table_const_iterator p = global_table.begin (); + p != global_table.end (); p++) + { + // We generate a list of symbol_record objects so that + // the results from regexp_variables and regexp_global_variables + // may be handled the same way. + + if (pat.match (p->first)) + retval.push_back (symbol_record (p->first, p->second, + symbol_record::global)); + } + + return retval; + } + static std::list glob_variables (const string_vector& patterns) { std::list retval; @@ -1572,6 +1617,23 @@ return retval; } + static std::list regexp_variables + (const string_vector& patterns) + { + std::list retval; + + size_t len = patterns.length (); + + for (size_t i = 0; i < len; i++) + { + std::list tmp = regexp_variables (patterns[i]); + + retval.insert (retval.begin (), tmp.begin (), tmp.end ()); + } + + return retval; + } + static std::list user_function_names (void) { std::list retval; @@ -2031,6 +2093,22 @@ } } + void do_clear_variable_regexp (const std::string& pat) + { + regex_match pattern (pat); + + for (table_iterator p = table.begin (); p != table.end (); p++) + { + symbol_record& sr = p->second; + + if (sr.is_defined () || sr.is_global ()) + { + if (pattern.match (sr.name ())) + sr.clear (); + } + } + } + void do_mark_hidden (const std::string& name) { table_iterator p = table.find (name); @@ -2088,6 +2166,29 @@ return retval; } + std::list do_regexp (const std::string& pattern, + bool vars_only = false) const + { + std::list retval; + + regex_match pat (pattern); + + for (table_const_iterator p = table.begin (); p != table.end (); p++) + { + if (pat.match (p->first)) + { + const symbol_record& sr = p->second; + + if (vars_only && ! sr.is_variable ()) + continue; + + retval.push_back (sr); + } + } + + return retval; + } + std::list do_variable_names (void) { std::list retval; diff --git a/src/variables.cc b/src/variables.cc --- a/src/variables.cc +++ b/src/variables.cc @@ -36,6 +36,7 @@ #include "oct-env.h" #include "file-ops.h" #include "glob-match.h" +#include "regex-match.h" #include "str-vec.h" #include @@ -1637,17 +1638,15 @@ std::string my_name = argv[0]; bool global_only = false; + bool have_regexp = false; int i; for (i = 1; i < argc; i++) { - if (argv[i] == "-regexp" || argv[i] == "-file") - { - error ("%s: `%s' option not implemented", my_name.c_str (), - argv[i].c_str ()); - - return retval; - } + if (argv[i] == "-file") + error ("%s: `-file' option not implemented", my_name.c_str ()); + else if (argv[i] == "-regexp") + have_regexp = true; else if (argv[i] == "global") global_only = true; else if (argv[i][0] == '-') @@ -1678,46 +1677,11 @@ { std::string pat = pats[j]; - size_t pos = pat.find_first_of (".({"); - - if (pos != NPOS && pos > 0) - { - if (verbose) - { - // NOTE: we can only display information for - // expressions based on global values if the variable is - // global in the current scope because we currently have - // no way of looking up the base value in the global - // scope and then evaluating the arguments in the - // current scope. - - std::string base_name = pat.substr (0, pos); - - if (symbol_table::is_variable (base_name)) - { - symbol_table::symbol_record sr - = symbol_table::find_symbol (base_name); - - if (! global_only || sr.is_global ()) - { - int parse_status; - - octave_value expr_val - = eval_string (pat, true, parse_status); - - if (! error_state) - symbol_stats.append (sr, pat, expr_val); - else - return retval; - } - } - } - } - else + if (have_regexp) { std::list tmp = global_only - ? symbol_table::glob_global_variables (pats[j]) - : symbol_table::glob_variables (pats[j]); + ? symbol_table::regexp_global_variables (pat) + : symbol_table::regexp_variables (pat); for (std::list::const_iterator p = tmp.begin (); p != tmp.end (); p++) @@ -1728,6 +1692,59 @@ symbol_names.push_back (p->name ()); } } + else + { + size_t pos = pat.find_first_of (".({"); + + if (pos != NPOS && pos > 0) + { + if (verbose) + { + // NOTE: we can only display information for + // expressions based on global values if the variable is + // global in the current scope because we currently have + // no way of looking up the base value in the global + // scope and then evaluating the arguments in the + // current scope. + + std::string base_name = pat.substr (0, pos); + + if (symbol_table::is_variable (base_name)) + { + symbol_table::symbol_record sr + = symbol_table::find_symbol (base_name); + + if (! global_only || sr.is_global ()) + { + int parse_status; + + octave_value expr_val + = eval_string (pat, true, parse_status); + + if (! error_state) + symbol_stats.append (sr, pat, expr_val); + else + return retval; + } + } + } + } + else + { + std::list tmp = global_only + ? symbol_table::glob_global_variables (pat) + : symbol_table::glob_variables (pat); + + for (std::list::const_iterator p = tmp.begin (); + p != tmp.end (); p++) + { + if (verbose) + symbol_stats.append (*p); + else + symbol_names.push_back (p->name ()); + } + } + } } if (return_list) @@ -1775,25 +1792,12 @@ may not be combined.\n\ \n\ @table @code\n\ -@item -all\n\ -List all currently defined symbols.\n\ -\n\ -@item -builtins\n\ -List built-in functions. This includes all currently\n\ -compiled function files, but does not include all function files that\n\ -are in the search path.\n\ -\n\ -@item -functions\n\ -List user-defined functions.\n\ -\n\ -@item -long\n\ -Print a long listing including the type and dimensions of any symbols.\n\ -The symbols in the first column of output indicate whether it is\n\ -possible to redefine the symbol, and whether it is possible for it to be\n\ -cleared.\n\ -\n\ -@item -variables\n\ -List user-defined variables.\n\ +@item global\n\ +List the variables in the global scope rather than the current scope.\n\ +@item -regexp\n\ +The patterns are considered as regular expressions and will be used\n\ +for matching the variables to display. The same pattern syntax as for\n\ +the @code{regexp} function is used.\n\ @end table\n\ \n\ Valid patterns are the same as described for the @code{clear} command\n\ @@ -1802,6 +1806,7 @@ visible in the local scope are displayed.\n\ \n\ The command @kbd{whos} is equivalent to @kbd{who -long}.\n\ +@seealso{regexp}\n\ @end deffn") { octave_value retval; @@ -2012,23 +2017,35 @@ // Deleting names from the symbol tables. static inline bool -name_matches_any_pattern (const std::string& nm, - const string_vector& argv, int argc, int idx) +name_matches_any_pattern (const std::string& nm, const string_vector& argv, + int argc, int idx, bool have_regexp = false) { bool retval = false; for (int k = idx; k < argc; k++) { std::string patstr = argv[k]; - if (! patstr.empty ()) { - glob_match pattern (patstr); - - if (pattern.match (nm)) + if (have_regexp) { - retval = true; - break; + regex_match pattern (patstr); + + if (pattern.match (nm)) + { + retval = true; + break; + } + } + else + { + glob_match pattern (patstr); + + if (pattern.match (nm)) + { + retval = true; + break; + } } } } @@ -2112,7 +2129,7 @@ static void do_clear_variables (const string_vector& argv, int argc, int idx, - bool exclusive = false) + bool exclusive = false, bool have_regexp = false) { if (idx == argc) symbol_table::clear_variables (); @@ -2128,14 +2145,18 @@ { std::string nm = lvars[i]; - if (! name_matches_any_pattern (nm, argv, argc, idx)) + if (! name_matches_any_pattern (nm, argv, argc, idx, have_regexp)) symbol_table::clear_variable (nm); } } else { - while (idx < argc) - symbol_table::clear_variable_pattern (argv[idx++]); + if (have_regexp) + while (idx < argc) + symbol_table::clear_variable_regexp (argv[idx++]); + else + while (idx < argc) + symbol_table::clear_variable_pattern (argv[idx++]); } } } @@ -2266,6 +2287,9 @@ Clears the global symbol names.\n\ @item -variables, -v\n\ Clears the local variable names.\n\ +@item -regexp, -r\n\ +The arguments are treated as regular expressions as any variables that\n\ +match will be cleared.\n\ @end table\n\ With the execption of @code{exclusive}, all long options can be used \n\ without the dash as well.\n\ @@ -2292,6 +2316,7 @@ bool clear_globals = false; bool clear_variables = false; bool exclusive = false; + bool have_regexp = false; bool have_dash_option = false; while (++idx < argc) @@ -2329,6 +2354,13 @@ have_dash_option = true; clear_variables = true; } + else if (argv[idx] == "-regexp" || argv[idx] == "-r") + { + CLEAR_OPTION_ERROR (have_dash_option && ! exclusive); + + have_dash_option = true; + have_regexp = true; + } else break; } @@ -2351,6 +2383,10 @@ symbol_table::clear_all (); } + else if (have_regexp) + { + do_clear_variables (argv, argc, idx, exclusive, true); + } else if (clear_functions) { do_clear_functions (argv, argc, idx, exclusive);