changeset 19763:17a7e9f26e50

improve compatibility of printf functions * oct-stream.h, octave-stream.cc (printf_format_elt::fw, printf_format_elt::prec): Use -1 to indicate uninitialized values and -2 to indicate star values were converted. Change all uses. (printf_value_cache::get_next_value): New arg, TYPE. Handle character string extraction. (printf_value_cache::string_value): Delete. (octave_base_stream::do_numeric_printf_conversion, printf_value_cache::int_value, ok_for_signed_int_conv, ok_for_unsigned_int_conv): Force string to double conversion. (do_printf_string): New static function. (octave_base_stream::do_printf): Improve compatibility of string and character conversions. * io.tst: New tests.
author John W. Eaton <jwe@octave.org>
date Tue, 17 Feb 2015 00:51:42 -0500
parents 928ecc95f395
children 554aaaf99644
files libinterp/corefcn/oct-stream.cc libinterp/corefcn/oct-stream.h test/io.tst
diffstat 3 files changed, 163 insertions(+), 72 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/oct-stream.cc	Tue Feb 17 00:44:06 2015 -0500
+++ b/libinterp/corefcn/oct-stream.cc	Tue Feb 17 00:51:42 2015 -0500
@@ -586,8 +586,8 @@
 
   int args = 0;
   std::string flags;
-  int fw = 0;
-  int prec = 0;
+  int fw = -1;
+  int prec = -1;
   char modifier = '\0';
   char type = '\0';
 
@@ -636,8 +636,8 @@
               {
                 args = 0;
                 flags = "";
-                fw = 0;
-                prec = 0;
+                fw = -1;
+                prec = -1;
                 modifier = '\0';
                 type = '\0';
                 *buf << s[i++];
@@ -707,8 +707,8 @@
 {
   args = 0;
   flags = "";
-  fw = 0;
-  prec = 0;
+  fw = -1;
+  prec = -1;
   modifier = '\0';
   type = '\0';
 
@@ -738,7 +738,7 @@
     {
       if (s[i] == '*')
         {
-          fw = -1;
+          fw = -2;
           args++;
           *buf << s[i++];
         }
@@ -758,13 +758,20 @@
 
   if (i < n && s[i] == '.')
     {
+      // nothing before the . means 0.
+      if (fw == -1)
+        fw = 0;
+
+      // . followed by nothing is 0.
+      prec = 0;
+
       *buf << s[i++];
 
       if (i < n)
         {
           if (s[i] == '*')
             {
-              prec = -1;
+              prec = -2;
               args++;
               *buf << s[i++];
             }
@@ -2181,14 +2188,11 @@
   ~printf_value_cache (void) { }
 
   // Get the current value as a double and advance the internal pointer.
-  octave_value get_next_value (void);
+  octave_value get_next_value (char type = 0);
 
   // 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.
-  std::string string_value (void);
-
   operator bool () const { return (curr_state == ok); }
 
   bool exhausted (void) { return (val_idx >= n_vals); }
@@ -2216,7 +2220,7 @@
 };
 
 octave_value
-printf_value_cache::get_next_value (void)
+printf_value_cache::get_next_value (char type)
 {
   octave_value retval;
 
@@ -2246,7 +2250,61 @@
 
       if (elt_idx < n_elts)
         {
-          retval = curr_val.fast_elem_extract (elt_idx++);
+          if (type == 's')
+            {
+              if (curr_val.is_string ())
+                {
+                  std::string sval = curr_val.string_value ();
+
+                  retval = sval.substr (elt_idx);
+
+                  // We've consumed the rest of the value.
+                  elt_idx = n_elts;
+                }
+              else
+                {
+                  // Convert to character string while values are
+                  // integers in the range [0 : char max]
+
+                  const NDArray val = curr_val.array_value ();
+
+                  octave_idx_type idx = elt_idx;
+
+                  for (; idx < n_elts; idx++)
+                    {
+                      double dval = val(idx);
+
+                      if (D_NINT (dval) != dval || dval < 0 || dval > 255)
+                        break;
+                    }
+
+                  octave_idx_type n = idx - elt_idx;
+
+                  if (n > 0)
+                    {
+                      std::string sval (n, '\0');
+
+                      for (octave_idx_type i = 0; i < n; i++)
+                        sval[i] = val(elt_idx++);
+
+                      retval = sval;
+                    }
+                  else
+                    retval = curr_val.fast_elem_extract (elt_idx++);
+                }
+            }
+          else
+            {
+              retval = curr_val.fast_elem_extract (elt_idx++);
+
+              if (type == 'c' && ! retval.is_string ())
+                {
+                  double dval = retval.double_value ();
+
+                  if (D_NINT (dval) == dval && dval >= 0 && dval < 256)
+                    retval = static_cast<char> (dval);
+                }
+            }
 
           if (elt_idx >= n_elts)
             {
@@ -2262,10 +2320,17 @@
           val_idx++;
           have_data = false;
 
-          if (n_elts == 0 && exhausted ())
-            curr_state = conversion_error;
-
-          continue;
+          if (n_elts == 0)
+            {
+              if (elt_idx == 0 && (type == 's' || type == 'c'))
+                {
+                  retval = "";
+                  break;
+                }
+
+              if (exhausted ())
+                curr_state = conversion_error;
+            }
         }
     }
 
@@ -2281,7 +2346,7 @@
 
   if (! error_state)
     {
-      double dval = val.double_value ();
+      double dval = val.double_value (true);
 
       if (! error_state)
         {
@@ -2295,44 +2360,6 @@
   return retval;
 }
 
-std::string
-printf_value_cache::string_value (void)
-{
-  std::string retval;
-
-  if (exhausted ())
-    curr_state = conversion_error;
-  else
-    {
-      octave_value tval = values (val_idx++);
-
-      if (tval.rows () == 1)
-        retval = tval.string_value ();
-      else
-        {
-          // In the name of Matlab compatibility.
-
-          charMatrix chm = tval.char_matrix_value ();
-
-          octave_idx_type nr = chm.rows ();
-          octave_idx_type nc = chm.columns ();
-
-          int k = 0;
-
-          retval.resize (nr * nc, '\0');
-
-          for (octave_idx_type j = 0; j < nc; j++)
-            for (octave_idx_type i = 0; i < nr; i++)
-              retval[k++] = chm(i,j);
-        }
-
-      if (error_state)
-        curr_state = conversion_error;
-    }
-
-  return retval;
-}
-
 // Ugh again and again.
 
 template <class T>
@@ -2364,6 +2391,35 @@
   return retval;
 }
 
+static size_t
+do_printf_string (std::ostream& os, const printf_format_elt *elt,
+                  int nsa, int sa_1, int sa_2, const std::string& arg,
+                  const std::string& who)
+{
+  size_t retval = 0;
+
+  if (nsa > 2)
+    {
+      ::error ("%s: internal error handling format", who.c_str ());
+      return retval;
+    }
+
+  std::string flags = elt->flags;
+
+  bool left = flags.find ('-') != std::string::npos;
+
+  size_t len = arg.length ();
+
+  size_t fw = nsa > 0 ? sa_1 : (elt->fw == -1 ? len : elt->fw);
+  size_t prec = nsa > 1 ? sa_2 : (elt->prec == -1 ? len : elt->prec);
+
+  os << std::setw (fw)
+     << (left ? std::left : std::right)
+     << (prec < len ? arg.substr (0, prec) : arg);
+
+  return len > fw ? len : fw;
+}
+
 static bool
 is_nan_or_inf (const octave_value& val)
 {
@@ -2378,7 +2434,9 @@
 {
   uint64_t limit = std::numeric_limits<int64_t>::max ();
 
-  if (val.is_integer_type ())
+  if (val.is_string ())
+    return false;
+  else if (val.is_integer_type ())
     {
       if (val.is_uint64_type ())
         {
@@ -2392,7 +2450,7 @@
     }
   else
     {
-      double dval = val.double_value ();
+      double dval = val.double_value (true);
 
       if (dval == xround (dval) && dval <= limit)
         return true;
@@ -2404,7 +2462,9 @@
 static bool
 ok_for_unsigned_int_conv (const octave_value& val)
 {
-  if (val.is_integer_type ())
+  if (val.is_string ())
+    return false;
+  else if (val.is_integer_type ())
     {
       // Easier than dispatching here...
 
@@ -2415,7 +2475,7 @@
     }
   else
     {
-      double dval = val.double_value ();
+      double dval = val.double_value (true);
 
       uint64_t limit = std::numeric_limits<uint64_t>::max ();
 
@@ -2461,7 +2521,7 @@
           && i2 < i1)
         {
           tfmt.erase (i2, i1-i2);
-          if (elt->prec < 0)
+          if (elt->prec == -2)
             nsa--;
         }
 
@@ -2508,7 +2568,7 @@
             {
               std::string tfmt = switch_to_g_format (elt);
 
-              double dval = val.double_value ();
+              double dval = val.double_value (true);
 
               if (! error_state)
                 retval += do_printf_conv (os, tfmt.c_str (), nsa,
@@ -2532,7 +2592,7 @@
             {
               std::string tfmt = switch_to_g_format (elt);
 
-              double dval = val.double_value ();
+              double dval = val.double_value (true);
 
               if (! error_state)
                 retval += do_printf_conv (os, tfmt.c_str (), nsa,
@@ -2543,7 +2603,7 @@
         case 'f': case 'e': case 'E':
         case 'g': case 'G':
           {
-            double dval = val.double_value ();
+            double dval = val.double_value (true);
 
             if (! error_state)
               retval += do_printf_conv (os, fmt, nsa, sa_1, sa_2, dval, who);
@@ -2591,7 +2651,7 @@
             {
               // NSA is the number of 'star' args to convert.
 
-              int nsa = (elt->fw < 0) + (elt->prec < 0);
+              int nsa = (elt->fw == -2) + (elt->prec == -2);
 
               int sa_1 = 0;
               int sa_2 = 0;
@@ -2624,13 +2684,23 @@
                   os << elt->text;
                   retval += strlen (elt->text);
                 }
-              else if (elt->type == 's')
+              else if (elt->type == 's' || elt->type == 'c')
                 {
-                  std::string val = val_cache.string_value ();
+                  octave_value val = val_cache.get_next_value (elt->type);
 
                   if (val_cache)
-                    retval += do_printf_conv (os, elt->text, nsa, sa_1,
-                                              sa_2, val.c_str (), who);
+                    {
+                      if (val.is_string ())
+                        {
+                          std::string sval = val.string_value ();
+
+                          retval += do_printf_string (os, elt, nsa, sa_1,
+                                                      sa_2, sval, who);
+                        }
+                      else
+                        retval += do_numeric_printf_conv (os, elt, nsa, sa_1,
+                                                          sa_2, val, who);
+                    }
                   else
                     break;
                 }
--- a/libinterp/corefcn/oct-stream.h	Tue Feb 17 00:44:06 2015 -0500
+++ b/libinterp/corefcn/oct-stream.h	Tue Feb 17 00:51:42 2015 -0500
@@ -190,8 +190,8 @@
 {
 public:
 
-  printf_format_elt (const char *txt = 0, int n = 0, int w = 0,
-                     int p = 0, const std::string& f = std::string (),
+  printf_format_elt (const char *txt = 0, int n = 0, int w = -1,
+                     int p = -1, 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) { }
--- a/test/io.tst	Tue Feb 17 00:44:06 2015 -0500
+++ b/test/io.tst	Tue Feb 17 00:51:42 2015 -0500
@@ -618,3 +618,24 @@
 %! assert (data, [97, 99; 98, 100]);
 %! assert (count, 4);
 %! fclose (id);
+
+%!assert (sprintf ("%1s", "foo"), "foo");
+%!assert (sprintf ("%.s", "foo"), char (zeros (1, 0)));
+%!assert (sprintf ("%1.s", "foo"), " ");
+%!assert (sprintf ("%.1s", "foo"), "f");
+%!assert (sprintf ("%1.1s", "foo"), "f");
+%!assert (sprintf ("|%4s|", "foo"), "| foo|");
+%!assert (sprintf ("|%-4s|", "foo"), "|foo |");
+%!assert (sprintf ("|%4.1s|", "foo"), "|   f|");
+%!assert (sprintf ("|%-4.1s|", "foo"), "|f   |");
+
+%!assert (sprintf ("%c ", "foo"), "f o o ");
+%!assert (sprintf ("%s ", "foo"), "foo ");
+
+%!assert (sprintf ("|%d|", "foo"), "|102||111||111|");
+%!assert (sprintf ("|%s|", [102, 111, 111]), "|foo|");
+
+%!assert (sprintf ("%s %d ", [102, 1e5, 111, 1e5, 111]), "f 100000 o 100000 o ");
+
+%!assert (sprintf ("%c,%c,%c,%c", "abcd"), "a,b,c,d");
+%!assert (sprintf ("%s,%s,%s,%s", "abcd"), "abcd,");