Logo Search packages:      
Sourcecode: octave2.0 version File versions

oct-stream.cc

/*

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 <cassert>
#include <cstring>

#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 "input.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, int& one_elt_size_spec,
        const char *warn_for)
{
  nr = -1;
  nc = -1;

  one_elt_size_spec = 0;

  double dnr = -1.0;
  double dnc = -1.0;

  int sz_nr = size.rows ();
  int sz_nc = size.cols ();

  if (sz_nr == 1 && sz_nc == 1)
    {
      one_elt_size_spec = 1;

      dnr = size (0, 0);
      dnc = 1.0;
    }
  else if (sz_nr == 1 && sz_nc > 0)
    {
      dnr = size (0, 0);

      if (sz_nc == 2)
      dnc = size (0, 1);
      else if (sz_nc > 2)
      ::error ("%s: invalid size specification", warn_for);
    }
  else if (sz_nc == 1 && sz_nr > 0)
    {
      dnr = size (0, 0);

      if (sz_nr == 2)
      dnc = size (1, 0);
      else if (sz_nr > 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;

  int width = 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, width, discard, type, modifier,
                        num_elts);
        have_more = (buf != 0);
      }
      else
      {
        width = 0;
        discard = false;
        modifier = '\0';
        type = '\0';
        *buf << s[i++];
      }

      if (nconv < 0)
      {
        have_more = false;
        break;
      }
    }

  if (have_more)
    add_elt_to_list (width, 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 (i);
      delete elt;
    } 
}

void
scanf_format_list::add_elt_to_list (int width, 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, width, discard, type, modifier);

            if (num_elts == list.length ())
            list.resize (2 * num_elts);

            list (num_elts++) = elt;
          }
        else
          delete [] text;
      }

      delete buf;
      buf = 0;
    }
}

void
scanf_format_list::process_conversion (const string& s, int& i, int n,
                               int& width, bool& discard, char& type,
                               char& modifier, int& num_elts)

{
  width = 0;
  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
          {
            char c = s[i++];
            width = width * 10 + c - '0';
            have_width = true;
            *buf << c;
            while (i < n && isdigit (s[i]))
            {
              c = s[i++];
              width = width * 10 + c - '0';
              *buf << c;
            }
          }
        break;

      case 'h': case 'l': case 'L':
        if (modifier != '\0')
          nconv = -1;
        else
          modifier = 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;
          }

        // No float or long double conversions, thanks.
        *buf << 'l';

        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, width, 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,
                              int& width, 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
      *buf << s[i++];

      nconv++;

      if (nconv > 0)
      add_elt_to_list (width, 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 (i);

      cerr << elt->width << "\t"
         << 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 (i);

        switch (elt->type)
          {
          case 'c': case 's': 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 (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 (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 (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 (i);

      cerr << elt->args<< "\t"
         << elt->type << "\t"
         << elt->modifier << "\t"
         << undo_string_escapes (elt->text) << "\n";
    }
}

int
octave_base_stream::file_number (void)
{
  // Kluge alert!

  if (name () == "stdin")
    return 0;

  if (name () == "stdout")
    return 1;

  if (name () == "stderr")
    return 2;

  int retval = -1;

  istream *is = input_stream ();
  ostream *os = output_stream ();

  int i_fid = is ? ((filebuf *) (is->rdbuf ()))->fd () : -1;
  int o_fid = os ? ((filebuf *) (os->rdbuf ()))->fd () : -1;

  if (i_fid >= 0)
    {
      if (o_fid >= 0)
      retval = (i_fid == o_fid) ? i_fid : -1;
      else
      retval = i_fid;
    }
  else if (o_fid >= 0)
    retval = o_fid;

  return retval;
}

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 (count == 0 && 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");
}

octave_value
octave_base_stream::read (const Matrix& size,
                    oct_data_conv::data_type dt, int skip,
                    oct_mach_info::float_format flt_fmt, int& count)
{
  Matrix retval;

  count = 0;

  istream *isp = input_stream ();

  if (isp)
    {
      istream& is = *isp;

      int nr = -1;
      int nc = -1;

      int ignore;

      get_size (size, nr, nc, ignore, "fread");

      if (! error_state)
      {
        if (flt_fmt == oct_mach_info::unknown)
          flt_fmt = float_format ();

        int tmp = retval.read (is, nr, nc, dt, skip, flt_fmt);

        if (tmp < 0)
          error ("fread: read error");
        else
          count = tmp;
      }
    }
  else
    invalid_operation ("fread", "reading");

  return retval;
}

template <class T>
void
do_scanf_conv (istream& is, const char *fmt, T valptr, Matrix& mval,
             double *data, int& idx, int& conv_count, int nr,
             int max_size, bool discard) 
{
  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)
      {
        conv_count++;
        data[idx++] = *(valptr);
      }
    }
}

template void
do_scanf_conv (istream&, const char*, int*, Matrix&, double*, int&,
             int&, int, int, bool);

template void
do_scanf_conv (istream&, const char*, long int*, Matrix&, double*, int&,
             int&, int, int, bool);

template void
do_scanf_conv (istream&, const char*, short int*, Matrix&, double*, int&,
             int&, int, int, bool);

#if 0
template void
do_scanf_conv (istream&, const char*, float*, Matrix&, double*, int&,
             int&, int, int, bool);
#endif

template void
do_scanf_conv (istream&, const char*, double*, Matrix&, double*, int&,
             int&, int, int, bool);


octave_value
octave_base_stream::do_scanf (scanf_format_list& fmt_list,
                        int nr, int nc, int one_elt_size_spec,
                        int& conversion_count)
{
  conversion_count = 0;

  int data_index = 0;

  octave_value retval = Matrix ();

  if (nr == 0 || nc == 0)
    {
      if (one_elt_size_spec)
      nc = 0;

      return Matrix (nr, nc, 0.0);
    }

  istream *isp = input_stream ();

  bool all_char_conv = fmt_list.all_character_conversions ();

  Matrix mval;
  double *data = 0;
  int max_size = 0;
  int max_conv = 0;

  int final_nr = 0;
  int final_nc = 0;

  if (all_char_conv)
    {
      if (one_elt_size_spec)
      {
        mval.resize (1, 512, 0.0);
        data = mval.fortran_vec ();
        max_size = 512;
        
        if (nr > 0)
          max_conv = nr;
      }
      else if (nr > 0 && nc > 0)
      {
        mval.resize (nr, 32, 0.0);
        data = mval.fortran_vec ();
        max_size = nr * 32;

        max_conv = nr * nc;
      }
    }
  else if (nr > 0)
    {
      if (nc > 0)
      {
        mval.resize (nr, nc, 0.0);
        data = mval.fortran_vec ();
        max_size = nr * nc;

        max_conv = max_size;
      }
      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 ();

      ios::fmtflags flags = is.flags ();

      for (;;)
      {
        if (elt)
          {
            if (max_conv > 0 && conversion_count == max_conv)
            {
              if (all_char_conv && one_elt_size_spec)
                {
                  final_nr = 1;
                  final_nc = data_index;
                }
              else
                {
                  final_nr = nr;
                  final_nc = (data_index - 1) / nr + 1;
                }

              break;
            }
            else if (data_index == max_size)
            {
              if (nr > 0)
                {
                  max_size *= 2;
                  mval.resize (nr, max_size / nr, 0.0);
                  data = mval.fortran_vec ();
                }
              else
                {
                  max_size *= 2;
                  mval.resize (max_size, 1, 0.0);
                  data = mval.fortran_vec ();
                }
            }

            const char *fmt = elt->text;

            bool discard = elt->discard;

            switch (elt->type)
            {
            case '%':
              {
                int dummy;

                is.scan (fmt, &dummy);
              }
            break;

            case 'd': case 'i': case 'o': case 'u': case 'x':
              {
                switch (elt->modifier)
                  {
                  case 'h':
                  {
                    short int tmp;
                    do_scanf_conv (is, fmt, &tmp, mval, data,
                               data_index, conversion_count,
                               nr, max_size, discard);
                  }
                  break;

                  case 'l':
                  {
                    long int tmp;
                    do_scanf_conv (is, fmt, &tmp, mval, data,
                               data_index, conversion_count,
                               nr, max_size, discard);
                  }
                  break;

                  default:
                  {
                    int tmp;
                    do_scanf_conv (is, fmt, &tmp, mval, data,
                               data_index, conversion_count,
                               nr, max_size, discard);
                  }
                  break;
                  }
              }
            break;

            case 'e': case 'f': case 'g':
              {
                double tmp;

                do_scanf_conv (is, fmt, &tmp, mval, data,
                           data_index, conversion_count,
                           nr, max_size, discard);
              }
            break;

            case 'c':
              is.unsetf (ios::skipws);
              // Fall through...

            case 's':
              {
                int width = elt->width;

                if (elt->type == 'c' && width == 0)
                  width = 1;

                char *tmp = 0;

                if (width)
                  {
                  tmp = new char [width+1];

                  is.scan (fmt, tmp);

                  tmp[width] = '\0';
                  }
                else
                  {
                  // We're looking at a `%s' format.  We have to
                  // skip initial whitespace and then read until
                  // the next whitespace character.

                  ostrstream buf;

                  int c = EOF;

                  while (is && (c = is.get ()) != EOF && isspace (c))
                    /* skip leading whitespace */;

                  if (is && c != EOF)
                    buf << (char) c;
                    
                  while (is && (c = is.get ()) != EOF && ! isspace (c))
                    buf << (char) c;

                  buf << ends;

                  tmp = buf.str ();
                  }

                width = strlen (tmp);

                if (is)
                  {
                  int i = 0;

                  if (! discard)
                    {
                      conversion_count++;

                      while (i < width && tmp[i] != '\0')
                        {
                        if (data_index == max_size)
                          {
                            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 ();
                          }

                        data[data_index++] = tmp[i++];
                        }
                    }
                  }

                delete [] tmp;

                is.setf (flags);
              }
            break;

            case 'p': case '[':
              error ("fscanf: unsupported format specifier");
              break;

            default:
              error ("fscanf: internal format error");
              break;
            }

            if (! ok ())
            {
              break;
            }
            else if (! is)
            {
              if (all_char_conv)
                {
                  if (one_elt_size_spec)
                  {
                    final_nr = 1;
                    final_nc = data_index;
                  }
                  else if (data_index > nr)
                  {
                    final_nr = nr;
                    final_nc = (data_index - 1) / nr + 1;
                  }
                  else
                  {
                    final_nr = data_index;
                    final_nc = 1;
                  }
                }
              else if (nr > 0)
                {
                  if (data_index > nr)
                  {
                    final_nr = nr;
                    final_nc = (data_index - 1) / nr + 1;
                  }
                  else
                  {
                    final_nr = data_index;
                    final_nc = 1;
                  }
                }
              else
                {
                  final_nr = data_index;
                  final_nc = 1;
                }

              // If it looks like we have a matching failure, then
              // reset the failbit in the stream state.

              if (is.rdstate () & ios::failbit)
                is.clear (is.rdstate () & (~ios::failbit));

              // XXX FIXME XXX -- is this the right thing to do?

              if (interactive && 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);

      retval = mval;

      if (all_char_conv)
      retval = retval.convert_to_str ();
    }

  return retval;
}

octave_value
octave_base_stream::scanf (const string& fmt, const Matrix& size,
                     int& count)
{
  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)
              {
                // If it looks like we have a matching failure, then
                // reset the failbit in the stream state.

                if (is.rdstate () & ios::failbit)
                  is.clear (is.rdstate () & (~ios::failbit));
                else
                  error ("fscanf: read error");

                // XXX FIXME XXX -- is this the right thing to do?

                if (interactive && 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;

          int one_elt_size_spec;

          get_size (size, nr, nc, one_elt_size_spec, "fscanf");

          if (! error_state)
            retval = do_scanf (fmt_list, nr, nc, one_elt_size_spec, count);
        }
        break;
      }
    }
  else
    invalid_operation ("fscanf", "reading");

  return retval;
}

bool
octave_base_stream::do_oscanf (const scanf_format_elt *elt,
                         octave_value& retval)
{
  bool quit = false;

  istream *isp = input_stream ();

  if (isp)
    {
      istream& is = *isp;

      ios::fmtflags flags = is.flags ();

      if (elt)
      {
        const char *fmt = elt->text;

        bool discard = elt->discard;

        switch (elt->type)
          {
          case '%':
            {
            int dummy;

            if (! is.scan (fmt, &dummy))
              quit = true;
            }
            break;

          case 'd': case 'i': case 'o': case 'u': case 'x':
            {
            int tmp;

            if (is.scan (fmt, &tmp))
              {
                if (! discard)
                  retval = (double) tmp;
              }
            else
              quit = true;
            }
            break;

          case 'e': case 'f': case 'g':
            {
            double tmp;

            if (is.scan (fmt, &tmp))
              {
                if (! discard)
                  retval = tmp;
              }
            else
              quit = true;
            }
            break;

          case 'c':
            {
            is.unsetf (ios::skipws);

            int width = elt->width ? elt->width : 1;

            char *tmp = new char[width + 1];

            if (is.scan (fmt, tmp))
              {
                if (! discard)
                  {
                  tmp[width] = '\0';
                  retval = tmp;
                  }
              }
            else
              quit = true;

            is.setf (flags);

            delete [] tmp;
            }
            break;

          case 's':
            {
            int width = elt->width;

            char *tmp = 0;

            if (width)
              {
                tmp = new char [width+1];

                is.scan (fmt, tmp);

                tmp[width] = '\0';
              }
            else
              {
                // We're looking at a `%s' format.  We have to
                // skip initial whitespace and then read until
                // the next whitespace character.

                ostrstream buf;

                int c = EOF;

                while (is && (c = is.get ()) != EOF && isspace (c))
                  /* skip leading whitespace */;

                if (is && c != EOF)
                  buf << (char) c;
                    
                while (is && (c = is.get ()) != EOF && ! isspace (c))
                  buf << (char) c;

                buf << ends;

                tmp = buf.str ();

                if (! discard)
                  retval = tmp;
              }

            delete [] tmp;

            if (! is)
              quit = true;
            }
            break;

          case 'p': case '[':
            error ("fscanf: unsupported format specifier");
            break;

          default:
            error ("fscanf: internal format error");
            break;
          }
      }

      if (ok () && is.fail ())
      {
        error ("fscanf: read error");
      
        // XXX FIXME XXX -- is this the right thing to do?

        if (interactive && name () == "stdin")
          {
            is.clear ();

            // Skip to end of line.

            bool err;
            do_gets (-1, err, false, "fscanf");
          }
      }
    }

  return quit;
}

octave_value_list
octave_base_stream::oscanf (const string& fmt)
{
  octave_value_list retval;

  istream *isp = input_stream ();

  if (isp)
    {
      istream& is = *isp;

      scanf_format_list fmt_list (fmt);

      int nconv = fmt_list.num_conversions ();

      switch (nconv)
      {
      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?

                if (interactive && name () == "stdin")
                  {
                  is.clear ();

                  // Skip to end of line.

                  bool err;
                  do_gets (-1, err, false, "fscanf");
                  }
              }
            }
        }
        break;

      default:
        {
          int len = fmt_list.length ();

          retval.resize (nconv, Matrix ());

          const scanf_format_elt *elt = fmt_list.first ();

          int num_values = 0;

          bool quit = false;

          for (int i = 0; i < nconv; i++)
            {
            octave_value tmp;

            quit = do_oscanf (elt, tmp);

            if (quit)
              break;
            else
              {
                if (tmp.is_defined ())
                  retval (num_values++) = tmp;

                if (! ok ())
                  break;
                elt = fmt_list.next ();
              }
            }

          retval.resize (num_values);

          if (! quit)
            {
            // Pick up any trailing stuff.
            if (ok () && len > nconv)
              {
                octave_value tmp;
                do_oscanf (elt, tmp);
              }
            }
        }
        break;
      }
    }
  else
    invalid_operation ("fscanf", "reading");

  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;
}

int
octave_base_stream::write (const octave_value& data,
                     oct_data_conv::data_type dt, int skip,
                     oct_mach_info::float_format flt_fmt)
{
  int retval = -1;

  ostream *osp = output_stream ();

  if (osp)
    {
      ostream& os = *osp;

      Matrix mval = data.matrix_value (true);

      if (! error_state)
      {
        if (flt_fmt == oct_mach_info::unknown)
          flt_fmt = float_format ();

        int tmp = mval.write (os, dt, skip, flt_fmt);

        if (tmp < 0)
          error ("fwrite: write error");
        else
          retval = tmp;
      }
    }
  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);

      // An empty string has zero rows and zero columns.

      if (tmp_val.is_string ())
      {
        int nr = tmp_val.rows ();

        retval = (nr == 1 || (nr == 0 && tmp_val.columns () == 0));
      }
    }

  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.

template <class T>
void
do_printf_conv (ostream& os, const char *fmt, int nsa, int sa_1,
            int sa_2, bool have_arg, T arg)
{
  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;
    }
}

template void
do_printf_conv (ostream&, const char*, int, int, int, bool, int);

template void
do_printf_conv (ostream&, const char*, int, int, int, bool, long);

template void
do_printf_conv (ostream&, const char*, int, int, int, bool, double);

template void
do_printf_conv (ostream&, const char*, int, int, int, bool, const char*);

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, false, 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, true,
                              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, true, (long) val);
                        else
                          do_printf_conv (os, fmt, nsa, sa_1,
                                      sa_2, true, (int) val);
                        }
                        break;

                      case 'f': case 'e': case 'E':
                      case 'g': case 'G':
                        do_printf_conv (os, fmt, nsa, sa_1,
                                    sa_2, true, 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& err_num)
{
  err_num = 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 (streamoff 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)
    {
      ios::seek_dir origin = ios::beg;

      if (tc_origin.is_string ())
      {
        string xorigin = tc_origin.string_value ();

        if (xorigin == "bof")
          origin = ios::beg;
        else if (xorigin == "cof")
          origin = ios::cur;
        else if (xorigin == "eof")
          origin = ios::end;
        else
          conv_err = -1;
      }
      else
      {
        int xorigin = convert_to_valid_int (tc_origin, conv_err);

        if (! conv_err)
          {
            if (xorigin == -1)
            origin = ios::beg;
            else if (xorigin == 0)
            origin = ios::cur;
            else if (xorigin == 1)
            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,
                 oct_data_conv::data_type dt, int skip,
                 oct_mach_info::float_format flt_fmt, int& count)
{
  octave_value retval;

  if (stream_ok ("fread"))
    retval = rep->read (size, dt, skip, flt_fmt, count);

  return retval;
}

int
octave_stream::write (const octave_value& data,
                  oct_data_conv::data_type dt, int skip,
                  oct_mach_info::float_format flt_fmt)
{
  int retval = -1;

  if (stream_ok ("fwrite"))
    retval = rep->write (data, dt, skip, flt_fmt);

  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;
}

octave_value_list
octave_stream::oscanf (const string& fmt)
{
  octave_value_list retval;

  if (stream_ok ("fscanf"))
    retval = rep->oscanf (fmt);

  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& err_num)
{
  string retval;

  if (stream_ok ("ferror", false))
    retval = rep->error (clear, err_num);

  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;
}

oct_mach_info::float_format
octave_stream::float_format (void)
{
  oct_mach_info::float_format retval = oct_mach_info::unknown;

  if (stream_ok ("float_format"))
    retval = rep->float_format ();

  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;
}

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 (i);

        if (! tmp)
          {
            list (i) = os;
            retval = i;
            break;
          }
      }

      if (retval < 0)
      {
        int total_len = list.length ();

        if (curr_len == total_len)
          list.resize (total_len * 2);

        list (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 (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 (fid);

      if (os)
      {
        delete os;
        list (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 (0) -> flush ();
  list (1) -> flush ();

  // But don't delete them or stdin.

  for (int i = 3; i < curr_len; i++)
    {
      octave_stream *os = list (i);

      delete os;

      list (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) = oct_mach_info::float_format_as_string (os->float_format ());
    }
  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 (i);

      if (os)
      {
        string mode = octave_stream::mode_as_string (os->mode ());

        string arch =
          oct_mach_info::float_format_as_string (os->float_format ());

        string name = os->name ();

        buf.form ("   %3d     %-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 (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::do_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 (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;
}

int
octave_stream_list::get_file_number (const octave_value& fid)
{
  int retval = -1;

  if (instance)
    retval = instance->do_get_file_number (fid);

  return retval;
}

/*
;;; Local Variables: ***
;;; mode: C++ ***
;;; End: ***
*/

Generated by  Doxygen 1.6.0   Back to index