changeset 2117:b240b2fce8ed

[project @ 1996-05-10 07:20:36 by jwe] Initial revision
author jwe
date Fri, 10 May 1996 07:20:36 +0000
parents 99658f9b74c8
children d5eeb60baedd
files src/oct-stream.cc src/oct-stream.h
diffstat 2 files changed, 3281 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/src/oct-stream.cc
@@ -0,0 +1,2750 @@
+/*
+
+Copyright (C) 1996 John W. Eaton
+
+This file is part of Octave.
+
+Octave is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+Octave is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <iomanip.h>
+#include <strstream.h>
+
+#include "lo-ieee.h"
+#include "lo-mappers.h"
+#include "lo-utils.h"
+#include "str-vec.h"
+
+#include "error.h"
+#include "oct-stream.h"
+#include "utils.h"
+
+// Possible values for conv_err:
+//
+//   1 : not a real scalar
+//   2 : error extracting scalar value (should not happen)
+//   3 : value is NaN
+//   4 : value is not an integer
+
+static int
+convert_to_valid_int (const octave_value& tc, int& conv_err)
+{
+  int retval = 0;
+
+  conv_err = 0;
+
+  if (tc.is_real_scalar ())
+    {
+      double dval = tc.double_value ();
+
+      if (! error_state)
+	{
+	  if (! xisnan (dval))
+	    {
+	      int ival = NINT (dval);
+
+	      if ((double) ival == dval)
+		retval = ival;
+	      else
+		conv_err = 4;
+	    }
+	  else
+	    conv_err = 3;
+	}
+      else
+	conv_err = 2;
+    }
+  else
+    conv_err = 1;
+
+  return retval;
+}
+
+static int
+get_size (double d, const char *warn_for)
+{
+  int retval = -1;
+
+  if (! xisnan (d))
+    {
+      if (! xisinf (d))
+	{
+	  if (d > 0.0)
+	    retval = NINT (d);
+	  else
+	    ::error ("%s: negative value invalid as size specification",
+		     warn_for);
+	}
+      else
+	retval = -1;
+    }
+  else
+    ::error ("%s: NaN is invalid as size specification", warn_for);
+
+  return retval;
+}
+
+static void
+get_size (const Matrix& size, int& nr, int& nc, const char *warn_for)
+{
+  nr = -1;
+  nc = -1;
+
+  double dnr = -1.0;
+  double dnc = -1.0;
+
+  if (size.rows () == 1 && size.cols () > 0)
+    {
+      dnr = size.elem (0, 0);
+
+      if (size.cols () == 2)
+	dnc = size.elem (0, 1);
+      else if (size.cols () > 2)
+	::error ("%s: invalid size specification", warn_for);
+    }
+  else if (size.cols () == 1 && size.rows () > 0)
+    {
+      dnr = size.elem (0, 0);
+
+      if (size.rows () == 2)
+	dnc = size.elem (1, 0);
+      else if (size.rows () > 2)
+	::error ("%s: invalid size specification", warn_for);
+    }
+  else
+    ::error ("%s: invalid size specification", warn_for);
+
+  if (! error_state)
+    {
+      nr = get_size (dnr, warn_for);
+
+      if (! error_state && dnc > 0.0)
+	nc = get_size (dnc, warn_for);
+    }
+}
+
+scanf_format_list::scanf_format_list (const string& s)
+  : nconv (0), curr_idx (0), list (16), buf (0)
+{
+  int num_elts = 0;
+
+  int n = s.length ();
+
+  int i = 0;
+
+  bool discard = false;
+  char modifier = '\0';
+  char type = '\0';
+
+  bool have_more = true;
+
+  while (i < n)
+    {
+      have_more = true;
+
+      if (! buf)
+	buf = new ostrstream ();
+
+      if (s[i] == '%')
+	{
+	  process_conversion (s, i, n, discard, type, modifier, num_elts);
+	  have_more = (buf != 0);
+	}
+      else
+	{
+	  discard = false;
+	  modifier = '\0';
+	  type = '\0';
+	  *buf << s[i++];
+	}
+
+      if (nconv < 0)
+	{
+	  have_more = false;
+	  break;
+	}
+    }
+
+  if (have_more)
+    add_elt_to_list (discard, type, modifier, num_elts);
+
+  list.resize (num_elts);
+
+  delete buf;
+}
+
+scanf_format_list::~scanf_format_list (void)
+{
+  int n = list.length ();
+
+  for (int i = 0; i < n; i++)
+    {
+      scanf_format_elt *elt = list.elem (i);
+      delete elt;
+    }	
+}
+
+void
+scanf_format_list::add_elt_to_list (bool discard, char type,
+				    char modifier, int& num_elts)
+{
+  if (buf)
+    {
+      *buf << ends;
+
+      char *text = buf->str ();
+
+      if (text)
+	{
+	  if (*text)
+	    {
+	      scanf_format_elt *elt
+		= new scanf_format_elt (text, discard, type, modifier);
+
+	      if (num_elts == list.length ())
+		list.resize (2 * num_elts);
+
+	      list.elem (num_elts++) = elt;
+	    }
+	  else
+	    delete [] text;
+	}
+
+      delete buf;
+      buf = 0;
+    }
+}
+
+void
+scanf_format_list::process_conversion (const string& s, int& i, int n,
+				       bool& discard, char& type,
+				       char& modifier, int& num_elts)
+
+{
+  discard = false;
+  modifier = '\0';
+  type = '\0';
+
+  *buf << s[i++];
+
+  bool have_width = false;
+
+  while (i < n)
+    {
+      switch (s[i])
+	{
+	case '*':
+	  if (discard)
+	    nconv = -1;
+	  else
+	    {
+	      discard = true;
+	      *buf << s[i++];
+	    }
+	  break;
+
+	case '0': case '1': case '2': case '3': case '4':
+	case '5': case '6': case '7': case '8': case '9':
+	  if (have_width)
+	    nconv = -1;
+	  else
+	    {
+	      have_width = true;
+	      *buf << s[i++];
+	      while (i < n && isdigit (s[i]))
+		*buf << s[i++];
+	    }
+	  break;
+
+	case 'h': case 'l': case 'L':
+	  if (modifier != '\0')
+	    nconv = -1;
+	  else
+	    {
+	      modifier = s[i];
+	      *buf << s[i++];
+	    }
+	  break;
+
+	case 'd': case 'i': case 'o': case 'u': case 'x':
+	  if (modifier == 'L')
+	    {
+	      nconv = -1;
+	      break;
+	    }
+	  goto fini;
+
+	case 'e': case 'f': case 'g':
+	  if (modifier == 'h')
+	    {
+	      nconv = -1;
+	      break;
+	    }
+	  goto fini;
+
+	case 'c': case 's': case 'p': case '%': case '[':
+	  if (modifier != '\0')
+	    {
+	      nconv = -1;
+	      break;
+	    }
+	  goto fini;
+
+	fini:
+	  {
+	    if (finish_conversion (s, i, n, discard, type,
+				   modifier, num_elts) == 0)
+	      return;
+	  }
+	  break;
+
+	default:
+	  nconv = -1;
+	  break;
+	}
+
+      if (nconv < 0)
+	break;
+    }
+
+  nconv = -1;
+}
+
+int
+scanf_format_list::finish_conversion (const string& s, int& i, int n,
+				      bool discard, char& type,
+				      char modifier, int& num_elts)
+{
+  int retval = 0;
+
+  if (s[i] == '%')
+    *buf << s[i++];
+  else
+    {
+      type = s[i];
+
+      if (s[i] == '[')
+	{
+	  *buf << s[i++];
+
+	  if (i < n)
+	    {
+	      if (s[i] == '^')
+		{
+		  type = '^';
+		  *buf << s[i++];
+		}
+	      else if (s[i] == ']')
+		*buf << s[i++];
+	    }
+
+	  while (i < n && s[i] != ']')
+	    *buf << s[i++];
+
+	  if (i < n && s[i] == ']')
+	    *buf << s[i++];
+
+	  if (s[i-1] != ']')
+	    retval = nconv = -1;
+	}
+      else
+	{
+	  // XXX FIXME XXX -- this is a kludge, and probably not the
+	  // right thing to do here.
+
+	  if (type == 's')
+	    {
+	      *buf << 'c';
+	      i++;
+	    }
+	  else
+	    *buf << s[i++];
+	}
+
+      nconv++;
+
+      if (nconv > 0)
+	add_elt_to_list (discard, type, modifier, num_elts);
+    }
+
+  return retval;
+}
+
+void
+scanf_format_list::printme (void) const
+{
+  int n = list.length ();
+
+  for (int i = 0; i < n; i++)
+    {
+      scanf_format_elt *elt = list.elem (i);
+
+      cerr << elt->discard << "\t"
+	   << elt->type << "\t"
+	   << elt->modifier << "\t"
+	   << undo_string_escapes (elt->text) << "\n";
+    }
+}
+
+bool
+scanf_format_list::all_character_conversions (void)
+{
+  int n = list.length ();
+
+  if (n > 0)
+    {
+      for (int i = 0; i < n; i++)
+	{
+	  scanf_format_elt *elt = list.elem (i);
+
+	  switch (elt->type)
+	    {
+	    case 'c': case 's': case 'p': case '%': case '[':
+	      break;
+
+	    default:
+	      return false;
+	      break;
+	    }
+	}
+
+      return true;
+    }
+  else
+    return false;
+}
+
+bool
+scanf_format_list::all_numeric_conversions (void)
+{
+  int n = list.length ();
+
+  if (n > 0)
+    {
+      for (int i = 0; i < n; i++)
+	{
+	  scanf_format_elt *elt = list.elem (i);
+
+	  switch (elt->type)
+	    {
+	    case 'd': case 'i': case 'o': case 'u': case 'x':
+	    case 'e': case 'f': case 'g':
+	      break;
+
+	    default:
+	      return false;
+	      break;
+	    }
+	}
+
+      return true;
+    }
+  else
+    return false;
+}
+
+// Ugh again.
+
+printf_format_list::printf_format_list (const string& s)
+  : nconv (0), curr_idx (0), list (16), buf (0)
+{
+  int num_elts = 0;
+
+  int n = s.length ();
+
+  int i = 0;
+
+  int args = 0;
+  char modifier = '\0';
+  char type = '\0';
+
+  bool have_more = true;
+
+  while (i < n)
+    {
+      have_more = true;
+
+      if (! buf)
+	buf = new ostrstream ();
+
+      switch (s[i])
+	{
+	case '%':
+	  process_conversion (s, i, n, args, type, modifier, num_elts);
+	  have_more = (buf != 0);
+	  break;
+
+	default:
+	  args = 0;
+	  modifier = '\0';
+	  type = '\0';
+	  *buf << s[i++];
+	  break;
+	}
+
+      if (nconv < 0)
+	{
+	  have_more = false;
+	  break;
+	}
+    }
+
+  if (have_more)
+    add_elt_to_list (args, type, modifier, num_elts);
+
+  list.resize (num_elts);
+
+  delete buf;
+}
+
+printf_format_list::~printf_format_list (void)
+{
+  int n = list.length ();
+
+  for (int i = 0; i < n; i++)
+    {
+      printf_format_elt *elt = list.elem (i);
+      delete elt;
+    }	
+}
+
+void
+printf_format_list::add_elt_to_list (int args, char type, char modifier,
+				     int& num_elts)
+{
+  if (buf)
+    {
+      *buf << ends;
+
+      char *text = buf->str ();
+
+      if (text)
+	{
+	  if (*text)
+	    {
+	      printf_format_elt *elt
+		= new printf_format_elt (text, args, type, modifier);
+
+	      if (num_elts == list.length ())
+		list.resize (2 * num_elts);
+
+	      list.elem (num_elts++) = elt;
+	    }
+	  else
+	    delete [] text;
+	}
+
+      delete buf;
+      buf = 0;
+    }
+}
+
+void
+printf_format_list::process_conversion (const string& s, int& i, int n,
+					int& args, char& modifier,
+					char& type, int& num_elts)
+{
+  args = 0;
+  modifier = '\0';
+  type = '\0';
+
+  *buf << s[i++];
+
+  bool next = false;
+
+  while (i < n)
+    {
+      switch (s[i])
+	{
+	case '-': case '+': case ' ': case '0': case '#':
+	  *buf << s[i++];
+	  break;
+
+	default:
+	  next = true;
+	  break;
+	}
+
+      if (next)
+	break;
+    }
+
+  if (i < n)
+    {
+      if (s[i] == '*')
+	{
+	  args++;
+	  *buf << s[i++];
+	}
+      else
+	{
+	  while (i < n && isdigit (s[i]))
+	    *buf << s[i++];
+	}
+    }
+
+  if (i < n && s[i] == '.')
+    {
+      *buf << s[i++];
+
+      if (i < n)
+	{
+	  if (s[i] == '*')
+	    {
+	      args++;
+	      *buf << s[i++];
+	    }
+	  else
+	    {
+	      while (i < n && isdigit (s[i]))
+		*buf << s[i++];
+	    }
+	}
+    }
+
+  if (i < n)
+    {
+      switch (s[i])
+	{
+	case 'h': case 'l': case 'L':
+	  modifier = s[i];
+	  *buf << s[i++];
+	  break;
+
+	default:
+	  break;
+	}
+    }
+
+  if (i < n)
+    finish_conversion (s, i, args, modifier, type, num_elts);
+  else
+    nconv = -1;
+}
+
+void
+printf_format_list::finish_conversion (const string& s, int& i,
+				       int args, char modifier,
+				       char& type, int& num_elts)
+
+{
+  switch (s[i])
+    {
+    case 'd': case 'i': case 'o': case 'x': case 'X':
+    case 'u': case 'c':
+      if (modifier == 'L')
+	{
+	  nconv = -1;
+	  break;
+	}
+      goto fini;
+
+    case 'f': case 'e': case 'E': case 'g': case 'G':
+      if (modifier == 'h' || modifier == 'l')
+	{
+	  nconv = -1;
+	  break;
+	}
+      goto fini;
+
+    case 's': case 'p': case '%':
+      if (modifier != '\0')
+	{
+	  nconv = -1;
+	  break;
+	}
+      goto fini;
+
+    fini:
+
+      if (s[i] == '%' && args == 0)
+	*buf << s[i++];
+      else
+	{
+	  if (s[i] != '%')
+	    args++;
+
+	  type = s[i];
+
+	  *buf << s[i++];
+
+	  add_elt_to_list (args, type, modifier, num_elts);
+
+	  nconv++;
+	}
+      break;
+
+    default:
+      nconv = -1;
+      break;
+    }
+}
+
+void
+printf_format_list::printme (void) const
+{
+  int n = list.length ();
+
+  for (int i = 0; i < n; i++)
+    {
+      printf_format_elt *elt = list.elem (i);
+
+      cerr << elt->args<< "\t"
+	   << elt->type << "\t"
+	   << elt->modifier << "\t"
+	   << undo_string_escapes (elt->text) << "\n";
+    }
+}
+
+void
+octave_base_stream::error (const string& msg)
+{
+  fail = true;
+  errmsg = msg;
+}
+
+void
+octave_base_stream::clear (void)
+{
+  fail = false;
+  errmsg = "";
+}
+
+// Functions that are defined for all input streams (input streams
+// are those that define is).
+
+string
+octave_base_stream::do_gets (int max_len, bool& err,
+			     bool strip_newline, const char *fcn)
+{
+  string retval;
+
+  err = false;
+
+  istream *isp = input_stream ();
+
+  if (isp)
+    {
+      istream is = *isp;
+
+      // XXX FIXME XXX -- this should probably be converted to use
+      // sstream when that is available.
+      ostrstream buf;
+
+      int c = 0;
+      int count = 0;
+      int newline_stripped = 0;
+
+      while (is && (c = is.get ()) != EOF)
+	{
+	  count++;
+
+	  if (c == '\n')
+	    {
+	      if (! strip_newline)
+		buf << (char) c;
+	      else
+		newline_stripped = 1;
+
+	      break;
+	    }
+	  else
+	    buf << (char) c;
+
+	  if (max_len > 0 && count == max_len)
+	    break;
+	}
+
+      if (is.fail ())
+	{
+	  err = true;
+	  string msg = fcn;
+	  msg.append (": read error");
+	  error (msg);
+	}
+      else if (is.eof ())
+	{
+	  err = true;
+	  string msg = fcn;
+	  msg.append (": at end of file");
+	  error (msg);
+	}
+      else
+	{
+	  buf << ends;
+	  char *tmp = buf.str ();
+	  retval = tmp;
+	  delete [] tmp;
+	}
+    }
+  else
+    {
+      err = true;
+      invalid_operation (fcn, "reading");
+    }
+
+  return retval;
+}
+
+string
+octave_base_stream::getl (int max_len, bool& err)
+{
+  return do_gets (max_len, err, true, "fgetl");
+}
+
+string
+octave_base_stream::gets (int max_len, bool& err)
+{
+  return do_gets (max_len, err, false, "fgets");
+}
+
+// XXX FIXME XXX -- need to handle architecture type conversions.
+
+#define do_read_elem(is, type, val) \
+  do \
+    { \
+      type tmp_val; \
+      is.read ((char *) &tmp_val, sizeof (type)); \
+      val = tmp_val; \
+    } \
+  while (0)
+
+octave_value
+octave_base_stream::do_read (int nr, int nc, data_type dt, int skip,
+			     arch_type at, int& count)
+{
+  count = 0;
+
+  octave_value retval = Matrix ();
+
+  istream *isp = input_stream ();
+
+  Matrix mval;
+  double *data = 0;
+  int max_size = 0;
+
+  int final_nr = 0;
+  int final_nc = 0;
+
+  if (nr > 0)
+    {
+      if (nc > 0)
+	{
+	  mval.resize (nr, nc, 0.0);
+	  data = mval.fortran_vec ();
+	  max_size = nr * nc;
+	}
+      else
+	{
+	  mval.resize (nr, 32, 0.0);
+	  data = mval.fortran_vec ();
+	  max_size = nr * nc;
+	}
+    }
+  else
+    {
+      mval.resize (32, 1, 0.0);
+      data = mval.fortran_vec ();
+      max_size = 32;
+    }
+
+  if (isp)
+    {
+      istream is = *isp;
+
+      for (;;)
+	{
+	  // XXX FIXME XXX -- maybe there should be a special case for
+	  // skip == 0.
+
+	  if (is)
+	    {
+	      if (nr > 0 && nc > 0 && count == max_size)
+		{
+		  final_nr = nr;
+		  final_nc = nc;
+
+		  break;
+		}
+
+	      if (skip != 0)
+		seek (skip, ios::cur);
+
+	      if (is)
+		{
+		  double tmp;
+
+		  switch (dt)
+		    {
+		    case dt_char:
+		      do_read_elem (is, char, tmp);
+		      break;
+
+		    case dt_schar:
+		      do_read_elem (is, signed char, tmp);
+		      break;
+
+		    case dt_uchar:
+		      do_read_elem (is, unsigned char, tmp);
+		      break;
+
+		    case dt_short:
+		      do_read_elem (is, short, tmp);
+		      break;
+
+		    case dt_ushort:
+		      do_read_elem (is, unsigned short, tmp);
+		      break;
+
+		    case dt_int:
+		      do_read_elem (is, int, tmp);
+		      break;
+
+		    case dt_uint:
+		      do_read_elem (is, unsigned int, tmp);
+		      break;
+
+		    case dt_long:
+		      do_read_elem (is, long, tmp);
+		      break;
+
+		    case dt_ulong:
+		      do_read_elem (is, unsigned long, tmp);
+		      break;
+
+		    case dt_float:
+		      do_read_elem (is, float, tmp);
+		      break;
+
+		    case dt_double:
+		      do_read_elem (is, double, tmp);
+		      break;
+
+		    default:
+		      error ("fread: invalid type specification");
+		    }
+
+		  if (is && ok ())
+		    {
+		      if (count == max_size)
+			{
+			  max_size *= 2;
+
+			  if (nr > 0)
+			    mval.resize (nr, max_size / 2, 0.0);
+			  else
+			    mval.resize (max_size, 1, 0.0);
+
+			  data = mval.fortran_vec ();
+			}
+
+		      data[count++] = tmp;
+		    }
+		  else
+		    {
+		      if (is.eof ())
+			{
+			  if (nr > 0)
+			    {
+			      if (count > nr)
+				{
+				  final_nr = nr;
+				  final_nc = (count - 1) / nr + 1;
+				}
+			      else
+				{
+				  final_nr = count;
+				  final_nc = 1;
+				}
+			    }
+			  else
+			    {
+			      final_nr = count;
+			      final_nc = 1;
+			    }
+			}
+
+		      break;
+		    }
+		}
+	      else
+		{
+		  error ("fread: read error");
+		  break;
+		}
+	    }
+	  else
+	    {
+	      error ("fread: read error");
+	      break;
+	    }
+	}
+    }
+
+  if (ok ())
+    {
+      mval.resize (final_nr, final_nc);
+
+      retval = mval;
+    }
+
+  return retval;
+}
+
+octave_value
+octave_base_stream::read (const Matrix& size, data_type dt, int skip,
+			  arch_type at, int& count)
+{
+  octave_value retval;
+
+  count = 0;
+
+  istream *is = input_stream ();
+
+  if (is)
+    {
+      int nr = -1;
+      int nc = -1;
+
+      get_size (size, nr, nc, "fread");
+
+      if (! error_state)
+	retval = do_read (nr, nc, dt, skip, at, count);
+    }
+  else
+    invalid_operation ("fread", "reading");
+
+  return retval;
+}
+
+#define do_scanf_conv(is, fmt, valptr, mval, data, idx, max_size, discard) \
+  do \
+    { \
+      is.clear (); \
+ \
+      is.scan (fmt, valptr); \
+ \
+      if (is) \
+	{ \
+	  if (idx == max_size && ! discard) \
+	    { \
+	      max_size *= 2; \
+ \
+	      if (nr > 0) \
+		mval.resize (nr, max_size / 2, 0.0); \
+	      else \
+		mval.resize (max_size, 1, 0.0); \
+ \
+	      data = mval.fortran_vec (); \
+	    } \
+ \
+	  if (! discard) \
+	    data[idx++] = *(valptr); \
+	} \
+    } \
+  while (0)
+
+octave_value
+octave_base_stream::do_scanf (scanf_format_list& fmt_list,
+			      int nr, int nc, int& count)
+{
+  octave_value retval = Matrix ();
+
+  istream *isp = input_stream ();
+
+  bool all_char_conv = fmt_list.all_character_conversions ();
+
+  Matrix mval;
+  double *data = 0;
+  int max_size = 0;
+
+  int final_nr = 0;
+  int final_nc = 0;
+
+  if (nr > 0)
+    {
+      if (nc > 0)
+	{
+	  mval.resize (nr, nc, 0.0);
+	  data = mval.fortran_vec ();
+	  max_size = nr * nc;
+	}
+      else
+	{
+	  mval.resize (nr, 32, 0.0);
+	  data = mval.fortran_vec ();
+	  max_size = nr * nc;
+	}
+    }
+  else
+    {
+      mval.resize (32, 1, 0.0);
+      data = mval.fortran_vec ();
+      max_size = 32;
+    }
+
+  if (isp)
+    {
+      istream is = *isp;
+
+      const scanf_format_elt *elt = fmt_list.first ();
+
+      for (;;)
+	{
+	  if (elt)
+	    {
+	      if (nr > 0 && nc > 0 && count == max_size)
+		{
+		  final_nr = nr;
+		  final_nc = nc;
+
+		  break;
+		}
+
+	      const char *fmt = elt->text;
+
+	      bool discard = elt->discard;
+
+	      switch (elt->type)
+		{
+		case '%':
+		  {
+		    int dummy;
+
+		    do_scanf_conv (is, fmt, &dummy, mval, data, count,
+				   max_size, discard);
+		  }
+		  break;
+
+		case 'd': case 'i': case 'o': case 'u': case 'x':
+		  {
+		    int tmp;
+
+		    do_scanf_conv (is, fmt, &tmp, mval, data, count,
+				   max_size, discard);
+		  }
+		  break;
+
+		case 'e': case 'f': case 'g':
+		  {
+		    if (elt->modifier == 'l')
+		      {
+			double tmp;
+
+			do_scanf_conv (is, fmt, &tmp, mval, data,
+				       count, max_size, discard); 
+		      }
+		    else
+		      {
+			float tmp;
+
+			do_scanf_conv (is, fmt, &tmp, mval, data,
+				       count, max_size, discard); 
+		      }
+		  }
+		  break;
+
+		case 's':
+		case 'c':
+		  {
+		    char tmp;
+
+		    do_scanf_conv (is, fmt, &tmp, mval, data, count,
+				   max_size, discard);
+		  }
+		  break;
+
+		case 'p': case '[':
+		  error ("fscanf: unrecognized format specifier");
+		  break;
+
+		default:
+		  error ("fscanf: internal format error");
+		  break;
+		}
+
+	      if (! ok ())
+		{
+		  break;
+		}
+	      else if (! is)
+		{
+		  if (is.eof ())
+		    {
+		      if (nr > 0)
+			{
+			  if (count > nr)
+			    {
+			      final_nr = nr;
+			      final_nc = (count - 1) / nr + 1;
+			    }
+			  else
+			    {
+			      final_nr = count;
+			      final_nc = 1;
+			    }
+			}
+		      else
+			{
+			  final_nr = count;
+			  final_nc = 1;
+			}
+		    }
+		  else
+		    {
+		      error ("fscanf: read error");
+
+		      // XXX FIXME XXX -- is this the right thing to do?
+		      // What about other streams?
+		      if (name () == "stdin")
+			{
+			  is.clear ();
+
+			  // Skip to end of line.
+
+			  bool err;
+			  do_gets (-1, err, false, "fscanf");
+			}
+		    }
+
+		  break;
+		}
+	    }
+	  else
+	    {
+	      error ("fscanf: internal format error");
+	      break;
+	    }
+
+	  elt = fmt_list.next ();
+	}
+    }
+
+  if (ok ())
+    {
+      mval.resize (final_nr, final_nc);
+
+      if (all_char_conv)
+	{
+	  if (nr < 0)
+	    mval = mval.transpose ();
+
+	  retval = mval;
+
+	  retval = retval.convert_to_str ();
+	}
+      else
+	retval = mval;
+    }
+
+  return retval;
+}
+
+octave_value
+octave_base_stream::scanf (const string& fmt, const Matrix& size,
+			   int& count)
+{
+  // XXX FIXME XXX -- is this the right thing to do?
+  if (name () == "stdin")
+    fail = false;
+
+  octave_value retval = Matrix ();
+
+  count = 0;
+
+  istream *isp = input_stream ();
+
+  if (isp)
+    {
+      istream is = *isp;
+
+      scanf_format_list fmt_list (fmt);
+
+      switch (fmt_list.num_conversions ())
+	{
+	case -1:
+	  ::error ("fscanf: invalid format specified");
+	  break;
+
+	case 0:
+	  {
+	    const scanf_format_elt *elt = fmt_list.first ();
+
+	    if (elt)
+	      {
+		is.clear ();
+
+		is.scan (elt->text);
+
+		if (! is)
+		  {
+		    error ("fscanf: read error");
+
+		    // XXX FIXME XXX -- is this the right thing to do?
+		    // Maybe.  We should probably also arrange to
+		    // flush the pending input prior to printing a
+		    // prompt.  Or maybe just blow off scanf for stdin
+		    // like the MathWorks did. What about other streams?
+
+		    if (name () == "stdin")
+		      {
+			is.clear ();
+
+			// Skip to end of line.
+
+			bool err;
+			do_gets (-1, err, false, "fscanf");
+		      }
+		  }
+	      }
+	  }
+	  break;
+
+	default:
+	  {
+	    int nr = -1;
+	    int nc = -1;
+
+	    get_size (size, nr, nc, "fscanf");
+
+	    if (! error_state)
+	      retval = do_scanf (fmt_list, nr, nc, count);
+	  }
+	  break;
+	}
+    }
+  else
+    invalid_operation ("fscanf", "writing");
+
+  return retval;
+}
+
+// Functions that are defined for all output streams (output streams
+// are those that define os).
+
+int
+octave_base_stream::flush (void)
+{
+  int retval = -1;
+
+  ostream *os = output_stream ();
+
+  if (os)
+    {
+      os->flush ();
+
+      if (os->good ())
+	retval = 0;
+    }
+  else
+    invalid_operation ("fflush", "writing");
+
+  return retval;
+}
+
+// XXX FIXME XXX -- need to handle architecture type conversions.
+
+#define do_write_elem(os, type, val) \
+  do \
+    { \
+      type tmp_val = (type) val; \
+      os.write ((char *) &tmp_val, sizeof (type)); \
+    } \
+  while (0)
+
+int
+octave_base_stream::do_write (const double *data, int n, data_type dt,
+			      int skip, arch_type at)
+{
+  int retval = -1;
+
+  int count = 0;
+
+  ostream *osp = output_stream ();
+
+  if (osp)
+    {
+      ostream os = *osp;
+
+      // XXX FIXME XXX -- maybe there should be a special case for
+      // skip == 0.
+
+      for (int i = 0; i < n; i++)
+	{
+	  if (os)
+	    {
+	      if (skip != 0)
+		seek (skip, ios::cur);
+
+	      if (os)
+		{
+		  double tmp = data[i];
+
+		  switch (dt)
+		    {
+		    case dt_char:
+		      do_write_elem (os, char, tmp);
+		      break;
+
+		    case dt_schar:
+		      do_write_elem (os, signed char, tmp);
+		      break;
+
+		    case dt_uchar:
+		      do_write_elem (os, unsigned char, tmp);
+		      break;
+
+		    case dt_short:
+		      do_write_elem (os, short, tmp);
+		      break;
+
+		    case dt_ushort:
+		      do_write_elem (os, unsigned short, tmp);
+		      break;
+
+		    case dt_int:
+		      do_write_elem (os, int, tmp);
+		      break;
+
+		    case dt_uint:
+		      do_write_elem (os, unsigned int, tmp);
+		      break;
+
+		    case dt_long:
+		      do_write_elem (os, long, tmp);
+		      break;
+
+		    case dt_ulong:
+		      do_write_elem (os, unsigned long, tmp);
+		      break;
+
+		    case dt_float:
+		      do_write_elem (os, float, tmp);
+		      break;
+
+		    case dt_double:
+		      do_write_elem (os, double, tmp);
+		      break;
+
+		    default:
+		      error ("fwrite: invalid type specification");
+		    }
+
+		  if (os && ok ())
+		    count++;
+		  else
+		    break;
+		}
+	      else
+		{
+		  error ("fwrite: write error");
+		  break;
+		}
+	    }
+	  else
+	    {
+	      error ("fwrite: write error");
+	      break;
+	    }
+	}
+    }
+
+  if (ok ())
+    retval = count;
+
+  return retval;
+}
+
+int
+octave_base_stream::write (const octave_value& data, data_type dt,
+			   int skip, arch_type at)
+{
+  int retval = -1;
+
+  ostream *os = output_stream ();
+
+  if (os)
+    {
+      Matrix mval = data.matrix_value ();
+
+      if (! error_state)
+	{
+	  int n = mval.length ();
+
+	  const double *d = mval.data ();
+
+	  retval = octave_base_stream::do_write (d, n, dt, skip, at);
+	}
+    }
+  else
+    invalid_operation ("fwrite", "writing");
+
+  return retval;
+}
+
+class
+printf_value_cache
+{
+public:
+
+  enum state { ok, list_exhausted, conversion_error };
+
+  printf_value_cache (const octave_value_list& args)
+    : values (args), val_idx (0), elt_idx (0),
+      n_vals (values.length ()), n_elts (0), data (0),
+      curr_state (ok) { }
+
+  ~printf_value_cache (void) { }
+
+  // Get the current value as a double and advance the internal pointer.
+  double double_value (void);
+
+  // Get the current value as an int and advance the internal pointer.
+  int int_value (void);
+
+  // Get the current value as a string and advance the internal pointer.
+  string string_value (void);
+
+  operator void* () const
+    { return (curr_state == ok) ? (void *) -1 : (void *) 0; }
+
+  bool no_more_values (void) { return curr_state == list_exhausted; }
+
+  bool looking_at_string (void);
+
+private:
+
+  const octave_value_list values;
+  int val_idx;
+  int elt_idx;
+  int n_vals;
+  int n_elts;
+  const double *data;
+  Matrix curr_val;
+  state curr_state;
+
+  // Must create value cache with values!
+
+  printf_value_cache (void);
+
+  // No copying!
+
+  printf_value_cache (const printf_value_cache&);
+
+  printf_value_cache& operator = (const printf_value_cache&);
+};
+
+bool
+printf_value_cache::looking_at_string (void)
+{
+  bool retval = false;
+
+  int idx = -1;
+
+  if (elt_idx == 0)
+    idx = val_idx;
+  else if (elt_idx >= n_elts)
+    idx = val_idx + 1;
+
+  if (idx >= 0 && idx < n_vals)
+    {
+      octave_value tmp_val = values (idx);
+
+      retval = tmp_val.is_string () && tmp_val.rows () == 1;
+    }
+
+  return retval;
+}
+
+double
+printf_value_cache::double_value (void)
+{
+  double retval = 0.0;
+
+  while (val_idx < n_vals)
+    {
+      if (! data)
+	{
+	  octave_value tmp_val = values (val_idx);
+
+	  curr_val = tmp_val.matrix_value ();
+
+	  if (! error_state)
+	    {
+	      elt_idx = 0;
+	      n_elts = curr_val.length ();
+	      data = curr_val.data ();
+	    }
+	  else
+	    {
+	      curr_state = conversion_error;
+	      break;
+	    }
+	}
+
+      if (elt_idx < n_elts)
+	{
+	  return data[elt_idx++];
+	  break;
+	}
+      else
+	{
+	  val_idx++;
+	  data = 0;
+	  continue;
+	}
+    }
+
+  curr_state = list_exhausted;
+
+  return retval;
+}
+
+int
+printf_value_cache::int_value (void)
+{
+  int retval = 0;
+
+  double dval = double_value ();
+
+  if (! error_state)
+    {
+      if (D_NINT (dval) == dval)
+	retval = NINT (dval);
+      else
+	curr_state = conversion_error;
+    }
+
+  return retval;
+}
+
+string
+printf_value_cache::string_value (void)
+{
+  string retval;
+
+  if (looking_at_string ())
+    {
+      if (elt_idx != 0)
+	{
+	  val_idx++;
+	  elt_idx = 0;
+	  data = 0;
+	}
+
+      retval = values (val_idx++).string_value ();
+    }
+  else
+    curr_state = conversion_error;
+
+  return retval;
+}
+
+// Ugh again and again.
+
+#define do_printf_conv(os, fmt, nsa, sa_1, sa_2, have_arg, arg) \
+do \
+  { \
+    switch (nsa) \
+      { \
+      case 2: \
+	if (have_arg) \
+	  os.form (fmt, sa_1, sa_2, arg); \
+	else \
+	  os.form (fmt, sa_1, sa_2); \
+	break; \
+ \
+      case 1: \
+	if (have_arg) \
+	  os.form (fmt, sa_1, arg); \
+	else \
+	  os.form (fmt, sa_1); \
+	break; \
+ \
+      case 0: \
+	if (have_arg) \
+	  os.form (fmt, arg); \
+	else \
+	  os.form (fmt); \
+	break; \
+ \
+      default: \
+	::error ("fprintf: internal error handling format"); \
+	break; \
+      } \
+  } \
+while (0)
+
+int
+octave_base_stream::do_printf (printf_format_list& fmt_list,
+			       const octave_value_list& args)
+{
+  int retval = -1;
+
+  ostream *osp = output_stream ();
+
+  if (osp)
+    {
+      ostream os = *osp;
+
+      const printf_format_elt *elt = fmt_list.first ();
+
+      printf_value_cache val_cache (args);
+
+      for (;;)
+	{
+	  if (elt)
+	    {
+	      int args = elt->args;
+	      int nsa = args;
+
+	      int doing_percent = elt->type == '%';
+
+	      if (args > 0 && ! doing_percent)
+		nsa--;
+
+	      int sa_1 = 0;
+	      int sa_2 = 0; 
+
+	      if (nsa > 0)
+		{
+		  sa_1 = val_cache.int_value ();
+
+		  if (! val_cache)
+		    break;
+		  else
+		    {
+		      if (nsa > 1)
+			{
+			  sa_2 = val_cache.int_value ();
+
+			  if (! val_cache)
+			    break;
+			}
+		    }
+		}
+
+	      const char *fmt = elt->text;
+
+	      if (doing_percent || args == 0)
+		do_printf_conv (os, fmt, nsa, sa_1, sa_2, 0, 0.0);
+	      else
+		{
+		  if (elt->type == 's' && val_cache.looking_at_string ())
+		    {
+		      string val = val_cache.string_value ();
+
+		      if (val_cache)
+			do_printf_conv (os, fmt, nsa, sa_1, sa_2, 1,
+					val.c_str ());
+		      else
+			break;
+		    }
+		  else
+		    {
+		      double val = val_cache.double_value ();
+
+		      if (val_cache)
+			{
+			  switch (elt->type)
+			    {
+			    case 'd': case 'i': case 'o': case 'x':
+			    case 'X': case 'u': case 'c':
+			      {
+				if (elt->modifier == 'l')
+				  do_printf_conv (os, fmt, nsa, sa_1,
+						  sa_2, 1, (long) val);
+				else
+				  do_printf_conv (os, fmt, nsa, sa_1,
+						  sa_2, 1, (int) val);
+			      }
+			      break;
+
+			    case 'f': case 'e': case 'E':
+			    case 'g': case 'G':
+			      do_printf_conv (os, fmt, nsa, sa_1,
+					      sa_2, 1, val);
+			      break;
+
+			    default:
+			      error ("fprintf: invalid format specifier");
+			      return -1;
+			      break;
+			    }
+			}
+		      else
+			break;
+		    }
+
+		  if (val_cache.no_more_values ())
+		    {
+		      retval = 0;
+		      break;
+		    }
+		}
+
+	      if (os)
+		retval += nsa + (doing_percent ? 0 : 1);
+	      else
+		{
+		  error ("fprintf: write error");
+		  retval = -1;
+		  break;
+		}
+	    }
+	  else
+	    {
+	      ::error ("fprintf: internal error handling format");
+	      retval = -1;
+	      break;
+	    }
+
+	  elt = fmt_list.next ();
+	}
+    }
+
+  return retval;
+}
+
+int
+octave_base_stream::printf (const string& fmt, const octave_value_list& args)
+{
+  int retval = -1;
+
+  ostream *osp = output_stream ();
+
+  if (osp)
+    {
+      ostream os = *osp;
+
+      printf_format_list fmt_list (fmt);
+
+      switch (fmt_list.num_conversions ())
+	{
+	case -1:
+	  ::error ("fprintf: invalid format specified");
+	  break;
+
+	case 0:
+	  {
+	    const printf_format_elt *elt = fmt_list.first ();
+
+	    if (elt)
+	      {
+		os.form (elt->text);
+
+		if (os)
+		  retval = 0;
+		else
+		  error ("fprintf: write error");
+	      }
+	  }
+	  break;
+
+	default:
+	  {
+	    if (args.length () == 0)
+	      ::error ("fprintf: no arguments available for specified format");
+	    else
+	      retval = do_printf (fmt_list, args);
+	  }
+	  break;
+	}
+    }
+  else
+    invalid_operation ("fprintf", "writing");
+
+  return retval;
+}
+
+int
+octave_base_stream::puts (const string& s)
+{
+  int retval = -1;
+
+  ostream *osp = output_stream ();
+
+  if (osp)
+    {
+      ostream os = *osp;
+
+      os << s;
+
+      if (os)
+	{
+	  // XXX FIXME XXX -- why does this seem to be necessary?
+	  // Without it, output from a loop like
+	  //
+	  //   for i = 1:100, fputs (stdout, "foo\n"); endfor
+	  //
+	  // doesn't seem to go to the pager immediately.
+
+	  os.flush ();
+
+	  if (os)
+	    retval = 0;
+	  else
+	    error ("fputs: write error");
+	}
+      else
+	error ("fputs: write error");
+    }
+  else
+    invalid_operation ("fputs", "writing");
+
+  return retval;
+}
+
+int
+octave_base_stream::rewind (void)
+{
+  return seek (0, ios::beg);
+}
+
+// Return current error message for this stream.
+
+string
+octave_base_stream::error (bool clear_err, int& errno)
+{
+  errno = fail ? -1 : 0;
+
+  string tmp = errmsg;
+
+  if (clear_err)
+    clear ();
+
+  return tmp;
+}
+
+void
+octave_base_stream::invalid_operation (const char *op, const char *rw)
+{
+  string msg = op;
+  msg.append (": stream not open for ");
+  msg.append (rw);
+  error (msg);
+}
+
+int
+octave_stream::flush (void)
+{
+  int retval = -1;
+
+  if (stream_ok ("fflush"))
+    retval = rep->flush ();
+
+  return retval;
+}
+
+string
+octave_stream::getl (int max_len, bool& err)
+{
+  string retval;
+
+  if (stream_ok ("getl"))
+    retval = rep->getl (max_len, err);
+
+  return retval;
+}
+
+string
+octave_stream::getl (const octave_value& tc_max_len, bool& err)
+{
+  string retval;
+
+  err = false;
+
+  int conv_err = 0;
+
+  int max_len = convert_to_valid_int (tc_max_len, conv_err);
+
+  if (conv_err || max_len < 0)
+    {
+      err = true;
+      ::error ("fgetl: invalid maximum length specified");
+    }
+  else
+    retval = getl (max_len, err);
+
+  return retval;
+}
+
+string
+octave_stream::gets (int max_len, bool& err)
+{
+  string retval;
+
+  if (stream_ok ("fgets"))
+    retval = rep->gets (max_len, err);
+
+  return retval;
+}
+
+string
+octave_stream::gets (const octave_value& tc_max_len, bool& err)
+{
+  string retval;
+
+  err = false;
+
+  int conv_err = 0;
+
+  int max_len = convert_to_valid_int (tc_max_len, conv_err);
+
+  if (conv_err || max_len < 0)
+    {
+      err = true;
+      ::error ("fgets: invalid maximum length specified");
+    }
+  else
+    retval = gets (max_len, err);
+
+  return retval;
+}
+
+int
+octave_stream::seek (streampos offset, ios::seek_dir origin)
+{
+  int retval = -1;
+
+  if (stream_ok ("fseek"))
+    retval = rep->seek (offset, origin);
+
+  return retval;
+}
+
+int
+octave_stream::seek (const octave_value& tc_offset,
+		     const octave_value& tc_origin)
+{
+  int retval = -1;
+
+  int conv_err = 0;
+
+  int xoffset = convert_to_valid_int (tc_offset, conv_err);
+
+  if (! conv_err)
+    {
+      int xorigin = convert_to_valid_int (tc_origin, conv_err);
+
+      ios::seek_dir origin = ios::beg;
+
+      // XXX FIXME XXX -- matlab allows origin to be:
+      //
+      //  "bof" or -1  ==  ios::beg
+      //  "cof" or  0  ==  ios::cur
+      //  "eof" or  1  ==  ios::end
+
+      if (! conv_err)
+	{
+	  if (xorigin == 0)
+	    origin = ios::beg;
+	  else if (xorigin == 1)
+	    origin = ios::cur;
+	  else if (xorigin == 2)
+	    origin = ios::end;
+	  else
+	    conv_err = -1;
+	}
+
+      if (! conv_err)
+	retval = seek (xoffset, origin);
+      else
+	error ("fseek: invalid value for origin");
+    }
+  else
+    error ("fseek: invalid value for offset");
+
+  return retval;
+}
+
+long
+octave_stream::tell (void) const
+{
+  long retval = -1;
+
+  if (stream_ok ("tell"))
+    retval = rep->tell ();
+
+  return retval;
+}
+
+int
+octave_stream::rewind (void)
+{
+  int retval = -1;
+
+  if (stream_ok ("frewind"))
+    retval = rep->rewind ();
+
+  return retval;
+}
+
+octave_value
+octave_stream::read (const Matrix& size,
+		     octave_base_stream::data_type dt, int skip,
+		     octave_base_stream::arch_type at, int& count)
+{
+  octave_value retval;
+
+  if (stream_ok ("fread"))
+    retval = rep->read (size, dt, skip, at, count);
+
+  return retval;
+}
+
+int
+octave_stream::write (const octave_value& data,
+		      octave_base_stream::data_type dt, int skip,
+		      octave_base_stream::arch_type at)
+{
+  int retval = -1;
+
+  if (stream_ok ("fwrite"))
+    retval = rep->write (data, dt, skip, at);
+
+  return retval;
+}
+
+octave_value
+octave_stream::scanf (const string& fmt, const Matrix& size, int& count)
+{
+  octave_value retval;
+
+  if (stream_ok ("fscanf"))
+    retval = rep->scanf (fmt, size, count);
+
+  return retval;
+}
+
+int
+octave_stream::printf (const string& fmt, const octave_value_list& args)
+{
+  int retval = -1;
+
+  if (stream_ok ("fprintf"))
+    retval = rep->printf (fmt, args);
+
+  return retval;
+}
+
+int
+octave_stream::puts (const string& s)
+{
+  int retval = -1;
+
+  if (stream_ok ("fputs"))
+    retval = rep->puts (s);
+
+  return retval;
+}
+
+// XXX FIXME XXX -- maybe this should work for string arrays too.
+
+int
+octave_stream::puts (const octave_value& tc_s)
+{
+  int retval = -1;
+
+  if (tc_s.is_string ())
+    {
+      string s = tc_s.string_value ();      
+      retval = rep->puts (s);
+    }
+  else
+    error ("fputs: argument must be a string");
+
+  return retval;
+}
+
+bool
+octave_stream::eof (void) const
+{
+  int retval = -1;
+
+  if (stream_ok ("feof"))
+    retval = rep->eof ();
+
+  return retval;
+}
+
+string
+octave_stream::error (bool clear, int& errno)
+{
+  string retval;
+
+  if (stream_ok ("ferror", false))
+    retval = rep->error (clear, errno);
+
+  cerr << retval;
+
+  return retval;
+}
+
+string
+octave_stream::name (void)
+{
+  string retval;
+
+  if (stream_ok ("name"))
+    retval = rep->name ();
+
+  return retval;
+}
+
+int
+octave_stream::mode (void)
+{
+  int retval = 0;
+
+  if (stream_ok ("mode"))
+    retval = rep->mode ();
+
+  return retval;
+}
+
+octave_base_stream::arch_type
+octave_stream::architecture (void)
+{
+  octave_base_stream::arch_type retval = octave_base_stream::at_unknown;
+
+  if (stream_ok ("architecture"))
+    retval = rep->architecture ();
+
+  return retval;
+}
+
+string
+octave_stream::mode_as_string (int mode)
+{
+  string retval = "???";
+
+  switch (mode)
+    {
+    case ios::in:
+      retval = "r";
+      break;
+
+    case ios::out:
+    case ios::out | ios::trunc:
+      retval = "w";
+      break;
+
+    case ios::out | ios::app:
+      retval = "a";
+      break;
+
+    case ios::in | ios::out:
+      retval = "r+";
+      break;
+
+    case ios::in | ios::out | ios::trunc:
+      retval = "w+";
+      break;
+
+    case ios::in | ios::out | ios::app:
+      retval = "a+";
+      break;
+
+    case ios::in | ios::bin:
+      retval = "rb";
+      break;
+
+    case ios::out | ios::bin:
+    case ios::out | ios::trunc | ios::bin:
+      retval = "wb";
+      break;
+
+    case ios::out | ios::app | ios::bin:
+      retval = "ab";
+      break;
+
+    case ios::in | ios::out | ios::bin:
+      retval = "r+b";
+      break;
+
+    case ios::in | ios::out | ios::trunc | ios::bin:
+      retval = "w+b";
+      break;
+
+    case ios::in | ios::out | ios::app | ios::bin:
+      retval = "a+b";
+      break;
+
+    default:
+      break;
+    }
+
+  return retval;
+}
+
+string
+octave_stream::arch_as_string (octave_base_stream::arch_type at)
+{
+  string retval = "unknown";
+
+  switch (at)
+    {
+    case octave_base_stream::at_native:
+      retval = "native";
+      break;
+
+    default:
+      break;
+    }
+
+  return retval;
+}
+
+octave_base_stream::data_type
+octave_stream::string_to_data_type (const string& s)
+{
+  octave_base_stream::data_type retval = octave_base_stream::dt_unknown;
+
+  // XXX FINISHME XXX
+
+  /*
+    int16
+    integer*2
+    int32
+    integer*4 */
+
+  // XXX FIXME XXX -- before checking s, need to strip spaces and downcase.
+
+  if (s == "char" || s == "char*1" || s == "integer*1" || s == "int8")
+    retval = octave_base_stream::dt_char;
+  else if (s == "schar" || s == "signedchar")
+    retval = octave_base_stream::dt_schar;
+  else if (s == "uchar" || s == "unsignedchar")
+    retval = octave_base_stream::dt_uchar;
+  else if (s == "short")
+    retval = octave_base_stream::dt_short;
+  else if (s == "ushort" || s == "unsignedshort")
+    retval = octave_base_stream::dt_ushort;
+  else if (s == "int")
+    retval = octave_base_stream::dt_int;
+  else if (s == "uint" || s == "unsignedint")
+    retval = octave_base_stream::dt_uint;
+  else if (s == "long")
+    retval = octave_base_stream::dt_long;
+  else if (s == "ulong" || s == "unsignedlong")
+    retval = octave_base_stream::dt_ulong;
+  else if (s == "float" || s == "float32" || s == "real*4")
+    retval = octave_base_stream::dt_float;
+  else if (s == "double" || s == "float64" || s == "real**")
+    retval = octave_base_stream::dt_double;
+  else
+    ::error ("invalid data type specified");
+
+  return retval;
+}
+
+octave_base_stream::arch_type
+octave_stream::string_to_arch_type (const string& s)
+{
+  octave_base_stream::arch_type retval = octave_base_stream::at_unknown;
+
+  if (s == "native")
+    retval = octave_base_stream::at_native;
+  else
+    ::error ("invalid architecture type specified");
+
+  return retval;
+}
+
+void
+octave_stream::invalid_stream_error (const char *op) const
+{
+  ::error ("%s: attempt to use invalid I/O stream", op);
+}
+
+octave_stream_list *octave_stream_list::instance = 0;
+
+int
+octave_stream_list::do_insert (octave_base_stream *obs)
+{
+  int retval = -1;
+
+  if (obs)
+    {
+      octave_stream *os = new octave_stream (obs);
+
+      // Insert item in first open slot, increasing size of list if
+      // necessary.
+
+      for (int i = 0; i < curr_len; i++)
+	{
+	  octave_stream *tmp = list.elem (i);
+
+	  if (! tmp)
+	    {
+	      list.elem (i) = os;
+	      retval = i;
+	      break;
+	    }
+	}
+
+      if (retval < 0)
+	{
+	  int total_len = list.length ();
+
+	  if (curr_len == total_len)
+	    list.resize (total_len * 2);
+
+	  list.elem (curr_len) = os;
+	  retval = curr_len;
+	  curr_len++;
+	}
+    }
+  else
+    ::error ("octave_stream_list: attempt to insert invalid stream");
+
+  return retval;
+}
+
+int
+octave_stream_list::insert (octave_base_stream *obs)
+{
+  int retval = -1;
+
+  if (! instance)
+    instance = new octave_stream_list ();
+
+  if (instance)
+    retval = instance->do_insert (obs);
+  else
+    panic_impossible ();
+
+  return retval;
+}
+
+octave_stream *
+octave_stream_list::do_lookup (int fid) const
+{
+  octave_stream *retval = 0;
+
+  if (fid >= 0 && fid < curr_len)
+    retval = list.elem (fid);
+
+  return retval;
+}
+
+octave_stream *
+octave_stream_list::do_lookup (const octave_value& fid) const
+{
+  octave_stream *retval = 0;
+
+  int i = get_file_number (fid);
+
+  if (! error_state)
+    retval = do_lookup (i);
+
+  return retval;
+}
+
+octave_stream *
+octave_stream_list::lookup (int fid)
+{
+  octave_stream *retval = 0;
+
+  if (instance)
+    retval = instance->do_lookup (fid);
+
+  return retval;
+}
+
+octave_stream *
+octave_stream_list::lookup (const octave_value& fid)
+{
+  octave_stream *retval = 0;
+
+  if (instance)
+    retval = instance->do_lookup (fid);
+
+  return retval;
+}
+
+int
+octave_stream_list::do_remove (int fid)
+{
+  int retval = -1;
+
+  // Can't remove stdin (cin), stdout (cout), or stderr (cerr).
+
+  if (fid > 2 && fid < curr_len)
+    {
+      octave_stream *os = list.elem (fid);
+
+      if (os)
+	{
+	  delete os;
+	  list.elem (fid) = 0;
+	  retval = 0;
+	}
+    }
+
+  return retval;
+}
+
+int
+octave_stream_list::do_remove (const octave_value& fid)
+{
+  int retval = -1;
+
+  int i = get_file_number (fid);
+
+  if (! error_state)
+    retval = do_remove (i);
+
+  return retval;
+}
+
+int
+octave_stream_list::remove (int fid)
+{
+  int retval = -1;
+
+  if (instance)
+    retval = instance->do_remove (fid);
+
+  return retval;
+}
+
+int
+octave_stream_list::remove (const octave_value& fid)
+{
+  int retval = -1;
+
+  if (instance)
+    retval = instance->do_remove (fid);
+
+  return retval;
+}
+
+void
+octave_stream_list::do_clear (void)
+{
+  // Do flush stdout and stderr.
+
+  list.elem (0) -> flush ();
+  list.elem (1) -> flush ();
+
+  // But don't delete them or stdin.
+
+  for (int i = 3; i < curr_len; i++)
+    {
+      octave_stream *os = list.elem (i);
+
+      delete os;
+
+      list.elem (i) = 0;
+    }
+}
+
+void
+octave_stream_list::clear (void)
+{
+  if (instance)
+    instance->do_clear ();
+}
+
+string_vector
+octave_stream_list::do_get_info (int fid) const
+{
+  string_vector retval;
+
+  octave_stream *os = do_lookup (fid);
+
+  if (os)
+    {
+      retval.resize (3);
+
+      retval(0) = os->name ();
+      retval(1) = octave_stream::mode_as_string (os->mode ());
+      retval(2) = octave_stream::arch_as_string (os->architecture ());
+    }
+  else
+    ::error ("invalid file id");
+
+  return retval;
+}
+
+string_vector
+octave_stream_list::do_get_info (const octave_value& fid) const
+{
+  string_vector retval;
+
+  int conv_err = 0;
+
+  int int_fid = convert_to_valid_int (fid, conv_err);
+
+  if (! conv_err)
+    retval = do_get_info (int_fid);
+  else
+    ::error ("file id must be valid integer");
+
+  return retval;
+}
+
+string_vector
+octave_stream_list::get_info (int fid)
+{
+  string_vector retval;
+
+  if (instance)
+    retval = instance->do_get_info (fid);
+
+  return retval;
+}
+
+string_vector
+octave_stream_list::get_info (const octave_value& fid)
+{
+  string_vector retval;
+
+  if (instance)
+    retval = instance->do_get_info (fid);
+
+  return retval;
+}
+
+string
+octave_stream_list::do_list_open_files (void) const
+{
+  string retval;
+
+  // XXX FIXME XXX -- this should probably be converted to use sstream
+  // when that is available.
+  ostrstream buf;
+
+  buf << "\n"
+      << "  number  mode  arch       name\n"
+      << "  ------  ----  ----       ----\n";
+
+  for (int i = 0; i < curr_len; i++)
+    {
+      octave_stream *os = list.elem (i);
+
+      if (os)
+	{
+	  string mode = octave_stream::mode_as_string (os->mode ());
+	  string arch = octave_stream::arch_as_string (os->architecture ());
+	  string name = os->name ();
+
+	  buf.form ("  %4d     %-3s  %-9s  %s\n",
+		    i, mode.c_str (), arch.c_str (), name.c_str ());
+	}
+    }
+
+  buf << "\n" << ends;
+
+  char *tmp = buf.str ();
+
+  retval = tmp;
+
+  delete [] tmp;
+
+  return retval;
+}
+
+string
+octave_stream_list::list_open_files (void)
+{
+  string retval;
+
+  if (instance)
+    retval = instance->do_list_open_files ();
+
+  return retval;
+}
+
+octave_value
+octave_stream_list::do_open_file_numbers (void) const
+{
+  Matrix retval (1, curr_len, 0.0);
+
+  int num_open = 0;
+
+  // Skip stdin, stdout, and stderr.
+
+  for (int i = 3; i < curr_len; i++)
+    {
+      if (list.elem (i))
+	retval (0, num_open++) = i;
+    }
+
+  retval.resize ((num_open > 0), num_open);
+
+  return retval;
+}
+
+octave_value
+octave_stream_list::open_file_numbers (void)
+{
+  octave_value retval;
+
+  if (instance)
+    retval = instance->do_open_file_numbers ();
+
+  return retval;
+}
+
+int
+octave_stream_list::get_file_number (const octave_value& fid) const
+{
+  int retval = -1;
+
+  if (fid.is_string ())
+    {
+      string nm = fid.string_value ();
+
+      // stdin (cin), stdout (cout), and stderr (cerr) are unnamed.
+
+      for (int i = 3; i < curr_len; i++)
+	{
+	  octave_stream *os = list.elem (i);
+
+	  if (os && os->name () == nm)
+	    {
+	      retval = i;
+	      break;
+	    }
+	}
+    }
+  else
+    {
+      int conv_err = 0;
+
+      int int_fid = convert_to_valid_int (fid, conv_err);
+
+      if (conv_err)
+	::error ("file id must be a string or integer value");
+      else
+	retval = int_fid;
+    }
+
+  return retval;
+}
+
+/*
+;;; Local Variables: ***
+;;; mode: C++ ***
+;;; End: ***
+*/
new file mode 100644
--- /dev/null
+++ b/src/oct-stream.h
@@ -0,0 +1,531 @@
+/*
+
+Copyright (C) 1996 John W. Eaton
+
+This file is part of Octave.
+
+Octave is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+Octave is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+
+#if !defined (octave_octave_stream_h)
+#define octave_octave_stream_h 1
+
+#include <string>
+
+#include <iostream.h>
+#include <strstream.h>
+
+#include "Array.h"
+
+#include "oct-obj.h"
+#include "str-vec.h"
+
+struct
+scanf_format_elt
+{
+  scanf_format_elt (const char *txt = 0, bool d = false,
+		    char typ = '\0', char mod = '\0')
+    : text (txt), discard (d), type (typ), modifier (mod) { }
+
+  ~scanf_format_elt (void) { delete text; }
+
+  const char *text;
+  bool discard;
+  char type;
+  char modifier;
+};
+
+class
+scanf_format_list
+{
+public:
+
+  scanf_format_list (const string& fmt = string ());
+
+  ~scanf_format_list (void);
+
+  int num_conversions (void) { return nconv; }
+
+  const scanf_format_elt *first (void)
+    {
+      curr_idx = 0;
+      return current ();
+    }
+
+  const scanf_format_elt *current (void) const
+    { return list.length () > 0 ? list.elem (curr_idx) : 0; }
+
+  const scanf_format_elt *next (void)
+    {
+      curr_idx++;
+      if (curr_idx >= list.length ())
+	curr_idx = 0;
+      return current ();
+    }
+
+  void printme (void) const;
+
+  bool ok (void) const { return (nconv >= 0); }
+
+  operator void* () const { return ok () ? (void *) -1 : (void *) 0; }
+
+  bool all_character_conversions (void);
+
+  bool all_numeric_conversions (void);
+
+private:
+
+  // Number of conversions specified by this format string, or -1 if
+  // invalid conversions have been found.
+  int nconv;
+
+  // Index to current element;
+  int curr_idx;
+
+  // List of format elements.
+  Array<scanf_format_elt*> list;
+
+  // Temporary buffer.
+  ostrstream *buf;
+
+  void add_elt_to_list (bool discard, char type, char modifier,
+			int& num_elts);
+
+  void process_conversion (const string& s, int& i, int n, bool& discard,
+			   char& type, char& modifier, int& num_elts);
+
+  int finish_conversion (const string& s, int& i, int n, bool discard,
+			 char& type, char modifier, int& num_elts);
+  // No copying!
+
+  scanf_format_list (const scanf_format_list&);
+
+  scanf_format_list& operator = (const scanf_format_list&);
+};
+
+struct
+printf_format_elt
+{
+  printf_format_elt (const char *txt = 0, int n = 0, char typ = '\0',
+		     char mod = '\0')
+    : text (txt), args (n), type (typ), modifier (mod) { }
+
+  ~printf_format_elt (void) { delete text; }
+
+  const char *text;
+  int args;
+  char type;
+  char modifier;
+};
+
+class
+printf_format_list
+{
+public:
+
+  printf_format_list (const string& fmt = string ());
+
+  ~printf_format_list (void);
+
+  int num_conversions (void) { return nconv; }
+
+  const printf_format_elt *first (void)
+    {
+      curr_idx = 0;
+      return current ();
+    }
+
+  const printf_format_elt *current (void) const
+    { return list.length () > 0 ? list.elem (curr_idx) : 0; }
+
+  const printf_format_elt *next (void)
+    {
+      curr_idx++;
+      if (curr_idx >= list.length ())
+	curr_idx = 0;
+      return current ();
+    }
+
+  void printme (void) const;
+
+  bool ok (void) const { return (nconv >= 0); }
+
+  operator void* () const { return ok () ? (void *) -1 : (void *) 0; }
+
+private:
+
+  // Number of conversions specified by this format string, or -1 if
+  // invalid conversions have been found.
+  int nconv;
+
+  // Index to current element;
+  int curr_idx;
+
+  // List of format elements.
+  Array<printf_format_elt*> list;
+
+  // Temporary buffer.
+  ostrstream *buf;
+
+  void add_elt_to_list (int args, char type, char modifier,
+			int& num_elts);
+
+  void process_conversion (const string& s, int& i, int n, int& args,
+			   char& modifier, char& type, int& num_elts);
+
+  void finish_conversion (const string& s, int& i, int args,
+			  char modifier, char& type, int& num_elts);
+
+  // No copying!
+
+  printf_format_list (const printf_format_list&);
+
+  printf_format_list& operator = (const printf_format_list&);
+};
+
+// Provide an interface for Octave streams.
+
+class
+octave_base_stream
+{
+friend class octave_stream;
+
+public:
+
+  enum arch_type
+    {
+      at_unknown,
+      at_native
+    };
+
+  enum data_type
+    {
+      dt_unknown,
+      dt_char,
+      dt_schar,
+      dt_uchar,
+      dt_short,
+      dt_ushort,
+      dt_int,
+      dt_uint,
+      dt_long,
+      dt_ulong,
+      dt_float,
+      dt_double,
+      dt_float_complex,
+      dt_double_complex
+    };
+
+  octave_base_stream (ios::openmode arg_md = ios::in|ios::out,
+		      arch_type arg_at = at_native)
+    : md (arg_md), at (arg_at), fail (false) { }
+
+  virtual ~octave_base_stream (void) { }
+
+  // The remaining functions are not specific to input or output only,
+  // and must be provided by the derived classes.
+
+  // Position a stream at OFFSET relative to ORIGIN.
+
+  virtual int seek (streampos offset, ios::seek_dir origin) = 0;
+
+  // Return current stream position.
+
+  virtual long tell (void) const = 0;
+
+  // Return non-zero if EOF has been reached on this stream.
+
+  virtual bool eof (void) const = 0;
+
+  // The name of the file.
+
+  virtual string name (void) = 0;
+
+  // If the derived class provides this function and it returns a
+  // pointer to a valid istream, scanf(), read(), getl(), and gets()
+  // will automatically work for this stream.
+
+  virtual istream *input_stream (void) { return 0; }
+
+  // If the derived class provides this function and it returns a
+  // pointer to a valid ostream, flush(), write(), and printf() will
+  // automatically work for this stream.
+
+  virtual ostream *output_stream (void) { return 0; }
+
+  bool ok (void) const { return ! fail; }
+
+  // Return current error message for this stream.
+
+  string error (bool clear, int& errno);
+
+protected:
+
+  int mode (void) { return md; }
+
+  arch_type architecture (void) { return at; }
+
+  // Set current error state and set fail to TRUE.
+
+  void error (const string& msg);
+
+  // Clear any error message and set fail to FALSE.
+
+  void clear (void);
+
+private:
+
+  // The permission bits for the file.  Should be some combination of
+  // ios::open_mode bits.
+  int md;
+
+  // Data format.
+  arch_type at;
+
+  // TRUE if an error has occurred.
+  bool fail;
+
+  // Should contain error message if fail is TRUE.
+  string errmsg;
+
+  // Functions that are defined for all input streams (input streams
+  // are those that define is).
+
+  string do_gets (int max_len, bool& err, bool strip_newline, const char *fcn);
+
+  string getl (int max_len, bool& err);
+  string gets (int max_len, bool& err);
+
+  octave_value do_read (int nr, int nc, data_type dt, int skip,
+			arch_type at, int& count);
+
+  octave_value read (const Matrix& size, data_type dt, int skip,
+		     arch_type at, int& count);
+
+  octave_value do_char_scanf (scanf_format_list& fmt_list,
+			      int nr, int nc, int& count);
+
+  octave_value do_real_scanf (scanf_format_list& fmt_list,
+			      int nr, int nc, int& count);
+
+  octave_value do_scanf (scanf_format_list& fmt_list, int nr, int nc,
+			 int& count);
+
+  octave_value scanf (const string& fmt, const Matrix& size, int& count);
+
+  // Functions that are defined for all output streams (output streams
+  // are those that define os).
+
+  int flush (void);
+
+  int do_write (const double *d, int n, data_type dt, int skip,
+		arch_type at);
+
+  int write (const octave_value& data, data_type dt, int skip,
+	     arch_type at);
+
+  int do_printf (printf_format_list& fmt_list, const octave_value_list& args);
+
+  int printf (const string& fmt, const octave_value_list& args);
+
+  int puts (const string& s);
+
+  // We can always do this in terms of seek(), so the derived class
+  // only has to provide that.
+
+  int rewind (void);
+
+  void invalid_operation (const char *op, const char *rw);
+
+  // No copying!
+
+  octave_base_stream (const octave_base_stream&);
+
+  octave_base_stream& operator = (const octave_base_stream&);
+};
+
+class
+octave_stream
+{
+public:
+
+  octave_stream (octave_base_stream *bs = 0) : rep (bs) { }
+
+  ~octave_stream (void) { delete rep; }
+
+  int flush (void);
+
+  string getl (int max_len, bool& err);
+  string getl (const octave_value& max_len, bool& err);
+
+  string gets (int max_len, bool& err);
+  string gets (const octave_value& max_len, bool& err);
+
+  int seek (streampos offset, ios::seek_dir origin);
+  int seek (const octave_value& offset, const octave_value& origin);
+
+  long tell (void) const;
+
+  int rewind (void);
+
+  octave_value read (const Matrix& size,
+		     octave_base_stream::data_type dt,
+		     int skip, octave_base_stream::arch_type at,
+		     int& count);
+
+  int write (const octave_value& data,
+	     octave_base_stream::data_type dt, int skip,
+	     octave_base_stream::arch_type at);
+
+  octave_value scanf (const string& fmt, const Matrix& size, int& count);
+
+  int printf (const string& fmt, const octave_value_list& args);
+
+  int puts (const string& s);
+  int puts (const octave_value& s);
+
+  bool eof (void) const;
+
+  string error (bool clear, int& errno);
+
+  string error (bool clear = false)
+    {
+      int errno;
+      return error (clear, errno);
+    }
+
+  bool ok (void) const { return rep && rep->ok (); }
+
+  operator void* () const { return ok () ? (void *) -1 : (void *) 0; }
+
+  string name (void);
+
+  int mode (void);
+
+  octave_base_stream::arch_type architecture (void);
+
+  static string mode_as_string (int mode);
+
+  static string arch_as_string (octave_base_stream::arch_type at);
+
+  static octave_base_stream::data_type string_to_data_type (const string& s);
+  static octave_base_stream::arch_type string_to_arch_type (const string& s);
+
+private:
+
+  // The actual representation of this stream.
+  octave_base_stream *rep;
+
+  void invalid_stream_error (const char *op) const;
+
+  bool stream_ok (const char *op, bool clear = true) const
+    {
+      bool retval = true;
+
+      if (rep)
+	{
+	  if (clear)
+	    rep->clear ();
+	}
+      else
+	{
+	  retval = false;
+	  invalid_stream_error (op);
+	}
+
+      return retval;
+    }
+
+  void error (const string& msg)
+    {
+      if (rep)
+	rep->error (msg);
+    }
+
+  // Must create named streams.
+
+  octave_stream (void);
+
+  // No copying!
+
+  octave_stream (const octave_stream&);
+
+  octave_stream& operator = (const octave_stream&);
+};
+
+class
+octave_stream_list
+{
+protected:
+
+  octave_stream_list (void) : list (32), curr_len (0) { }
+
+public:
+
+  ~octave_stream_list (void) { }
+
+  static int insert (octave_base_stream *obs);
+
+  static octave_stream *lookup (int fid);
+  static octave_stream *lookup (const octave_value& fid);
+
+  static int remove (int fid);
+  static int remove (const octave_value& fid);
+
+  static void clear (void);
+
+  static string_vector get_info (int fid);
+  static string_vector get_info (const octave_value& fid);
+
+  static string list_open_files (void);
+
+  static octave_value open_file_numbers (void);
+
+private:
+
+  Array<octave_stream*> list;
+
+  int curr_len;
+
+  static octave_stream_list *instance;
+
+  int do_insert (octave_base_stream *obs);
+
+  octave_stream *do_lookup (int fid) const;
+  octave_stream *do_lookup (const octave_value& fid) const;
+
+  int do_remove (int fid);
+  int do_remove (const octave_value& fid);
+
+  void do_clear (void);
+
+  string_vector do_get_info (int fid) const;
+  string_vector do_get_info (const octave_value& fid) const;
+
+  string do_list_open_files (void) const;
+
+  octave_value do_open_file_numbers (void) const;
+
+  int get_file_number (const octave_value& fid) const;
+};
+
+#endif
+
+/*
+;;; Local Variables: ***
+;;; mode: C++ ***
+;;; End: ***
+*/