view src/oct-stream.cc @ 2121:bc6ecd8f1175

[project @ 1996-05-12 06:45:55 by jwe]
author jwe
date Sun, 12 May 1996 06:45:55 +0000
parents b240b2fce8ed
children 6c8706cd60fb
line wrap: on
line source

/*

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 * 32;
	}
    }
  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, 0.0);

      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 / nr, 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)
{
  count = 0;

  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 * 32;
	}
    }
  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, 0.0);

      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: ***
*/