changeset 5567:80e629357483

[project @ 2005-12-07 06:31:28 by jwe]
author jwe
date Wed, 07 Dec 2005 06:31:28 +0000
parents 2f5d0d8a7f13
children e9cde940b271
files scripts/ChangeLog scripts/audio/wavread.m scripts/audio/wavwrite.m src/ChangeLog src/error.cc src/error.h
diffstat 6 files changed, 680 insertions(+), 205 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/ChangeLog
+++ b/scripts/ChangeLog
@@ -1,3 +1,8 @@
+2005-12-06  John W. Eaton  <jwe@octave.org>
+
+	* audio/wavread.m, audio/wavwrite.m: Adapt to Octave coding style.
+	Avoid for loop in interleave/deinterleave steps.
+
 2005-12-06  Michael Zeising  <michael@michaels-website.de>
 
 	* audio/wavread.m, audio/wavwrite.m: New files.
--- a/scripts/audio/wavread.m
+++ b/scripts/audio/wavread.m
@@ -18,25 +18,24 @@
 ## 02110-1301, USA.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} {} @var{y} = wavread(@var{filename})
-## Load the RIFF/WAVE sound file @var{filename}, returning the samples in vector 
-## @var{y}. If the file contains multichannel data, then @var{y} is a matrix with the 
-## channels represented as columns.
+## @deftypefn {Function File} {@var{y}} = wavread (@var{filename})
+## Load the RIFF/WAVE sound file @var{filename}, and return the samples
+## in vector @var{y}.  If the file contains multichannel data, then
+## @var{y} is a matrix with the channels represented as columns.
 ##
-## @deftypefnx {Function File} {} [@var{y},@var{Fs},@var{bits}] = wavread(@var{filename})
+## @deftypefnx {Function File} {[@var{y}, @var{Fs}, @var{bits}]} = wavread (@var{filename})
 ## Additionally return the sample rate (@var{fs}) in Hz and the number of bits 
 ## per sample (@var{bits}).
 ##
-## @deftypefnx {Function File} {} [...] = wavread(@var{filename},@var{n})
+## @deftypefnx {Function File} {[@dots{}]} = wavread (@var{filename}, @var{n})
 ## Read only the first @var{n} samples from each channel.
 ##
-## @deftypefnx {Function File} {} [...] = wavread(@var{filename},[@var{n1} @var{n2}])
+## @deftypefnx {Function File} {[@dots{}]} = wavread(@var{filename},[@var{n1} @var{n2}])
 ## Read only samples @var{n1} through @var{n2} from each channel.
 ##
-## @deftypefnx {Function File} {} [@var{samples} @var{channels}] = wavread(@var{filename},'size')
-## Return the number of samples (@var{n}) and channels (@var{ch}) instead of the 
-## audio data.
-##
+## @deftypefnx {Function File} {[@var{samples}, @var{channels}]} = wavread (@var{filename}, "size")
+## Return the number of samples (@var{n}) and channels (@var{ch})
+## instead of the audio data.
 ## @end deftypefn
 ##
 ## @seealso{wavwrite}
@@ -44,7 +43,8 @@
 ## Author: Michael Zeising <michael.zeising@stud.uni-erlangen.de>
 ## Created: 06 December 2005
 
-function [y, samplesPerSec, bitsPerSample] = wavread (filename, param)
+function [y, samples_per_sec, bits_per_sample] = wavread (filename, param)
+
   FORMAT_PCM        = 0x0001;   # PCM (8/16/32 bit)
   FORMAT_IEEE_FLOAT = 0x0003;   # IEEE float (32/64 bit)
   FORMAT_ALAW       = 0x0006;   # 8-bit ITU-T G.711 A-law   (not yet supported)
@@ -52,65 +52,83 @@
   FORMAT_IMA_ADPCM  = 0x0011;   # IMA/ADPCM 4:1 compression (not yet supported)
   BYTEORDER         = "ieee-le";
 
+  if (nargin < 1 || nargin > 2)
+    usage ("wavread (filename, param)");
+  endif
+
   # open file for binary reading
+
+  if (! ischar (filename))
+    error ("wavwrite: expecting filename to be a character string");
+  endif
+
   [fid, msg] = fopen (filename, "rb");
   if (fid < 0)
     error ("wavread: %s", msg)
   endif
   
-  # check for RIFF/WAVE header
-  ckID = char (fread (fid, 4))';                     # chunk ID: "RIFF"
+  ## check for RIFF/WAVE header
+  ck_id = char (fread (fid, 4))';
   fseek (fid, 4, SEEK_CUR);
-  WAVEID = char (fread (fid, 4))';                   # WAVE ID: "WAVE"
-  if ((ckID ~= "RIFF") || (WAVEID ~= "WAVE"))
+  wave_id = char (fread (fid, 4))';
+  if (ck_id != "RIFF" || wave_id != "WAVE")
     fclose (fid);
     error ("wavread: file contains no RIFF/WAVE signature");
   endif
   
-  # find format chunk within the next 256 (4*64) bytes
+  ## find format chunk within the next 256 (4*64) bytes
   i = 1;
-  while 1
-    if (char (fread (fid, 4))' == "fmt ")
-      break
+  while (true)
+    if (char (fread (fid, 4))' == "fmt ");
+      break;
     endif
     if (i++ == 64)
       fclose (fid);
-      error ("wavread: file contains no format chunk") 
+      error ("wavread: file contains no format chunk");
     endif
   endwhile
-  
-  ckSize = fread (fid, 1, "ulong", 0, BYTEORDER);           # format chunk size
-  
-  formatTag = fread (fid, 1, "short", 0, BYTEORDER);        # sample format code
-  if ((formatTag ~= FORMAT_PCM) && (formatTag ~= FORMAT_IEEE_FLOAT))
-    fclose (fid);
-    error ("wavread: sample format %#x is not supported", formatTag) 
-  endif
+
+  ## format chunk size
+  ck_size = fread (fid, 1, "ulong", 0, BYTEORDER);         
   
-  channels = fread (fid, 1, "short", 0, BYTEORDER);         # number of interleaved channels
-  samplesPerSec = fread (fid, 1, "ulong", 0, BYTEORDER);    # sample rate
+  ## sample format code
+  format_tag = fread (fid, 1, "short", 0, BYTEORDER);
+  if (format_tag != FORMAT_PCM && format_tag != FORMAT_IEEE_FLOAT)
+    fclose (fid);
+    error ("wavread: sample format %#x is not supported", format_tag);
+  endif
+
+  ## number of interleaved channels  
+  channels = fread (fid, 1, "short", 0, BYTEORDER);
+
+  ## sample rate
+  samples_per_sec = fread (fid, 1, "ulong", 0, BYTEORDER);
+
+  ## bits per sample
   fseek (fid, 6, SEEK_CUR);
-  bitsPerSample = fread (fid, 1, "short", 0, BYTEORDER);    # bits per sample
-  # ignore the rest of the chunk
-  fseek (fid, ckSize-16, SEEK_CUR);
+  bits_per_sample = fread (fid, 1, "short", 0, BYTEORDER);
+
+  ## ignore the rest of the chunk
+  fseek (fid, ck_size-16, SEEK_CUR);
   
-  # find data chunk
+  ## find data chunk
   i = 1;
-  while 1
-    if (char (fread(fid, 4))' == "data")
-      break
+  while (true)
+    if (char (fread (fid, 4))' == "data")
+      break;
     endif
     if (i++ == 64)
       fclose (fid);
-      error ("wavread: file contains no data chunk")
+      error ("wavread: file contains no data chunk");
     endif
   end
-  
-  ckSize = fread (fid, 1, "ulong", 0, BYTEORDER);            # data chunk size
+
+  ## data chunk size
+  ck_size = fread (fid, 1, "ulong", 0, BYTEORDER);
   
-  # determine sample data type
-  if (formatTag == FORMAT_PCM)
-    switch bitsPerSample
+  ## determine sample data type
+  if (format_tag == FORMAT_PCM)
+    switch bits_per_sample
       case 8
         format = "int8";
       case 16 
@@ -119,34 +137,37 @@
         format = "int32";
       otherwise
         fclose (fid);
-        error ("wavread: %d bits sample resolution is not supported with PCM", bitsPerSample);
+        error ("wavread: %d bits sample resolution is not supported with PCM", bits_per_sample);
     endswitch
   else
-    switch bitsPerSample
+    switch (bits_per_sample)
       case 32 
         format = "float32";
       case 64 
         format = "float64";
       otherwise
         fclose (fid);
-        error ("wavread: %d bits sample resolution is not supported with IEEE float", bitsPerSample);
+        error ("wavread: %d bits sample resolution is not supported with IEEE float", bits_per_sample);
     endswitch
   endif
   
-  # parse arguments
-  if (exist ("param","var") < 1)
+  ## parse arguments
+  if (nargin == 1)
     length = inf;
   else
-    if (size(param)(2) == 1)                                 # number of samples is given
+    if (size (param, 2) == 1)
+      ## number of samples is given
       length = param * channels;
-    elseif (size(param)(2) == 2)                             # sample range is given
-      if fseek(fid, param(1) * channels * (bitsPerSample/8), SEEK_CUR) < 0
-        warning ("wavread: seeking failed")
+    elseif (size (param, 2) == 2)
+      ## sample range is given
+      if (fseek (fid, param(1) * channels * (bits_per_sample/8), SEEK_CUR) < 0)
+        warning ("wavread: seeking failed");
       endif
       length = (param(2)-param(1)) * channels;
-    elseif ((size (param)(2) == 4) && (char(param) == "size"))   # size of the file is requested
+    elseif (size (param, 2) == 4 && char (param) == "size")
+      ## size of the file is requested
       fclose (fid);
-      y = [ckSize/channels/bitsPerSample/8 channels];
+      y = [ck_size/channels/bits_per_sample/8, channels];
       return
     else
       fclose (fid);
@@ -154,25 +175,28 @@
     endif
   endif
   
-  # read samples
+  ## read samples
   [yi, n] = fread (fid, length, format, 0, BYTEORDER);
   
   fclose (fid);
   
-  if (formatTag == FORMAT_PCM)
-    # normalize samples
-    switch bitsPerSample
+  if (format_tag == FORMAT_PCM)
+    ## normalize samples
+    switch (bits_per_sample)
       case 8
         yi = (yi - 127)/127;      # 8-bit samples are unsigned
-      case {16,32}
-        yi = yi/((2 ** bitsPerSample) / 2 - 1);
+      case {16, 32}
+        yi = yi/((2 ** bits_per_sample) / 2 - 1);
     endswitch
   endif
   
-  # deinterleave
-  y = [];
-  for (i = 1:channels)
-    y = [y yi(i:channels:n)];
-  endfor
+  ## deinterleave
+  ## y = [];
+  ## for i = 1:channels
+  ##   y = [y, yi(i:channels:n)];
+  ## endfor
+
+  nr = numel (yi) / channels;
+  y = reshape (yi, channels, nr)';
   
 endfunction
--- a/scripts/audio/wavwrite.m
+++ b/scripts/audio/wavwrite.m
@@ -36,21 +36,27 @@
 ## Author: Michael Zeising <michael.zeising@stud.uni-erlangen.de>
 ## Created: 06 December 2005
 
-function wavwrite (filename, y, samplesPerSec, bitsPerSample)
+function wavwrite (filename, y, samples_per_sec, bits_per_sample)
+
   BYTEORDER = "ieee-le";
   
-  # parse arguments
-  if (exist ("samplesPerSec","var") < 1)
-    warning ("wavwrite: sample rate set to 8000 Hz")
-    samplesPerSec = 8000;
+  if (nargin < 2 || nargin > 4)
+    usage ("wavwrite (filename, y, samples_per_sec, bits_per_sample)");
   endif
-  if (exist ("bitsPerSample","var") < 1)
-    warning ("wavwrite: sample resolution set to 16-bit")
-    bitsPerSample = 16;
+
+  ## parse arguments
+  if (nargin < 3)
+    warning ("wavwrite: sample rate set to 8000 Hz");
+    samples_per_sec = 8000;
+  endif
+
+  if (nargin < 4)
+    warning ("wavwrite: sample resolution set to 16-bit");
+    bits_per_sample = 16;
   endif
   
-  # determine sample format
-  switch bitsPerSample
+  ## determine sample format
+  switch (bits_per_sample)
     case 8  
       format = "int8";
     case 16 
@@ -58,57 +64,80 @@
     case 32 
       format = "int32";
     otherwise
-      fclose (fid);
       error ("wavread: sample resolution not supported");
   endswitch
   
-  # calculate filesize
+  ## calculate filesize
   channels = size(y)(2);
   n = size(y)(1);
+
+  ## size of data chunk
+  ck_size = n*channels*(bits_per_sample/8);
   
-  ckSize = n*channels*(bitsPerSample/8);       # size of data chunk
-  
-  # open file for writing binary
+  ## open file for writing binary
+
+  if (! ischar (filename))
+    error ("wavwrite: expecting filename to be a character string");
+  endif
+    
   [fid, msg] = fopen (filename, "wb");
   if (fid < 0)
     error ("wavwrite: %s", msg)
   endif
   
-  # write RIFF/WAVE header
+  ## write RIFF/WAVE header
   c = 0;
-  c += fwrite (fid, "RIFF",        "uchar");
-  c += fwrite (fid, ckSize + 36,   "ulong", 0, BYTEORDER);   # file size - 8
-  c += fwrite (fid, "WAVEfmt ",    "uchar");
-  c += fwrite (fid, 16,            "ulong", 0, BYTEORDER);   # size of fmt chunk
-  c += fwrite (fid, 0x0001,        "short", 0, BYTEORDER);   # sample format code (PCM)
-  c += fwrite (fid, channels,      "short", 0, BYTEORDER);   # channels
-  c += fwrite (fid, samplesPerSec, "ulong", 0, BYTEORDER);   # sample rate
-  c += fwrite (fid, samplesPerSec*channels*bitsPerSample/8, "ulong", 0, BYTEORDER);   # bytes per second
-  c += fwrite (fid, channels*bitsPerSample/8,               "short", 0, BYTEORDER);   # block align
-  c += fwrite (fid, bitsPerSample, "short", 0, BYTEORDER);   # bits/sample
-  c += fwrite (fid, "data",        "uchar");
-  c += fwrite (fid, ckSize,        "ulong", 0, BYTEORDER);   # size of data chunk
+  c += fwrite (fid, "RIFF", "uchar");
+
+  ## file size - 8
+  c += fwrite (fid, ck_size + 36, "ulong", 0, BYTEORDER);
+  c += fwrite (fid, "WAVEfmt ", "uchar");
+
+  ## size of fmt chunk
+  c += fwrite (fid, 16, "ulong", 0, BYTEORDER);
+
+  ## sample format code (PCM)
+  c += fwrite (fid, 0x0001, "short", 0, BYTEORDER);
+
+  ## channels
+  c += fwrite (fid, channels, "short", 0, BYTEORDER);
+
+  ## sample rate
+  c += fwrite (fid, samples_per_sec, "ulong", 0, BYTEORDER);
+
+  ## bytes per second
+  bps = samples_per_sec*channels*bits_per_sample/8;
+  c += fwrite (fid, bps, "ulong", 0, BYTEORDER);
+
+  ## block align
+  c += fwrite (fid, channels*bits_per_sample/8, "short", 0, BYTEORDER);
+
+  c += fwrite (fid, bits_per_sample, "short", 0, BYTEORDER);   
+  c += fwrite (fid, "data", "uchar");
+  c += fwrite (fid, ck_size, "ulong", 0, BYTEORDER);
   
   if (c < 25)
     fclose (fid);
-    error ("wavread: writing to file failed")
+    error ("wavread: writing to file failed");
   endif
   
-  # scale samples
-  switch bitsPerSample
+  ## scale samples
+  switch (bits_per_sample)
     case 8
       y = floor (y*127 + 127);
-    case {16,32}
-      y = floor (y*((2 ** bitsPerSample) / 2 - 1));
+    case {16, 32}
+      y = floor (y*((2 ** bits_per_sample) / 2 - 1));
   endswitch
   
-  # interleave samples
-  l = n*channels;
-  for (i = 1:channels)
-    yi(i:channels:l) = y(:,i);
-  endfor
+  ## interleave samples
+  ## l = n*channels;
+  ## for i = 1:channels
+  ##  yi(i:channels:l) = y(:,i);
+  ## endfor
+
+  yi = reshape (y', n*channels, 1);
   
-  # write to file
+  ## write to file
   c = fwrite (fid, yi, format, 0, BYTEORDER);
   
   fclose (fid);
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,25 @@
+2005-12-07  John W. Eaton  <jwe@octave.org>
+
+	* error.cc (Vbacktrace_on_warning, warning_options,
+	Vlast_warning_id, Vlast_error_id): New file-scope variables.
+	(Vwarning_frequency, Vwarning_option): Delete unused variables.
+	(set_warning_option): Delete unused function.
+	(Fwarning): Update for compatibility.  Use DEFCMD, not DEFUN.
+	(warning_enabled, check_state): New functions.
+	(warning (const char *fmt, ...)): Use it.
+	(init_warning_options): New function.
+	(symbols_of_error): Call it.
+	(vwarning, Flastwarn): Handle Vlast_warning_id.  Improve compatibility.
+	(handle_message): New arg, ID.  Change all callers.
+	(vwarning, verror, error_1): New arg, ID.  Change all callers.
+	(verror, Flasterr): Handle Vlast_error_id.  Improve compatibility.
+
+	* error.cc (message_with_id, usage_with_id, warning_with_id,
+	error_with_id, parse_error_with_id): New functions.
+	(error_2, warning_1, usage_1): New functions.
+	* error.h (message_with_id, usage_with_id, warning_with_id,
+	error_with_id, parse_error_with_id): Provide decls.
+
 2005-11-30  John W. Eaton  <jwe@octave.org>
 
 	* DLD-FUNCTIONS/schur.cc (Fschur): Doc string fix.
--- a/src/error.cc
+++ b/src/error.cc
@@ -37,6 +37,7 @@
 #include "input.h"
 #include "pager.h"
 #include "oct-obj.h"
+#include "oct-map.h"
 #include "utils.h"
 #include "ov.h"
 #include "ov-usr-fcn.h"
@@ -59,19 +60,27 @@
 // is encountered.
 static bool Vdebug_on_warning;
 
+// TRUE means that Octave will try to display a stack trace when a
+// warning is encountered.
+static bool Vbacktrace_on_warning = false;
+
+// TRUE means that Octave will print a verbose warning.  Currently unused.
+static bool Vverbose_warning;
+
+// A structure containing (most of) the current state of warnings.
+static Octave_map warning_options;
+
 // The text of the last error message.
 static std::string Vlast_error_message;
 
 // The text of the last warning message.
 static std::string Vlast_warning_message;
 
-// The warning frequency for Matlab handle graphics backwards
-// compatibility warnings (currently not used).
-static std::string Vwarning_frequency = "once";
+// The last warning message id.
+static std::string Vlast_warning_id;
 
-// The current warning state.  Valid values are "on", "off",
-// "backtrace", or "debug".
-std::string Vwarning_option = "backtrace";
+// The last error message id.
+static std::string Vlast_error_id;
 
 // Current error state.
 //
@@ -116,10 +125,19 @@
   discard_error_messages = false;
 }
 
+static void
+init_warning_options (const std::string& state = "on")
+{
+  warning_options.clear ();
+
+  warning_options.assign ("identifier", "all");
+  warning_options.assign ("state", state);
+}
+
 // Warning messages are never buffered.
 
 static void
-vwarning (const char *name, const char *fmt, va_list args)
+vwarning (const char *name, const char *id, const char *fmt, va_list args)
 {
   if (discard_warning_messages)
     return;
@@ -146,6 +164,8 @@
   if (! warning_state)
     {
       // This is the first warning in a possible series.
+
+      Vlast_warning_id = id;
       Vlast_warning_message = msg_string;
     }
 
@@ -156,7 +176,7 @@
 
 static void
 verror (bool save_last_error, std::ostream& os,
-	const char *name, const char *fmt, va_list args)
+	const char *name, const char *id, const char *fmt, va_list args)
 {
   if (discard_error_messages)
     return;
@@ -189,6 +209,8 @@
   if (! error_state && save_last_error)
     {
       // This is the first error in a possible series.
+
+      Vlast_error_id = id;
       Vlast_error_message = msg_string;
     }
 
@@ -227,7 +249,8 @@
 // just set the error state.
 
 static void
-error_1 (std::ostream& os, const char *name, const char *fmt, va_list args)
+error_1 (std::ostream& os, const char *name, const char *id,
+	 const char *fmt, va_list args)
 {
   if (error_state != -2)
     {
@@ -242,14 +265,14 @@
 		    {
 		      char *tmp_fmt = strsave (fmt);
 		      tmp_fmt[len - 1] = '\0';
-		      verror (true, os, name, tmp_fmt, args);
+		      verror (true, os, name, id, tmp_fmt, args);
 		      delete [] tmp_fmt;
 		    }
 
 		  error_state = -2;
 		}
 	      else
-		verror (true, os, name, fmt, args);
+		verror (true, os, name, id, fmt, args);
 	    }
 	}
       else
@@ -265,17 +288,41 @@
 {
   va_list args;
   va_start (args, fmt);
-  verror (false, std::cerr, name, fmt, args);
+  verror (false, std::cerr, name, "", fmt, args);
   va_end (args);
 }
 
 void
+message_with_id (const char *name, const char *id, const char *fmt, ...)
+{
+  va_list args;
+  va_start (args, fmt);
+  verror (false, std::cerr, name, id, fmt, args);
+  va_end (args);
+}
+
+void
+usage_1 (const char *id, const char *fmt, va_list args)
+{
+  verror (true, std::cerr, "usage", id, fmt, args);
+  error_state = -1;
+}
+
+void
 usage (const char *fmt, ...)
 {
   va_list args;
   va_start (args, fmt);
-  verror (true, std::cerr, "usage", fmt, args);
-  error_state = -1;
+  usage_1 ("", fmt, args);
+  va_end (args);
+}
+
+void
+usage_with_id (const char *id, const char *fmt, ...)
+{
+  va_list args;
+  va_start (args, fmt);
+  usage_1 (id, fmt, args);
   va_end (args);
 }
 
@@ -293,12 +340,12 @@
 		{
 		  char *tmp_fmt = strsave (fmt);
 		  tmp_fmt[len - 1] = '\0';
-		  verror (false, std::cerr, 0, tmp_fmt, args);
+		  verror (false, std::cerr, 0, "", tmp_fmt, args);
 		  delete [] tmp_fmt;
 		}
 	    }
 	  else
-	    verror (false, std::cerr, 0, fmt, args);
+	    verror (false, std::cerr, 0, "", fmt, args);
 	}
     }
   else
@@ -387,21 +434,113 @@
     }
 }
 
-void
-warning (const char *fmt, ...)
+static int
+check_state (const std::string& state)
+{
+  // -1: not found
+  //  0: found, "off"
+  //  1: found, "on"
+  //  2: found, "error"
+
+  if (state == "off")
+    return 0;
+  else if (state == "on")
+    return 1;
+  else if (state == "error")
+    return 2;
+  else
+    return -1;
+}
+
+// For given warning ID, return 0 if warnings are disabled, 1 if
+// enabled, and 2 if this ID should be an error instead of a warning.
+
+static int
+warning_enabled (const std::string& id)
 {
-  if (Vwarning_option != "off")
+  int retval = 0;
+
+  int all_state = -1;
+  int id_state = -1;
+
+  octave_idx_type nel = warning_options.numel ();
+
+  if (nel > 0)
+    {
+      Cell identifier = warning_options.contents ("identifier");
+      Cell state = warning_options.contents ("state");
+
+      bool all_found = false;
+      bool id_found = false;
+
+      for (octave_idx_type i = 0; i < nel; i++)
+	{
+	  octave_value ov = identifier(i);
+	  std::string ovs = ov.string_value ();
+
+	  if (! all_found && ovs == "all")
+	    {
+	      all_state = check_state (state(i).string_value ());
+
+	      if (all_state >= 0)
+		all_found = true;
+	    }
+
+	  if (! id_found && ovs == id)
+	    {
+	      id_state = check_state (state(i).string_value ());
+
+	      if (id_state >= 0)
+		id_found = true;
+	    }
+
+	  if (all_found && id_found)
+	    break;
+	}
+
+    }
+
+  if (all_state == -1)
+    panic_impossible ();
+
+  if (all_state == 0)
+    {
+      if (id_state >= 0)
+	retval = id_state;
+    }
+  else if (all_state == 1)
+    {
+      if (id_state == 0 || id_state == 2)
+	retval = id_state;
+      else
+	retval = all_state;
+    }
+  else if (all_state == 2)
+    retval = 2;
+
+  return retval;
+}
+
+static void
+warning_1 (const char *id, const char *fmt, va_list args)
+{
+  int warn_opt = warning_enabled (id);
+
+  if (warn_opt == 2)
+    {
+      // Handle this warning as an error.
+
+      error (id, fmt, args);
+    }
+  else if (warn_opt == 1)
     {
       if (curr_sym_tab != top_level_sym_tab
-	  && Vwarning_option == "backtrace"
+	  && Vbacktrace_on_warning
 	  && ! warning_state
 	  && ! discard_warning_messages)
 	pr_where ("warning", false);
 
-      va_list args;
-      va_start (args, fmt);
-      vwarning ("warning", fmt, args);
-      va_end (args);
+      vwarning ("warning", id, fmt, args);
 
       warning_state = 1;
 
@@ -419,14 +558,29 @@
 }
 
 void
-error (const char *fmt, ...)
+warning (const char *fmt, ...)
+{
+  va_list args;
+  va_start (args, fmt);
+  warning_1 ("", fmt, args);
+  va_end (args);
+}
+
+void
+warning_with_id (const char *id, const char *fmt, ...)
+{
+  va_list args;
+  va_start (args, fmt);
+  warning_1 (id, fmt, args);
+  va_end (args);
+}
+
+static void
+error_2 (const char *id, const char *fmt, va_list args)
 {
   int init_state = error_state;
 
-  va_list args;
-  va_start (args, fmt);
-  error_1 (std::cerr, "error", fmt, args);
-  va_end (args);
+  error_1 (std::cerr, "error", id, fmt, args);
 
   if ((interactive || forced_interactive)
       && Vdebug_on_error && init_state == 0 && curr_function)
@@ -445,11 +599,38 @@
 }
 
 void
+error (const char *fmt, ...)
+{
+  va_list args;
+  va_start (args, fmt);
+  error_2 ("", fmt, args);
+  va_end (args);
+}
+
+void
+error_with_id (const char *id, const char *fmt, ...)
+{
+  va_list args;
+  va_start (args, fmt);
+  error_2 (id, fmt, args);
+  va_end (args);
+}
+
+void
 parse_error (const char *fmt, ...)
 {
   va_list args;
   va_start (args, fmt);
-  error_1 (std::cerr, 0, fmt, args);
+  error_1 (std::cerr, 0, "", fmt, args);
+  va_end (args);
+}
+
+void
+parse_error_with_id (const char *id, const char *fmt, ...)
+{
+  va_list args;
+  va_start (args, fmt);
+  error_1 (std::cerr, 0, id, fmt, args);
   va_end (args);
 }
 
@@ -460,7 +641,7 @@
   va_start (args, fmt);
   buffer_error_messages = 0;
   discard_error_messages = false;
-  verror (false, std::cerr, "panic", fmt, args);
+  verror (false, std::cerr, "panic", "", fmt, args);
   va_end (args);
   abort ();
 }
@@ -470,7 +651,7 @@
 {
   va_list args;
   va_start (args, fmt);
-  error_1 (octave_stdout, 0, fmt, args);
+  error_1 (octave_stdout, 0, "", fmt, args);
   va_end (args);
 }
 
@@ -480,12 +661,13 @@
   defun_usage_message_1 ("%s", msg.c_str ());
 }
 
-typedef void (*error_fun)(const char *, ...);
+typedef void (*error_fun)(const char *, const char *, ...);
 
 extern octave_value_list Fsprintf (const octave_value_list&, int);
 
 static std::string
-handle_message (error_fun f, const char *msg, const octave_value_list& args)
+handle_message (error_fun f, const char *id, const char *msg,
+		const octave_value_list& args)
 {
   std::string retval;
 
@@ -529,14 +711,14 @@
 	{
 	  char *tmp_msg = strsave (msg);
 	  tmp_msg[len - 1] = '\0';
-	  f ("%s\n", tmp_msg);
+	  f (id, "%s\n", tmp_msg);
 	  retval = tmp_msg;
 	  delete [] tmp_msg;
 	}
     }
   else
     {
-      f ("%s", msg);
+      f (id, "%s", msg);
       retval = msg;
     }
 
@@ -599,33 +781,15 @@
 @end example\n\
 @end deftypefn")
 {
+  // XXX FIXME XXX -- need to extract and pass message id to
+  // handle_message.
+
   octave_value_list retval;
-  handle_message (error, "unspecified error", args);
+  handle_message (error_with_id, "", "unspecified error", args);
   return retval;
 }
 
-static inline octave_value_list
-set_warning_option (const std::string& state,
-		    const std::string& frequency, int nargout)
-{
-  octave_value_list retval;
-
-  if (nargout > 1)
-    retval(1) = Vwarning_frequency;
-
-  if (nargout >= 0)
-    retval(0) = Vwarning_option;
-
-  if (! state.empty ())
-    Vwarning_option = state;
-    
-  if (! frequency.empty ())
-    Vwarning_frequency = frequency;
-    
-  return retval;
-}
-
-DEFUN (warning, args, nargout,
+DEFCMD (warning, args, nargout,
   "-*- texinfo -*-\n\
 @deftypefn {Built-in Function} {} warning (@var{msg})\n\
 Print a warning message @var{msg} prefixed by the string @samp{warning: }.  \n\
@@ -635,84 +799,279 @@
 to go on.\n\
 @end deftypefn")
 {
-  octave_value_list retval;
+  octave_value retval;
 
-  int argc = args.length () + 1;
+  int nargin = args.length ();
+  int argc = nargin + 1;
 
   bool done = false;
 
-  if (args.all_strings_p ())
+  if (argc > 1 && args.all_strings_p ())
     {
       string_vector argv = args.make_argv ("warning");
 
       if (! error_state)
 	{
-	  if (argc == 1)
+	  std::string arg1 = argv(1);
+	  std::string arg2 = "all";
+
+	  if (argc == 3)
+	    arg2 = argv(2);
+
+	  if (arg1 == "on" || arg1 == "off" || arg1 == "error")
 	    {
-	      retval = set_warning_option ("", "", nargout);
-	      done = true;
-	    }
-	  else if (argc == 2)
-	    {
-	      std::string arg = argv(1);
+	      Octave_map old_warning_options = warning_options;
+
+	      if (arg2 == "all")
+		{
+		  Octave_map tmp;
 
-	      if (arg == "on" || arg == "off" || arg == "backtrace")
-		{
-		  retval = set_warning_option (arg, "", nargout);
+		  tmp.assign ("identifier", arg2);
+		  tmp.assign ("state", arg1);
+
+		  warning_options = tmp;
+
 		  done = true;
 		}
-	      else if (arg == "once" || arg == "always")
+	      else if (arg2 == "backtrace")
+		{
+		  if (arg1 != "error")
+		    {
+		      Vbacktrace_on_warning = (arg1 == "on");
+		      done = true;
+		    }
+		}
+	      else if (arg2 == "debug")
+		{
+		  if (arg1 != "error")
+		    {
+		      bind_builtin_variable ("debug_on_warning", arg1 == "on");
+		      done = true;
+		    }
+		}
+	      else if (arg2 == "verbose")
+		{
+		  if (arg1 != "error")
+		    {
+		      Vverbose_warning = (arg1 == "on");
+		      done = true;
+		    }
+		}
+	      else
 		{
-		  retval = set_warning_option ("", arg, nargout);
+		  if (arg2 == "last")
+		    arg2 = Vlast_warning_id;
+
+		  if (arg2 == "all")
+		    init_warning_options (arg1);
+		  else
+		    {
+		      Cell ident = warning_options.contents ("identifier");
+		      Cell state = warning_options.contents ("state");
+
+		      octave_idx_type nel = ident.numel ();
+
+		      bool found = false;
+
+		      for (octave_idx_type i = 0; i < nel; i++)
+			{
+			  if (ident(i).string_value () == arg2)
+			    {
+			      // XXX FIXME XXX -- if state for "all" is
+			      // same as arg1, we can simply remove the
+			      // item from the list.
+
+			      state(i) = arg1;
+			      warning_options.assign ("state", state);
+			      found = true;
+			      break;
+			    }
+			}
+
+		      if (! found)
+			{
+			  // XXX FIXME XXX -- if state for "all" is
+			  // same as arg1, we don't need to do anything.
+
+			  ident.resize (dim_vector (1, nel+1));
+			  state.resize (dim_vector (1, nel+1));
+
+			  ident(nel) = arg2;
+			  state(nel) = arg1;
+
+			  warning_options.clear ();
+
+			  warning_options.assign ("identifier", ident);
+			  warning_options.assign ("state", state);
+			}
+		    }
+
 		  done = true;
 		}
-	      else if (arg == "debug")
+
+	      if (done && nargout > 0)
+		retval = warning_options;
+	    }
+	  else if (arg1 == "query")
+	    {
+	      if (arg2 == "all")
+		retval = warning_options;
+	      else if (arg2 == "backtrace" || arg2 == "debug"
+		       || arg2 == "verbose")
+		{
+		  Octave_map tmp;
+		  tmp.assign ("identifier", arg2);
+		  if (arg2 == "backtrace")
+		    tmp.assign ("state", Vbacktrace_on_warning ? "on" : "off");
+		  else if (arg2 == "debug")
+		    tmp.assign ("state", Vdebug_on_warning ? "on" : "off");
+		  else
+		    tmp.assign ("state", Vverbose_warning ? "on" : "off");
+		}
+	      else
 		{
-		  bind_builtin_variable ("debug_on_warning", true);
-		  retval = set_warning_option ("", "", nargout);
-		  done = true;
+		  if (arg2 == "last")
+		    arg2 = Vlast_warning_id;
+
+		  Cell ident = warning_options.contents ("identifier");
+		  Cell state = warning_options.contents ("state");
+
+		  octave_idx_type nel = ident.numel ();
+
+		  bool found = false;
+		  
+		  std::string val;
+
+		  for (octave_idx_type i = 0; i < nel; i++)
+		    {
+		      if (ident(i).string_value () == arg2)
+			{
+			  val = state(i).string_value ();
+			  found = true;
+			  break;
+			}
+		    }
+
+		  if (found)
+		    {
+		      Octave_map tmp;
+
+		      tmp.assign ("identifier", arg2);
+		      tmp.assign ("state", val);
+
+		      retval = tmp;
+		    }
+		  else
+		    error ("warning: invalid warning tag `%s'", arg2.c_str ());
 		}
+
+	      done = true;
 	    }
 	}
     }
+  else if (argc == 1)
+    {
+      retval = warning_options;
 
-  if (! done)
+      done = true;
+    }
+  else if (argc == 2)
+    {
+      octave_value arg = args(0);
+
+      Octave_map old_warning_options = warning_options;
+
+      if (arg.is_map ())
+	{
+	  Octave_map m = arg.map_value ();
+
+	  if (m.contains ("identifier") && m.contains ("state"))
+	    warning ("warning: setting state with structure not implemented");
+	  else
+	    error ("warning: expecting structure with fields `identifier' and `state'");
+
+	  done = true;
+
+	  if (nargout > 0)
+	    retval = old_warning_options;
+	}
+    }
+
+  if (! (error_state || done))
     {
+      octave_value_list nargs = args;
+
+      std::string id;
+
+      if (nargin > 1)
+	{
+	  std::string arg1 = args(0).string_value ();
+
+	  if (! error_state)
+	    {
+	      if (arg1.find ('%') == NPOS)
+		{
+		  id = arg1;
+
+		  nargs.resize (nargin-1);
+
+		  for (int i = 1; i < nargin; i++)
+		    nargs(i-1) = args(i);
+		}
+	    }
+	  else
+	    return retval;
+	}
+
+      // handle_message.
+
       std::string prev_msg = Vlast_warning_message;
 
-      std::string curr_msg
-	= handle_message (warning, "unspecified warning", args);
+      std::string curr_msg = handle_message (warning_with_id, id.c_str (),
+					     "unspecified warning", nargs);
 
       if (nargout > 0)
-	retval(0) = Vlast_warning_message;
+	retval = prev_msg;
     }
 
   return retval;
 }
 
-DEFUN (lasterr, args, ,
+DEFUN (lasterr, args, nargout,
   "-*- texinfo -*-\n\
-@deftypefn {Built-in Function} {} lasterr ()\n\
-@deftypefnx {Built-in Function} {} lasterr (@var{msg})\n\
+@deftypefn {Built-in Function} {[@var{msg}, @var{msgid}] =} lasterr (@var{msg}, @var{msgid})\n\
 Without any arguments, return the last error message.  With one\n\
-argument, set the last error message to @var{msg}.\n\
+argument, set the last error message to @var{msg}.  With two arguments,\n\
+also set the last message identifier.\n\
 @end deftypefn")
 {
   octave_value_list retval;
 
   int argc = args.length () + 1;
 
-  if (argc == 1 || argc == 2)
+  if (argc < 4)
     {
       string_vector argv = args.make_argv ("lasterr");
 
       if (! error_state)
 	{
-	  if (argc == 1)
-	    retval(0) = Vlast_error_message;
-	  else
+	  std::string prev_error_id = Vlast_error_id;
+	  std::string prev_error_message = Vlast_error_message;
+
+	  if (argc > 2)
+	    Vlast_error_id = argv(2);
+
+	  if (argc > 1)
 	    Vlast_error_message = argv(1);
+
+	  if (argc == 1 || nargout > 0)
+	    {
+	      retval(1) = prev_error_id;
+	      retval(0) = prev_error_message;
+	    }
 	}
+      else
+	error ("lastwarn: expecting arguments to be character strings");
     }
   else
     print_usage ("lasterr");
@@ -724,24 +1083,42 @@
 DEFALIAS (error_text, lasterr);
 DEFALIAS (__error_text__, lasterr);
 
-DEFUN (lastwarn, args, ,
+DEFUN (lastwarn, args, nargout,
   "-*- texinfo -*-\n\
-@deftypefn {Built-in Function} {} lastwarn ()\n\
-@deftypefnx {Built-in Function} {} lastwarn (@var{msg})\n\
+@deftypefn {Built-in Function} {[@var{msg}, @var{msgid}] =} lastwarn (@var{msg}, @var{msgid})\n\
 Without any arguments, return the last warning message.  With one\n\
-argument, set the last warning message to @var{msg}.\n\
+argument, set the last warning message to @var{msg}.  With two arguments,\n\
+also set the last message identifier.\n\
 @end deftypefn")
 {
   octave_value_list retval;
 
   int argc = args.length () + 1;
 
-  string_vector argv = args.make_argv ("lastwarn");
+  if (argc < 4)
+    {
+      string_vector argv = args.make_argv ("lastwarn");
+
+      if (! error_state)
+	{
+	  std::string prev_warning_id = Vlast_warning_id;
+	  std::string prev_warning_message = Vlast_warning_message;
+
+	  if (argc > 2)
+	    Vlast_warning_id = argv(2);
 
-  if (argc == 1)
-    retval(0) = Vlast_warning_message;
-  else if (argc == 2)
-    Vlast_warning_message = argv(1);
+	  if (argc > 1)
+	    Vlast_warning_message = argv(1);
+
+	  if (argc == 1 || nargout > 0)
+	    {
+	      retval(1) = prev_warning_id;
+	      retval(0) = prev_warning_message;
+	    }
+	}
+      else
+	error ("lastwarn: expecting arguments to be character strings");
+    }
   else
     print_usage ("lastwarn");
 
@@ -778,7 +1155,7 @@
 @end deftypefn")
 {
   octave_value_list retval;
-  handle_message (usage, "unknown", args);
+  handle_message (usage_with_id, "", "unknown", args);
   return retval;
 }
 
@@ -809,6 +1186,8 @@
 void
 symbols_of_error (void)
 {
+  init_warning_options ();
+
   DEFVAR (beep_on_error, false, beep_on_error,
     "-*- texinfo -*-\n\
 @defvr {Built-in Variable} beep_on_error\n\
--- a/src/error.h
+++ b/src/error.h
@@ -37,6 +37,22 @@
 extern void warning (const char *fmt, ...);
 extern void error (const char *fmt, ...);
 extern void parse_error (const char *fmt, ...);
+
+extern void
+message_with_id (const char *id, const char *name, const char *fmt, ...);
+
+extern void
+usage_with_id (const char *id, const char *fmt, ...);
+
+extern void
+warning_with_id (const char *id, const char *fmt, ...);
+
+extern void
+error_with_id (const char *id, const char *fmt, ...);
+
+extern void
+parse_error_with_id (const char *id, const char *fmt, ...);
+
 extern void panic (const char *fmt, ...) GCC_ATTR_NORETURN;
 
 // Helper function for print_usage defined in defun.cc.