changeset 5582:6bf56668b01a

[project @ 2005-12-15 01:08:20 by jwe]
author jwe
date Thu, 15 Dec 2005 01:10:05 +0000
parents 79ecf64976ce
children 8eebdcfde94e
files doc/ChangeLog doc/interpreter/Makefile.in doc/interpreter/octave.texi doc/interpreter/strings.txi doc/interpreter/testfun.txi src/ChangeLog src/DLD-FUNCTIONS/dispatch.cc src/DLD-FUNCTIONS/regexp.cc src/Makefile.in src/error.cc src/oct-stream.h src/ov-struct.cc
diffstat 12 files changed, 1113 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -1,3 +1,10 @@
+2005-12-14  David Bateman  <dbateman@free.fr>
+
+	* interpreter/testfun.txi: New test/demo documentation.
+	* interpreter/strings.txi: Include regexp/regexi docstrings.
+	* interpreter/octave.texi: Include test/demo appendix.
+	* interpreter/Makefile.in (SUB_SOURCE): Include test.txi.
+
 2005-12-13  David Bateman <dbateman@free.fr>
 
 	* interpreter/sparse.txi: Add new gplot, etreeplot and treeplot
--- a/doc/interpreter/Makefile.in
+++ b/doc/interpreter/Makefile.in
@@ -27,7 +27,7 @@
 	op-idx.txi optim.txi plot.txi poly.txi preface.txi \
 	quad.txi quaternion.txi set.txi signal.txi sparse.txi stats.txi \
 	stmt.txi stream.txi strings.txi struct.txi system.txi \
-	tips.txi var.txi vr-idx.txi
+	testfun.txi tips.txi var.txi vr-idx.txi
 
 SOURCES := $(SUB_SOURCE)
 
--- a/doc/interpreter/octave.texi
+++ b/doc/interpreter/octave.texi
@@ -154,6 +154,7 @@
 * Audio Processing::            
 * Quaternions::                 
 * System Utilities::            
+* Test and Demo Functions::
 * Tips::                        
 * Trouble::                     If you have trouble installing Octave.
 * Installation::                How to configure, compile and install Octave.
@@ -454,6 +455,11 @@
 * Group Database Functions::    
 * System Information::          
 
+Test and Demo Functions
+
+* Test Functions::
+* Demonstration Functions::
+
 Tips and Standards
 
 * Style Tips::                  Writing clean and robust programs.
@@ -546,6 +552,7 @@
 @c for them, and there appears to be no way to go back to the original
 @c set of indices once a redirection has taken place.
 
+@include testfun.texi
 @include tips.texi
 @include bugs.texi
 @include install.texi
--- a/doc/interpreter/strings.txi
+++ b/doc/interpreter/strings.txi
@@ -162,6 +162,10 @@
 
 @DOCSTRING(substr)
 
+@DOCSTRING(regexp)
+
+@DOCSTRING(regexpi)
+
 @node String Conversions
 @section String Conversions
 
new file mode 100644
--- /dev/null
+++ b/doc/interpreter/testfun.txi
@@ -0,0 +1,283 @@
+@c Copyright (C) 2005 David Bateman
+@c Copyright (C) 2002-2005 Paul Kienzle
+@c This is part of the Octave manual.
+@c For copying conditions, see the file gpl.texi.
+
+@node Test and Demo Functions
+@appendix Test and Demo Functions
+@cindex test functions
+
+Octave includes a number of functions to allow the integration of testing
+and demonstration code in the source code of the functions themselves.
+
+@menu
+* Test Functions::
+* Demonstration Functions::
+@end menu
+
+@node Test Functions
+@section Test Functions
+
+@DOCSTRING(test)
+
+@code{test} scans the named script file looking for lines which
+start with @code{%!}. The prefix is stripped off and the rest of the
+line is processed through the octave interpreter. If the code
+generates an error, then the test is said to fail.
+
+Since @code{eval()} will stop at the first error it encounters, you must
+divide your tests up into blocks, with anything in a separate
+block evaluated separately.  Blocks are introduced by the keyword
+@code{test} immediately following the @code{%!}.  For example,
+
+@example
+@group
+   %!test error("this test fails!");
+   %!test "this test doesn't fail since it doesn't generate an error";
+@end group
+@end example
+
+When a test fails, you will see something like:
+
+@example
+@group
+     ***** test error('this test fails!')
+   !!!!! test failed
+   this test fails!
+@end group
+@end example
+
+Generally, to test if something works, you want to assert that it
+produces a correct value.  A real test might look something like
+
+@example
+@group
+   %!test
+   %! @var{a} = [1, 2, 3; 4, 5, 6]; B = [1; 2];
+   %! expect = [ @var{a} ; 2*@var{a} ];
+   %! get = kron (@var{b}, @var{a});
+   %! if (any(size(expect) != size(get)))
+   %!    error ("wrong size: expected %d,%d but got %d,%d",
+   %!           size(expect), size(get));
+   %! elseif (any(any(expect!=get)))
+   %!    error ("didn't get what was expected.");
+   %! endif
+@end group
+@end example
+
+To make the process easier, use the @code{assert} function.  For example,
+with @code{assert} the previous test is reduced to:
+
+@example
+@group
+   %!test
+   %! @var{a} = [1, 2, 3; 4, 5, 6]; @var{b} = [1; 2];
+   %! assert (kron (@var{b}, @var{a}), [ @var{a}; 2*@var{a} ]);
+@end group
+@end example
+
+@code{assert} can accept a tolerance so that you can compare results
+absolutely or relatively. For example, the following all succeed:
+
+@example
+@group
+   %!test assert (1+eps, 1, 2*eps)          # absolute error
+   %!test assert (100+100*eps, 100, -2*eps) # relative error
+@end group
+@end example
+
+You can also do the comparison yourself, but still have assert
+generate the error:
+
+@example
+@group
+   %!test assert (isempty([]))
+   %!test assert ([ 1,2; 3,4 ] > 0)
+@end group
+@end example
+
+Because @code{assert} is so frequently used alone in a test block, there
+is a shorthand form:
+
+@example
+   %!assert (@dots{})
+@end example
+
+which is equivalent to:
+
+@example
+   %!test assert (@dots{})
+@end example
+
+Each block is evaluated in its own function environment, which means
+that variables defined in one block are not automatically shared
+with other blocks.  If you do want to share variables, then you
+must declare them as @code{shared} before you use them.  For example, the
+following declares the variable @var{a}, gives it an initial value (default
+is empty), then uses it in several subsequent tests.
+
+@example
+@group
+   %!shared @var{a}
+   %! @var{a} = [1, 2, 3; 4, 5, 6];
+   %!assert (kron ([1; 2], @var{a}), [ @var{a}; 2*@var{a} ]);
+   %!assert (kron ([1, 2], @var{a}), [ @var{a}, 2*@var{a} ]);
+   %!assert (kron ([1,2; 3,4], @var{a}), [ @var{a},2*@var{a}; 3*@var{a},4*@var{a} ]);
+@end group
+@end example
+
+You can share several variables at the same time:
+
+@example
+   %!shared @var{a}, @var{b}
+@end example
+
+You can also share test functions:
+
+@example
+@group
+   %!function @var{a} = fn(@var{b})
+   %!  @var{a} = 2*@var{b};
+   %!assert (@var{a}(2),4);
+@end group
+@end example
+
+Note that all previous variables and values are lost when a new 
+shared block is declared.
+
+Error and warning blocks are like test blocks, but they only succeed 
+if the code generates an error.  You can check the text of the error
+is correct using an optional regular expression @code{<pattern>}.  
+For example:
+
+@example
+   %!error <passes!> error('this test passes!');
+@end example
+
+If the code doesn't generate an error, the test fails. For example,
+
+@example
+   %!error "this is an error because it succeeds.";
+@end example
+
+produces
+
+@example
+@group
+   ***** error "this is an error because it succeeds.";
+   !!!!! test failed: no error
+@end group
+@end example
+
+It is important to automate the tests as much as possible, however
+some tests require user interaction.  These can be isolated into
+demo blocks, which if you are in batch mode, are only run when 
+called with @code{demo} or @code{verbose}. The code is displayed before
+it is executed. For example,
+
+@example
+@group
+   %!demo
+   %! @var{t}=[0:0.01:2*pi]; @var{x}=sin(@var{t});
+   %! plot(@var{t},@var{x});
+   %! you should now see a sine wave in your figure window
+@end group
+@end example
+
+produces
+
+@example
+@group
+   > @var{t}=[0:0.01:2*pi]; @var{x}=sin(@var{t});
+   > plot(@var{t},@var{x});
+   > you should now see a sine wave in your figure window
+   Press <enter> to continue: 
+@end group
+@end example
+
+Note that demo blocks cannot use any shared variables.  This is so
+that they can be executed by themselves, ignoring all other tests.
+
+If you want to temporarily disable a test block, put @code{#} in place
+of the block type.  This creates a comment block which is echoed
+in the log file, but is not executed.  For example:
+
+@example
+@group
+   %!#demo
+   %! @var{t}=[0:0.01:2*pi]; @var{x}=sin(@var{t});
+   %! plot(@var{t},@var{x});
+   %! you should now see a sine wave in your figure window
+@end group
+@end example
+
+Block type summary:
+
+@table @code
+@item %!test
+check that entire block is correct
+@item %!error
+check for correct error message
+@item %!warning
+check for correct warning message
+@item %!demo
+demo only executes in interactive mode
+@item %!#
+comment: ignore everything within the block
+@item %!shared x,y,z
+declares variables for use in multiple tests
+@item %!function
+defines a function value for a shared variable
+@item %!assert (x, y, tol)
+shorthand for %!test assert (x, y, tol)
+@end table
+
+You can also create test scripts for builtins and your own C++
+functions. Just put a file of the function name on your path without
+any extension and it will be picked up by the test procedure.  You
+can even embed tests directly in your C++ code:
+
+@example
+@group
+   #if 0
+   %!test disp('this is a test')
+   #endif
+@end group
+@end example
+
+or
+
+@example
+@group
+   /*
+   %!test disp('this is a test')
+   */
+@end group
+@end example
+
+but then the code will have to be on the load path and the user 
+will have to remember to type test('name.cc').  Conversely, you
+can separate the tests from normal octave script files by putting
+them in plain files with no extension rather than in script files.
+@c DO I WANT TO INCLUDE THE EDITOR SPECIFIC STATEMENT BELOW???
+@c Don't forget to tell emacs that the plain text file you are using
+@c is actually octave code, using something like:
+@c   -*-octave-*-
+
+@DOCSTRING(assert)
+
+@DOCSTRING(fail)
+
+@node Demonstration Functions
+@section Demonstration Functions
+
+@DOCSTRING(demo)
+
+@DOCSTRING(example)
+
+@DOCSTRING(speed)
+
+
+@c Local Variables: ***
+@c Mode: texinfo ***
+@c End: ***
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,26 @@
+2005-12-14  John W. Eaton  <jwe@octave.org>
+
+	* oct-stream.cc (octave_stream::invalid_stream_error): Delete.
+	* oct-stream.h (octave_stream::stream_ok): Don't fail with error.
+
+2005-12-14  David Bateman  <dbateman@free.fr>
+
+	* DLD-FUNCTIONS/regexp.cc: New file.
+
+	* DLD-FUNCTIONS/dispatch.cc: Update tests for string/sq_string 
+	differences.
+
+	* error.cc (Vquiet_warning): New variable.
+	(vwarning): Use Vquiet_warning to prevent warning output.
+	(Fwarning): Include "quiet" option to Fwarning function.
+	Assign retval when using "query".  Typo in error message.
+	(Flastwarn): Clear warning_state when using Flastwarn to probe warning
+	message.
+
+	* ov-struct.cc: Update Fstruct tests for change in error output.
+
+	* Makefile.in: Include regexp when needed with appropriate libraries.
+
 2005-12-13  David Bateman <dbateman@free.fr>
 
 	* Makefile.in: Change references to gplot.l to __gnuplot_raw__.l.
--- a/src/DLD-FUNCTIONS/dispatch.cc
+++ b/src/DLD-FUNCTIONS/dispatch.cc
@@ -485,12 +485,12 @@
 
 %!test # builtin function replacement
 %! dispatch('sin','length','string')
-%! assert(sin('abc'),3)
+%! assert(sin("abc"),3)
 %! assert(sin(0),0,10*eps); 
 %!test # 'any' function
 %! dispatch('sin','exp','any')
 %! assert(sin(0),1,eps);
-%! assert(sin('abc'),3);
+%! assert(sin("abc"),3);
 %!test # 'builtin' function
 %! assert(builtin('sin',0),0,eps);
 %! builtin('eval','x=1;');
@@ -502,25 +502,25 @@
 %!test # oct-file replacement
 %! dispatch('fft','length','string')
 %! assert(fft([1,1]),[2,0]);
-%! assert(fft('abc'),3)
+%! assert(fft("abc"),3)
 %! dispatch('fft','string');
 %!test # m-file replacement
 %! dispatch('hamming','length','string')
 %! assert(hamming(1),1)
-%! assert(hamming('abc'),3)
+%! assert(hamming("abc"),3)
 %! dispatch('hamming','string')
 
 %!test # override preloaded builtin
 %! evalin('base','cos(1);');
 %! dispatch('cos','length','string')
-%! evalin('base',"assert(cos('abc'),3)");
-%! evalin('base',"assert(cos(0),1,eps)");
+%! evalin('base','assert(cos("abc"),3)');
+%! evalin('base','assert(cos(0),1,eps)');
 %! dispatch('cos','string')
 %!test # override pre-loaded oct-file
 %! evalin('base','qr(1);');
 %! dispatch('qr','length','string')
-%! evalin('base',"assert(qr('abc'),3)");
-%! evalin('base',"assert(qr(1),1)");
+%! evalin('base','assert(qr("abc"),3)');
+%! evalin('base','assert(qr(1),1)');
 %! dispatch('qr','string');
 %!test # override pre-loaded m-file
 %! evalin('base','hanning(1);');
@@ -536,11 +536,11 @@
 %! system("echo 'function a=dispatch_x(a)'>dispatch_x.m");
 %! dispatch('dispatch_x','length','string')
 %! assert(dispatch_x(3),3)
-%! assert(dispatch_x('a'),1)
+%! assert(dispatch_x("a"),1)
 %! pause(1);
 %! system("echo 'function a=dispatch_x(a),++a;'>dispatch_x.m");
 %! assert(dispatch_x(3),4)
-%! assert(dispatch_x('a'),1)
+%! assert(dispatch_x("a"),1)
 %!test 
 %! system("rm dispatch_x.m");
 
new file mode 100644
--- /dev/null
+++ b/src/DLD-FUNCTIONS/regexp.cc
@@ -0,0 +1,741 @@
+/*
+
+Copyright (C) 2005 David Bateman
+Copyright (C) 2002-2005 Paul Kienzle
+
+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 this program; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.
+
+*/
+
+// XXX FIXME XXX
+// regexprep should be written as an m-file based on regexp
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "defun-dld.h"
+#include "error.h"
+#include "gripes.h"
+#include "oct-obj.h"
+#include "utils.h"
+
+#include "Cell.h"
+#include "oct-map.h"
+#include "str-vec.h"
+
+#ifdef HAVE_PCRE
+#include <pcre.h>
+#else
+#ifdef HAVE_REGEX
+#ifdef __MINGW32__
+#define __restrict
+#endif
+#include <regex.h>
+#endif
+#endif
+
+static octave_value_list
+octregexp (const octave_value_list &args, int nargout, const std::string &nm,
+	   bool case_insensitive)
+{
+  octave_value_list retval;
+#if defined (HAVE_REGEX) || defined (HAVE_PCRE) 
+  int nargin = args.length();
+  int nopts = nargin - 2;
+  bool once = false;
+
+  if (nargin < 2)
+    {
+      print_usage(nm);
+      return retval;
+    }
+
+  std::string buffer = args(0).string_value ();
+  if (error_state)
+    {
+      gripe_wrong_type_arg (nm.c_str(), args(0));
+      return retval;
+    }
+
+  std::string pattern = args(1).string_value ();
+  if (error_state)
+    {
+      gripe_wrong_type_arg (nm.c_str(), args(1));
+      return retval;
+    }
+
+  for (int i = 2; i < nargin; i++)
+    {
+      std::string str = args(i).string_value();
+      if (error_state)
+	{
+	  error ("%s: optional arguments must be strings", nm.c_str());
+	  break;
+	}
+      std::transform (str.begin (), str.end (), str.begin (), tolower);
+      if (str.find("once", 0) == 0)
+	{
+	  once = true;
+	  nopts--;
+	}
+#ifdef HAVE_PCRE
+      // XXX FIXME XXX named tokens still broken. Disable for now
+#if 0
+      else if (str.find("start", 0) && str.find("end", 0) &&
+	       str.find("tokenextents", 0) && str.find("match", 0) &&
+	       str.find("tokens", 0) && str.find("names", 0))
+	error ("%s: unrecognized option", nm.c_str());
+#else
+      else if (str.find("names", 0) == 0)
+	error ("%s: named tokens not implemented in this version", nm.c_str());
+      else if (str.find("start", 0) && str.find("end", 0) &&
+	       str.find("tokenextents", 0) && str.find("match", 0) &&
+	       str.find("tokens", 0))
+	error ("%s: unrecognized option", nm.c_str());
+#endif
+#else
+      else if (str.find("names", 0) == 0)
+	error ("%s: named tokens not implemented in this version", nm.c_str());
+      else if (str.find("start", 0) && str.find("end", 0) &&
+	       str.find("tokenextents", 0) && str.find("match", 0) &&
+	       str.find("tokens", 0))
+	error ("%s: unrecognized option", nm.c_str());
+#endif
+    }
+
+  if (!error_state)
+    {
+      Octave_map nmap;
+      Cell t, m, te;
+      NDArray s, e;
+
+      // named tokens "(?<name>...)" are only treated with PCRE not regex.
+#if HAVE_PCRE
+      // The syntax of named tokens in pcre is "(?P<name>...)" while we need
+      // a syntax "(?<name>...)", so fix that here. Also an expression like
+      // "(?<first>\w+)\s+(?<last>\w+)|(?<last>\w+),\s+(?<first>\w+)" should
+      // be perfectly legal, while pcre does not allow the same named token
+      // name of both sides of the alternative. Also fix that here by replacing
+      // duplicate name tokens by dummy names, and dealing with the dummy names
+      // later.
+      
+
+
+
+      // Compile expression
+      pcre *re;
+      const char *err;
+      int erroffset;
+      re = pcre_compile(pattern.c_str(), (case_insensitive ? PCRE_CASELESS : 0),
+			&err, &erroffset, NULL);
+    
+      if (re == NULL) {
+	error("%s: %s at position %d of expression", nm.c_str(), 
+	      err, erroffset);
+	return retval;
+      }
+
+      int subpatterns;
+      int namecount;
+      int nameentrysize;
+      char *nametable;
+      int idx = 0;
+      int sz = 0;
+
+      pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT,  &subpatterns);
+      pcre_fullinfo(re, NULL, PCRE_INFO_NAMECOUNT, &namecount);
+      pcre_fullinfo(re, NULL, PCRE_INFO_NAMEENTRYSIZE, &nameentrysize);
+      pcre_fullinfo(re, NULL, PCRE_INFO_NAMETABLE, &nametable);
+
+      OCTAVE_LOCAL_BUFFER(int, ovector, (subpatterns+1)*3);
+      OCTAVE_LOCAL_BUFFER(int, nidx, namecount);
+      string_vector names (namecount);
+
+      for (int i = 0; i < namecount; i++)
+	{
+	  // Index of subpattern in first two bytes MSB first of name.
+	  // Extract name and index.
+	  nidx[i] = ((int)nametable[i*nameentrysize]) << 8 |
+	    (int)nametable[i*nameentrysize+1];
+	  names(i) = std::string((&(nametable[i*nameentrysize+2])));
+	}
+
+      Cell named_tokens(dim_vector(namecount,1));
+
+      while(true)
+	{
+	  int matches = pcre_exec(re, NULL, buffer.c_str(), 
+				  buffer.length(), idx, 
+				  (idx ? PCRE_NOTBOL : 0),
+				  ovector, (subpatterns+1)*3);
+
+	  if (matches < 0 && matches != PCRE_ERROR_NOMATCH)
+	    {
+	      error ("%s: internal error calling pcre_exec", nm.c_str());
+	      pcre_free(re);
+	      return retval;
+	    }
+	  else if (matches == PCRE_ERROR_NOMATCH)
+	    break;
+	  else
+	    {
+	      s.resize (dim_vector(1, sz+1));
+	      s(sz) = double (ovector[0]+1);
+	      e.resize (dim_vector(1, sz+1));
+	      e(sz) = double (ovector[1]);
+	      te.resize(dim_vector(1, sz+1));
+	      Matrix mat_te(matches-1,2);
+	      for (int i = 1; i < matches; i++)
+		{
+		  mat_te(i-1,0) = double (ovector[2*i]+1);
+		  mat_te(i-1,1) = double (ovector[2*i+1]);
+		}
+	      te(sz) = mat_te;
+
+	      const char **listptr;
+	      int status = pcre_get_substring_list(buffer.c_str(), ovector, 
+						   matches, &listptr);
+
+	      if (status == PCRE_ERROR_NOMEMORY) {
+		error("%s: cannot allocate memory in pcre_get_substring_list",
+		      nm.c_str());
+		pcre_free(re);
+		return retval;
+	      }
+
+	      m.resize (dim_vector(1, sz+1));
+	      m(sz) =  std::string(*listptr);
+
+	      t.resize (dim_vector(1, sz+1));
+	      Cell cell_t (dim_vector(1,matches-1));
+	      for (int i = 1; i < matches; i++)
+		cell_t(i-1) = std::string(*(listptr+i));
+	      t(sz) = cell_t;
+
+	      for (int i = 0; i < namecount; i++)
+		{
+		  Cell tmp = named_tokens(i);
+		  tmp.resize(dim_vector(1,sz+1));
+		  tmp(sz) = 
+		    std::string(*(listptr+nidx[i]));
+		  named_tokens(i) = tmp;
+		}
+
+	      pcre_free_substring_list(listptr);
+
+	      if (once)
+		break;
+
+	      idx = ovector[1];
+	      sz++;
+	    }
+	}
+
+      for (int i = 0; i < namecount; i++)
+	nmap.assign (names(i), named_tokens(i));
+
+      pcre_free(re);
+#else
+      regex_t compiled;
+      int err=regcomp(&compiled, pattern.c_str(), REG_EXTENDED | 
+		      (case_insensitive ? REG_ICASE : 0));
+      if (err)
+	{
+	  int len = regerror(err, &compiled, NULL, 0);
+	  char *errmsg = (char *)malloc(len);
+	  if (errmsg)
+	    {
+	      regerror(err, &compiled, errmsg, len);
+	      error("%s: %s in pattern (%s)", nm.c_str(), errmsg, 
+		    pattern.c_str());
+	      free(errmsg);
+	    }
+	  else
+	    {
+	      error("out of memory");
+	    }
+	  regfree(&compiled);
+	  return retval;
+	}
+
+      int subexpr = 1;
+      int idx = 0;
+      int sz = 0;
+      for (unsigned int i=0; i < pattern.length(); i++)
+	  subexpr += ( pattern[i] == '(' ? 1 : 0 );
+      OCTAVE_LOCAL_BUFFER (regmatch_t, match, subexpr );
+
+      while(true)
+	{
+	  if (regexec(&compiled, buffer.c_str() + idx, subexpr, 
+		      match, (idx ? REG_NOTBOL : 0)) == 0) 
+	    {
+	      // Count actual matches
+	      int matches = 0;
+	      while (matches < subexpr && match[matches].rm_so >= 0) 
+		matches++;
+
+	      s.resize (dim_vector(1, sz+1));
+	      s(sz) = double (match[0].rm_so+1+idx);
+	      e.resize (dim_vector(1, sz+1));
+	      e(sz) = double (match[0].rm_eo+idx);
+	      te.resize(dim_vector(1, sz+1));
+	      Matrix mat_te(matches-1,2);
+	      for (int i = 1; i < matches; i++)
+		{
+		  mat_te(i-1,0) = double (match[i].rm_so+1+idx);
+		  mat_te(i-1,1) = double (match[i].rm_eo+idx);
+		}
+	      te(sz) = mat_te;
+
+	      m.resize (dim_vector(1, sz+1));
+	      m(sz) =  buffer.substr (match[0].rm_so+idx, 
+					 match[0].rm_eo-match[0].rm_so);
+
+	      t.resize (dim_vector(1, sz+1));
+	      Cell cell_t (dim_vector(1,matches-1));
+	      for (int i = 1; i < matches; i++)
+		cell_t(i-1) = buffer.substr (match[i].rm_so+idx, 
+					     match[i].rm_eo-match[i].rm_so);
+	      t(sz) = cell_t;
+
+	      idx += match[0].rm_eo;
+	      sz++;
+
+	      if (once)
+		break;
+	    }
+	  else
+	    break;
+	}
+      regfree(&compiled);
+#endif
+
+      retval(5) = nmap;
+      retval(4) = t;
+      retval(3) = m;
+      retval(2) = te;
+      retval(1) = e;
+      retval(0) = s;
+
+      // Alter the order of the output arguments
+      if (nopts > 0)
+	{
+	  int n = 0;
+	  octave_value_list new_retval;
+	  new_retval.resize(nargout);
+
+	  OCTAVE_LOCAL_BUFFER (int, arg_used, 6);
+	  for (int i = 0; i < 6; i++)
+	    arg_used[i] = false;
+	  
+	  for (int i = 2; i < nargin; i++)
+	    {
+	      int k = 0;
+	      std::string str = args(i).string_value();
+	      std::transform (str.begin (), str.end (), str.begin (), tolower);
+	      if (str.find("once", 0) == 0)
+		continue;
+	      else if (str.find("start", 0) == 0)
+		k = 0;
+	      else if (str.find("end", 0) == 0)
+		k = 1;
+	      else if (str.find("tokenextents", 0) == 0)
+		k = 2;
+	      else if (str.find("match", 0) == 0)
+		k = 3;
+	      else if (str.find("tokens", 0) == 0)
+		k = 4;
+	      else if (str.find("names", 0) == 0)
+		k = 5;
+
+	      new_retval(n++) = retval(k);
+	      arg_used[k] = true;
+
+	      if (n == nargout)
+		break;
+	    }
+
+	  // Fill in the rest of the arguments
+	  if (n < nargout)
+	    {
+	      for (int i = 0; i < 6; i++)
+		{
+		  if (! arg_used[i])
+		    new_retval(n++) = retval(i);
+		}
+	    }
+
+	  retval = new_retval;
+	}
+    }
+
+#else
+  error ("%s: not available in this version of Octave", nm);
+#endif
+  return retval;
+}
+
+DEFUN_DLD (regexp, args, nargout,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {[@var{s}, @var{e}, @var{te}, @var{m}, @var{t}, @var{nm}] =} regexp (@var{str}, @var{pat})\n\
+@deftypefnx {Loadable Function} {[@dots{}] =} regexp (@var{str}, @var{pat}, @var{opts}, @dots{})\n\
+\n\
+Regular expression string matching. Matches @var{pat} in @var{str} and\n\
+returns the position and matching substrings or empty values if there are\n\
+none.\n\
+\n\
+The matched pattern @var{pat} can include any of the standard regex\n\
+operators, including:\n\
+\n\
+@table @code\n\
+@item .\n\
+Match any character\n\
+@item * + ? @{@}\n\
+Repetition operators, representing\n\
+@table @code\n\
+@item *\n\
+Match zero or more times\n\
+@item +\n\
+Match one or more times\n\
+@item ?\n\
+Match zero or one times\n\
+@item @{@}\n\
+Match range operator, which is of the form @code{@{@var{n}@}} to match exactly\n\
+@var{n} times, @code{@{@var{m},@}} to match @var{m} or more times,\n\
+@code{@{@var{m},@var{n}@}} to match between @var{m} and @var{n} times.\n\
+@end table\n\
+@item [@dots{}] [^@dots{}]\n\
+List operators, where for example @code{[ab]c} matches @code{ac} and @code{bc}\n\
+@item ()\n\
+Grouping operator\n\
+@item |\n\
+Alternation operator. Match one of a choice of regular expressions. The\n\
+alternatives must be delimited by the grouoing operator @code{()} above\n\
+@item ^ $\n\
+Anchoring operator. @code{^} matches the start of the string @var{str} and\n\
+@code{$} the end\n\
+@end table\n\
+\n\
+In addition the following escaped characters have special meaning. It should\n\
+be noted that it is recommended to quote @var{pat} in single quotes rather\n\
+than double quotes, to avoid the escape sequences being interpreted by octave\n\
+before being passed to @code{regexp}.\n\
+\n\
+@table @code\n\
+@item \\b\n\
+Match a word boundary\n\
+@item \\B\n\
+Match within a word\n\
+@item \\w\n\
+Matches any word character\n\
+@item \\W\n\
+Matches any non word character\n\
+@item \\<\n\
+Matches the beginning of a word\n\
+@item \\>\n\
+Matches the end of a word\n\
+@item \\s\n\
+Matches any whitespace character\n\
+@item \\S\n\
+Matches any non whitespace character\n\
+@item \\d\n\
+Matches any digit\n\
+@item \\D\n\
+Matches any non-digit\n\
+@end table\n\
+\n\
+The outputs of @code{regexp} by default are in the order as given below\n\
+\n\
+@table @asis\n\
+@item @var{s}\n\
+The start indices of each of the matching substrings\n\
+\n\
+@item @var{e}\n\
+The end indices of each matching substring\n\
+\n\
+@item @var{te}\n\
+The extents of each of the matched token surrounded by @code{(@dots{})} in\n\
+@var{pat}.\n\
+\n\
+@item @var{m}\n\
+A cell array of the text of each match.\n\
+\n\
+@item @var{t}\n\
+A cell array of the text of each token matched.\n\
+\n\
+@item @var{nm}\n\
+A structure containing the text of each matched named token, with the name\n\
+being used as the fieldname. A named token is denoted as\n\
+@code{(?<name>@dots{})}\n\
+@end table\n\
+\n\
+Particular output arguments or the order of the output arguments can be\n\
+selected by additional @var{opts} arguments. These are strings and the\n\
+correspondence between the output arguments and the optional argument\n\
+are\n\
+\n\
+@multitable @columnfractions 0.2 0.3 0.3 0.2\n\
+@item @tab 'start'        @tab @var{s}  @tab\n\
+@item @tab 'end'          @tab @var{e}  @tab\n\
+@item @tab 'tokenExtents' @tab @var{te} @tab\n\
+@item @tab 'match'        @tab @var{m}  @tab\n\
+@item @tab 'tokens'       @tab @var{t}  @tab\n\
+@item @tab 'names'        @tab @var{nm}  @tab\n\
+@end multitable\n\
+\n\
+A further optional argument is 'once', that limits the number of returned\n\
+matches to the first match.\n\
+@end deftypefn")
+{
+  return octregexp (args, nargout, "regexp", false);
+}
+
+/*
+
+## seg-fault test
+%!assert(regexp("abcde","."),[1,2,3,4,5])
+
+## Check that anchoring of pattern works correctly
+%!assert(regexp('abcabc','^abc'),1);
+%!assert(regexp('abcabc','abc$'),4);
+%!assert(regexp('abcabc','^abc$'),[]);
+
+%!test
+%! [s, e, te, m, t] = regexp(' No Match ', 'f(.*)uck');
+%! assert (s,[])
+%! assert (e,[])
+%! assert (te,{})
+%! assert (m, {})
+%! assert (t, {})
+
+%!test
+%! [s, e, te, m, t] = regexp(' FiRetrUck ', 'f(.*)uck');
+%! assert (s,[])
+%! assert (e,[])
+%! assert (te,{})
+%! assert (m, {})
+%! assert (t, {})
+
+%!test
+%! [s, e, te, m, t] = regexp(' firetruck ', 'f(.*)uck');
+%! assert (s,2)
+%! assert (e,10)
+%! assert (te{1},[3,7])
+%! assert (m{1}, 'firetruck')
+%! assert (t{1}{1}, 'iretr')
+
+%!test
+%! [s, e, te, m, t] = regexp('short test string','\w*r\w*');
+%! assert (s,[1,12])
+%! assert (e,[5,17])
+%! assert (size(te), [1,2])
+%! assert (isempty(te{1}))
+%! assert (isempty(te{2}))
+%! assert (m{1},'short')
+%! assert (m{2},'string')
+%! assert (size(t), [1,2])
+%! assert (isempty(t{1}))
+%! assert (isempty(t{2}))
+
+%!test
+%! [s, e, te, m, t] = regexp('short test string','\w*r\w*','once');
+%! assert (s,1)
+%! assert (e,5)
+%! assert (size(te), [1,1])
+%! assert (isempty(te{1}))
+%! assert (m{1},'short')
+%! ## Matlab gives [1,0] here but that seems wrong.
+%! assert (size(t), [1,1])
+
+%!test
+%! [m, te, e, s, t] = regexp('short test string','\w*r\w*','once', 'match', 'tokenExtents', 'end', 'start', 'tokens');
+%! assert (s,1)
+%! assert (e,5)
+%! assert (size(te), [1,1])
+%! assert (isempty(te{1}))
+%! assert (m{1},'short')
+%! ## Matlab gives [1,0] here but that seems wrong.
+%! assert (size(t), [1,1])
+
+## XXX FIXME XXX Disable test for now as PCRE version not written
+%!#test
+%! ## This test is expected to fail if PCRE is not installed
+%! [s, e, te, m, t, nm] = regexp('short test string','(?<word1>\w*t)\s*(?<word2>\w*t)');
+%! assert (s,1)
+%! assert (e,10)
+%! assert (size(te), [1,1])
+%! assert (te{1}, [1 5; 7, 10])
+%! assert (m{1},'short test')
+%! assert (size(t),[1,1])
+%! assert (t{1}{1},'short')
+%! assert (t{1}{2},'test')
+%! assert (size(nm), [1,1])
+%! assert (isempty(fieldnames(nm)))
+%! assert (sort(fieldnames(nm)),{'word1','word2'})
+%! assert (nm.word1,'short')
+%! assert (nm.word2,'test')
+
+## XXX FIXME XXX Disable test for now as PCRE version not written
+%!#test
+%! ## This test is expected to fail if PCRE is not installed
+%! [nm, m, te, e, s, t] = regexp('short test string','(?<word1>\w*t)\s*(?<word2>\w*t)', 'names', 'match', 'tokenExtents', 'end', 'start', 'tokens');
+%! assert (s,1)
+%! assert (e,10)
+%! assert (size(te), [1,1])
+%! assert (te{1}, [1 5; 7, 10])
+%! assert (m{1},'short test')
+%! assert (size(t),[1,1])
+%! assert (t{1}{1},'short')
+%! assert (t{1}{2},'test')
+%! assert (size(nm), [1,1])
+%! assert (isempty(fieldnames(nm)))
+%! assert (sort(fieldnames(nm)),{'word1','word2'})
+%! assert (nm.word1,'short')
+%! assert (nm.word2,'test')
+
+%!error regexp('string', 'tri', 'BadArg');
+%!error regexp('string');
+
+*/
+
+DEFUN_DLD(regexpi, args, nargout,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {[@var{s}, @var{e}, @var{te}, @var{m}, @var{t}, @var{nm}] =} regexpi (@var{str}, @var{pat})\n\
+@deftypefnx {Loadable Function} {[@dots{}] =} regexpi (@var{str}, @var{pat}, @var{opts}, @dots{})\n\
+\n\
+Case insensitive regular expression string matching. Matches @var{pat} in\n\
+@var{str} and returns the position and matching substrings or empty values\n\
+if there are none. See @code{regexp} for more details\n\
+@end deftypefn")
+{
+  return octregexp (args, nargout, "regexp", true);
+}
+
+/*
+
+## seg-fault test
+%!assert(regexpi("abcde","."),[1,2,3,4,5])
+
+## Check that anchoring of pattern works correctly
+%!assert(regexpi('abcabc','^abc'),1);
+%!assert(regexpi('abcabc','abc$'),4);
+%!assert(regexpi('abcabc','^abc$'),[]);
+
+%!test
+%! [s, e, te, m, t] = regexpi(' No Match ', 'f(.*)uck');
+%! assert (s,[])
+%! assert (e,[])
+%! assert (te,{})
+%! assert (m, {})
+%! assert (t, {})
+
+%!test
+%! [s, e, te, m, t] = regexpi(' FiRetrUck ', 'f(.*)uck');
+%! assert (s,2)
+%! assert (e,10)
+%! assert (te{1},[3,7])
+%! assert (m{1}, 'FiRetrUck')
+%! assert (t{1}{1}, 'iRetr')
+
+%!test
+%! [s, e, te, m, t] = regexpi(' firetruck ', 'f(.*)uck');
+%! assert (s,2)
+%! assert (e,10)
+%! assert (te{1},[3,7])
+%! assert (m{1}, 'firetruck')
+%! assert (t{1}{1}, 'iretr')
+
+%!test
+%! [s, e, te, m, t] = regexpi('ShoRt Test String','\w*r\w*');
+%! assert (s,[1,12])
+%! assert (e,[5,17])
+%! assert (size(te), [1,2])
+%! assert (isempty(te{1}))
+%! assert (isempty(te{2}))
+%! assert (m{1},'ShoRt')
+%! assert (m{2},'String')
+%! assert (size(t), [1,2])
+%! assert (isempty(t{1}))
+%! assert (isempty(t{2}))
+
+%!test
+%! [s, e, te, m, t] = regexpi('ShoRt Test String','\w*r\w*','once');
+%! assert (s,1)
+%! assert (e,5)
+%! assert (size(te), [1,1])
+%! assert (isempty(te{1}))
+%! assert (m{1},'ShoRt')
+%! ## Matlab gives [1,0] here but that seems wrong.
+%! assert (size(t), [1,1])
+
+%!test
+%! [m, te, e, s, t] = regexpi('ShoRt Test String','\w*r\w*','once', 'match', 'tokenExtents', 'end', 'start', 'tokens');
+%! assert (s,1)
+%! assert (e,5)
+%! assert (size(te), [1,1])
+%! assert (isempty(te{1}))
+%! assert (m{1},'ShoRt')
+%! ## Matlab gives [1,0] here but that seems wrong.
+%! assert (size(t), [1,1])
+
+## XXX FIXME XXX Disable test for now as PCRE version not written
+%!#test
+%! ## This test is expected to fail if PCRE is not installed
+%! [s, e, te, m, t, nm] = regexpi('ShoRt Test String','(?<word1>\w*t)\s*(?<word2>\w*t)');
+%! assert (s,1)
+%! assert (e,10)
+%! assert (size(te), [1,1])
+%! assert (te{1}, [1 5; 7, 10])
+%! assert (m{1},'ShoRt Test')
+%! assert (size(t),[1,1])
+%! assert (t{1}{1},'ShoRt')
+%! assert (t{1}{2},'Test')
+%! assert (size(nm), [1,1])
+%! assert (isempty(fieldnames(nm)))
+%! assert (sort(fieldnames(nm)),{'word1','word2'})
+%! assert (nm.word1,'ShoRt')
+%! assert (nm.word2,'Test')
+
+## XXX FIXME XXX Disable test for now as PCRE version not written
+%!#test
+%! ## This test is expected to fail if PCRE is not installed
+%! [nm, m, te, e, s, t] = regexpi('ShoRt Test String','(?<word1>\w*t)\s*(?<word2>\w*t)', 'names', 'match', 'tokenExtents', 'end', 'start', 'tokens');
+%! assert (s,1)
+%! assert (e,10)
+%! assert (size(te), [1,1])
+%! assert (te{1}, [1 5; 7, 10])
+%! assert (m{1},'ShoRt Test')
+%! assert (size(t),[1,1])
+%! assert (t{1}{1},'ShoRt')
+%! assert (t{1}{2},'Test')
+%! assert (size(nm), [1,1])
+%! assert (isempty(fieldnames(nm)))
+%! assert (sort(fieldnames(nm)),{'word1','word2'})
+%! assert (nm.word1,'ShoRt')
+%! assert (nm.word2,'Test')
+
+%!error regexpi('string', 'tri', 'BadArg');
+%!error regexpi('string');
+
+*/
+
+/*
+;;; Local Variables: ***
+;;; mode: C++ ***
+;;; End: ***
+*/
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -49,9 +49,10 @@
 	filter.cc find.cc fsolve.cc gammainc.cc gcd.cc getgrent.cc \
 	getpwent.cc getrusage.cc givens.cc hess.cc inv.cc kron.cc \
 	lpsolve.cc lsode.cc lu.cc luinc.cc matrix_type.cc minmax.cc \
-	pinv.cc qr.cc quad.cc qz.cc rand.cc schur.cc sort.cc sparse.cc \
-	spchol.cc spdet.cc spkron.cc splu.cc spparms.cc sqrtm.cc svd.cc \
-	syl.cc time.cc __gnuplot_raw__.l __glpk__.cc __qp__.cc
+	pinv.cc qr.cc quad.cc qz.cc rand.cc regexp.cc schur.cc sort.cc \
+	sparse.cc spchol.cc spdet.cc spkron.cc splu.cc spparms.cc \
+	sqrtm.cc svd.cc syl.cc time.cc \
+	__gnuplot_raw__.l __glpk__.cc __qp__.cc
 
 DLD_SRC := $(addprefix DLD-FUNCTIONS/, $(DLD_XSRC))
 
@@ -241,7 +242,7 @@
     $(LIBPLPLOT) $(LIBGLOB) $(LIBDLFCN)
 else
   OCTAVE_LIBS = $(LIBOCTINTERP) $(LIBOCTAVE) \
-    $(GLPK_LIBS) $(SPECIAL_MATH_LIB) $(LIBCRUFT) \
+    $(GLPK_LIBS) $(REGEX_LIBS) $(SPECIAL_MATH_LIB) $(LIBCRUFT) \
     $(LIBPLPLOT) $(LIBGLOB) $(LIBDLFCN)
 endif
 
@@ -569,9 +570,13 @@
 
 ifeq ($(ENABLE_DYNAMIC_LINKING), true)
   ifdef CXXPICFLAG
+    regexp.oct : pic/regexp.o octave$(EXEEXT)
+	  $(DL_LD) $(DL_LDFLAGS) -o $@ $< $(OCT_LINK_DEPS) $(REGEX_LIBS)
     __glpk__.oct : pic/__glpk__.o octave$(EXEEXT)
 	  $(DL_LD) $(DL_LDFLAGS) -o $@ $< $(OCT_LINK_DEPS) $(GLPK_LIBS)
   else
+    regexp.oct : regexp.o octave$(EXEEXT)
+	  $(DL_LD) $(DL_LDFLAGS) -o $@ $< $(OCT_LINK_DEPS) $(REGEX_LIBS)
     __glpk__.oct : __glpk__.o octave$(EXEEXT)
 	  $(DL_LD) $(DL_LDFLAGS) -o $@ $< $(OCT_LINK_DEPS) $(GLPK_LIBS)
   endif
--- a/src/error.cc
+++ b/src/error.cc
@@ -67,6 +67,10 @@
 // TRUE means that Octave will print a verbose warning.  Currently unused.
 static bool Vverbose_warning;
 
+// TRUE means that Octave will print no warnings, but lastwarn will be 
+//updated
+static bool Vquiet_warning = false;
+
 // A structure containing (most of) the current state of warnings.
 static Octave_map warning_options;
 
@@ -169,9 +173,12 @@
       Vlast_warning_message = msg_string;
     }
 
-  octave_diary << msg_string;
+  if (! Vquiet_warning)
+    {
+      octave_diary << msg_string;
 
-  std::cerr << msg_string;
+      std::cerr << msg_string;
+    }
 }
 
 static void
@@ -857,6 +864,14 @@
 		      done = true;
 		    }
 		}
+	      else if (arg2 == "quiet")
+		{
+		  if (arg1 != "error")
+		    {
+		      Vquiet_warning = (arg1 == "on");
+		      done = true;
+		    }
+		}
 	      else
 		{
 		  if (arg2 == "last")
@@ -917,7 +932,7 @@
 	      if (arg2 == "all")
 		retval = warning_options;
 	      else if (arg2 == "backtrace" || arg2 == "debug"
-		       || arg2 == "verbose")
+		       || arg2 == "verbose" || arg2 == "quiet")
 		{
 		  Octave_map tmp;
 		  tmp.assign ("identifier", arg2);
@@ -925,8 +940,12 @@
 		    tmp.assign ("state", Vbacktrace_on_warning ? "on" : "off");
 		  else if (arg2 == "debug")
 		    tmp.assign ("state", Vdebug_on_warning ? "on" : "off");
+		  else if (arg2 == "verbose")
+		    tmp.assign ("state", Vverbose_warning ? "on" : "off");
 		  else
-		    tmp.assign ("state", Vverbose_warning ? "on" : "off");
+		    tmp.assign ("state", Vquiet_warning ? "on" : "off");
+
+		  retval = tmp;
 		}
 	      else
 		{
@@ -1071,7 +1090,7 @@
 	    }
 	}
       else
-	error ("lastwarn: expecting arguments to be character strings");
+	error ("lasterr: expecting arguments to be character strings");
     }
   else
     print_usage ("lasterr");
@@ -1112,6 +1131,7 @@
 
 	  if (argc == 1 || nargout > 0)
 	    {
+	      warning_state = 0;
 	      retval(1) = prev_warning_id;
 	      retval(0) = prev_warning_message;
 	    }
--- a/src/oct-stream.h
+++ b/src/oct-stream.h
@@ -603,10 +603,7 @@
 	    rep->clear ();
 	}
       else
-	{
-	  retval = false;
-	  invalid_stream_error (who);
-	}
+	retval = false;
 
       return retval;
     }
--- a/src/ov-struct.cc
+++ b/src/ov-struct.cc
@@ -498,8 +498,8 @@
 %!assert(isstruct(x));
 %!assert(isempty(fieldnames(x)));
 %!fail("struct('a',{1,2},'b',{1,2,3})","dimensions of parameter 2 do not match those of parameter 4")
-%!fail("struct(1,2,3,4)","struct expects alternating 'field',value pairs");
-%!fail("struct('1',2,'3')","struct expects alternating 'field',value pairs");
+%!fail("struct(1,2,3,4)","struct expects alternating \"field\", VALUE pairs");
+%!fail("struct('1',2,'3')","struct expects alternating \"field\", VALUE pairs");
 */
 
 DEFUN (struct, args, ,