# HG changeset patch # User John W. Eaton # Date 1424152302 18000 # Node ID 17a7e9f26e50d85b142cb2524e4bef1f2feb5425 # Parent 928ecc95f3952397af254a8264848d8d08cee813 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. diff -r 928ecc95f395 -r 17a7e9f26e50 libinterp/corefcn/oct-stream.cc --- 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 (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 @@ -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::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::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; } diff -r 928ecc95f395 -r 17a7e9f26e50 libinterp/corefcn/oct-stream.h --- 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) { } diff -r 928ecc95f395 -r 17a7e9f26e50 test/io.tst --- 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,");