changeset 3640:d3b0ff09dda7

[project @ 2000-03-24 10:28:19 by jwe]
author jwe
date Fri, 24 Mar 2000 10:28:20 +0000
parents fed1847dfd6c
children 5ac1b0cb4fe8
files src/ChangeLog src/oct-stream.cc src/oct-stream.h
diffstat 3 files changed, 433 insertions(+), 375 deletions(-) [+]
line wrap: on
line diff
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,35 @@
+2000-03-24  John W. Eaton  <jwe@bevo.che.wisc.edu>
+
+	* oct-stream.cc (printf_format_list::add_elt_to_list,
+	printf_format_list::process_conversion,
+	printf_format_list::finish_conversion): New args, flags, fw, and prec.
+	(printf_format_list::printf_format_list): Save more complete info.
+	(printf_format_list::printme): Print flags, fw, and prec.
+	(octave_base_stream::printf): Simplify.
+	(do_printf_conv): Delete have_arg arg, since it is always true now.
+	(octave_base_stream::do_printf): Handle case of no args and %
+	directly instead of using do_printf_conv.
+	(printf_value_cache::exhausted): Rename from no_more_values.
+	(DO_PCT_CONVERSION): New macro
+	(octave_base_streain::do_scanf, octave_base_streain::do_oscanf):
+	Use it.
+	(scanf_format_list::finish_conversion): `%' counts as a conversion too.
+	Also don't forget to set type for it.
+	(OCTAVE_SCAN_0): Delete.
+	(OCTAVE_SCAN): Rename from OCTAVE_SCAN_1.
+	(octave_base_stream::scanf, octave_base_stream::oscanf): Don't
+	special-case number of conversions here.
+	(octave_base_stream::oscanf, octave_base_stream::do_oscanf): Only
+	cycle through fmt elements if the number of conversions is greater
+	than 0.
+
+	* oct-stream.h (scanf_format_list::next): New arg, `cycle'.
+	(printf_format_list::next): New arg, `cycle'.
+	(printf_format_list::last_elt_p): New function.
+	(printf_format_elt): New fields fw, prec, and flags.
+	Define copy constructor and assignment operator.
+	(scanf_format_elt): Define copy constructor and assignment operator.
+
 2000-03-23  John W. Eaton  <jwe@bevo.che.wisc.edu>
 
 	* oct-stream.cc (OCTAVE_SCAN_0, OCTAVE_SCAN_1): New macros.
--- a/src/oct-stream.cc
+++ b/src/oct-stream.cc
@@ -25,7 +25,6 @@
 #endif
 
 #include <cassert>
-#include <cstdarg>
 #include <cstring>
 
 #include <iomanip>
@@ -268,8 +267,8 @@
 
 	      list(num_elts++) = elt;
 	    }
-	  else
-	    delete [] text;
+
+	  delete [] text;
 	}
 
       delete buf;
@@ -423,13 +422,16 @@
 
   std::string char_class;
 
+  int beg_idx = -1;
+  int end_idx = -1;
+
   if (s[i] == '%')
-    *buf << s[i++];
+    {
+      type = '%';
+      *buf << s[i++];
+    }
   else
     {
-      int beg_idx = -1;
-      int end_idx = -1;
-
       type = s[i];
 
       if (s[i] == '[')
@@ -471,18 +473,17 @@
 	}
       else
 	*buf << s[i++];
-
-      nconv++;
-
-      if (nconv > 0)
-	{
-	  if (beg_idx >= 0 && end_idx >= 0)
-	    char_class = expand_char_class (s.substr (beg_idx,
-						      end_idx - beg_idx +1));
-
-	  add_elt_to_list (width, discard, type, modifier, num_elts,
-			   char_class);
-	}
+    }
+
+  nconv++;
+
+  if (nconv > 0)
+    {
+      if (beg_idx >= 0 && end_idx >= 0)
+	char_class = expand_char_class (s.substr (beg_idx,
+						  end_idx - beg_idx + 1));
+
+      add_elt_to_list (width, discard, type, modifier, num_elts, char_class);
     }
 
   return retval;
@@ -587,30 +588,53 @@
   int i = 0;
 
   int args = 0;
+  string flags;
+  int fw = 0;
+  int prec = 0;
   char modifier = '\0';
   char type = '\0';
 
   bool have_more = true;
+  bool empty_buf = true;
 
   while (i < n)
     {
       have_more = true;
 
       if (! buf)
-	buf = new std::ostrstream ();
+	{
+	  buf = new std::ostrstream ();
+	  empty_buf = true;
+	}
 
       switch (s[i])
 	{
 	case '%':
-	  process_conversion (s, i, n, args, type, modifier, num_elts);
-	  have_more = (buf != 0);
+	  {
+	    if (empty_buf)
+	      {
+		process_conversion (s, i, n, args, flags, fw, prec,
+				    type, modifier, num_elts);
+
+		have_more = (buf != 0);
+	      }
+	    else
+	      add_elt_to_list (args, flags, fw, prec, type, modifier,
+			       num_elts);
+	  }
 	  break;
 
 	default:
-	  args = 0;
-	  modifier = '\0';
-	  type = '\0';
-	  *buf << s[i++];
+	  {
+	    args = 0;
+	    flags = "";
+	    fw = 0;
+	    prec = 0;
+	    modifier = '\0';
+	    type = '\0';
+	    *buf << s[i++];
+	    empty_buf = false;
+	  }
 	  break;
 	}
 
@@ -622,7 +646,7 @@
     }
 
   if (have_more)
-    add_elt_to_list (args, type, modifier, num_elts);
+    add_elt_to_list (args, flags, fw, prec, type, modifier, num_elts);
 
   list.resize (num_elts);
 
@@ -641,8 +665,9 @@
 }
 
 void
-printf_format_list::add_elt_to_list (int args, char type, char modifier,
-				     int& num_elts)
+printf_format_list::add_elt_to_list (int args, const std::string& flags,
+				     int fw, int prec, char type,
+				     char modifier, int& num_elts)
 {
   if (buf)
     {
@@ -655,15 +680,16 @@
 	  if (*text)
 	    {
 	      printf_format_elt *elt
-		= new printf_format_elt (text, args, type, modifier);
+		= new printf_format_elt (text, args, fw, prec, flags,
+					 type, modifier);
 
 	      if (num_elts == list.length ())
 		list.resize (2 * num_elts);
 
 	      list(num_elts++) = elt;
 	    }
-	  else
-	    delete [] text;
+
+	  delete [] text;
 	}
 
       delete buf;
@@ -672,11 +698,14 @@
 }
 
 void
-printf_format_list::process_conversion (const std::string& s, int& i, int n,
-					int& args, char& modifier,
-					char& type, int& num_elts)
+printf_format_list::process_conversion
+  (const std::string& s, int& i, int n, int& args, std::string& flags,
+   int& fw, int& prec, char& modifier, char& type, int& num_elts)
 {
   args = 0;
+  flags = "";
+  fw = 0;
+  prec = 0;
   modifier = '\0';
   type = '\0';
 
@@ -689,6 +718,7 @@
       switch (s[i])
 	{
 	case '-': case '+': case ' ': case '0': case '#':
+	  flags += s[i];
 	  *buf << s[i++];
 	  break;
 
@@ -705,11 +735,19 @@
     {
       if (s[i] == '*')
 	{
+	  fw = -1;
 	  args++;
 	  *buf << s[i++];
 	}
       else
 	{
+	  if (isdigit (s[i]))
+	    {
+	      int n = 0;
+	      string tmp = s.substr (i);
+	      sscanf (tmp.c_str (), "%d%n", &fw, &n);
+	    }
+
 	  while (i < n && isdigit (s[i]))
 	    *buf << s[i++];
 	}
@@ -723,11 +761,19 @@
 	{
 	  if (s[i] == '*')
 	    {
+	      prec = -1;
 	      args++;
 	      *buf << s[i++];
 	    }
 	  else
 	    {
+	      if (isdigit (s[i]))
+		{
+		  int n = 0;
+		  string tmp = s.substr (i);
+		  sscanf (tmp.c_str (), "%d%n", &prec, &n);
+		}
+
 	      while (i < n && isdigit (s[i]))
 		*buf << s[i++];
 	    }
@@ -749,15 +795,15 @@
     }
 
   if (i < n)
-    finish_conversion (s, i, args, modifier, type, num_elts);
+    finish_conversion (s, i, args, flags, fw, prec, modifier, type, num_elts);
   else
     nconv = -1;
 }
 
 void
-printf_format_list::finish_conversion (const std::string& s, int& i,
-				       int args, char modifier,
-				       char& type, int& num_elts)
+printf_format_list::finish_conversion
+  (const std::string& s, int& i, int args, const std::string& flags,
+   int fw, int prec, char modifier, char& type, int& num_elts)
 
 {
   switch (s[i])
@@ -789,21 +835,18 @@
 
     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++;
-	}
+      type = s[i];
+
+      *buf << s[i++];
+
+      if (type != '%' || args != 0)
+	nconv++;
+
+      if (type != '%')
+	args++;
+
+      add_elt_to_list (args, flags, fw, prec, type, modifier, num_elts);
+
       break;
 
     default:
@@ -821,10 +864,14 @@
     {
       printf_format_elt *elt = list(i);
 
-      std::cerr << elt->args<< "\t"
-		<< elt->type << "\t"
-		<< elt->modifier << "\t"
-		<< undo_string_escapes (elt->text) << "\n";
+      std::cerr
+	<< "args:     " << elt->args << "\n"
+	<< "flags:    `" << elt->flags << "'\n"
+	<< "width:    " << elt->fw << "\n"
+	<< "prec:     " << elt->prec << "\n"
+	<< "type:     `" << elt->type << "'\n"
+	<< "modifier: `" << elt->modifier << "'\n"
+	<< "text:     `" << undo_string_escapes (elt->text) << "'\n\n";
     }
 }
 
@@ -1009,28 +1056,18 @@
 
 #if defined (__GNUG__)
 
-#define OCTAVE_SCAN_0(is, fmt) is.scan ((fmt).text)
-
-#define OCTAVE_SCAN_1(is, fmt, arg) is.scan ((fmt).text, arg)
+#define OCTAVE_SCAN(is, fmt, arg) is.scan ((fmt).text, arg)
 
 #else
 
-#define OCTAVE_SCAN_0(is, fmt) octave_scan (is, fmt)
-
-#define OCTAVE_SCAN_1(is, fmt, arg) octave_scan (is, fmt, arg)
-
-std::istream&
-octave_scan (std::istream& is, const scanf_format_elt& fmt)
-{
-  return is;
-}
+#define OCTAVE_SCAN(is, fmt, arg) octave_scan (is, fmt, arg)
 
 template <class T>
 std::istream&
 octave_scan (std::istream& is, const scanf_format_elt& fmt, T valptr)
 {
-  // Someone else will have to fix this code.  I refuse to waste my
-  // time working on it when a reasonable alternative like
+  // Someone else who cares will have to fix this code.  I refuse to
+  // waste my time working on it when a reasonable alternative like
   // istream::scan exists in the GNU iostream library.  --jwe
 
   error ("formatted input only works when Octave is compiled with G++");
@@ -1069,7 +1106,7 @@
 	       int& conversion_count, int nr, int max_size,
 	       bool discard) 
 {
-  OCTAVE_SCAN_1 (is, fmt, valptr);
+  OCTAVE_SCAN (is, fmt, valptr);
 
   if (is)
     {
@@ -1155,6 +1192,24 @@
     } \
   while (0)
 
+#define DO_PCT_CONVERSION() \
+  do \
+    { \
+      int c = is.get (); \
+ \
+      if (c != EOF) \
+	{ \
+	  if (c != '%') \
+	    { \
+	      is.putback (c); \
+	      is.setstate (std::ios::failbit); \
+	    } \
+	} \
+      else \
+	is.setstate (std::ios::failbit); \
+    } \
+  while (0)
+
 #define BEGIN_C_CONVERSION() \
   is.unsetf (std::ios::skipws); \
  \
@@ -1183,7 +1238,7 @@
 	{ \
 	  tmp = new char [width+1]; \
  \
-	  OCTAVE_SCAN_1 (is, *elt, tmp); \
+	  OCTAVE_SCAN (is, *elt, tmp); \
  \
 	  tmp[width] = '\0'; \
 	} \
@@ -1224,7 +1279,7 @@
 	{ \
 	  tmp = new char[width+1]; \
  \
-	  OCTAVE_SCAN_1 (is, *elt, tmp); \
+	  OCTAVE_SCAN (is, *elt, tmp); \
  \
 	  tmp[width] = '\0'; \
 	} \
@@ -1310,6 +1365,8 @@
 {
   conversion_count = 0;
 
+  int nconv = fmt_list.num_conversions ();
+
   int data_index = 0;
 
   octave_value retval = Matrix ();
@@ -1435,11 +1492,7 @@
 		  break;
 
 		case '%':
-		  {
-		    int dummy;
-
-		    OCTAVE_SCAN_1 (is, *elt, &dummy);
-		  }
+		  DO_PCT_CONVERSION ();
 		  break;
 
 		case 'd': case 'i': case 'o': case 'u': case 'x':
@@ -1591,7 +1644,7 @@
 	      break;
 	    }
 
-	  elt = fmt_list.next ();
+	  elt = fmt_list.next (nconv > 0);
 	}
     }
 
@@ -1620,66 +1673,22 @@
 
   if (isp)
     {
-      std::istream& is = *isp;
-
       scanf_format_list fmt_list (fmt);
 
-      switch (fmt_list.num_conversions ())
+      if (fmt_list.num_conversions () == -1)
+	::error ("fscanf: invalid format specified");
+      else
 	{
-	case -1:
-	  ::error ("fscanf: invalid format specified");
-	  break;
-
-	case 0:
-	  {
-	    const scanf_format_elt *elt = fmt_list.first ();
-
-	    if (elt)
-	      {
-		is.clear ();
-
-		OCTAVE_SCAN_0 (is, *elt);
-
-		if (! is)
-		  {
-		    // If it looks like we have a matching failure, then
-		    // reset the failbit in the stream state.
-
-		    if (is.rdstate () & std::ios::failbit)
-		      is.clear (is.rdstate () & (~std::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;
-
-	    bool 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,
-				 conversion_count);
-	  }
-	  break;
+	int nr = -1;
+	int nc = -1;
+
+	bool 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,
+			     conversion_count);
 	}
     }
   else
@@ -1720,10 +1729,11 @@
 
 	    case '%':
 	      {
-		int dummy;
-
-		if (! OCTAVE_SCAN_1 (is, *elt, &dummy))
+		DO_PCT_CONVERSION ();
+
+		if (! is)
 		  quit = true;
+
 	      }
 	      break;
 
@@ -1731,7 +1741,7 @@
 	      {
 		int tmp;
 
-		if (OCTAVE_SCAN_1 (is, *elt, &tmp))
+		if (OCTAVE_SCAN (is, *elt, &tmp))
 		  {
 		    if (! discard)
 		      retval = static_cast<double> (tmp);
@@ -1745,7 +1755,7 @@
 	      {
 		double tmp;
 
-		if (OCTAVE_SCAN_1 (is, *elt, &tmp))
+		if (OCTAVE_SCAN (is, *elt, &tmp))
 		  {
 		    if (! discard)
 		      retval = tmp;
@@ -1843,88 +1853,53 @@
 
       int nconv = fmt_list.num_conversions ();
 
-      switch (nconv)
+      if (nconv == -1)
+	::error ("fscanf: invalid format specified");
+      else
 	{
-	case -1:
-	  ::error ("fscanf: invalid format specified");
-	  break;
-
-	case 0:
-	  {
-	    const scanf_format_elt *elt = fmt_list.first ();
-
-	    if (elt)
-	      {
-		is.clear ();
-
-		OCTAVE_SCAN_0 (is, *elt);
-
-		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:
-	  {
-	    is.clear ();
-
-	    int len = fmt_list.length ();
-
-	    retval.resize (nconv+1, Matrix ());
-
-	    const scanf_format_elt *elt = fmt_list.first ();
-
-	    int num_values = 0;
-
-	    bool quit = false;
-
-	    for (int i = 0; i < len; 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 (nconv) = static_cast<double> (num_values);
-
-	    if (! quit)
-	      {
-		// Pick up any trailing stuff.
-		if (ok () && len > nconv)
-		  {
-		    octave_value tmp;
-		    do_oscanf (elt, tmp);
-		  }
-	      }
-	  }
-	  break;
+	  is.clear ();
+
+	  int len = fmt_list.length ();
+
+	  retval.resize (nconv+1, Matrix ());
+
+	  const scanf_format_elt *elt = fmt_list.first ();
+
+	  int num_values = 0;
+
+	  bool quit = false;
+
+	  for (int i = 0; i < len; 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 (nconv > 0);
+		}
+	    }
+
+	  retval (nconv) = static_cast<double> (num_values);
+
+	  if (! quit)
+	    {
+	      // Pick up any trailing stuff.
+	      if (ok () && len > nconv)
+		{
+		  octave_value tmp;
+		  do_oscanf (elt, tmp);
+		}
+	    }
 	}
     }
   else
@@ -2015,7 +1990,8 @@
 
   operator bool () const { return (curr_state == ok); }
 
-  bool no_more_values (void) { return curr_state == list_exhausted; }
+  bool exhausted (void)
+    { return (curr_state == list_exhausted || val_idx + 1 >= n_vals); }
 
   bool looking_at_string (void);
 
@@ -2159,31 +2135,22 @@
 template <class T>
 int
 do_printf_conv (std::ostream& os, const char *fmt, int nsa, int sa_1,
-		int sa_2, bool have_arg, T arg)
+		int sa_2, T arg)
 {
   int retval = 0;
 
   switch (nsa)
     {
     case 2:
-      if (have_arg)
-	retval = octave_format (os, fmt, sa_1, sa_2, arg);
-      else
-	retval = octave_format (os, fmt, sa_1, sa_2);
+      retval = octave_format (os, fmt, sa_1, sa_2, arg);
       break;
 
     case 1:
-      if (have_arg)
-	retval = octave_format (os, fmt, sa_1, arg);
-      else
-	retval = octave_format (os, fmt, sa_1);
+      retval = octave_format (os, fmt, sa_1, arg);
       break;
 
     case 0:
-      if (have_arg)
-	retval = octave_format (os, fmt, arg);
-      else
-	retval = octave_format (os, fmt);
+      retval = octave_format (os, fmt, arg);
       break;
 
     default:
@@ -2195,16 +2162,16 @@
 }
 
 template int
-do_printf_conv (std::ostream&, const char*, int, int, int, bool, int);
-
-template int
-do_printf_conv (std::ostream&, const char*, int, int, int, bool, long);
-
-template int
-do_printf_conv (std::ostream&, const char*, int, int, int, bool, double);
-
-template int
-do_printf_conv (std::ostream&, const char*, int, int, int, bool, const char*);
+do_printf_conv (std::ostream&, const char*, int, int, int, int);
+							   
+template int						   
+do_printf_conv (std::ostream&, const char*, int, int, int, long);
+							   
+template int						   
+do_printf_conv (std::ostream&, const char*, int, int, int, double);
+							   
+template int						   
+do_printf_conv (std::ostream&, const char*, int, int, int, const char*);
 
 int
 octave_base_stream::do_printf (printf_format_list& fmt_list,
@@ -2212,6 +2179,8 @@
 {
   int retval = 0;
 
+  int nconv = fmt_list.num_conversions ();
+
   std::ostream *osp = output_stream ();
 
   if (osp)
@@ -2226,13 +2195,9 @@
 	{
 	  if (elt)
 	    {
-	      int args = elt->args;
-	      int nsa = args;
-
-	      int doing_percent = elt->type == '%';
-
-	      if (args > 0 && ! doing_percent)
-		nsa--;
+	      // NSA is the number of `star' args to convert.
+
+	      int nsa = (elt->fw < 0) + (elt->prec < 0);
 
 	      int sa_1 = 0;
 	      int sa_2 = 0; 
@@ -2257,70 +2222,67 @@
 
 	      const char *fmt = elt->text;
 
-	      if (doing_percent || args == 0)
-		retval += do_printf_conv (os, fmt, nsa, sa_1, sa_2,
-					  false, 0.0);
+	      if (elt->type == '%')
+		{
+		  os << "%";
+		  retval++;
+		}
+	      else if (elt->args == 0 && elt->text)
+		{
+		  os << elt->text;
+		  retval += strlen (elt->text);
+		}	      
+	      else if (elt->type == 's' && val_cache.looking_at_string ())
+		{
+		  std::string val = val_cache.string_value ();
+
+		  if (val_cache)
+		    retval += do_printf_conv (os, fmt, nsa, sa_1,
+					      sa_2, val.c_str ());
+		  else
+		    break;
+		}
 	      else
 		{
-		  if (elt->type == 's' && val_cache.looking_at_string ())
+		  double val = val_cache.double_value ();
+
+		  if (val_cache)
 		    {
-		      std::string val = val_cache.string_value ();
-
-		      if (val_cache)
-			retval += do_printf_conv (os, fmt, nsa, sa_1,
-						  sa_2, true, val.c_str ());
-		      else
-			break;
+		      switch (elt->type)
+			{
+			case 'd': case 'i': case 'o': case 'x':
+			case 'X': case 'u': case 'c':
+			  {
+			    if (elt->modifier == 'l')
+			      retval
+				+= do_printf_conv (os, fmt, nsa, sa_1, sa_2,
+						   static_cast<long> (val));
+			    else
+			      retval
+				+= do_printf_conv (os, fmt, nsa, sa_1, sa_2,
+						   static_cast<int> (val));
+			  }
+			  break;
+
+			case 'f': case 'e': case 'E':
+			case 'g': case 'G':
+			  retval
+			    += do_printf_conv (os, fmt, nsa, sa_1, sa_2, val);
+			  break;
+
+			default:
+			  error ("fprintf: invalid format specifier");
+			  return -1;
+			  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')
-				  retval
-				    += do_printf_conv (os, fmt, nsa,
-						       sa_1, sa_2, true,
-						       static_cast<long> (val));
-				else
-				  retval
-				    += do_printf_conv (os, fmt, nsa,
-						       sa_1, sa_2, true,
-						       static_cast<int> (val));
-			      }
-			      break;
-
-			    case 'f': case 'e': case 'E':
-			    case 'g': case 'G':
-			      retval
-				+= 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 ())
 		    break;
 		}
 
 	      if (! os)
 		{
 		  error ("fprintf: write error");
-		  retval = -1;
 		  break;
 		}
 	    }
@@ -2331,61 +2293,30 @@
 	      break;
 	    }
 
-	  elt = fmt_list.next ();
-	}
+	  elt = fmt_list.next (nconv > 0 && ! val_cache.exhausted ());
+
+	  if (! elt || (val_cache.exhausted () && elt->args > 0))
+	    break;
+	}	      
     }
+  else
+    invalid_operation ("fprintf", "writing");
 
   return retval;
 }
 
 int
-octave_base_stream::printf (const std::string& fmt, const octave_value_list& args)
+octave_base_stream::printf (const std::string& fmt,
+			    const octave_value_list& args)
 {
-  int retval = -1;
-
-  std::ostream *osp = output_stream ();
-
-  if (osp)
-    {
-      std::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)
-	      {
-		retval = octave_format (os, elt->text);
-
-		if (! os)
-		  {
-		    retval = -1;
-		    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;
-	}
-    }
+  int retval = 0;
+
+  printf_format_list fmt_list (fmt);
+
+  if (fmt_list.num_conversions () == -1)
+    ::error ("fprintf: invalid format specified");
   else
-    invalid_operation ("fprintf", "writing");
+    retval = do_printf (fmt_list, args);
 
   return retval;
 }
--- a/src/oct-stream.h
+++ b/src/oct-stream.h
@@ -34,11 +34,14 @@
 
 #include "Array.h"
 #include "data-conv.h"
+#include "lo-utils.h"
 #include "mach-info.h"
 
-struct
+class
 scanf_format_elt
 {
+public:
+
   enum special_conversion
     {
       whitespace_conversion = 1,
@@ -48,16 +51,47 @@
   scanf_format_elt (const char *txt = 0, int w = 0, bool d = false,
 		    char typ = '\0', char mod = '\0',
 		    const std::string& ch_class = std::string ())
-    : text (txt), width (w), discard (d), type (typ), modifier (mod),
-      char_class (ch_class) { }
+    : text (strsave (txt)), width (w), discard (d), type (typ),
+      modifier (mod), char_class (ch_class) { }
+
+  scanf_format_elt (const scanf_format_elt& e)
+    : text (strsave (e.text)), width (e.width), discard (e.discard),
+      type (e.type), modifier (e.modifier), char_class (e.char_class) { }
 
-  ~scanf_format_elt (void) { delete text; }
+  scanf_format_elt& operator = (const scanf_format_elt& e)
+    {
+      if (this != &e)
+	{
+	  text = strsave (e.text);
+	  width = e.width;
+	  discard = e.discard;
+	  type = e.type;
+	  modifier = e.modifier;
+	  char_class = e.char_class;
+	}
 
+      return *this;
+    }
+
+  ~scanf_format_elt (void) { delete [] text; }
+
+  // The C-style format string.
   const char *text;
+
+  // The maximum field width.
   int width;
+
+  // TRUE if we are not storing the result of this conversion.
   bool discard;
+
+  // Type of conversion -- `d', `i', `o', `u', `x', `e', `f', `g',
+  // `c', `s', `p', `%', or `['.
   char type;
+
+  // A length modifier -- `h', `l', or `L'.
   char modifier;
+
+  // The class of characters in a `[' format.
   std::string char_class;
 };
 
@@ -88,11 +122,17 @@
   const scanf_format_elt *current (void) const
     { return list.length () > 0 ? list.elem (curr_idx) : 0; }
 
-  const scanf_format_elt *next (void)
+  const scanf_format_elt *next (bool cycle = true)
     {
       curr_idx++;
+
       if (curr_idx >= list.length ())
-	curr_idx = 0;
+	{
+	  if (cycle)
+	    curr_idx = 0;
+	  else
+	    return 0;
+	}
       return current ();
     }
 
@@ -122,7 +162,8 @@
   std::ostrstream *buf;
 
   void add_elt_to_list (int width, bool discard, char type, char modifier,
-			int& num_elts, const std::string& char_class = std::string ());
+			int& num_elts,
+			const std::string& char_class = std::string ()); 
 
   void process_conversion (const std::string& s, int& i, int n, int& width,
 			   bool& discard, char& type, char& modifier,
@@ -138,18 +179,59 @@
   scanf_format_list& operator = (const scanf_format_list&);
 };
 
-struct
+class
 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) { }
+public:
+
+  printf_format_elt (const char *txt = 0, int n = 0, int w = 0,
+		     int p = 0, const std::string& f = std::string (),
+		     char typ = '\0', char mod = '\0')
+    : text (strsave (txt)), args (n), fw (w), prec (p), flags (f),
+      type (typ), modifier (mod) { }
+
+  printf_format_elt (const printf_format_elt& e)
+    : text (strsave (e.text)), args (e.args), fw (e.fw), prec (e.prec),
+      flags (e.flags), type (e.type), modifier (e.modifier) { }
+
+  printf_format_elt& operator = (const printf_format_elt& e)
+    {
+      if (this != &e)
+	{
+	  text = strsave (e.text);
+	  args = e.args;
+	  fw = e.fw;
+	  prec = e.prec;
+	  flags = e.flags;
+	  type = e.type;
+	  modifier = e.modifier;
+	}
 
-  ~printf_format_elt (void) { delete text; }
+      return *this;
+    }
 
+  ~printf_format_elt (void) { delete [] text; }
+ 
+  // The C-style format string.
   const char *text;
+
+  // How many args do we expect to consume?
   int args;
+
+  // Field width.
+  int fw;
+
+  // Precision.
+  int prec;
+
+  // Flags -- `-', `+', ` ', `0', or `#'.
+  string flags;
+
+  // Type of conversion -- `d', `i', `o', `x', `X', `u', `c', `s',
+  // `f', `e', `E', `g', `G', `p', or `%'
   char type;
+
+  // A length modifier -- `h', `l', or `L'.
   char modifier;
 };
 
@@ -173,14 +255,23 @@
   const printf_format_elt *current (void) const
     { return list.length () > 0 ? list.elem (curr_idx) : 0; }
 
-  const printf_format_elt *next (void)
+  const printf_format_elt *next (bool cycle = true)
     {
       curr_idx++;
+
       if (curr_idx >= list.length ())
-	curr_idx = 0;
+	{
+	  if (cycle)
+	    curr_idx = 0;
+	  else
+	    return 0;
+	}
+
       return current ();
     }
 
+  bool last_elt_p (void) { return (curr_idx + 1 == list.length ()); }
+
   void printme (void) const;
 
   bool ok (void) const { return (nconv >= 0); }
@@ -202,13 +293,17 @@
   // Temporary buffer.
   std::ostrstream *buf;
 
-  void add_elt_to_list (int args, char type, char modifier,
-			int& num_elts);
-
-  void process_conversion (const std::string& s, int& i, int n, int& args,
-			   char& modifier, char& type, int& num_elts);
-
+  void add_elt_to_list (int args, const std::string& flags, int fw,
+			int prec, char type, char modifier,
+ 			int& num_elts);
+ 
+  void process_conversion (const std::string& s, int& i, int n,
+			   int& args, std::string& flags, int& fw,
+			   int& prec, char& modifier, char& type,
+			   int& num_elts); 
+ 
   void finish_conversion (const std::string& s, int& i, int args,
+			  const std::string& flags, int fw, int prec,
 			  char modifier, char& type, int& num_elts);
 
   // No copying!