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