1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1996-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING.  If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 #  include "config.h"
28 #endif
29 
30 #include <cassert>
31 #include <cctype>
32 #include <cstring>
33 
34 #include <algorithm>
35 #include <deque>
36 #include <fstream>
37 #include <iomanip>
38 #include <iostream>
39 #include <sstream>
40 #include <string>
41 
42 #include "Array.h"
43 #include "Cell.h"
44 #include "byte-swap.h"
45 #include "lo-ieee.h"
46 #include "lo-mappers.h"
47 #include "lo-utils.h"
48 #include "oct-locbuf.h"
49 #include "quit.h"
50 #include "str-vec.h"
51 
52 #include "error.h"
53 #include "errwarn.h"
54 #include "input.h"
55 #include "interpreter-private.h"
56 #include "interpreter.h"
57 #include "octave.h"
58 #include "oct-iostrm.h"
59 #include "oct-stdstrm.h"
60 #include "oct-string.h"
61 #include "oct-stream.h"
62 #include "ov.h"
63 #include "ovl.h"
64 #include "pager.h"
65 #include "utils.h"
66 
67 namespace octave
68 {
69   // Programming Note: There are two very different error functions used
70   // in the stream code.  When invoked with "error (...)" the member
71   // function from stream or base_stream is called.  This
72   // function sets the error state on the stream AND returns control to
73   // the caller.  The caller must then return a value at the end of the
74   // function.  When invoked with "::error (...)" the exception-based
75   // error function from error.h is used.  This function will throw an
76   // exception and not return control to the caller.  BE CAREFUL and
77   // invoke the correct error function!
78 
79   // Possible values for conv_err:
80   //
81   //   1 : not a real scalar
82   //   2 : value is NaN
83   //   3 : value is not an integer
84 
85   static int
convert_to_valid_int(const octave_value & tc,int & conv_err)86   convert_to_valid_int (const octave_value& tc, int& conv_err)
87   {
88     conv_err = 0;
89 
90     int retval = 0;
91 
92     double dval = 0.0;
93 
94     try
95       {
96         dval = tc.double_value ();
97       }
98     catch (const execution_exception&)
99       {
100         octave::interpreter& interp
101           = __get_interpreter__ ("convert_to_valid_int");
102 
103         interp.recover_from_exception ();
104 
105         conv_err = 1;
106       }
107 
108     if (! conv_err)
109       {
110         if (! lo_ieee_isnan (dval))
111           {
112             int ival = math::nint (dval);
113 
114             if (ival == dval)
115               retval = ival;
116             else
117               conv_err = 3;
118           }
119         else
120           conv_err = 2;
121       }
122 
123     return retval;
124   }
125 
126   static octave_idx_type
get_size(double d,const std::string & who)127   get_size (double d, const std::string& who)
128   {
129     octave_idx_type retval = -1;
130 
131     if (lo_ieee_isnan (d))
132       ::error ("%s: NaN invalid as size specification", who.c_str ());
133 
134     if (math::isinf (d))
135       retval = -1;
136     else
137       {
138         if (d < 0.0)
139           ::error ("%s: negative value invalid as size specification",
140                    who.c_str ());
141 
142         if (d > std::numeric_limits<octave_idx_type>::max ())
143           ::error ("%s: dimension too large for Octave's index type",
144                    who.c_str ());
145 
146         retval = math::nint_big (d);
147       }
148 
149     return retval;
150   }
151 
152   static void
get_size(const Array<double> & size,octave_idx_type & nr,octave_idx_type & nc,bool & one_elt_size_spec,const std::string & who)153   get_size (const Array<double>& size,
154             octave_idx_type& nr, octave_idx_type& nc,
155             bool& one_elt_size_spec, const std::string& who)
156   {
157     nr = -1;
158     nc = -1;
159 
160     one_elt_size_spec = false;
161 
162     double dnr = -1.0;
163     double dnc = -1.0;
164 
165     octave_idx_type sz_len = size.numel ();
166 
167     if (sz_len == 1)
168       {
169         one_elt_size_spec = true;
170 
171         dnr = size(0);
172 
173         dnc = (dnr == 0.0) ? 0.0 : 1.0;
174       }
175     else if (sz_len == 2)
176       {
177         dnr = size(0);
178 
179         if (math::isinf (dnr))
180           ::error ("%s: infinite value invalid as size specification",
181                    who.c_str ());
182 
183         dnc = size(1);
184       }
185     else
186       ::error ("%s: invalid size specification (must be 2-D)", who.c_str ());
187 
188     nr = get_size (dnr, who);
189 
190     if (dnc >= 0.0)
191       {
192         nc = get_size (dnc, who);
193 
194         // Check for overflow.
195         if (nr > 0 && nc > 0
196             && nc > std::numeric_limits<octave_idx_type>::max () / nr)
197           ::error ("%s: size too large for Octave's index type", who.c_str ());
198       }
199   }
200 
201   static std::string
expand_char_class(const std::string & s)202   expand_char_class (const std::string& s)
203   {
204     std::string retval;
205 
206     std::size_t len = s.length ();
207 
208     std::size_t i = 0;
209 
210     while (i < len)
211       {
212         unsigned char c = s[i++];
213 
214         if (c == '-' && i > 1 && i < len
215             && (   static_cast<unsigned char> (s[i-2])
216                    <= static_cast<unsigned char> (s[i])))
217           {
218             // Add all characters from the range except the first (we
219             // already added it below).
220 
221             for (c = s[i-2]+1; c < s[i]; c++)
222               retval += c;
223           }
224         else
225           {
226             // Add the character to the class.  Only add '-' if it is
227             // the last character in the class.
228 
229             if (c != '-' || i == len)
230               retval += c;
231           }
232       }
233 
234     return retval;
235   }
236 
237   class
238   scanf_format_elt
239   {
240   public:
241 
242     enum special_conversion
243     {
244       whitespace_conversion = 1,
245       literal_conversion = 2,
246       null = 3
247     };
248 
scanf_format_elt(const std::string & txt="",int w=0,bool d=false,char typ='\\0',char mod='\\0',const std::string & ch_class="")249     scanf_format_elt (const std::string& txt = "", int w = 0, bool d = false,
250                       char typ = '\0', char mod = '\0',
251                       const std::string& ch_class = "")
252       : text (txt), width (w), discard (d), type (typ),
253         modifier (mod), char_class (ch_class)
254     { }
255 
256     scanf_format_elt (const scanf_format_elt&) = default;
257 
258     scanf_format_elt& operator = (const scanf_format_elt&) = default;
259 
260     ~scanf_format_elt (void) = default;
261 
262     // The C-style format string.
263     std::string text;
264 
265     // The maximum field width.
266     int width;
267 
268     // TRUE if we are not storing the result of this conversion.
269     bool discard;
270 
271     // Type of conversion -- 'd', 'i', 'o', 'u', 'x', 'e', 'f', 'g',
272     // 'c', 's', 'p', '%', or '['.
273     char type;
274 
275     // A length modifier -- 'h', 'l', or 'L'.
276     char modifier;
277 
278     // The class of characters in a '[' format.
279     std::string char_class;
280   };
281 
282   class
283   scanf_format_list
284   {
285   public:
286 
287     scanf_format_list (const std::string& fmt = "");
288 
289     // No copying!
290 
291     scanf_format_list (const scanf_format_list&) = delete;
292 
293     scanf_format_list& operator = (const scanf_format_list&) = delete;
294 
295     ~scanf_format_list (void);
296 
num_conversions(void)297     octave_idx_type num_conversions (void) { return nconv; }
298 
299     // The length can be different than the number of conversions.
300     // For example, "x %d y %d z" has 2 conversions but the length of
301     // the list is 3 because of the characters that appear after the
302     // last conversion.
303 
length(void) const304     std::size_t length (void) const { return fmt_elts.size (); }
305 
first(void)306     const scanf_format_elt * first (void)
307     {
308       curr_idx = 0;
309       return current ();
310     }
311 
current(void) const312     const scanf_format_elt * current (void) const
313     {
314       return length () > 0 ? fmt_elts[curr_idx] : nullptr;
315     }
316 
next(bool cycle=true)317     const scanf_format_elt * next (bool cycle = true)
318     {
319       static scanf_format_elt dummy
320         ("", 0, false, scanf_format_elt::null, '\0', "");
321 
322       curr_idx++;
323 
324       if (curr_idx >= length ())
325         {
326           if (cycle)
327             curr_idx = 0;
328           else
329             return &dummy;
330         }
331 
332       return current ();
333     }
334 
335     void printme (void) const;
336 
ok(void) const337     bool ok (void) const { return (nconv >= 0); }
338 
operator bool() const339     operator bool () const { return ok (); }
340 
341     bool all_character_conversions (void);
342 
343     bool all_numeric_conversions (void);
344 
345   private:
346 
347     // Number of conversions specified by this format string, or -1 if
348     // invalid conversions have been found.
349     octave_idx_type nconv;
350 
351     // Index to current element;
352     std::size_t curr_idx;
353 
354     // List of format elements.
355     std::deque<scanf_format_elt*> fmt_elts;
356 
357     // Temporary buffer.
358     std::ostringstream buf;
359 
360     void add_elt_to_list (int width, bool discard, char type, char modifier,
361                           const std::string& char_class = "");
362 
363     void process_conversion (const std::string& s, std::size_t& i, std::size_t n,
364                              int& width, bool& discard, char& type,
365                              char& modifier);
366 
367     int finish_conversion (const std::string& s, std::size_t& i, std::size_t n,
368                            int width, bool discard, char& type,
369                            char modifier);
370   };
371 
scanf_format_list(const std::string & s)372   scanf_format_list::scanf_format_list (const std::string& s)
373     : nconv (0), curr_idx (0), fmt_elts (), buf ()
374   {
375     std::size_t n = s.length ();
376 
377     std::size_t i = 0;
378 
379     int width = 0;
380     bool discard = false;
381     char modifier = '\0';
382     char type = '\0';
383 
384     bool have_more = true;
385 
386     while (i < n)
387       {
388         have_more = true;
389 
390         if (s[i] == '%')
391           {
392             // Process percent-escape conversion type.
393 
394             process_conversion (s, i, n, width, discard, type, modifier);
395 
396             have_more = (buf.tellp () != 0);
397           }
398         else if (isspace (s[i]))
399           {
400             type = scanf_format_elt::whitespace_conversion;
401 
402             width = 0;
403             discard = false;
404             modifier = '\0';
405             buf << ' ';
406 
407             while (++i < n && isspace (s[i]))
408               ; // skip whitespace
409 
410             add_elt_to_list (width, discard, type, modifier);
411 
412             have_more = false;
413           }
414         else
415           {
416             type = scanf_format_elt::literal_conversion;
417 
418             width = 0;
419             discard = false;
420             modifier = '\0';
421 
422             while (i < n && ! isspace (s[i]) && s[i] != '%')
423               buf << s[i++];
424 
425             add_elt_to_list (width, discard, type, modifier);
426 
427             have_more = false;
428           }
429 
430         if (nconv < 0)
431           {
432             have_more = false;
433             break;
434           }
435       }
436 
437     if (have_more)
438       add_elt_to_list (width, discard, type, modifier);
439 
440     buf.clear ();
441     buf.str ("");
442   }
443 
~scanf_format_list(void)444   scanf_format_list::~scanf_format_list (void)
445   {
446     std::size_t n = fmt_elts.size ();
447 
448     for (std::size_t i = 0; i < n; i++)
449       {
450         scanf_format_elt *elt = fmt_elts[i];
451         delete elt;
452       }
453   }
454 
455   void
add_elt_to_list(int width,bool discard,char type,char modifier,const std::string & char_class)456   scanf_format_list::add_elt_to_list (int width, bool discard, char type,
457                                       char modifier,
458                                       const std::string& char_class)
459   {
460     std::string text = buf.str ();
461 
462     if (! text.empty ())
463       {
464         scanf_format_elt *elt
465           = new scanf_format_elt (text, width, discard, type,
466                                   modifier, char_class);
467 
468         fmt_elts.push_back (elt);
469       }
470 
471     buf.clear ();
472     buf.str ("");
473   }
474 
475   void
process_conversion(const std::string & s,std::size_t & i,std::size_t n,int & width,bool & discard,char & type,char & modifier)476   scanf_format_list::process_conversion (const std::string& s, std::size_t& i,
477                                          std::size_t n, int& width, bool& discard,
478                                          char& type, char& modifier)
479   {
480     width = 0;
481     discard = false;
482     modifier = '\0';
483     type = '\0';
484 
485     buf << s[i++];
486 
487     bool have_width = false;
488 
489     while (i < n)
490       {
491         switch (s[i])
492           {
493           case '*':
494             if (discard)
495               nconv = -1;
496             else
497               {
498                 discard = true;
499                 buf << s[i++];
500               }
501             break;
502 
503           case '0': case '1': case '2': case '3': case '4':
504           case '5': case '6': case '7': case '8': case '9':
505             if (have_width)
506               nconv = -1;
507             else
508               {
509                 char c = s[i++];
510                 width = 10 * width + c - '0';
511                 have_width = true;
512                 buf << c;
513                 while (i < n && isdigit (s[i]))
514                   {
515                     c = s[i++];
516                     width = 10 * width + c - '0';
517                     buf << c;
518                   }
519               }
520             break;
521 
522           case 'h': case 'l': case 'L':
523             if (modifier != '\0')
524               nconv = -1;
525             else
526               modifier = s[i++];
527             break;
528 
529           // We accept X for compatibility with undocumented Matlab behavior.
530           case 'd': case 'i': case 'o': case 'u': case 'x':
531           case 'X':
532             if (modifier == 'L')
533               {
534                 nconv = -1;
535                 break;
536               }
537             goto fini;
538 
539           // We accept E and G for compatibility with undocumented
540           // Matlab behavior.
541           case 'e': case 'f': case 'g':
542           case 'E': case 'G':
543             if (modifier == 'h')
544               {
545                 nconv = -1;
546                 break;
547               }
548 
549             // No float or long double conversions, thanks.
550             buf << 'l';
551 
552             goto fini;
553 
554           case 'c': case 's': case 'p': case '%': case '[':
555             if (modifier != '\0')
556               {
557                 nconv = -1;
558                 break;
559               }
560             goto fini;
561 
562           fini:
563             {
564               if (finish_conversion (s, i, n, width, discard,
565                                      type, modifier) == 0)
566                 return;
567             }
568             break;
569 
570           default:
571             nconv = -1;
572             break;
573           }
574 
575         if (nconv < 0)
576           break;
577       }
578 
579     nconv = -1;
580   }
581 
582   int
finish_conversion(const std::string & s,std::size_t & i,std::size_t n,int width,bool discard,char & type,char modifier)583   scanf_format_list::finish_conversion (const std::string& s, std::size_t& i,
584                                         std::size_t n, int width, bool discard,
585                                         char& type, char modifier)
586   {
587     int retval = 0;
588 
589     std::string char_class;
590 
591     std::size_t beg_idx = std::string::npos;
592     std::size_t end_idx = std::string::npos;
593 
594     if (s[i] == '%')
595       {
596         type = '%';
597         buf << s[i++];
598       }
599     else
600       {
601         type = s[i];
602 
603         if (s[i] == '[')
604           {
605             buf << s[i++];
606 
607             if (i < n)
608               {
609                 beg_idx = i;
610 
611                 if (s[i] == '^')
612                   {
613                     type = '^';
614                     buf << s[i++];
615 
616                     if (i < n)
617                       {
618                         beg_idx = i;
619 
620                         if (s[i] == ']')
621                           buf << s[i++];
622                       }
623                   }
624                 else if (s[i] == ']')
625                   buf << s[i++];
626               }
627 
628             while (i < n && s[i] != ']')
629               buf << s[i++];
630 
631             if (i < n && s[i] == ']')
632               {
633                 end_idx = i-1;
634                 buf << s[i++];
635               }
636 
637             if (s[i-1] != ']')
638               retval = nconv = -1;
639           }
640         else
641           buf << s[i++];
642 
643         nconv++;
644       }
645 
646     if (nconv >= 0)
647       {
648         if (beg_idx != std::string::npos && end_idx != std::string::npos)
649           char_class = expand_char_class (s.substr (beg_idx,
650                                                     end_idx - beg_idx + 1));
651 
652         add_elt_to_list (width, discard, type, modifier, char_class);
653       }
654 
655     return retval;
656   }
657 
658   void
printme(void) const659   scanf_format_list::printme (void) const
660   {
661     std::size_t n = fmt_elts.size ();
662 
663     for (std::size_t i = 0; i < n; i++)
664       {
665         scanf_format_elt *elt = fmt_elts[i];
666 
667         std::cerr
668           << "width:      " << elt->width << "\n"
669           << "discard:    " << elt->discard << "\n"
670           << "type:       ";
671 
672         if (elt->type == scanf_format_elt::literal_conversion)
673           std::cerr << "literal text\n";
674         else if (elt->type == scanf_format_elt::whitespace_conversion)
675           std::cerr << "whitespace\n";
676         else
677           std::cerr << elt->type << "\n";
678 
679         std::cerr
680           << "modifier:   " << elt->modifier << "\n"
681           << "char_class: '" << undo_string_escapes (elt->char_class) << "'\n"
682           << "text:       '" << undo_string_escapes (elt->text) << "'\n\n";
683       }
684   }
685 
686   bool
all_character_conversions(void)687   scanf_format_list::all_character_conversions (void)
688   {
689     std::size_t n = fmt_elts.size ();
690 
691     if (n > 0)
692       {
693         for (std::size_t i = 0; i < n; i++)
694           {
695             scanf_format_elt *elt = fmt_elts[i];
696 
697             switch (elt->type)
698               {
699               case 'c': case 's': case '%': case '[': case '^':
700               case scanf_format_elt::literal_conversion:
701               case scanf_format_elt::whitespace_conversion:
702                 break;
703 
704               default:
705                 return false;
706                 break;
707               }
708           }
709 
710         return true;
711       }
712     else
713       return false;
714   }
715 
716   bool
all_numeric_conversions(void)717   scanf_format_list::all_numeric_conversions (void)
718   {
719     std::size_t n = fmt_elts.size ();
720 
721     if (n > 0)
722       {
723         for (std::size_t i = 0; i < n; i++)
724           {
725             scanf_format_elt *elt = fmt_elts[i];
726 
727             switch (elt->type)
728               {
729               case 'd': case 'i': case 'o': case 'u': case 'x': case 'X':
730               case 'e': case 'f': case 'g': case 'E': case 'G':
731                 break;
732 
733               default:
734                 return false;
735                 break;
736               }
737           }
738 
739         return true;
740       }
741     else
742       return false;
743   }
744 
745   class
746   printf_format_elt
747   {
748   public:
749 
printf_format_elt(const std::string & txt="",int n=0,int w=-1,int p=-1,const std::string & f="",char typ='\\0',char mod='\\0')750     printf_format_elt (const std::string& txt = "", int n = 0, int w = -1,
751                        int p = -1, const std::string& f = "",
752                        char typ = '\0', char mod = '\0')
753       : text (txt), args (n), fw (w), prec (p), flags (f),
754         type (typ), modifier (mod)
755     { }
756 
757     printf_format_elt (const printf_format_elt&) = default;
758 
759     printf_format_elt& operator = (const printf_format_elt&) = default;
760 
761     ~printf_format_elt (void) = default;
762 
763     // The C-style format string.
764     std::string text;
765 
766     // How many args do we expect to consume?
767     int args;
768 
769     // Field width.
770     int fw;
771 
772     // Precision.
773     int prec;
774 
775     // Flags -- '-', '+', ' ', '0', or '#'.
776     std::string flags;
777 
778     // Type of conversion -- 'd', 'i', 'o', 'x', 'X', 'u', 'c', 's',
779     // 'f', 'e', 'E', 'g', 'G', 'p', or '%'
780     char type;
781 
782     // A length modifier -- 'h', 'l', or 'L'.
783     char modifier;
784   };
785 
786   class
787   printf_format_list
788   {
789   public:
790 
791     printf_format_list (const std::string& fmt = "");
792 
793     // No copying!
794 
795     printf_format_list (const printf_format_list&) = delete;
796 
797     printf_format_list& operator = (const printf_format_list&) = delete;
798 
799     ~printf_format_list (void);
800 
num_conversions(void)801     octave_idx_type num_conversions (void) { return nconv; }
802 
first(void)803     const printf_format_elt * first (void)
804     {
805       curr_idx = 0;
806       return current ();
807     }
808 
current(void) const809     const printf_format_elt * current (void) const
810     {
811       return length () > 0 ? fmt_elts[curr_idx] : nullptr;
812     }
813 
length(void) const814     std::size_t length (void) const { return fmt_elts.size (); }
815 
next(bool cycle=true)816     const printf_format_elt * next (bool cycle = true)
817     {
818       curr_idx++;
819 
820       if (curr_idx >= length ())
821         {
822           if (cycle)
823             curr_idx = 0;
824           else
825             return nullptr;
826         }
827 
828       return current ();
829     }
830 
last_elt_p(void)831     bool last_elt_p (void) { return (curr_idx + 1 == length ()); }
832 
833     void printme (void) const;
834 
ok(void) const835     bool ok (void) const { return (nconv >= 0); }
836 
operator bool() const837     operator bool () const { return ok (); }
838 
839   private:
840 
841     // Number of conversions specified by this format string, or -1 if
842     // invalid conversions have been found.
843     octave_idx_type nconv;
844 
845     // Index to current element;
846     std::size_t curr_idx;
847 
848     // List of format elements.
849     std::deque<printf_format_elt*> fmt_elts;
850 
851     // Temporary buffer.
852     std::ostringstream buf;
853 
854     void add_elt_to_list (int args, const std::string& flags, int fw,
855                           int prec, char type, char modifier);
856 
857     void process_conversion (const std::string& s, std::size_t& i, std::size_t n,
858                              int& args, std::string& flags, int& fw,
859                              int& prec, char& modifier, char& type);
860 
861     void finish_conversion (const std::string& s, std::size_t& i, int args,
862                             const std::string& flags, int fw, int prec,
863                             char modifier, char& type);
864   };
865 
printf_format_list(const std::string & s)866   printf_format_list::printf_format_list (const std::string& s)
867     : nconv (0), curr_idx (0), fmt_elts (), buf ()
868   {
869     std::size_t n = s.length ();
870 
871     std::size_t i = 0;
872 
873     int args = 0;
874     std::string flags;
875     int fw = -1;
876     int prec = -1;
877     char modifier = '\0';
878     char type = '\0';
879 
880     bool have_more = true;
881     bool empty_buf = true;
882 
883     if (n == 0)
884       {
885         printf_format_elt *elt
886           = new printf_format_elt ("", args, fw, prec, flags, type, modifier);
887 
888         fmt_elts.push_back (elt);
889       }
890     else
891       {
892         while (i < n)
893           {
894             have_more = true;
895 
896             empty_buf = (buf.tellp () == 0);
897 
898             switch (s[i])
899               {
900               case '%':
901                 {
902                   if (empty_buf)
903                     {
904                       process_conversion (s, i, n, args, flags, fw, prec,
905                                           modifier, type);
906 
907                       // If there is nothing in the buffer, then
908                       // add_elt_to_list must have just been called, so we
909                       // are already done with the current element and we
910                       // don't need to call add_elt_to_list if this is our
911                       // last trip through the loop.
912 
913                       have_more = (buf.tellp () != 0);
914                     }
915                   else
916                     add_elt_to_list (args, flags, fw, prec, type, modifier);
917                 }
918                 break;
919 
920               default:
921                 {
922                   args = 0;
923                   flags = "";
924                   fw = -1;
925                   prec = -1;
926                   modifier = '\0';
927                   type = '\0';
928                   buf << s[i++];
929                 }
930                 break;
931               }
932 
933             if (nconv < 0)
934               {
935                 have_more = false;
936                 break;
937               }
938           }
939 
940         if (have_more)
941           add_elt_to_list (args, flags, fw, prec, type, modifier);
942 
943         buf.clear ();
944         buf.str ("");
945       }
946   }
947 
~printf_format_list(void)948   printf_format_list::~printf_format_list (void)
949   {
950     std::size_t n = fmt_elts.size ();
951 
952     for (std::size_t i = 0; i < n; i++)
953       {
954         printf_format_elt *elt = fmt_elts[i];
955         delete elt;
956       }
957   }
958 
959   void
add_elt_to_list(int args,const std::string & flags,int fw,int prec,char type,char modifier)960   printf_format_list::add_elt_to_list (int args, const std::string& flags,
961                                        int fw, int prec, char type,
962                                        char modifier)
963   {
964     std::string text = buf.str ();
965 
966     if (! text.empty ())
967       {
968         printf_format_elt *elt
969           = new printf_format_elt (text, args, fw, prec, flags,
970                                    type, modifier);
971 
972         fmt_elts.push_back (elt);
973       }
974 
975     buf.clear ();
976     buf.str ("");
977   }
978 
979   void
process_conversion(const std::string & s,std::size_t & i,std::size_t n,int & args,std::string & flags,int & fw,int & prec,char & modifier,char & type)980   printf_format_list::process_conversion (const std::string& s, std::size_t& i,
981                                           std::size_t n, int& args,
982                                           std::string& flags, int& fw,
983                                           int& prec, char& modifier,
984                                           char& type)
985   {
986     args = 0;
987     flags = "";
988     fw = -1;
989     prec = -1;
990     modifier = '\0';
991     type = '\0';
992 
993     buf << s[i++];
994 
995     bool nxt = false;
996 
997     while (i < n)
998       {
999         switch (s[i])
1000           {
1001           case '-': case '+': case ' ': case '0': case '#':
1002             flags += s[i];
1003             buf << s[i++];
1004             break;
1005 
1006           default:
1007             nxt = true;
1008             break;
1009           }
1010 
1011         if (nxt)
1012           break;
1013       }
1014 
1015     if (i < n)
1016       {
1017         if (s[i] == '*')
1018           {
1019             fw = -2;
1020             args++;
1021             buf << s[i++];
1022           }
1023         else
1024           {
1025             if (isdigit (s[i]))
1026               {
1027                 int nn = 0;
1028                 std::string tmp = s.substr (i);
1029                 sscanf (tmp.c_str (), "%d%n", &fw, &nn);
1030               }
1031 
1032             while (i < n && isdigit (s[i]))
1033               buf << s[i++];
1034           }
1035       }
1036 
1037     if (i < n && s[i] == '.')
1038       {
1039         // nothing before the . means 0.
1040         if (fw == -1)
1041           fw = 0;
1042 
1043         // . followed by nothing is 0.
1044         prec = 0;
1045 
1046         buf << s[i++];
1047 
1048         if (i < n)
1049           {
1050             if (s[i] == '*')
1051               {
1052                 prec = -2;
1053                 args++;
1054                 buf << s[i++];
1055               }
1056             else
1057               {
1058                 if (isdigit (s[i]))
1059                   {
1060                     int nn = 0;
1061                     std::string tmp = s.substr (i);
1062                     sscanf (tmp.c_str (), "%d%n", &prec, &nn);
1063                   }
1064 
1065                 while (i < n && isdigit (s[i]))
1066                   buf << s[i++];
1067               }
1068           }
1069       }
1070 
1071     if (i < n)
1072       {
1073         // Accept and record modifier, but don't place it in the format
1074         // item text.  All integer conversions are handled as 64-bit
1075         // integers.
1076 
1077         switch (s[i])
1078           {
1079           case 'h': case 'l': case 'L':
1080             modifier = s[i++];
1081             break;
1082 
1083           default:
1084             break;
1085           }
1086       }
1087 
1088     if (i < n)
1089       finish_conversion (s, i, args, flags, fw, prec, modifier, type);
1090     else
1091       nconv = -1;
1092   }
1093 
1094   void
finish_conversion(const std::string & s,std::size_t & i,int args,const std::string & flags,int fw,int prec,char modifier,char & type)1095   printf_format_list::finish_conversion (const std::string& s, std::size_t& i,
1096                                          int args, const std::string& flags,
1097                                          int fw, int prec, char modifier,
1098                                          char& type)
1099   {
1100     switch (s[i])
1101       {
1102       case 'd': case 'i': case 'o': case 'x': case 'X':
1103       case 'u': case 'c':
1104         if (modifier == 'L')
1105           {
1106             nconv = -1;
1107             break;
1108           }
1109         goto fini;
1110 
1111       case 'f': case 'e': case 'E': case 'g': case 'G':
1112         if (modifier == 'h' || modifier == 'l')
1113           {
1114             nconv = -1;
1115             break;
1116           }
1117         goto fini;
1118 
1119       case 's': case 'p': case '%':
1120         if (modifier != '\0')
1121           {
1122             nconv = -1;
1123             break;
1124           }
1125         goto fini;
1126 
1127       fini:
1128 
1129         type = s[i];
1130 
1131         buf << s[i++];
1132 
1133         if (type != '%' || args != 0)
1134           nconv++;
1135 
1136         if (type != '%')
1137           args++;
1138 
1139         add_elt_to_list (args, flags, fw, prec, type, modifier);
1140 
1141         break;
1142 
1143       default:
1144         nconv = -1;
1145         break;
1146       }
1147   }
1148 
1149   void
printme(void) const1150   printf_format_list::printme (void) const
1151   {
1152     std::size_t n = fmt_elts.size ();
1153 
1154     for (std::size_t i = 0; i < n; i++)
1155       {
1156         printf_format_elt *elt = fmt_elts[i];
1157 
1158         std::cerr
1159           << "args:     " << elt->args << "\n"
1160           << "flags:    '" << elt->flags << "'\n"
1161           << "width:    " << elt->fw << "\n"
1162           << "prec:     " << elt->prec << "\n"
1163           << "type:     '" << elt->type << "'\n"
1164           << "modifier: '" << elt->modifier << "'\n"
1165           << "text:     '" << undo_string_escapes (elt->text) << "'\n\n";
1166       }
1167   }
1168 
1169   // Calculate x^n.  Used for ...e+nn so that, for example, 1e2 is
1170   // exactly 100 and 5e-1 is 1/2
1171 
1172   static double
pown(double x,unsigned int n)1173   pown (double x, unsigned int n)
1174   {
1175     double retval = 1;
1176 
1177     for (unsigned int d = n; d; d >>= 1)
1178       {
1179         if (d & 1)
1180           retval *= x;
1181         x *= x;
1182       }
1183 
1184     return retval;
1185   }
1186 
1187   static Cell
init_inf_nan(void)1188   init_inf_nan (void)
1189   {
1190     Cell retval (dim_vector (1, 2));
1191 
1192     retval(0) = Cell (octave_value ("inf"));
1193     retval(1) = Cell (octave_value ("nan"));
1194 
1195     return retval;
1196   }
1197 
1198   // Delimited stream, optimized to read strings of characters separated
1199   // by single-character delimiters.
1200   //
1201   // The reason behind this class is that octstream doesn't provide
1202   // seek/tell, but the opportunity has been taken to optimise for the
1203   // textscan workload.
1204   //
1205   // The function reads chunks into a 4kiB buffer, and marks where the
1206   // last delimiter occurs.  Reads up to this delimiter can be fast.
1207   // After that last delimiter, the remaining text is moved to the front
1208   // of the buffer and the buffer is refilled.  This also allows cheap
1209   // seek and tell operations within a "fast read" block.
1210 
1211   class
1212   delimited_stream
1213   {
1214   public:
1215 
1216     delimited_stream (std::istream& is, const std::string& delimiters,
1217                       int longest_lookahead, octave_idx_type bsize = 4096);
1218 
1219     delimited_stream (std::istream& is, const delimited_stream& ds);
1220 
1221     // No copying!
1222 
1223     delimited_stream (const delimited_stream&) = delete;
1224 
1225     delimited_stream& operator = (const delimited_stream&) = delete;
1226 
1227     ~delimited_stream (void);
1228 
1229     // Called when optimized sequence of get is finished.  Ensures that
1230     // there is a remaining delimiter in buf, or loads more data in.
field_done(void)1231     void field_done (void)
1232     {
1233       if (idx >= last)
1234         refresh_buf ();
1235     }
1236 
1237     // Load new data into buffer, and set eob, last, idx.
1238     // Return EOF at end of file, 0 otherwise.
1239     int refresh_buf (void);
1240 
1241     // Get a character, relying on caller to call field_done if
1242     // a delimiter has been reached.
get(void)1243     int get (void)
1244     {
1245       if (delimited)
1246         return eof () ? std::istream::traits_type::eof () : *idx++;
1247       else
1248         return get_undelim ();
1249     }
1250 
1251     // Get a character, checking for underrun of the buffer.
1252     int get_undelim (void);
1253 
1254     // Read character that will be got by the next get.
1255     // FIXME: This will not set EOF if delimited stream is at EOF and a peek
1256     // is attempted.  This does *NOT* behave like C++ input stream.
1257     // For a compatible peek function, use peek_undelim.  See bug #56917.
peek(void)1258     int peek (void) { return eof () ? std::istream::traits_type::eof () : *idx; }
1259 
1260     // Read character that will be got by the next get.
1261     int peek_undelim (void);
1262 
1263     // Undo a 'get' or 'get_undelim'.  It is the caller's responsibility
1264     // to avoid overflow by calling putbacks only for a character got by
1265     // get() or get_undelim(), with no intervening
1266     // get, get_delim, field_done, refresh_buf, getline, read or seekg.
putback(char=0)1267     void putback (char /*ch*/ = 0) { if (! eof ()) --idx; }
1268 
1269     int getline  (std::string& dest, char delim);
1270 
1271     // int skipline (char delim);
1272 
1273     char * read (char *buffer, int size, char* &new_start);
1274 
1275     // Return a position suitable to "seekg", valid only within this
1276     // block between calls to field_done.
tellg(void)1277     char * tellg (void) { return idx; }
1278 
seekg(char * old_idx)1279     void seekg (char *old_idx) { idx = old_idx; }
1280 
eof(void)1281     bool eof (void)
1282     {
1283       return (eob == buf && i_stream.eof ()) || (flags & std::ios_base::eofbit);
1284     }
1285 
operator const void*(void)1286     operator const void* (void) { return (! eof () && ! flags) ? this : nullptr; }
1287 
fail(void)1288     bool fail (void) { return flags & std::ios_base::failbit; }
1289 
rdstate(void)1290     std::ios_base::iostate rdstate (void) { return flags; }
1291 
setstate(std::ios_base::iostate m)1292     void setstate (std::ios_base::iostate m) { flags = flags | m; }
1293 
clear(std::ios_base::iostate m=(std::ios_base::eofbit & ~std::ios_base::eofbit))1294     void clear (std::ios_base::iostate m
1295                 = (std::ios_base::eofbit & ~std::ios_base::eofbit))
1296     {
1297       flags = flags & m;
1298     }
1299 
1300     // Report if any characters have been consumed.
1301     // (get, read, etc. not cancelled by putback or seekg)
1302 
progress_benchmark(void)1303     void progress_benchmark (void) { progress_marker = idx; }
1304 
no_progress(void)1305     bool no_progress (void) { return progress_marker == idx; }
1306 
1307   private:
1308 
1309     // Number of characters to read from the file at once.
1310     int bufsize;
1311 
1312     // Stream to read from.
1313     std::istream& i_stream;
1314 
1315     // Temporary storage for a "chunk" of data.
1316     char *buf;
1317 
1318     // Current read pointer.
1319     char *idx;
1320 
1321     // Location of last delimiter in the buffer at buf (undefined if
1322     // delimited is false).
1323     char *last;
1324 
1325     // Position after last character in buffer.
1326     char *eob;
1327 
1328     // True if there is delimiter in the buffer after idx.
1329     bool delimited;
1330 
1331     // Longest lookahead required.
1332     int longest;
1333 
1334     // Sequence of single-character delimiters.
1335     const std::string delims;
1336 
1337     // Position of start of buf in original stream.
1338     std::streampos buf_in_file;
1339 
1340     // Marker to see if a read consumes any characters.
1341     char *progress_marker;
1342 
1343     std::ios_base::iostate flags;
1344   };
1345 
1346   // Create a delimited stream, reading from is, with delimiters delims,
1347   // and allowing reading of up to tellg + longest_lookeahead.  When is
1348   // is at EOF, lookahead may be padded by ASCII nuls.
1349 
delimited_stream(std::istream & is,const std::string & delimiters,int longest_lookahead,octave_idx_type bsize)1350   delimited_stream::delimited_stream (std::istream& is,
1351                                       const std::string& delimiters,
1352                                       int longest_lookahead,
1353                                       octave_idx_type bsize)
1354     : bufsize (bsize), i_stream (is), longest (longest_lookahead),
1355       delims (delimiters),
1356       flags (std::ios::failbit & ~std::ios::failbit) // can't cast 0
1357   {
1358     buf = new char[bufsize];
1359     eob = buf + bufsize;
1360     idx = eob;                    // refresh_buf shouldn't try to copy old data
1361     progress_marker = idx;
1362     refresh_buf ();               // load the first batch of data
1363   }
1364 
1365   // Used to create a stream from a strstream from data read from a dstr.
delimited_stream(std::istream & is,const delimited_stream & ds)1366   delimited_stream::delimited_stream (std::istream& is,
1367                                       const delimited_stream& ds)
1368     : delimited_stream (is, ds.delims, ds.longest, ds.bufsize)
1369   { }
1370 
~delimited_stream(void)1371   delimited_stream::~delimited_stream (void)
1372   {
1373     // Seek to the correct position in i_stream.
1374     if (! eof ())
1375       {
1376         i_stream.clear ();
1377         i_stream.seekg (buf_in_file);
1378         i_stream.read (buf, idx - buf);
1379       }
1380 
1381     delete [] buf;
1382   }
1383 
1384   // Read a character from the buffer, refilling the buffer from the file
1385   // if necessary.
1386 
1387   int
get_undelim(void)1388   delimited_stream::get_undelim (void)
1389   {
1390     int retval;
1391     if (eof ())
1392       {
1393         setstate (std::ios_base::failbit);
1394         return std::istream::traits_type::eof ();
1395       }
1396 
1397     if (idx < eob)
1398       retval = *idx++;
1399     else
1400       {
1401         refresh_buf ();
1402 
1403         if (eof ())
1404           {
1405             setstate (std::ios_base::eofbit);
1406             retval = std::istream::traits_type::eof ();
1407           }
1408         else
1409           retval = *idx++;
1410       }
1411 
1412     if (idx >= last)
1413       delimited = false;
1414 
1415     return retval;
1416   }
1417 
1418   // Return the next character to be read without incrementing the
1419   // pointer, refilling the buffer from the file if necessary.
1420 
1421   int
peek_undelim(void)1422   delimited_stream::peek_undelim (void)
1423   {
1424     int retval = get_undelim ();
1425     putback ();
1426 
1427     return retval;
1428   }
1429 
1430   // Copy remaining unprocessed data to the start of the buffer and load
1431   // new data to fill it.  Return EOF if the file is at EOF before
1432   // reading any data and all of the data that has been read has been
1433   // processed.
1434 
1435   int
refresh_buf(void)1436   delimited_stream::refresh_buf (void)
1437   {
1438     if (eof ())
1439       return std::istream::traits_type::eof ();
1440 
1441     int retval;
1442 
1443     if (eob < idx)
1444       idx = eob;
1445 
1446     std::size_t old_remaining = eob - idx;
1447 
1448     octave_quit ();                       // allow ctrl-C
1449 
1450     if (old_remaining > 0)
1451       {
1452         buf_in_file += (idx - buf);
1453         memmove (buf, idx, old_remaining);
1454       }
1455     else
1456       buf_in_file = i_stream.tellg ();    // record for destructor
1457 
1458     progress_marker -= idx - buf;         // where original idx would have been
1459     idx = buf;
1460 
1461     int gcount;   // chars read
1462     if (! i_stream.eof ())
1463       {
1464         i_stream.read (buf + old_remaining, bufsize - old_remaining);
1465         gcount = i_stream.gcount ();
1466       }
1467     else
1468       gcount = 0;
1469 
1470     eob = buf + old_remaining + gcount;
1471     last = eob;
1472     if (gcount == 0)
1473       {
1474         delimited = false;
1475 
1476         if (eob != buf)              // no more data in file, but still some to go
1477           retval = 0;
1478         else
1479           // file and buffer are both done.
1480           retval = std::istream::traits_type::eof ();
1481       }
1482     else
1483       {
1484         delimited = true;
1485 
1486         for (last = eob - longest; last - buf >= 0; last--)
1487           {
1488             if (delims.find (*last) != std::string::npos)
1489               break;
1490           }
1491 
1492         if (last < buf)
1493           delimited = false;
1494 
1495         retval = 0;
1496       }
1497 
1498     // Ensure fast peek doesn't give valid char
1499     if (retval == std::istream::traits_type::eof ())
1500       *idx = '\0';    // FIXME: check that no TreatAsEmpty etc starts w. \0?
1501 
1502     return retval;
1503   }
1504 
1505   // Return a pointer to a block of data of size size, assuming that a
1506   // sufficiently large buffer is available in buffer, if required.
1507   // If called when delimited == true, and size is no greater than
1508   // longest_lookahead then this will not call refresh_buf, so seekg
1509   // still works.  Otherwise, seekg may be invalidated.
1510 
1511   char *
read(char * buffer,int size,char * & prior_tell)1512   delimited_stream::read (char *buffer, int size, char* &prior_tell)
1513   {
1514     char *retval;
1515 
1516     if (eob  - idx > size)
1517       {
1518         retval = idx;
1519         idx += size;
1520         if (idx > last)
1521           delimited = false;
1522       }
1523     else
1524       {
1525         // If there was a tellg pointing to an earlier point than the current
1526         // read position, try to keep it in the active buffer.
1527         // In the current code, prior_tell==idx for each call,
1528         // so this is not necessary, just a precaution.
1529 
1530         if (eob - prior_tell + size < bufsize)
1531           {
1532             octave_idx_type gap = idx - prior_tell;
1533             idx = prior_tell;
1534             refresh_buf ();
1535             idx += gap;
1536           }
1537         else      // can't keep the tellg in range.  May skip some data.
1538           {
1539             refresh_buf ();
1540           }
1541 
1542         prior_tell = buf;
1543 
1544         if (eob - idx > size)
1545           {
1546             retval = idx;
1547             idx += size;
1548             if (idx > last)
1549               delimited = false;
1550           }
1551         else
1552           {
1553             if (size <= bufsize)          // small read, but reached EOF
1554               {
1555                 retval = idx;
1556                 memset (eob, 0, size + (idx - buf));
1557                 idx += size;
1558               }
1559             else  // Reading more than the whole buf; return it in buffer
1560               {
1561                 retval = buffer;
1562                 // FIXME: read bufsize at a time
1563                 int i;
1564                 for (i = 0; i < size && ! eof (); i++)
1565                   *buffer++ = get_undelim ();
1566                 if (eof ())
1567                   memset (buffer, 0, size - i);
1568               }
1569           }
1570       }
1571 
1572     return retval;
1573   }
1574 
1575   // Return in OUT an entire line, terminated by delim.  On input, OUT
1576   // must have length at least 1.
1577 
1578   int
getline(std::string & out,char delim)1579   delimited_stream::getline (std::string& out, char delim)
1580   {
1581     int len = out.length ();
1582     int used = 0;
1583     int ch;
1584     while ((ch = get_undelim ()) != delim
1585            && ch != std::istream::traits_type::eof ())
1586       {
1587         out[used++] = ch;
1588         if (used == len)
1589           {
1590             len <<= 1;
1591             out.resize (len);
1592           }
1593       }
1594     out.resize (used);
1595     field_done ();
1596 
1597     return ch;
1598   }
1599 
1600   // A single conversion specifier, such as %f or %c.
1601 
1602   class
1603   textscan_format_elt
1604   {
1605   public:
1606 
1607     enum special_conversion
1608     {
1609       whitespace_conversion = 1,
1610       literal_conversion = 2
1611     };
1612 
textscan_format_elt(const std::string & txt,int w=0,int p=-1,int bw=0,bool dis=false,char typ='\\0',const std::string & ch_class=std::string ())1613     textscan_format_elt (const std::string& txt, int w = 0, int p = -1,
1614                          int bw = 0, bool dis = false, char typ = '\0',
1615                          const std::string& ch_class = std::string ())
1616       : text (txt), width (w), prec (p), bitwidth (bw),
1617         char_class (ch_class), type (typ), discard (dis),
1618         numeric (typ == 'd' || typ == 'u' || type == 'f' || type == 'n')
1619     { }
1620 
textscan_format_elt(const textscan_format_elt & e)1621     textscan_format_elt (const textscan_format_elt& e)
1622       : text (e.text), width (e.width), prec (e.prec),
1623         bitwidth (e.bitwidth), char_class (e.char_class), type (e.type),
1624         discard (e.discard), numeric (e.numeric)
1625     { }
1626 
operator =(const textscan_format_elt & e)1627     textscan_format_elt& operator = (const textscan_format_elt& e)
1628     {
1629       if (this != &e)
1630         {
1631           text = e.text;
1632           width = e.width;
1633           prec = e.prec;
1634           bitwidth = e.bitwidth;
1635           discard = e.discard;
1636           type = e.type;
1637           numeric = e.numeric;
1638           char_class = e.char_class;
1639         }
1640 
1641       return *this;
1642     }
1643 
1644     // The C-style format string.
1645     std::string text;
1646 
1647     // The maximum field width.
1648     unsigned int width;
1649 
1650     // The maximum number of digits to read after the decimal in a
1651     // floating point conversion.
1652     int prec;
1653 
1654     // The size of the result.  For integers, bitwidth may be 8, 16, 34,
1655     // or 64.  For floating point values, bitwidth may be 32 or 64.
1656     int bitwidth;
1657 
1658     // The class of characters in a '[' or '^' format.
1659     std::string char_class;
1660 
1661     // Type of conversion
1662     //  -- 'd', 'u', 'f', 'n', 's', 'q', 'c', '%', 'C', 'D', '[' or '^'.
1663     char type;
1664 
1665     // TRUE if we are not storing the result of this conversion.
1666     bool discard;
1667 
1668     // TRUE if the type is 'd', 'u', 'f', 'n'
1669     bool numeric;
1670   };
1671 
1672   // The (parsed) sequence of format specifiers.
1673 
1674   class textscan;
1675 
1676   class
1677   textscan_format_list
1678   {
1679   public:
1680 
1681     textscan_format_list (const std::string& fmt = std::string (),
1682                           const std::string& who = "textscan");
1683     // No copying!
1684 
1685     textscan_format_list (const textscan_format_list&) = delete;
1686 
1687     textscan_format_list& operator = (const textscan_format_list&) = delete;
1688 
1689     ~textscan_format_list (void);
1690 
num_conversions(void) const1691     octave_idx_type num_conversions (void) const { return nconv; }
1692 
1693     // The length can be different than the number of conversions.
1694     // For example, "x %d y %d z" has 2 conversions but the length of
1695     // the list is 3 because of the characters that appear after the
1696     // last conversion.
1697 
numel(void) const1698     std::size_t numel (void) const { return fmt_elts.size (); }
1699 
first(void)1700     const textscan_format_elt * first (void)
1701     {
1702       curr_idx = 0;
1703       return current ();
1704     }
1705 
current(void) const1706     const textscan_format_elt * current (void) const
1707     {
1708       return numel () > 0 ? fmt_elts[curr_idx] : nullptr;
1709     }
1710 
next(bool cycle=true)1711     const textscan_format_elt * next (bool cycle = true)
1712     {
1713       curr_idx++;
1714 
1715       if (curr_idx >= numel ())
1716         {
1717           if (cycle)
1718             curr_idx = 0;
1719           else
1720             return nullptr;
1721         }
1722 
1723       return current ();
1724     }
1725 
1726     void printme (void) const;
1727 
ok(void) const1728     bool ok (void) const { return (nconv >= 0); }
1729 
operator const void*(void) const1730     operator const void* (void) const { return ok () ? this : nullptr; }
1731 
1732     // What function name should be shown when reporting errors.
1733     std::string who;
1734 
1735     // True if number of %f to be set from data file.
1736     bool set_from_first;
1737 
1738     // At least one conversion specifier is s,q,c, or [...].
1739     bool has_string;
1740 
1741     int read_first_row (delimited_stream& is, textscan& ts);
1742 
out_buf(void) const1743     std::list<octave_value> out_buf (void) const { return (output_container); }
1744 
1745   private:
1746 
1747     // Number of conversions specified by this format string, or -1 if
1748     // invalid conversions have been found.
1749     octave_idx_type nconv;
1750 
1751     // Index to current element;
1752     std::size_t curr_idx;
1753 
1754     // List of format elements.
1755     std::deque<textscan_format_elt*> fmt_elts;
1756 
1757     // list holding column arrays of types specified by conversions
1758     std::list<octave_value> output_container;
1759 
1760     // Temporary buffer.
1761     std::ostringstream buf;
1762 
1763     void add_elt_to_list (unsigned int width, int prec, int bitwidth,
1764                           octave_value val_type, bool discard,
1765                           char type,
1766                           const std::string& char_class = std::string ());
1767 
1768     void process_conversion (const std::string& s, std::size_t& i, std::size_t n);
1769 
1770     std::string parse_char_class (const std::string& pattern) const;
1771 
1772     int finish_conversion (const std::string& s, std::size_t& i, std::size_t n,
1773                            unsigned int width, int prec, int bitwidth,
1774                            octave_value& val_type,
1775                            bool discard, char& type);
1776   };
1777 
1778   // Main class to implement textscan.  Read data and parse it
1779   // according to a format.
1780   //
1781   // The calling sequence is
1782   //
1783   //   textscan scanner ();
1784   //   scanner.scan (...);
1785 
1786   class
1787   OCTINTERP_API
1788   textscan
1789   {
1790   public:
1791 
1792     textscan (const std::string& who_arg = "textscan",
1793               const std::string& encoding = "utf-8");
1794 
1795     // No copying!
1796 
1797     textscan (const textscan&) = delete;
1798 
1799     textscan& operator = (const textscan&) = delete;
1800 
1801     ~textscan (void) = default;
1802 
1803     octave_value scan (std::istream& isp, const std::string& fmt,
1804                        octave_idx_type ntimes,
1805                        const octave_value_list& options,
1806                        octave_idx_type& read_count);
1807 
1808   private:
1809 
1810     friend class textscan_format_list;
1811 
1812     // What function name should be shown when reporting errors.
1813     std::string who;
1814 
1815     std::string m_encoding;
1816 
1817     std::string buf;
1818 
1819     // Three cases for delim_table and delim_list
1820     // 1. delim_table empty, delim_list empty:  whitespace delimiters
1821     // 2. delim_table = look-up table of delim chars, delim_list empty.
1822     // 3. delim_table non-empty, delim_list = Cell array of delim strings
1823 
1824     std::string whitespace_table;
1825 
1826     // delim_table[i] == '\0' if i is not a delimiter.
1827     std::string delim_table;
1828 
1829     // String of delimiter characters.
1830     std::string delims;
1831 
1832     Cell comment_style;
1833 
1834     // How far ahead to look to detect an open comment.
1835     int comment_len;
1836 
1837     // First character of open comment.
1838     int comment_char;
1839 
1840     octave_idx_type buffer_size;
1841 
1842     std::string date_locale;
1843 
1844     // 'inf' and 'nan' for formatted_double.
1845     Cell inf_nan;
1846 
1847     // Array of strings of delimiters.
1848     Cell delim_list;
1849 
1850     // Longest delimiter.
1851     int delim_len;
1852 
1853     octave_value empty_value;
1854     std::string exp_chars;
1855     int header_lines;
1856     Cell treat_as_empty;
1857 
1858     // Longest string to treat as "N/A".
1859     int treat_as_empty_len;
1860 
1861     std::string whitespace;
1862 
1863     short eol1;
1864     short eol2;
1865     short return_on_error;
1866 
1867     bool collect_output;
1868     bool multiple_delims_as_one;
1869     bool default_exp;
1870 
1871     octave_idx_type lines;
1872 
1873     octave_value do_scan (std::istream& isp, textscan_format_list& fmt_list,
1874                           octave_idx_type ntimes);
1875 
1876     void parse_options (const octave_value_list& args,
1877                         textscan_format_list& fmt_list);
1878 
1879     int read_format_once (delimited_stream& isp, textscan_format_list& fmt_list,
1880                           std::list<octave_value>& retval,
1881                           Array<octave_idx_type> row, int& done_after);
1882 
1883     void scan_one (delimited_stream& is, const textscan_format_elt& fmt,
1884                    octave_value& ov, Array<octave_idx_type> row);
1885 
1886     // Methods to process a particular conversion specifier.
1887     double read_double (delimited_stream& is,
1888                         const textscan_format_elt& fmt) const;
1889 
1890     void scan_complex (delimited_stream& is, const textscan_format_elt& fmt,
1891                        Complex& val) const;
1892 
1893     int scan_bracket (delimited_stream& is, const std::string& pattern,
1894                       std::string& val) const;
1895 
1896     int scan_caret (delimited_stream& is, const std::string& pattern,
1897                     std::string& val) const;
1898 
1899     void scan_string (delimited_stream& is, const textscan_format_elt& fmt,
1900                       std::string& val) const;
1901 
1902     void scan_cstring (delimited_stream& is, const textscan_format_elt& fmt,
1903                        std::string& val) const;
1904 
1905     void scan_qstring (delimited_stream& is, const textscan_format_elt& fmt,
1906                        std::string& val);
1907 
1908     // Helper methods.
1909     std::string read_until (delimited_stream& is, const Cell& delimiters,
1910                             const std::string& ends) const;
1911 
1912     int lookahead (delimited_stream& is, const Cell& targets, int max_len,
1913                    bool case_sensitive = true) const;
1914 
1915     bool match_literal (delimited_stream& isp, const textscan_format_elt& elem);
1916 
1917     int skip_whitespace (delimited_stream& is, bool EOLstop = false);
1918 
1919     int skip_delim (delimited_stream& is);
1920 
is_delim(unsigned char ch) const1921     bool is_delim (unsigned char ch) const
1922     {
1923       return ((delim_table.empty () && (isspace (ch) || ch == eol1 || ch == eol2))
1924               || delim_table[ch] != '\0');
1925     }
1926 
isspace(unsigned int ch) const1927     bool isspace (unsigned int ch) const { return whitespace_table[ch & 0xff]; }
1928 
1929     // True if the only delimiter is whitespace.
whitespace_delim(void) const1930     bool whitespace_delim (void) const { return delim_table.empty (); }
1931   };
1932 
textscan_format_list(const std::string & s,const std::string & who_arg)1933   textscan_format_list::textscan_format_list (const std::string& s,
1934                                               const std::string& who_arg)
1935     : who (who_arg), set_from_first (false), has_string (false),
1936       nconv (0), curr_idx (0), fmt_elts (), buf ()
1937   {
1938     std::size_t n = s.length ();
1939 
1940     std::size_t i = 0;
1941 
1942     unsigned int width = -1;              // Unspecified width = max (except %c)
1943     int prec = -1;
1944     int bitwidth = 0;
1945     bool discard = false;
1946     char type = '\0';
1947 
1948     bool have_more = true;
1949 
1950     if (s.empty ())
1951       {
1952         buf.clear ();
1953         buf.str ("");
1954 
1955         buf << "%f";
1956 
1957         bitwidth = 64;
1958         type = 'f';
1959         add_elt_to_list (width, prec, bitwidth, octave_value (NDArray ()),
1960                          discard, type);
1961         have_more = false;
1962         set_from_first = true;
1963         nconv = 1;
1964       }
1965     else
1966       {
1967         set_from_first = false;
1968 
1969         while (i < n)
1970           {
1971             have_more = true;
1972 
1973             if (s[i] == '%' && (i+1 == n || s[i+1] != '%'))
1974               {
1975                 // Process percent-escape conversion type.
1976 
1977                 process_conversion (s, i, n);
1978 
1979                 // If there is nothing in the buffer, then add_elt_to_list
1980                 // must have just been called, so we are already done with
1981                 // the current element and we don't need to call
1982                 // add_elt_to_list if this is our last trip through the
1983                 // loop.
1984 
1985                 have_more = (buf.tellp () != 0);
1986               }
1987             else if (isspace (s[i]))
1988               {
1989                 while (++i < n && isspace (s[i]))
1990                   /* skip whitespace */;
1991 
1992                 have_more = false;
1993               }
1994             else
1995               {
1996                 type = textscan_format_elt::literal_conversion;
1997 
1998                 width = 0;
1999                 prec = -1;
2000                 bitwidth = 0;
2001                 discard = true;
2002 
2003                 while (i < n && ! isspace (s[i])
2004                        && (s[i] != '%' || (i+1 < n && s[i+1] == '%')))
2005                   {
2006                     if (s[i] == '%')      // if double %, skip one
2007                       i++;
2008                     buf << s[i++];
2009                     width++;
2010                   }
2011 
2012                 add_elt_to_list (width, prec, bitwidth, octave_value (),
2013                                  discard, type);
2014 
2015                 have_more = false;
2016               }
2017 
2018             if (nconv < 0)
2019               {
2020                 have_more = false;
2021                 break;
2022               }
2023           }
2024       }
2025 
2026     if (have_more)
2027       add_elt_to_list (width, prec, bitwidth, octave_value (), discard, type);
2028 
2029     buf.clear ();
2030     buf.str ("");
2031   }
2032 
~textscan_format_list(void)2033   textscan_format_list::~textscan_format_list (void)
2034   {
2035     std::size_t n = numel ();
2036 
2037     for (std::size_t i = 0; i < n; i++)
2038       {
2039         textscan_format_elt *elt = fmt_elts[i];
2040         delete elt;
2041       }
2042   }
2043 
2044   void
add_elt_to_list(unsigned int width,int prec,int bitwidth,octave_value val_type,bool discard,char type,const std::string & char_class)2045   textscan_format_list::add_elt_to_list (unsigned int width, int prec,
2046                                          int bitwidth, octave_value val_type,
2047                                          bool discard, char type,
2048                                          const std::string& char_class)
2049   {
2050     std::string text = buf.str ();
2051 
2052     if (! text.empty ())
2053       {
2054         textscan_format_elt *elt
2055           = new textscan_format_elt (text, width, prec, bitwidth, discard, type,
2056                                      char_class);
2057 
2058         if (! discard)
2059           output_container.push_back (val_type);
2060 
2061         fmt_elts.push_back (elt);
2062       }
2063 
2064     buf.clear ();
2065     buf.str ("");
2066   }
2067 
2068   void
process_conversion(const std::string & s,std::size_t & i,std::size_t n)2069   textscan_format_list::process_conversion (const std::string& s, std::size_t& i,
2070                                             std::size_t n)
2071   {
2072     unsigned width = 0;
2073     int prec = -1;
2074     int bitwidth = 0;
2075     bool discard = false;
2076     octave_value val_type;
2077     char type = '\0';
2078 
2079     buf << s[i++];
2080 
2081     bool have_width = false;
2082 
2083     while (i < n)
2084       {
2085         switch (s[i])
2086           {
2087           case '*':
2088             if (discard)
2089               nconv = -1;
2090             else
2091               {
2092                 discard = true;
2093                 buf << s[i++];
2094               }
2095             break;
2096 
2097           case '0': case '1': case '2': case '3': case '4':
2098           case '5': case '6': case '7': case '8': case '9':
2099             if (have_width)
2100               nconv = -1;
2101             else
2102               {
2103                 char c = s[i++];
2104                 width = width * 10 + c - '0';
2105                 have_width = true;
2106                 buf << c;
2107                 while (i < n && isdigit (s[i]))
2108                   {
2109                     c = s[i++];
2110                     width = width * 10 + c - '0';
2111                     buf << c;
2112                   }
2113 
2114                 if (i < n && s[i] == '.')
2115                   {
2116                     buf << s[i++];
2117                     prec = 0;
2118                     while (i < n && isdigit (s[i]))
2119                       {
2120                         c = s[i++];
2121                         prec = prec * 10 + c - '0';
2122                         buf << c;
2123                       }
2124                   }
2125               }
2126             break;
2127 
2128           case 'd': case 'u':
2129             {
2130               bool done = true;
2131               buf << (type = s[i++]);
2132               if (i < n)
2133                 {
2134                   if (s[i] == '8')
2135                     {
2136                       bitwidth = 8;
2137                       if (type == 'd')
2138                         val_type = octave_value (int8NDArray ());
2139                       else
2140                         val_type = octave_value (uint8NDArray ());
2141                       buf << s[i++];
2142                     }
2143                   else if (s[i] == '1' && i+1 < n && s[i+1] == '6')
2144                     {
2145                       bitwidth = 16;
2146                       if (type == 'd')
2147                         val_type = octave_value (int16NDArray ());
2148                       else
2149                         val_type = octave_value (uint16NDArray ());
2150                       buf << s[i++];
2151                       buf << s[i++];
2152                     }
2153                   else if (s[i] == '3' && i+1 < n && s[i+1] == '2')
2154                     {
2155                       done = false;       // use default size below
2156                       buf << s[i++];
2157                       buf << s[i++];
2158                     }
2159                   else if (s[i] == '6' && i+1 < n && s[i+1] == '4')
2160                     {
2161                       bitwidth = 64;
2162                       if (type == 'd')
2163                         val_type = octave_value (int64NDArray ());
2164                       else
2165                         val_type = octave_value (uint64NDArray ());
2166                       buf << s[i++];
2167                       buf << s[i++];
2168                     }
2169                   else
2170                     done = false;
2171                 }
2172               else
2173                 done = false;
2174 
2175               if (! done)
2176                 {
2177                   bitwidth = 32;
2178                   if (type == 'd')
2179                     val_type = octave_value (int32NDArray ());
2180                   else
2181                     val_type = octave_value (uint32NDArray ());
2182                 }
2183               goto fini;
2184             }
2185 
2186           case 'f':
2187             buf << (type = s[i++]);
2188             bitwidth = 64;
2189             if (i < n)
2190               {
2191                 if (s[i] == '3' && i+1 < n && s[i+1] == '2')
2192                   {
2193                     bitwidth = 32;
2194                     val_type = octave_value (FloatNDArray ());
2195                     buf << s[i++];
2196                     buf << s[i++];
2197                   }
2198                 else if (s[i] == '6' && i+1 < n && s[i+1] == '4')
2199                   {
2200                     val_type = octave_value (NDArray ());
2201                     buf << s[i++];
2202                     buf << s[i++];
2203                   }
2204                 else
2205                   val_type = octave_value (NDArray ());
2206               }
2207             else
2208               val_type = octave_value (NDArray ());
2209             goto fini;
2210 
2211           case 'n':
2212             buf << (type = s[i++]);
2213             bitwidth = 64;
2214             val_type = octave_value (NDArray ());
2215             goto fini;
2216 
2217           case 's': case 'q': case '[': case 'c':
2218             if (! discard)
2219               val_type = octave_value (Cell ());
2220             buf << (type = s[i++]);
2221             has_string = true;
2222             goto fini;
2223 
2224           fini:
2225             {
2226               if (! have_width)
2227                 {
2228                   if (type == 'c')        // %c defaults to one character
2229                     width = 1;
2230                   else
2231                     width = static_cast<unsigned int> (-1); // others: unlimited
2232                 }
2233 
2234               if (finish_conversion (s, i, n, width, prec, bitwidth, val_type,
2235                                      discard, type) == 0)
2236                 return;
2237             }
2238             break;
2239 
2240           default:
2241             error ("%s: '%%%c' is not a valid format specifier",
2242                    who.c_str (), s[i]);
2243           }
2244 
2245         if (nconv < 0)
2246           break;
2247       }
2248 
2249     nconv = -1;
2250   }
2251 
2252   // Parse [...] and [^...]
2253   //
2254   // Matlab does not expand expressions like A-Z, but they are useful, and
2255   // so we parse them "carefully".  We treat '-' as a usual character
2256   // unless both start and end characters are from the same class (upper
2257   // case, lower case, numeric), or this is not the first '-' in the
2258   // pattern.
2259   //
2260   // Keep both a running list of characters and a mask of which chars have
2261   // occurred.  The first is efficient for patterns with few characters.
2262   // The latter is efficient for [^...] patterns.
2263 
2264   std::string
parse_char_class(const std::string & pattern) const2265   textscan_format_list::parse_char_class (const std::string& pattern) const
2266   {
2267     int len = pattern.length ();
2268     if (len == 0)
2269       return "";
2270 
2271     std::string retval (256, '\0');
2272     std::string mask   (256, '\0');       // number of times chr has been seen
2273 
2274     int in = 0, out = 0;
2275     unsigned char ch, prev = 0;
2276     bool flip = false;
2277 
2278     ch = pattern[in];
2279     if (ch == '^')
2280       {
2281         in++;
2282         flip = true;
2283       }
2284     mask[pattern[in]] = '\1';
2285     retval[out++] = pattern[in++];        // even copy ']' if it is first
2286 
2287     bool prev_was_range = false;          // disallow "a-m-z" as a pattern
2288     bool prev_prev_was_range = false;
2289     for (; in < len; in++)
2290       {
2291         bool was_range = false;
2292         ch = pattern[in];
2293         if (ch == ']')
2294           break;
2295 
2296         if (prev == '-' && in > 1 && isalnum (ch) && ! prev_prev_was_range)
2297           {
2298             unsigned char start_of_range = pattern[in-2];
2299             if (start_of_range < ch
2300                 && ((isupper (ch) && isupper (start_of_range))
2301                     || (islower (ch) && islower (start_of_range))
2302                     || (isdigit (ch) && isdigit (start_of_range))
2303                     || mask['-'] > 1))    // not the first '-'
2304               {
2305                 was_range = true;
2306                 out--;
2307                 mask['-']--;
2308                 for (int i = start_of_range; i <= ch; i++)
2309                   {
2310                     if (mask[i] == '\0')
2311                       {
2312                         mask[i] = '\1';
2313                         retval[out++] = i;
2314                       }
2315                   }
2316               }
2317           }
2318         if (! was_range)
2319           {
2320             if (mask[ch]++ == 0)
2321               retval[out++] = ch;
2322             else if (ch != '-')
2323               warning_with_id ("Octave:textscan-pattern",
2324                                "%s: [...] contains two '%c's",
2325                                who.c_str (), ch);
2326 
2327             if (prev == '-' && mask['-'] >= 2)
2328               warning_with_id
2329                 ("Octave:textscan-pattern",
2330                  "%s: [...] contains two '-'s outside range expressions",
2331                  who.c_str ());
2332           }
2333         prev = ch;
2334         prev_prev_was_range = prev_was_range;
2335         prev_was_range = was_range;
2336       }
2337 
2338     if (flip)                             // [^...]
2339       {
2340         out = 0;
2341         for (int i = 0; i < 256; i++)
2342           if (! mask[i])
2343             retval[out++] = i;
2344       }
2345 
2346     retval.resize (out);
2347 
2348     return retval;
2349   }
2350 
2351   int
finish_conversion(const std::string & s,std::size_t & i,std::size_t n,unsigned int width,int prec,int bitwidth,octave_value & val_type,bool discard,char & type)2352   textscan_format_list::finish_conversion (const std::string& s, std::size_t& i,
2353                                            std::size_t n, unsigned int width,
2354                                            int prec, int bitwidth,
2355                                            octave_value& val_type, bool discard,
2356                                            char& type)
2357   {
2358     int retval = 0;
2359 
2360     std::string char_class;
2361 
2362     std::size_t beg_idx = std::string::npos;
2363     std::size_t end_idx = std::string::npos;
2364 
2365     if (type != '%')
2366       {
2367         nconv++;
2368         if (type == '[')
2369           {
2370             if (i < n)
2371               {
2372                 beg_idx = i;
2373 
2374                 if (s[i] == '^')
2375                   {
2376                     type = '^';
2377                     buf << s[i++];
2378 
2379                     if (i < n)
2380                       {
2381                         beg_idx = i;
2382 
2383                         if (s[i] == ']')
2384                           buf << s[i++];
2385                       }
2386                   }
2387                 else if (s[i] == ']')
2388                   buf << s[i++];
2389               }
2390 
2391             while (i < n && s[i] != ']')
2392               buf << s[i++];
2393 
2394             if (i < n && s[i] == ']')
2395               {
2396                 end_idx = i-1;
2397                 buf << s[i++];
2398               }
2399 
2400             if (s[i-1] != ']')
2401               retval = nconv = -1;
2402           }
2403       }
2404 
2405     if (nconv >= 0)
2406       {
2407         if (beg_idx != std::string::npos && end_idx != std::string::npos)
2408           char_class = parse_char_class (s.substr (beg_idx,
2409                                                    end_idx - beg_idx + 1));
2410 
2411         add_elt_to_list (width, prec, bitwidth, val_type, discard, type,
2412                          char_class);
2413       }
2414 
2415     return retval;
2416   }
2417 
2418   void
printme(void) const2419   textscan_format_list::printme (void) const
2420   {
2421     std::size_t n = numel ();
2422 
2423     for (std::size_t i = 0; i < n; i++)
2424       {
2425         textscan_format_elt *elt = fmt_elts[i];
2426 
2427         std::cerr
2428           << "width:      " << elt->width << "\n"
2429           << "digits      " << elt->prec << "\n"
2430           << "bitwidth:   " << elt->bitwidth << "\n"
2431           << "discard:    " << elt->discard << "\n"
2432           << "type:       ";
2433 
2434         if (elt->type == textscan_format_elt::literal_conversion)
2435           std::cerr << "literal text\n";
2436         else if (elt->type == textscan_format_elt::whitespace_conversion)
2437           std::cerr << "whitespace\n";
2438         else
2439           std::cerr << elt->type << "\n";
2440 
2441         std::cerr
2442           << "char_class: '" << undo_string_escapes (elt->char_class) << "'\n"
2443           << "text:       '" << undo_string_escapes (elt->text) << "'\n\n";
2444       }
2445   }
2446 
2447   // If FORMAT is explicitly "", it is assumed to be "%f" repeated enough
2448   // times to read the first row of the file.  Set it now.
2449 
2450   int
read_first_row(delimited_stream & is,textscan & ts)2451   textscan_format_list::read_first_row (delimited_stream& is, textscan& ts)
2452   {
2453     // Read first line and strip end-of-line, which may be two characters
2454     std::string first_line (20, ' ');
2455 
2456     is.getline (first_line, static_cast<char> (ts.eol2));
2457 
2458     if (! first_line.empty () && first_line.back () == ts.eol1)
2459       first_line.pop_back ();
2460 
2461     std::istringstream strstr (first_line);
2462     delimited_stream ds (strstr, is);
2463 
2464     dim_vector dv (1,1);      // initial size of each output_container
2465     Complex val;
2466     octave_value val_type;
2467     nconv = 0;
2468     int max_empty = 1000;     // failsafe, if ds fails but not with eof
2469     int retval = 0;
2470 
2471     // read line, creating output_container as we go
2472     while (! ds.eof ())
2473       {
2474         bool already_skipped_delim = false;
2475         ts.skip_whitespace (ds);
2476         ds.progress_benchmark ();
2477         ts.scan_complex (ds, *fmt_elts[0], val);
2478         if (ds.fail ())
2479           {
2480             ds.clear (ds.rdstate () & ~std::ios::failbit);
2481 
2482             if (ds.eof ())
2483               break;
2484 
2485             // Unless this was a missing value (i.e., followed by a delimiter),
2486             // return with an error status.
2487             ts.skip_delim (ds);
2488             if (ds.no_progress ())
2489               {
2490                 retval = 4;
2491                 break;
2492               }
2493             already_skipped_delim = true;
2494 
2495             val = ts.empty_value.scalar_value ();
2496 
2497             if (! --max_empty)
2498               break;
2499           }
2500 
2501         if (val.imag () == 0)
2502           val_type = octave_value (NDArray (dv, val.real ()));
2503         else
2504           val_type = octave_value (ComplexNDArray (dv, val));
2505 
2506         output_container.push_back (val_type);
2507 
2508         if (! already_skipped_delim)
2509           ts.skip_delim (ds);
2510 
2511         if (ds.no_progress ())
2512           break;
2513 
2514         nconv++;
2515       }
2516 
2517     output_container.pop_front (); // discard empty element from constructor
2518 
2519     // Create fmt_list now that the size is known
2520     for (octave_idx_type i = 1; i < nconv; i++)
2521       fmt_elts.push_back (new textscan_format_elt (*fmt_elts[0]));
2522 
2523     return retval;             // May have returned 4 above.
2524   }
2525 
textscan(const std::string & who_arg,const std::string & encoding)2526   textscan::textscan (const std::string& who_arg, const std::string& encoding)
2527     : who (who_arg), m_encoding (encoding), buf (), whitespace_table (),
2528       delim_table (), delims (), comment_style (), comment_len (0),
2529       comment_char (-2), buffer_size (0), date_locale (),
2530       inf_nan (init_inf_nan ()), empty_value (numeric_limits<double>::NaN ()),
2531       exp_chars ("edED"), header_lines (0), treat_as_empty (),
2532       treat_as_empty_len (0), whitespace (" \b\t"), eol1 ('\r'), eol2 ('\n'),
2533       return_on_error (1), collect_output (false),
2534       multiple_delims_as_one (false), default_exp (true), lines (0)
2535   { }
2536 
2537   octave_value
scan(std::istream & isp,const std::string & fmt,octave_idx_type ntimes,const octave_value_list & options,octave_idx_type & count)2538   textscan::scan (std::istream& isp, const std::string& fmt,
2539                   octave_idx_type ntimes, const octave_value_list& options,
2540                   octave_idx_type& count)
2541   {
2542     textscan_format_list fmt_list (fmt);
2543 
2544     parse_options (options, fmt_list);
2545 
2546     octave_value result = do_scan (isp, fmt_list, ntimes);
2547 
2548     // FIXME: this is probably not the best way to get count.  The
2549     // position could easily be larger than octave_idx_type when using
2550     // 32-bit indexing.
2551 
2552     std::ios::iostate state = isp.rdstate ();
2553     isp.clear ();
2554     count = static_cast<octave_idx_type> (isp.tellg ());
2555     isp.setstate (state);
2556 
2557     return result;
2558   }
2559 
2560   octave_value
do_scan(std::istream & isp,textscan_format_list & fmt_list,octave_idx_type ntimes)2561   textscan::do_scan (std::istream& isp, textscan_format_list& fmt_list,
2562                      octave_idx_type ntimes)
2563   {
2564     octave_value retval;
2565 
2566     if (fmt_list.num_conversions () == -1)
2567       error ("%s: invalid format specified", who.c_str ());
2568 
2569     if (fmt_list.num_conversions () == 0)
2570       error ("%s: no valid format conversion specifiers", who.c_str ());
2571 
2572     // skip the first header_lines
2573     std::string dummy;
2574     for (int i = 0; i < header_lines && isp; i++)
2575       getline (isp, dummy, static_cast<char> (eol2));
2576 
2577     // Create our own buffered stream, for fast get/putback/tell/seek.
2578 
2579     // First, see how far ahead it should let us look.
2580     int max_lookahead = std::max (std::max (comment_len, treat_as_empty_len),
2581                                   std::max (delim_len, 3)); // 3 for NaN and Inf
2582 
2583     // Next, choose a buffer size to avoid reading too much, or too often.
2584     octave_idx_type buf_size = 4096;
2585     if (buffer_size)
2586       buf_size = buffer_size;
2587     else if (ntimes > 0)
2588       {
2589         // Avoid overflow of 80*ntimes...
2590         buf_size = std::min (buf_size, std::max (ntimes, 80 * ntimes));
2591         buf_size = std::max (buf_size, ntimes);
2592       }
2593     // Finally, create the stream.
2594     delimited_stream is (isp,
2595                          (delim_table.empty () ? whitespace + "\r\n" : delims),
2596                          max_lookahead, buf_size);
2597 
2598     // Grow retval dynamically.  "size" is half the initial size
2599     // (FIXME: Should we start smaller if ntimes is large?)
2600     octave_idx_type size = ((ntimes < 8 && ntimes >= 0) ? ntimes : 1);
2601     Array<octave_idx_type> row_idx (dim_vector (1,2));
2602     row_idx(1) = 0;
2603 
2604     int err = 0;
2605     octave_idx_type row = 0;
2606 
2607     if (multiple_delims_as_one)           // bug #44750?
2608       skip_delim (is);
2609 
2610     int done_after;  // Number of columns read when EOF seen.
2611 
2612     // If FORMAT explicitly "", read first line and see how many "%f" match
2613     if (fmt_list.set_from_first)
2614       {
2615         err = fmt_list.read_first_row (is, *this);
2616         lines = 1;
2617 
2618         done_after = fmt_list.numel () + 1;
2619         if (! err)
2620           row = 1;  // the above puts the first line into fmt_list.out_buf ()
2621       }
2622     else
2623       done_after = fmt_list.out_buf ().size () + 1;
2624 
2625     std::list<octave_value> out = fmt_list.out_buf ();
2626 
2627     // We will later merge adjacent columns of the same type.
2628     // Check now which columns to merge.
2629     // Reals may become complex, and so we can't trust types
2630     // after reading in data.
2631     // If the format was "", that conversion may already have happened,
2632     // so force all to be merged (as all are %f).
2633     bool merge_with_prev[fmt_list.numel ()];
2634     int conv = 0;
2635     if (collect_output)
2636       {
2637         int prev_type = -1;
2638         for (const auto& col : out)
2639           {
2640             if (col.type_id () == prev_type
2641                 || (fmt_list.set_from_first && prev_type != -1))
2642               merge_with_prev[conv++] = true;
2643             else
2644               merge_with_prev[conv++] = false;
2645 
2646             prev_type = col.type_id ();
2647           }
2648       }
2649 
2650     // This should be caught by earlier code, but this avoids a possible
2651     // infinite loop below.
2652     if (fmt_list.num_conversions () == 0)
2653       error ("%s: No conversions specified", who.c_str ());
2654 
2655     // Read the data.  This is the main loop.
2656     if (! err)
2657       {
2658         for (/* row set ~30 lines above */; row < ntimes || ntimes == -1; row++)
2659           {
2660             if (row == 0 || row >= size)
2661               {
2662                 size += (size+1);
2663                 for (auto& col : out)
2664                   col = col.resize (dim_vector (size, 1), 0);
2665               }
2666 
2667             row_idx(0) = row;
2668             err = read_format_once (is, fmt_list, out, row_idx, done_after);
2669 
2670             if ((err & ~1) > 0 || ! is || (lines >= ntimes && ntimes > -1))
2671               break;
2672           }
2673       }
2674 
2675     if ((err & 4) && ! return_on_error)
2676       error ("%s: Read error in field %d of row %" OCTAVE_IDX_TYPE_FORMAT,
2677              who.c_str (), done_after + 1, row + 1);
2678 
2679     // If file does not end in EOL, do not pad columns with NaN.
2680     bool uneven_columns = false;
2681     if (err & 4)
2682       uneven_columns = true;
2683     else if (isp.eof ())
2684       {
2685         isp.clear ();
2686         isp.seekg (-1, std::ios_base::end);
2687         int last_char = isp.get ();
2688         isp.setstate (isp.eofbit);
2689         uneven_columns = (last_char != eol1 && last_char != eol2);
2690       }
2691 
2692     // convert return value to Cell array
2693     Array<octave_idx_type> ra_idx (dim_vector (1,2));
2694 
2695     // (err & 1) means "error, and no columns read this row
2696     // FIXME: This may redundant now that done_after=0 says the same
2697     if (err & 1)
2698       done_after = out.size () + 1;
2699 
2700     int valid_rows = (row == ntimes
2701                       ? ntimes
2702                       : ((err & 1) && (err & 8)) ? row : row+1);
2703     dim_vector dv (valid_rows, 1);
2704 
2705     ra_idx(0) = 0;
2706     int i = 0;
2707     if (! collect_output)
2708       {
2709         retval = Cell (dim_vector (1, out.size ()));
2710         for (auto& col : out)
2711           {
2712             // trim last columns if that was requested
2713             if (i == done_after && uneven_columns)
2714               dv = dim_vector (std::max (valid_rows - 1, 0), 1);
2715 
2716             ra_idx(1) = i;
2717             retval = do_cat_op (retval, octave_value (Cell (col.resize (dv,0))),
2718                                 ra_idx);
2719             i++;
2720           }
2721       }
2722     else  // group adjacent cells of the same type into a single cell
2723       {
2724         octave_value cur;                // current cell, accumulating columns
2725         octave_idx_type group_size = 0;  // columns in this cell
2726         int prev_type = -1;
2727 
2728         conv = 0;
2729         retval = Cell ();
2730         for (auto& col : out)
2731           {
2732             if (! merge_with_prev[conv++])  // including first time
2733               {
2734                 if (prev_type != -1)
2735                   {
2736                     ra_idx(1) = i++;
2737                     retval = do_cat_op (retval, octave_value (Cell (cur)),
2738                                         ra_idx);
2739                   }
2740                 cur = octave_value (col.resize (dv,0));
2741                 group_size = 1;
2742                 prev_type = col.type_id ();
2743               }
2744             else
2745               {
2746                 ra_idx(1) = group_size++;
2747                 cur = do_cat_op (cur, octave_value (col.resize (dv,0)),
2748                                  ra_idx);
2749               }
2750           }
2751         ra_idx(1) = i;
2752         retval = do_cat_op (retval, octave_value (Cell (cur)), ra_idx);
2753       }
2754 
2755     return retval;
2756   }
2757 
2758   // Read a double considering the "precision" field of FMT and the
2759   // EXP_CHARS option of OPTIONS.
2760 
2761   double
read_double(delimited_stream & is,const textscan_format_elt & fmt) const2762   textscan::read_double (delimited_stream& is,
2763                          const textscan_format_elt& fmt) const
2764   {
2765     int sign = 1;
2766     unsigned int width_left = fmt.width;
2767     double retval = 0;
2768     bool valid = false;         // syntactically correct double?
2769 
2770     int ch = is.peek ();
2771 
2772     if (ch == '+')
2773       {
2774         is.get ();
2775         ch = is.peek ();
2776         if (width_left)
2777           width_left--;
2778       }
2779     else if (ch == '-')
2780       {
2781         sign = -1;
2782         is.get ();
2783         ch = is.peek ();
2784         if (width_left)
2785           width_left--;
2786       }
2787 
2788     // Read integer part
2789     if (ch != '.')
2790       {
2791         if (ch >= '0' && ch <= '9')       // valid if at least one digit
2792           valid = true;
2793         while (width_left-- && is && (ch = is.get ()) >= '0' && ch <= '9')
2794           retval = retval * 10 + (ch - '0');
2795         width_left++;
2796       }
2797 
2798     // Read fractional part, up to specified precision
2799     if (ch == '.' && width_left)
2800       {
2801         double multiplier = 1;
2802         int precision = fmt.prec;
2803         int i;
2804 
2805         width_left--;                  // Consider width of '.'
2806 
2807         if (precision == -1)
2808           precision = 1<<30;           // FIXME: Should be MAXINT
2809 
2810         if (! valid)                   // if there was nothing before '.'...
2811           is.get ();                   // ...ch was a "peek", not "get".
2812 
2813         for (i = 0; i < precision; i++)
2814           {
2815             if (width_left-- && is && (ch = is.get ()) >= '0' && ch <= '9')
2816               retval += (ch - '0') * (multiplier *= 0.1);
2817             else
2818               {
2819                 width_left++;
2820                 break;
2821               }
2822           }
2823 
2824         // round up if we truncated and the next digit is >= 5
2825         if ((i == precision || ! width_left) && (ch = is.get ()) >= '5'
2826             && ch <= '9')
2827           retval += multiplier;
2828 
2829         if (i > 0)
2830           valid = true;           // valid if at least one digit after '.'
2831 
2832         // skip remainder after '.', to field width, to look for exponent
2833         if (i == precision)
2834           while (width_left-- && is && (ch = is.get ()) >= '0' && ch <= '9')
2835             ;  // discard
2836 
2837         width_left++;
2838       }
2839 
2840     // look for exponent part in, e.g.,  6.023E+23
2841     bool used_exp = false;
2842     if (valid && width_left > 1 && exp_chars.find (ch) != std::string::npos)
2843       {
2844         int ch1 = is.peek ();
2845         if (ch1 == '-' || ch1 == '+' || (ch1 >= '0' && ch1 <= '9'))
2846           {
2847             // if 1.0e+$ or some such, this will set failbit, as we want
2848             width_left--;                         // count "E"
2849             int exp = 0;
2850             int exp_sign = 1;
2851             if (ch1 == '+')
2852               {
2853                 width_left--;
2854                 is.get ();
2855               }
2856             else if (ch1 == '-')
2857               {
2858                 width_left--;
2859                 exp_sign = -1;
2860                 is.get ();
2861               }
2862             valid = false;
2863             while (width_left-- && is && (ch = is.get ()) >= '0' && ch <= '9')
2864               {
2865                 exp = exp*10 + ch - '0';
2866                 valid = true;
2867               }
2868             width_left++;
2869             if (ch != std::istream::traits_type::eof () && width_left)
2870               is.putback (ch);
2871 
2872             double multiplier = pown (10, exp);
2873             if (exp_sign > 0)
2874               retval *= multiplier;
2875             else
2876               retval /= multiplier;
2877 
2878             used_exp = true;
2879           }
2880       }
2881     is.clear ();
2882     if (! used_exp && ch != std::istream::traits_type::eof () && width_left)
2883       is.putback (ch);
2884 
2885     // Check for +/- inf and NaN
2886     if (! valid && width_left >= 3)
2887       {
2888         int i = lookahead (is, inf_nan, 3, false);   // false -> case insensitive
2889         if (i == 0)
2890           {
2891             retval = numeric_limits<double>::Inf ();
2892             valid = true;
2893           }
2894         else if (i == 1)
2895           {
2896             retval = numeric_limits<double>::NaN ();
2897             valid = true;
2898           }
2899       }
2900 
2901     if (! valid)
2902       is.setstate (std::ios::failbit);
2903     else
2904       is.setstate (is.rdstate () & ~std::ios::failbit);
2905 
2906     return retval * sign;
2907   }
2908 
2909   // Read a single number: real, complex, inf, NaN, possibly with limited
2910   // precision.  Calls to this should be preceded by skip_whitespace.
2911   // Calling that inside scan_complex would violate its const declaration.
2912 
2913   void
scan_complex(delimited_stream & is,const textscan_format_elt & fmt,Complex & val) const2914   textscan::scan_complex (delimited_stream& is, const textscan_format_elt& fmt,
2915                           Complex& val) const
2916   {
2917     double im = 0;
2918     double re = 0;
2919     bool as_empty = false;   // did we fail but match a "treat_as_empty" string?
2920     bool inf = false;
2921 
2922     int ch = is.peek ();
2923     if (ch == '+' || ch == '-')   // check for [+-][ij] with no coefficients
2924       {
2925         ch = is.get ();
2926         int ch2 = is.peek ();
2927         if (ch2 == 'i' || ch2 == 'j')
2928           {
2929             double value = 1;
2930             is.get ();
2931             // Check not -inf
2932             if (is.peek () == 'n')
2933               {
2934                 char *pos = is.tellg ();
2935                 std::ios::iostate state = is.rdstate ();
2936 
2937                 is.get ();
2938                 ch2 = is.get ();
2939                 if (ch2 == 'f')
2940                   {
2941                     inf = true;
2942                     re = (ch == '+' ? numeric_limits<double>::Inf ()
2943                                     : -numeric_limits<double>::Inf ());
2944                     value = 0;
2945                   }
2946                 else
2947                   {
2948                     is.clear (state);
2949                     is.seekg (pos);   // reset to position before look-ahead
2950                   }
2951               }
2952 
2953             im = (ch == '+') ? value : -value;
2954           }
2955         else
2956           is.putback (ch);
2957       }
2958 
2959     if (! im && ! inf)        // if not [+-][ij] or [+-]inf, read real normally
2960       {
2961         char *pos = is.tellg ();
2962         std::ios::iostate state = is.rdstate ();
2963         //re = octave_read_value<double> (is);
2964         re = read_double (is, fmt);
2965 
2966         // check for "treat as empty" string
2967         if (treat_as_empty.numel ()
2968             && (is.fail () || math::is_NaN_or_NA (Complex (re))
2969                 || re == numeric_limits<double>::Inf ()))
2970           {
2971 
2972             for (int i = 0; i < treat_as_empty.numel (); i++)
2973               {
2974                 if (ch == treat_as_empty (i).string_value ()[0])
2975                   {
2976                     as_empty = true;   // first char matches, so read the lot
2977                     break;
2978                   }
2979               }
2980             if (as_empty)              // if first char matched...
2981               {
2982                 as_empty = false;      // ...look for the whole string
2983 
2984                 is.clear (state);      // treat_as_empty "-" causes partial read
2985                 is.seekg (pos);        // reset to position before failed read
2986 
2987                 // treat_as_empty strings may be different sizes.
2988                 // Read ahead longest, put it all back, then re-read the string
2989                 // that matches.
2990                 std::string look_buf (treat_as_empty_len, '\0');
2991                 char *look = is.read (&look_buf[0], look_buf.size (), pos);
2992 
2993                 is.clear (state);
2994                 is.seekg (pos);        // reset to position before look-ahead
2995                                        // FIXME: is.read could invalidate pos
2996 
2997                 for (int i = 0; i < treat_as_empty.numel (); i++)
2998                   {
2999                     std::string s = treat_as_empty (i).string_value ();
3000                     if (! strncmp (s.c_str (), look, s.size ()))
3001                       {
3002                         as_empty = true;
3003                         // read just the right amount
3004                         is.read (&look_buf[0], s.size (), pos);
3005                         break;
3006                       }
3007                   }
3008               }
3009           }
3010 
3011         if (! is.eof () && ! as_empty)
3012           {
3013             state = is.rdstate ();   // before tellg, since that fails at EOF
3014 
3015             ch = is.peek ();   // ch == EOF if read failed; no need to chk fail
3016             if (ch == 'i' || ch == 'j')           // pure imaginary
3017               {
3018                 is.get ();
3019                 im = re;
3020                 re = 0;
3021               }
3022             else if (ch == '+' || ch == '-')      // see if it is real+imag[ij]
3023               {
3024                 // save stream state in case we have to restore it
3025                 pos   = is.tellg ();
3026                 state = is.rdstate ();
3027 
3028                 //im = octave_read_value<double> (is);
3029                 im = read_double (is, fmt);
3030                 if (is.fail ())
3031                   im = 1;
3032 
3033                 if (is.peek () == 'i' || is.peek () == 'j')
3034                   is.get ();
3035                 else
3036                   {
3037                     im = 0;           // no valid imaginary part.  Restore state
3038                     is.clear (state); // eof shouldn't cause fail.
3039                     is.seekg (pos);
3040                   }
3041               }
3042             else if (is.eof ())       // we've read enough to be a "valid" read
3043               is.clear (state);       // failed peek shouldn't cause fail
3044           }
3045       }
3046     if (as_empty)
3047       val = empty_value.scalar_value ();
3048     else
3049       val = Complex (re, im);
3050   }
3051 
3052   // Return in VAL the run of characters from IS NOT contained in PATTERN.
3053 
3054   int
scan_caret(delimited_stream & is,const std::string & pattern,std::string & val) const3055   textscan::scan_caret (delimited_stream& is, const std::string& pattern,
3056                         std::string& val) const
3057   {
3058     int c1 = std::istream::traits_type::eof ();
3059     std::ostringstream obuf;   // FIXME: is this optimized for growing?
3060 
3061     while (is && ((c1 = (is && ! is.eof ())
3062                    ? is.get_undelim ()
3063                    : std::istream::traits_type::eof ())
3064                   != std::istream::traits_type::eof ())
3065            && pattern.find (c1) == std::string::npos)
3066       obuf << static_cast<char> (c1);
3067 
3068     val = obuf.str ();
3069 
3070     if (c1 != std::istream::traits_type::eof ())
3071       is.putback (c1);
3072 
3073     return c1;
3074   }
3075 
3076   // Read until one of the strings in DELIMITERS is found.  For
3077   // efficiency, ENDS is a list of the last character of each delimiter.
3078 
3079   std::string
read_until(delimited_stream & is,const Cell & delimiters,const std::string & ends) const3080   textscan::read_until (delimited_stream& is, const Cell& delimiters,
3081                         const std::string& ends) const
3082   {
3083     std::string retval ("");
3084     bool done = false;
3085     do
3086       {
3087         // find sequence ending with an ending char
3088         std::string next;
3089         scan_caret (is, ends.c_str (), next);
3090         retval = retval + next;   // FIXME: could use repeated doubling of size
3091 
3092         int last = (! is.eof ()
3093                     ? is.get_undelim () : std::istream::traits_type::eof ());
3094 
3095         if (last != std::istream::traits_type::eof ())
3096           {
3097             if (last == eol1 || last == eol2)
3098               break;
3099 
3100             retval = retval + static_cast<char> (last);
3101             for (int i = 0; i < delimiters.numel (); i++)
3102               {
3103                 std::string delim = delimiters(i).string_value ();
3104                 std::size_t start = (retval.length () > delim.length ()
3105                                 ? retval.length () - delim.length ()
3106                                 : 0);
3107                 std::string may_match = retval.substr (start);
3108                 if (may_match == delim)
3109                   {
3110                     done = true;
3111                     retval = retval.substr (0, start);
3112                     if (start == 0)
3113                       is.putback (last);
3114                     break;
3115                   }
3116               }
3117           }
3118       }
3119     while (! done && is && ! is.eof ());
3120 
3121     return retval;
3122   }
3123 
3124   // Read stream until either fmt.width chars have been read, or
3125   // options.delimiter has been found.  Does *not* rely on fmt being 's'.
3126   // Used by formats like %6f to limit to 6.
3127 
3128   void
scan_string(delimited_stream & is,const textscan_format_elt & fmt,std::string & val) const3129   textscan::scan_string (delimited_stream& is, const textscan_format_elt& fmt,
3130                          std::string& val) const
3131   {
3132     if (delim_list.isempty ())
3133       {
3134         unsigned int i = 0;
3135         unsigned int width = fmt.width;
3136 
3137         for (i = 0; i < width; i++)
3138           {
3139             // Grow string in an exponential fashion if necessary.
3140             if (i >= val.length ())
3141               val.append (std::max (val.length (),
3142                                     static_cast<std::size_t> (16)), '\0');
3143 
3144             int ch = is.get ();
3145             if (is_delim (ch) || ch == std::istream::traits_type::eof ())
3146               {
3147                 is.putback (ch);
3148                 break;
3149               }
3150             else
3151               val[i] = ch;
3152           }
3153         val = val.substr (0, i);          // trim pre-allocation
3154       }
3155     else  // Cell array of multi-character delimiters
3156       {
3157         std::string ends (delim_list.numel () + 2, '\0');
3158         int i;
3159         for (i = 0; i < delim_list.numel (); i++)
3160           {
3161             std::string tmp = delim_list(i).string_value ();
3162             ends[i] = tmp.back ();
3163           }
3164         ends[i++] = eol1;
3165         ends[i++] = eol2;
3166         val = textscan::read_until (is, delim_list, ends);
3167       }
3168 
3169     // convert from codepage
3170     if (m_encoding.compare ("utf-8"))
3171       val = string::u8_from_encoding ("textscan", val, m_encoding);
3172   }
3173 
3174   // Return in VAL the run of characters from IS contained in PATTERN.
3175 
3176   int
scan_bracket(delimited_stream & is,const std::string & pattern,std::string & val) const3177   textscan::scan_bracket (delimited_stream& is, const std::string& pattern,
3178                           std::string& val) const
3179   {
3180     int c1 = std::istream::traits_type::eof ();
3181     std::ostringstream obuf;              // Is this optimized for growing?
3182 
3183     while (is && pattern.find (c1 = is.get_undelim ()) != std::string::npos)
3184       obuf << static_cast<char> (c1);
3185 
3186     val = obuf.str ();
3187     if (c1 != std::istream::traits_type::eof ())
3188       is.putback (c1);
3189     return c1;
3190   }
3191 
3192   // Return in VAL a string, either delimited by whitespace/delimiters, or
3193   // enclosed in a pair of double quotes ("...").  Enclosing quotes are
3194   // removed.  A consecutive pair "" is inserted into VAL as a single ".
3195 
3196   void
scan_qstring(delimited_stream & is,const textscan_format_elt & fmt,std::string & val)3197   textscan::scan_qstring (delimited_stream& is, const textscan_format_elt& fmt,
3198                           std::string& val)
3199   {
3200     skip_whitespace (is);
3201 
3202     if (is.peek () != '"')
3203       scan_string (is, fmt, val);
3204     else
3205       {
3206         is.get ();
3207         scan_caret (is, R"(")", val);     // read everything until "
3208         is.get ();                        // swallow "
3209 
3210         while (is && is.peek_undelim () == '"')  // if double ",
3211           {                                      // insert one in stream,
3212             is.get ();                           // keep looking for single "
3213             std::string val1;
3214             scan_caret (is, R"(")", val1);
3215             val = val + '"' + val1;
3216             is.get_undelim ();
3217           }
3218       }
3219 
3220     // convert from codepage
3221     if (m_encoding.compare ("utf-8"))
3222       val = string::u8_from_encoding ("textscan", val, m_encoding);
3223   }
3224 
3225   // Read from IS into VAL a string of the next fmt.width characters,
3226   // including any whitespace or delimiters.
3227 
3228   void
3229   textscan::scan_cstring (delimited_stream& is, const textscan_format_elt& fmt,
3230                           std::string& val) const
3231   {
3232     val.resize (fmt.width);
3233 
3234     for (unsigned int i = 0; is && i < fmt.width; i++)
3235       {
3236         int ch = is.get_undelim ();
3237         if (ch != std::istream::traits_type::eof ())
3238           val[i] = ch;
3239         else
3240           {
3241             val.resize (i);
3242             break;
3243           }
3244       }
3245 
3246     // convert from codepage
3247     if (m_encoding.compare ("utf-8"))
3248       val = string::u8_from_encoding ("textscan", val, m_encoding);
3249   }
3250 
3251   //  Read a single '%...' conversion and place it in position ROW of OV.
3252 
3253   void
3254   textscan::scan_one (delimited_stream& is, const textscan_format_elt& fmt,
3255                       octave_value& ov, Array<octave_idx_type> row)
3256   {
3257     skip_whitespace (is);
3258 
3259     is.clear ();
3260 
3261     octave_value val;
3262     if (fmt.numeric)
3263       {
3264         if (fmt.type == 'f' || fmt.type == 'n')
3265           {
3266             Complex v;
3267             skip_whitespace (is);
3268             scan_complex (is, fmt, v);
3269 
3270             if (! fmt.discard && ! is.fail ())
3271               {
3272                 if (fmt.bitwidth == 64)
3273                   {
3274                     if (ov.isreal () && v.imag () == 0)
3275                       ov.internal_rep ()->fast_elem_insert (row(0), v.real ());
3276                     else
3277                       {
3278                         if (ov.isreal ())  // cat does type conversion
3279                           ov = do_cat_op (ov, octave_value (v), row);
3280                         else
3281                           ov.internal_rep ()->fast_elem_insert (row(0), v);
3282                       }
3283                   }
3284                 else
3285                   {
3286                     if (ov.isreal () && v.imag () == 0)
3287                       ov.internal_rep ()->fast_elem_insert (row(0),
3288                                                             float (v.real ()));
3289                     else
3290                       {
3291                         if (ov.isreal ())  // cat does type conversion
3292                           ov = do_cat_op (ov, octave_value (v), row);
3293                         else
3294                           ov.internal_rep ()->fast_elem_insert (row(0),
3295                                                                 FloatComplex (v));
3296                       }
3297                   }
3298               }
3299           }
3300         else
3301           {
3302             double v;    // Matlab docs say 1e30 etc should be valid for %d and
3303                          // 1000 as a %d8 should be 127, so read as double.
3304                          // Some loss of precision for d64 and u64.
3305             skip_whitespace (is);
3306             v = read_double (is, fmt);
3307             if (! fmt.discard && ! is.fail ())
3308               switch (fmt.bitwidth)
3309                 {
3310                 case 64:
3311                   switch (fmt.type)
3312                     {
3313                     case 'd':
3314                       {
3315                         octave_int64 vv = v;
3316                         ov.internal_rep ()->fast_elem_insert (row(0), vv);
3317                       }
3318                       break;
3319 
3320                     case 'u':
3321                       {
3322                         octave_uint64 vv = v;
3323                         ov.internal_rep ()->fast_elem_insert (row(0), vv);
3324                       }
3325                       break;
3326                     }
3327                   break;
3328 
3329                 case 32:
3330                   switch (fmt.type)
3331                     {
3332                     case 'd':
3333                       {
3334                         octave_int32 vv = v;
3335                         ov.internal_rep ()->fast_elem_insert (row(0), vv);
3336                       }
3337                       break;
3338 
3339                     case 'u':
3340                       {
3341                         octave_uint32 vv = v;
3342                         ov.internal_rep ()->fast_elem_insert (row(0), vv);
3343                       }
3344                       break;
3345                     }
3346                   break;
3347 
3348                 case 16:
3349                   if (fmt.type == 'd')
3350                     {
3351                       octave_int16 vv = v;
3352                       ov.internal_rep ()->fast_elem_insert (row(0), vv);
3353                     }
3354                   else
3355                     {
3356                       octave_uint16 vv = v;
3357                       ov.internal_rep ()->fast_elem_insert (row(0), vv);
3358                     }
3359                   break;
3360 
3361                 case 8:
3362                   if (fmt.type == 'd')
3363                     {
3364                       octave_int8 vv = v;
3365                       ov.internal_rep ()->fast_elem_insert (row(0), vv);
3366                     }
3367                   else
3368                     {
3369                       octave_uint8 vv = v;
3370                       ov.internal_rep ()->fast_elem_insert (row(0), vv);
3371                     }
3372                   break;
3373                 }
3374           }
3375 
3376         if (is.fail () & ! fmt.discard)
3377           ov = do_cat_op (ov, empty_value, row);
3378       }
3379     else
3380       {
3381         std::string vv ("        ");      // initial buffer.  Grows as needed
3382         switch (fmt.type)
3383           {
3384           case 's':
3385             scan_string (is, fmt, vv);
3386             break;
3387 
3388           case 'q':
3389             scan_qstring (is, fmt, vv);
3390             break;
3391 
3392           case 'c':
3393             scan_cstring (is, fmt, vv);
3394             break;
3395 
3396           case '[':
3397             scan_bracket (is, fmt.char_class.c_str (), vv);
3398             break;
3399 
3400           case '^':
3401             scan_caret   (is, fmt.char_class.c_str (), vv);
3402             break;
3403           }
3404 
3405         if (! fmt.discard)
3406           ov.internal_rep ()->fast_elem_insert (row (0),
3407                                                 Cell (octave_value (vv)));
3408 
3409         // FIXME: why does failbit get set at EOF, instead of eofbit?
3410         if (! vv.empty ())
3411           is.clear (is.rdstate () & ~std::ios_base::failbit);
3412       }
3413 
3414     is.field_done ();
3415   }
3416 
3417   // Read data corresponding to the entire format string once, placing the
3418   // values in row ROW of retval.
3419 
3420   int
3421   textscan::read_format_once (delimited_stream& is,
3422                               textscan_format_list& fmt_list,
3423                               std::list<octave_value>& retval,
3424                               Array<octave_idx_type> row, int& done_after)
3425   {
3426     const textscan_format_elt *elem = fmt_list.first ();
3427     auto out = retval.begin ();
3428     bool no_conversions = true;
3429     bool done = false;
3430     bool conversion_failed = false;       // Record for ReturnOnError
3431     bool nothing_worked = true;
3432 
3433     octave_quit ();
3434 
3435     for (std::size_t i = 0; i < fmt_list.numel (); i++)
3436       {
3437         bool this_conversion_failed = false;
3438 
3439         // Clear fail of previous numeric conversions.
3440         is.clear ();
3441 
3442         switch (elem->type)
3443           {
3444           case 'C':
3445           case 'D':
3446             warning ("%s: conversion %c not yet implemented",
3447                      who.c_str (), elem->type);
3448             break;
3449 
3450           case 'u':
3451           case 'd':
3452           case 'f':
3453           case 'n':
3454           case 's':
3455           case '[':
3456           case '^':
3457           case 'q':
3458           case 'c':
3459             scan_one (is, *elem, *out, row);
3460             break;
3461 
3462           case textscan_format_elt::literal_conversion :
3463             match_literal (is, *elem);
3464             break;
3465 
3466           default:
3467             error ("Unknown format element '%c'", elem->type);
3468           }
3469 
3470         if (! is.fail ())
3471           {
3472             if (! elem->discard)
3473               no_conversions = false;
3474           }
3475         else
3476           {
3477             is.clear (is.rdstate () & ~std::ios::failbit);
3478 
3479             if (! is.eof ())
3480               {
3481                 if (delim_list.isempty ())
3482                   {
3483                     if (! is_delim (is.peek ()))
3484                       this_conversion_failed = true;
3485                   }
3486                 else  // Cell array of multi-character delimiters
3487                   {
3488                     char *pos = is.tellg ();
3489                     if (-1 == lookahead (is, delim_list, delim_len))
3490                       this_conversion_failed = true;
3491                     is.clear ();
3492                     is.seekg (pos);     // reset to position before look-ahead
3493                   }
3494               }
3495           }
3496 
3497         if (! elem->discard)
3498           out++;
3499 
3500         elem = fmt_list.next ();
3501         char *pos = is.tellg ();
3502 
3503         // FIXME: these conversions "ignore delimiters".  Should they include
3504         // delimiters at the start of the conversion, or can those be skipped?
3505         if (elem->type != textscan_format_elt::literal_conversion
3506             // && elem->type != '[' && elem->type != '^' && elem->type != 'c'
3507            )
3508           skip_delim (is);
3509 
3510         if (is.eof ())
3511           {
3512             if (! done)
3513               done_after = i+1;
3514 
3515             // note EOF, but process others to get empty_val.
3516             done = true;
3517           }
3518 
3519         if (this_conversion_failed)
3520           {
3521             if (is.tellg () == pos && ! conversion_failed)
3522               {
3523                 // done_after = first failure
3524                 done_after = i; // note fail, but parse others to get empty_val
3525                 conversion_failed = true;
3526               }
3527             else
3528               this_conversion_failed = false;
3529           }
3530         else if (! done && ! conversion_failed)
3531           nothing_worked = false;
3532       }
3533 
3534     if (done)
3535       is.setstate (std::ios::eofbit);
3536 
3537     return no_conversions
3538            + (is.eof () ? 2 : 0)
3539            + (conversion_failed ? 4 : 0)
3540            + (nothing_worked ? 8 : 0);
3541 
3542   }
3543 
3544   void
3545   textscan::parse_options (const octave_value_list& args,
3546                            textscan_format_list& fmt_list)
3547   {
3548     int last = args.length ();
3549     int n = last;
3550 
3551     if (n & 1)
3552       error ("%s: %d parameters given, but only %d values",
3553              who.c_str (), n-n/2, n/2);
3554 
3555     delim_len = 1;
3556     bool have_delims = false;
3557     for (int i = 0; i < last; i += 2)
3558       {
3559         std::string param = args(i).xstring_value ("%s: Invalid parameter type <%s> for parameter %d",
3560                                                    who.c_str (),
3561                                                    args(i).type_name ().c_str (),
3562                                                    i/2 + 1);
3563         std::transform (param.begin (), param.end (), param.begin (), ::tolower);
3564 
3565         if (param == "delimiter")
3566           {
3567             bool invalid = true;
3568             if (args(i+1).is_string ())
3569               {
3570                 invalid = false;
3571                 have_delims = true;
3572                 delims = args(i+1).string_value ();
3573                 if (args(i+1).is_sq_string ())
3574                   delims = do_string_escapes (delims);
3575               }
3576             else if (args(i+1).iscell ())
3577               {
3578                 invalid = false;
3579                 delim_list = args(i+1).cell_value ();
3580                 delim_table = " "; // non-empty, to flag non-default delim
3581 
3582                 // Check that all elements are strings, and find max length
3583                 for (int j = 0; j < delim_list.numel (); j++)
3584                   {
3585                     if (! delim_list(j).is_string ())
3586                       invalid = true;
3587                     else
3588                       {
3589                         if (delim_list(j).is_sq_string ())
3590                           delim_list(j) = do_string_escapes (delim_list(j)
3591                                                              .string_value ());
3592                         octave_idx_type len = delim_list(j).string_value ()
3593                                               .length ();
3594                         delim_len = std::max (static_cast<int> (len), delim_len);
3595                       }
3596                   }
3597               }
3598             if (invalid)
3599               error ("%s: Delimiters must be either a string or cell array of strings",
3600                      who.c_str ());
3601           }
3602         else if (param == "commentstyle")
3603           {
3604             if (args(i+1).is_string ())
3605               {
3606                 // check here for names like "C++", "C", "shell", ...?
3607                 comment_style = Cell (args(i+1));
3608               }
3609             else if (args(i+1).iscell ())
3610               {
3611                 comment_style = args(i+1).cell_value ();
3612                 int len = comment_style.numel ();
3613                 if ((len >= 1 && ! comment_style (0).is_string ())
3614                     || (len >= 2 && ! comment_style (1).is_string ())
3615                     || (len >= 3))
3616                   error ("%s: CommentStyle must be either a string or cell array of one or two strings",
3617                          who.c_str ());
3618               }
3619             else
3620               error ("%s: CommentStyle must be either a string or cell array of one or two strings",
3621                      who.c_str ());
3622 
3623             // How far ahead do we need to look to detect an open comment
3624             // and which character do we look for?
3625             if (comment_style.numel () >= 1)
3626               {
3627                 comment_len  = comment_style (0).string_value ().size ();
3628                 comment_char = comment_style (0).string_value ()[0];
3629               }
3630           }
3631         else if (param == "treatasempty")
3632           {
3633             bool invalid = false;
3634             if (args(i+1).is_string ())
3635               {
3636                 treat_as_empty = Cell (args(i+1));
3637                 treat_as_empty_len = args(i+1).string_value ().size ();
3638               }
3639             else if (args(i+1).iscell ())
3640               {
3641                 treat_as_empty = args(i+1).cell_value ();
3642                 for (int j = 0; j < treat_as_empty.numel (); j++)
3643                   if (! treat_as_empty (j).is_string ())
3644                     invalid = true;
3645                   else
3646                     {
3647                       int k = treat_as_empty (j).string_value ().size ();
3648                       if (k > treat_as_empty_len)
3649                         treat_as_empty_len = k;
3650                     }
3651               }
3652             if (invalid)
3653               error ("%s: TreatAsEmpty must be either a string or cell array of one or two strings",
3654                      who.c_str ());
3655 
3656             // FIXME: Ensure none is a prefix of a later one.  Sort by length?
3657           }
3658         else if (param == "collectoutput")
3659           {
3660             collect_output = args(i+1).xbool_value ("%s: CollectOutput must be logical or numeric", who.c_str ());
3661           }
3662         else if (param == "emptyvalue")
3663           {
3664             empty_value = args(i+1).xscalar_value ("%s: EmptyValue must be numeric", who.c_str ());
3665           }
3666         else if (param == "headerlines")
3667           {
3668             header_lines = args(i+1).xscalar_value ("%s: HeaderLines must be numeric", who.c_str ());
3669           }
3670         else if (param == "bufsize")
3671           {
3672             buffer_size = args(i+1).xscalar_value ("%s: BufSize must be numeric", who.c_str ());
3673           }
3674         else if (param == "multipledelimsasone")
3675           {
3676             multiple_delims_as_one = args(i+1).xbool_value ("%s: MultipleDelimsAsOne must be logical or numeric", who.c_str ());
3677           }
3678         else if (param == "returnonerror")
3679           {
3680             return_on_error = args(i+1).xbool_value ("%s: ReturnOnError must be logical or numeric", who.c_str ());
3681           }
3682         else if (param == "whitespace")
3683           {
3684             whitespace = args(i+1).xstring_value ("%s: Whitespace must be a character string", who.c_str ());
3685           }
3686         else if (param == "expchars")
3687           {
3688             exp_chars = args(i+1).xstring_value ("%s: ExpChars must be a character string", who.c_str ());
3689             default_exp = false;
3690           }
3691         else if (param == "endofline")
3692           {
3693             bool valid = true;
3694             std::string s = args(i+1).xstring_value (R"(%s: EndOfLine must be at most one character or '\r\n')", who.c_str ());
3695             if (args(i+1).is_sq_string ())
3696               s = do_string_escapes (s);
3697             int l = s.length ();
3698             if (l == 0)
3699               eol1 = eol2 = -2;
3700             else if (l == 1)
3701               eol1 = eol2 = s.c_str ()[0];
3702             else if (l == 2)
3703               {
3704                 eol1 = s.c_str ()[0];
3705                 eol2 = s.c_str ()[1];
3706                 if (eol1 != '\r' || eol2 != '\n')    // Why limit it?
3707                   valid = false;
3708               }
3709             else
3710               valid = false;
3711 
3712             if (! valid)
3713               error (R"(%s: EndOfLine must be at most one character or '\r\n')",
3714                      who.c_str ());
3715           }
3716         else
3717           error ("%s: unrecognized option '%s'", who.c_str (), param.c_str ());
3718       }
3719 
3720     // Remove any user-supplied delimiter from whitespace list
3721     for (unsigned int j = 0; j < delims.length (); j++)
3722       {
3723         whitespace.erase (std::remove (whitespace.begin (),
3724                                        whitespace.end (),
3725                                        delims[j]),
3726                           whitespace.end ());
3727       }
3728     for (int j = 0; j < delim_list.numel (); j++)
3729       {
3730         std::string delim = delim_list(j).string_value ();
3731         if (delim.length () == 1)
3732           whitespace.erase (std::remove (whitespace.begin (),
3733                                          whitespace.end (),
3734                                          delim[0]),
3735                             whitespace.end ());
3736       }
3737 
3738     whitespace_table = std::string (256, '\0');
3739     for (unsigned int i = 0; i < whitespace.length (); i++)
3740       whitespace_table[whitespace[i]] = '1';
3741 
3742     // For Matlab compatibility, add 0x20 to whitespace, unless
3743     // whitespace is explicitly ignored.
3744     if (! (whitespace.empty () && fmt_list.has_string))
3745       whitespace_table[' '] = '1';
3746 
3747     // Create look-up table of delimiters, based on 'delimiter'
3748     delim_table = std::string (256, '\0');
3749     if (eol1 >= 0 && eol1 < 256)
3750       delim_table[eol1] = '1';        // EOL is always a delimiter
3751     if (eol2 >= 0 && eol2 < 256)
3752       delim_table[eol2] = '1';        // EOL is always a delimiter
3753     if (! have_delims)
3754       for (unsigned int i = 0; i < 256; i++)
3755         {
3756           if (isspace (i))
3757             delim_table[i] = '1';
3758         }
3759     else
3760       for (unsigned int i = 0; i < delims.length (); i++)
3761         delim_table[delims[i]] = '1';
3762   }
3763 
3764   // Skip comments, and characters specified by the "Whitespace" option.
3765   // If EOLstop == true, don't skip end of line.
3766 
3767   int
3768   textscan::skip_whitespace (delimited_stream& is, bool EOLstop)
3769   {
3770     int c1 = std::istream::traits_type::eof ();
3771     bool found_comment = false;
3772 
3773     do
3774       {
3775         found_comment = false;
3776         int prev = -1;
3777         while (is && (c1 = is.get_undelim ()) != std::istream::traits_type::eof ()
3778                && ( ( (c1 == eol1 || c1 == eol2) && ++lines && ! EOLstop)
3779                     || isspace (c1)))
3780           {
3781             if (prev == eol1 && eol1 != eol2 && c1 == eol2)
3782               lines--;
3783             prev = c1;
3784           }
3785 
3786         if (c1 == comment_char)           // see if we match an open comment
3787           {
3788             // save stream state in case we have to restore it
3789             char *pos = is.tellg ();
3790             std::ios::iostate state = is.rdstate ();
3791 
3792             std::string tmp (comment_len, '\0');
3793             char *look = is.read (&tmp[0], comment_len-1, pos); // already read first char
3794             if (is && comment_style.numel () > 0
3795                 && ! strncmp (comment_style(0).string_value ().substr (1).c_str (),
3796                               look, comment_len-1))
3797               {
3798                 found_comment = true;
3799 
3800                 std::string dummy;
3801                 if (comment_style.numel () == 1)  // skip to end of line
3802                   {
3803                     std::string eol (3, '\0');
3804                     eol[0] = eol1;
3805                     eol[1] = eol2;
3806 
3807                     scan_caret (is, eol, dummy);
3808                     c1 = is.get_undelim ();
3809                     if (c1 == eol1 && eol1 != eol2 && is.peek_undelim () == eol2)
3810                       is.get_undelim ();
3811                     lines++;
3812                   }
3813                 else      // matching pair
3814                   {
3815                     std::string end_c = comment_style(1).string_value ();
3816                     // last char of end-comment sequence
3817                     std::string last = end_c.substr (end_c.size () - 1);
3818                     std::string may_match ("");
3819                     do
3820                       {
3821                         // find sequence ending with last char
3822                         scan_caret (is, last, dummy);
3823                         is.get_undelim ();        // (read LAST itself)
3824 
3825                         may_match = may_match + dummy + last;
3826                         if (may_match.length () > end_c.length ())
3827                           {
3828                             std::size_t start = may_match.length () - end_c.length ();
3829                             may_match = may_match.substr (start);
3830                           }
3831                       }
3832                     while (may_match != end_c && is && ! is.eof ());
3833                   }
3834               }
3835             else  // wasn't really a comment; restore state
3836               {
3837                 is.clear (state);
3838                 is.seekg (pos);
3839               }
3840           }
3841       }
3842     while (found_comment);
3843 
3844     if (c1 != std::istream::traits_type::eof ())
3845       is.putback (c1);
3846 
3847     return c1;
3848   }
3849 
3850   // See if the next few characters match one of the strings in target.
3851   // For efficiency, MAX_LEN is the cached longest length of any target.
3852   // Return -1 if none is found, or the index of the match.
3853 
3854   int
3855   textscan::lookahead (delimited_stream& is, const Cell& targets, int max_len,
3856                        bool case_sensitive) const
3857   {
3858     // target strings may be different sizes.
3859     // Read ahead longest, put it all back, then re-read the string
3860     // that matches.
3861 
3862     char *pos = is.tellg ();
3863 
3864     std::string tmp (max_len, '\0');
3865     char *look = is.read (&tmp[0], tmp.size (), pos);
3866 
3867     is.clear ();
3868     is.seekg (pos);              // reset to position before read
3869                                  // FIXME: pos may be corrupted by is.read
3870 
3871     int i;
3872     int (*compare)(const char *, const char *, std::size_t);
3873     compare = (case_sensitive ? strncmp : strncasecmp);
3874 
3875     for (i = 0; i < targets.numel (); i++)
3876       {
3877         std::string s = targets (i).string_value ();
3878         if (! (*compare) (s.c_str (), look, s.size ()))
3879           {
3880             is.read (&tmp[0], s.size (), pos); // read just the right amount
3881             break;
3882           }
3883       }
3884 
3885     if (i == targets.numel ())
3886       i = -1;
3887 
3888     return i;
3889   }
3890 
3891   // Skip delimiters -- multiple if MultipleDelimsAsOne specified.
3892   int
3893   textscan::skip_delim (delimited_stream& is)
3894   {
3895     int c1 = skip_whitespace (is, true);  // 'true': stop once EOL is read
3896     if (delim_list.numel () == 0)         // single character delimiter
3897       {
3898         if (is_delim (c1) || c1 == eol1 || c1 == eol2)
3899           {
3900             is.get ();
3901             if (c1 == eol1 && is.peek_undelim () == eol2)
3902               is.get_undelim ();          // if \r\n, skip the \n too.
3903 
3904             if (multiple_delims_as_one)
3905               {
3906                 int prev = -1;
3907                 // skip multiple delims.
3908                 // Increment lines for each end-of-line seen; for \r\n, decrement
3909                 while (is && ((c1 = is.get_undelim ())
3910                               != std::istream::traits_type::eof ())
3911                        && (((c1 == eol1 || c1 == eol2) && ++lines)
3912                            || isspace (c1) || is_delim (c1)))
3913                   {
3914                     if (prev == eol1 && eol1 != eol2 && c1 == eol2)
3915                       lines--;
3916                     prev = c1;
3917                   }
3918                 if (c1 != std::istream::traits_type::eof ())
3919                   is.putback (c1);
3920               }
3921           }
3922       }
3923     else                                  // multi-character delimiter
3924       {
3925         int first_match;
3926 
3927         if (c1 == eol1 || c1 == eol2
3928             || (-1 != (first_match = lookahead (is, delim_list, delim_len))))
3929           {
3930             if (c1 == eol1)
3931               {
3932                 is.get_undelim ();
3933                 if (is.peek_undelim () == eol2)
3934                   is.get_undelim ();
3935               }
3936             else if (c1 == eol2)
3937               {
3938                 is.get_undelim ();
3939               }
3940 
3941             if (multiple_delims_as_one)
3942               {
3943                 int prev = -1;
3944                 // skip multiple delims.
3945                 // Increment lines for each end-of-line seen; for \r\n, decrement
3946                 while (is && ((c1 = skip_whitespace (is, true))
3947                               != std::istream::traits_type::eof ())
3948                        && (((c1 == eol1 || c1 == eol2) && ++lines)
3949                            || -1 != lookahead (is, delim_list, delim_len)))
3950                   {
3951                     if (prev == eol1 && eol1 != eol2 && c1 == eol2)
3952                       lines--;
3953                     prev = c1;
3954                   }
3955               }
3956           }
3957       }
3958 
3959     return c1;
3960   }
3961 
3962   // Read in as much of the input as coincides with the literal in the
3963   // format string.  Return "true" if the entire literal is matched, else
3964   // false (and set failbit).
3965 
3966   bool
3967   textscan::match_literal (delimited_stream& is, const textscan_format_elt& fmt)
3968   {
3969     // "false" -> treat EOL as normal space
3970     // since a delimiter at the start of a line is a mismatch, not empty field
3971     skip_whitespace (is, false);
3972 
3973     for (unsigned int i = 0; i < fmt.width; i++)
3974       {
3975         int ch = is.get_undelim ();
3976         if (ch != fmt.text[i])
3977           {
3978             if (ch != std::istream::traits_type::eof ())
3979               is.putback (ch);
3980             is.setstate (std::ios::failbit);
3981             return false;
3982           }
3983       }
3984     return true;
3985   }
3986 
3987   void
3988   base_stream::error (const std::string& msg)
3989   {
3990     m_fail = true;
3991     m_errmsg = msg;
3992   }
3993 
3994   void
3995   base_stream::error (const std::string& who, const std::string& msg)
3996   {
3997     m_fail = true;
3998     m_errmsg = who + ": " + msg;
3999   }
4000 
4001   void
4002   base_stream::clear (void)
4003   {
4004     m_fail = false;
4005     m_errmsg = "";
4006   }
4007 
4008   void
4009   base_stream::clearerr (void)
4010   {
4011     std::istream *is = input_stream ();
4012     std::ostream *os = output_stream ();
4013 
4014     if (is)
4015       is->clear ();
4016 
4017     if (os)
4018       os->clear ();
4019   }
4020 
4021   // Functions that are defined for all input streams (input streams
4022   // are those that define is).
4023 
4024   std::string
4025   base_stream::do_gets (octave_idx_type max_len, bool& err,
4026                         bool strip_newline, const std::string& who)
4027   {
4028     interpreter& interp = __get_interpreter__ ("base_stream::do_gets");
4029 
4030     if (interp.interactive () && file_number () == 0)
4031       ::error ("%s: unable to read from stdin while running interactively",
4032                who.c_str ());
4033 
4034     std::string retval;
4035 
4036     err = false;
4037 
4038     std::istream *isp = input_stream ();
4039 
4040     if (! isp)
4041       {
4042         err = true;
4043         invalid_operation (who, "reading");
4044       }
4045     else
4046       {
4047         std::istream& is = *isp;
4048 
4049         std::ostringstream buf;
4050 
4051         int c = 0;
4052         int char_count = 0;
4053 
4054         if (max_len != 0)
4055           {
4056             while (is && (c = is.get ()) != std::istream::traits_type::eof ())
4057               {
4058                 char_count++;
4059 
4060                 // Handle CRLF, CR, or LF as line ending.
4061                 if (c == '\r')
4062                   {
4063                     if (! strip_newline)
4064                       buf << static_cast<char> (c);
4065 
4066                     c = is.get ();
4067 
4068                     if (c != std::istream::traits_type::eof ())
4069                       {
4070                         if (c == '\n')
4071                           {
4072                             char_count++;
4073 
4074                             if (! strip_newline)
4075                               buf << static_cast<char> (c);
4076                           }
4077                         else
4078                           is.putback (c);
4079                       }
4080 
4081                     break;
4082                   }
4083                 else if (c == '\n')
4084                   {
4085                     if (! strip_newline)
4086                       buf << static_cast<char> (c);
4087 
4088                     break;
4089                   }
4090                 else
4091                   buf << static_cast<char> (c);
4092 
4093                 if (max_len > 0 && char_count == max_len)
4094                   break;
4095               }
4096           }
4097 
4098         if (! is.eof () && char_count > 0)
4099           {
4100             // GAGME.  Matlab seems to check for EOF even if the last character
4101             // in a file is a newline character.  This is NOT what the
4102             // corresponding C-library functions do.
4103             int disgusting_compatibility_hack = is.get ();
4104             if (! is.eof ())
4105               is.putback (disgusting_compatibility_hack);
4106           }
4107 
4108         if (is.good () || (is.eof () && char_count > 0))
4109           {
4110             retval = buf.str ();
4111             if (encoding ().compare ("utf-8"))
4112               retval = string::u8_from_encoding (who, retval, encoding ());
4113           }
4114         else
4115           {
4116             err = true;
4117 
4118             if (is.eof () && char_count == 0)
4119               error (who, "at end of file");
4120             else
4121               error (who, "read error");
4122           }
4123       }
4124 
4125     return retval;
4126   }
4127 
4128   std::string
4129   base_stream::getl (octave_idx_type max_len, bool& err,
4130                      const std::string& who)
4131   {
4132     return do_gets (max_len, err, true, who);
4133   }
4134 
4135   std::string
4136   base_stream::gets (octave_idx_type max_len, bool& err,
4137                      const std::string& who)
4138   {
4139     return do_gets (max_len, err, false, who);
4140   }
4141 
4142   off_t
4143   base_stream::skipl (off_t num, bool& err, const std::string& who)
4144   {
4145     interpreter& interp = __get_interpreter__ ("base_stream::skipl");
4146 
4147     if (interp.interactive () && file_number () == 0)
4148       ::error ("%s: unable to read from stdin while running interactively",
4149                who.c_str ());
4150 
4151     off_t cnt = -1;
4152 
4153     err = false;
4154 
4155     std::istream *isp = input_stream ();
4156 
4157     if (! isp)
4158       {
4159         err = true;
4160         invalid_operation (who, "reading");
4161       }
4162     else
4163       {
4164         std::istream& is = *isp;
4165 
4166         int c = 0;
4167         int lastc = -1;
4168         cnt = 0;
4169 
4170         while (is && (c = is.get ()) != std::istream::traits_type::eof ())
4171           {
4172             // Handle CRLF, CR, or LF as line ending.
4173             if (c == '\r' || (c == '\n' && lastc != '\r'))
4174               {
4175                 if (++cnt == num)
4176                   break;
4177               }
4178 
4179             lastc = c;
4180           }
4181 
4182         // Maybe eat the following \n if \r was just met.
4183         if (c == '\r' && is.peek () == '\n')
4184           is.get ();
4185 
4186         if (is.bad ())
4187           {
4188             err = true;
4189             error (who, "read error");
4190           }
4191 
4192         if (err)
4193           cnt = -1;
4194       }
4195 
4196     return cnt;
4197   }
4198 
4199   template <typename T>
4200   std::istream&
4201   octave_scan_1 (std::istream& is, const scanf_format_elt& fmt,
4202                  T *valptr)
4203   {
4204     T value = T ();
4205 
4206     switch (fmt.type)
4207       {
4208       case 'o':
4209         is >> std::oct >> value >> std::dec;
4210         break;
4211 
4212       case 'x':
4213       case 'X':
4214         is >> std::hex >> value >> std::dec;
4215         break;
4216 
4217       case 'i':
4218         {
4219           int c1 = std::istream::traits_type::eof ();
4220 
4221           while (is && (c1 = is.get ()) != std::istream::traits_type::eof ()
4222                  && isspace (c1))
4223             ; // skip whitespace
4224 
4225           if (c1 != std::istream::traits_type::eof ())
4226             {
4227               if (c1 == '0')
4228                 {
4229                   int c2 = is.peek ();
4230 
4231                   if (c2 == 'x' || c2 == 'X')
4232                     {
4233                       is.ignore ();
4234                       if (std::isxdigit (is.peek ()))
4235                         is >> std::hex >> value >> std::dec;
4236                       else
4237                         value = 0;
4238                     }
4239                   else
4240                     {
4241                       if (c2 == '0' || c2 == '1' || c2 == '2'
4242                           || c2 == '3' || c2 == '4' || c2 == '5'
4243                           || c2 == '6' || c2 == '7')
4244                         is >> std::oct >> value >> std::dec;
4245                       else if (c2 == '8' || c2 == '9')
4246                         {
4247                           // FIXME: Would like to set error state on octave
4248                           // stream.  See bug #46493.  But only std::istream is
4249                           // input to fcn.
4250                           // error ("internal failure to match octal format");
4251                           value = 0;
4252                         }
4253                       else
4254                         value = 0;
4255                     }
4256                 }
4257               else
4258                 {
4259                   is.putback (c1);
4260 
4261                   is >> value;
4262                 }
4263             }
4264         }
4265         break;
4266 
4267       default:
4268         is >> value;
4269         break;
4270       }
4271 
4272     // If conversion produces an integer that overflows, failbit is set but
4273     // value is non-zero.  We want to treat this case as success, so clear
4274     // failbit from the stream state to keep going.
4275     // FIXME: Maybe set error state on octave stream as above? Matlab does
4276     // *not* indicate an error message on overflow.
4277     if ((is.rdstate () & std::ios::failbit) && value != T ())
4278       is.clear (is.rdstate () & ~std::ios::failbit);
4279 
4280     // Only copy the converted value if the stream is in a state where we
4281     // want to continue reading.
4282     if (! (is.rdstate () & std::ios::failbit))
4283       *valptr = value;
4284 
4285     return is;
4286   }
4287 
4288   template <typename T>
4289   std::istream&
4290   octave_scan (std::istream& is, const scanf_format_elt& fmt, T *valptr)
4291   {
4292     if (fmt.width)
4293       {
4294         // Limit input to fmt.width characters by reading into a
4295         // temporary stringstream buffer.
4296         std::string strbuf;
4297 
4298         auto orig_pos = is.tellg ();
4299 
4300         is.width (fmt.width);
4301         is >> strbuf;
4302 
4303         std::istringstream ss (strbuf);
4304 
4305         octave_scan_1 (ss, fmt, valptr);
4306 
4307         if (! ss.eof ())
4308           {
4309             // If fewer characters than width were used to read a number then
4310             // the original istream object positioning is incorrect.
4311             // Rather than attempt to update istream state and positioning,
4312             // just redo the '>>' operation with the correct width so that
4313             // all flags get set correctly.
4314 
4315             is.clear ();  // Clear EOF, FAILBIT, BADBIT
4316             is.seekg (orig_pos, is.beg);
4317 
4318             int chars_read = ss.tellg ();
4319             if (chars_read > 0)
4320               {
4321                 is.width (chars_read);
4322                 is >> strbuf;
4323               }
4324           }
4325 
4326         // If pattern failed to match then propagate fail bit to 'is' stream.
4327         if (ss.fail ())
4328           is.setstate (std::ios::failbit);
4329 
4330       }
4331     else
4332       octave_scan_1 (is, fmt, valptr);
4333 
4334     return is;
4335   }
4336 
4337   // Note that this specialization is only used for reading characters, not
4338   // character strings.  See BEGIN_S_CONVERSION for details.
4339 
4340   template <>
4341   std::istream&
4342   octave_scan<> (std::istream& is, const scanf_format_elt& /* fmt */,
4343                  char *valptr)
4344   {
4345     return is >> valptr;
4346   }
4347 
4348   template <>
4349   std::istream&
4350   octave_scan<> (std::istream& is, const scanf_format_elt& fmt, double *valptr)
4351   {
4352     double& ref = *valptr;
4353 
4354     switch (fmt.type)
4355       {
4356       case 'e':
4357       case 'f':
4358       case 'g':
4359       case 'E':
4360       case 'G':
4361         {
4362           int c1 = std::istream::traits_type::eof ();
4363 
4364           while (is && (c1 = is.get ()) != std::istream::traits_type::eof ()
4365                  && isspace (c1))
4366             ; // skip whitespace
4367 
4368           if (c1 != std::istream::traits_type::eof ())
4369             {
4370               is.putback (c1);
4371 
4372               ref = octave_read_value<double> (is);
4373             }
4374         }
4375         break;
4376 
4377       default:
4378         panic_impossible ();
4379         break;
4380       }
4381 
4382     return is;
4383   }
4384 
4385   template <typename T>
4386   void
4387   do_scanf_conv (std::istream& is, const scanf_format_elt& fmt,
4388                  T valptr, Matrix& mval, double *data, octave_idx_type& idx,
4389                  octave_idx_type& conversion_count, octave_idx_type nr,
4390                  octave_idx_type max_size, bool discard)
4391   {
4392     octave_scan (is, fmt, valptr);
4393 
4394     if (! is)
4395       return;
4396 
4397     if (idx == max_size && ! discard)
4398       {
4399         max_size *= 2;
4400 
4401         if (nr > 0)
4402           mval.resize (nr, max_size / nr, 0.0);
4403         else
4404           mval.resize (max_size, 1, 0.0);
4405 
4406         data = mval.fortran_vec ();
4407       }
4408 
4409     if (! discard)
4410       {
4411         conversion_count++;
4412         data[idx++] = *(valptr);
4413       }
4414   }
4415 
4416   template void
4417   do_scanf_conv (std::istream&, const scanf_format_elt&, double*,
4418                  Matrix&, double*, octave_idx_type&, octave_idx_type&,
4419                  octave_idx_type, octave_idx_type, bool);
4420 
4421 #define DO_WHITESPACE_CONVERSION()                                      \
4422   do                                                                    \
4423     {                                                                   \
4424       int c = std::istream::traits_type::eof ();                        \
4425                                                                         \
4426       while (is && (c = is.get ()) != std::istream::traits_type::eof () \
4427              && isspace (c))                                            \
4428         { /* skip whitespace */ }                                       \
4429                                                                         \
4430       if (c != std::istream::traits_type::eof ())                       \
4431         is.putback (c);                                                 \
4432     }                                                                   \
4433   while (0)
4434 
4435 #define DO_LITERAL_CONVERSION()                                         \
4436   do                                                                    \
4437     {                                                                   \
4438       int c = std::istream::traits_type::eof ();                        \
4439                                                                         \
4440       int n = fmt.length ();                                            \
4441       int i = 0;                                                        \
4442                                                                         \
4443       while (i < n && is && (c = is.get ()) != std::istream::traits_type::eof ()) \
4444         {                                                               \
4445           if (c == static_cast<unsigned char> (fmt[i]))                 \
4446             {                                                           \
4447               i++;                                                      \
4448               continue;                                                 \
4449             }                                                           \
4450           else                                                          \
4451             {                                                           \
4452               is.putback (c);                                           \
4453               break;                                                    \
4454             }                                                           \
4455         }                                                               \
4456                                                                         \
4457       if (i != n)                                                       \
4458         is.setstate (std::ios::failbit);                                \
4459     }                                                                   \
4460   while (0)
4461 
4462 #define DO_PCT_CONVERSION()                             \
4463   do                                                    \
4464     {                                                   \
4465       int c = is.get ();                                \
4466                                                         \
4467       if (c != std::istream::traits_type::eof ())       \
4468         {                                               \
4469           if (c != '%')                                 \
4470             {                                           \
4471               is.putback (c);                           \
4472               is.setstate (std::ios::failbit);          \
4473             }                                           \
4474         }                                               \
4475       else                                              \
4476         is.setstate (std::ios::failbit);                \
4477     }                                                   \
4478   while (0)
4479 
4480 #define BEGIN_C_CONVERSION()                                            \
4481   is.unsetf (std::ios::skipws);                                         \
4482                                                                         \
4483   int width = (elt->width ? elt->width : 1);                            \
4484                                                                         \
4485   std::string tmp (width, '\0');                                        \
4486                                                                         \
4487   int c = std::istream::traits_type::eof ();                            \
4488   int n = 0;                                                            \
4489                                                                         \
4490   while (is && n < width                                                \
4491          && (c = is.get ()) != std::istream::traits_type::eof ())       \
4492     tmp[n++] = static_cast<char> (c);                                   \
4493                                                                         \
4494   if (n > 0 && c == std::istream::traits_type::eof ())                  \
4495     is.clear ();                                                        \
4496                                                                         \
4497   tmp.resize (n)
4498 
4499   // For a '%s' format, skip initial whitespace and then read until the
4500   // next whitespace character or until WIDTH characters have been read.
4501 #define BEGIN_S_CONVERSION()                                            \
4502   int width = elt->width;                                               \
4503                                                                         \
4504   std::string tmp;                                                      \
4505                                                                         \
4506   do                                                                    \
4507     {                                                                   \
4508       if (width)                                                        \
4509         {                                                               \
4510           tmp = std::string (width, '\0');                              \
4511                                                                         \
4512           int c = std::istream::traits_type::eof ();                    \
4513                                                                         \
4514           int n = 0;                                                    \
4515                                                                         \
4516           while (is && (c = is.get ()) != std::istream::traits_type::eof ()) \
4517             {                                                           \
4518               if (! isspace (c))                                        \
4519                 {                                                       \
4520                   tmp[n++] = static_cast<char> (c);                     \
4521                   break;                                                \
4522                 }                                                       \
4523             }                                                           \
4524                                                                         \
4525           while (is && n < width                                        \
4526                  && (c = is.get ()) != std::istream::traits_type::eof ()) \
4527             {                                                           \
4528               if (isspace (c))                                          \
4529                 {                                                       \
4530                   is.putback (c);                                       \
4531                   break;                                                \
4532                 }                                                       \
4533               else                                                      \
4534                 tmp[n++] = static_cast<char> (c);                       \
4535             }                                                           \
4536                                                                         \
4537           if (n > 0 && c == std::istream::traits_type::eof ())          \
4538             is.clear ();                                                \
4539                                                                         \
4540           tmp.resize (n);                                               \
4541         }                                                               \
4542       else                                                              \
4543         {                                                               \
4544           is >> std::ws >> tmp;                                         \
4545         }                                                               \
4546     }                                                                   \
4547   while (0)
4548 
4549   // This format must match a nonempty sequence of characters.
4550 #define BEGIN_CHAR_CLASS_CONVERSION()                                   \
4551   int width = elt->width;                                               \
4552                                                                         \
4553   std::string tmp;                                                      \
4554                                                                         \
4555   do                                                                    \
4556     {                                                                   \
4557       if (! width)                                                      \
4558         width = std::numeric_limits<int>::max ();                       \
4559                                                                         \
4560       std::ostringstream buf;                                           \
4561                                                                         \
4562       std::string char_class = elt->char_class;                         \
4563                                                                         \
4564       int c = std::istream::traits_type::eof ();                        \
4565                                                                         \
4566       if (elt->type == '[')                                             \
4567         {                                                               \
4568           int chars_read = 0;                                           \
4569           while (is && chars_read++ < width                             \
4570                  && (c = is.get ()) != std::istream::traits_type::eof () \
4571                  && char_class.find (c) != std::string::npos)           \
4572             buf << static_cast<char> (c);                               \
4573         }                                                               \
4574       else                                                              \
4575         {                                                               \
4576           int chars_read = 0;                                           \
4577           while (is && chars_read++ < width                             \
4578                  && (c = is.get ()) != std::istream::traits_type::eof () \
4579                  && char_class.find (c) == std::string::npos)           \
4580             buf << static_cast<char> (c);                               \
4581         }                                                               \
4582                                                                         \
4583       if (width == std::numeric_limits<int>::max ()                     \
4584           && c != std::istream::traits_type::eof ())                    \
4585         is.putback (c);                                                 \
4586                                                                         \
4587       tmp = buf.str ();                                                 \
4588                                                                         \
4589       if (tmp.empty ())                                                 \
4590         is.setstate (std::ios::failbit);                                \
4591       else if (c == std::istream::traits_type::eof ())                  \
4592         is.clear ();                                                    \
4593                                                                         \
4594     }                                                                   \
4595   while (0)
4596 
4597 #define FINISH_CHARACTER_CONVERSION()                                   \
4598   do                                                                    \
4599     {                                                                   \
4600       if (encoding ().compare ("utf-8"))                                \
4601         tmp = string::u8_from_encoding (who, tmp, encoding ());         \
4602       width = tmp.length ();                                            \
4603                                                                         \
4604       if (is)                                                           \
4605         {                                                               \
4606           int i = 0;                                                    \
4607                                                                         \
4608           if (! discard)                                                \
4609             {                                                           \
4610               conversion_count++;                                       \
4611                                                                         \
4612               while (i < width)                                         \
4613                 {                                                       \
4614                   if (data_index == max_size)                           \
4615                     {                                                   \
4616                       max_size *= 2;                                    \
4617                                                                         \
4618                       if (all_char_conv)                                \
4619                         {                                               \
4620                           if (one_elt_size_spec)                        \
4621                             mval.resize (1, max_size, 0.0);             \
4622                           else if (nr > 0)                              \
4623                             mval.resize (nr, max_size / nr, 0.0);       \
4624                           else                                          \
4625                             panic_impossible ();                        \
4626                         }                                               \
4627                       else if (nr > 0)                                  \
4628                         mval.resize (nr, max_size / nr, 0.0);           \
4629                       else                                              \
4630                         mval.resize (max_size, 1, 0.0);                 \
4631                                                                         \
4632                       data = mval.fortran_vec ();                       \
4633                     }                                                   \
4634                                                                         \
4635                   data[data_index++] = static_cast<unsigned char>       \
4636                                                   (tmp[i++]);           \
4637                 }                                                       \
4638             }                                                           \
4639         }                                                               \
4640     }                                                                   \
4641   while (0)
4642 
4643   octave_value
4644   base_stream::do_scanf (scanf_format_list& fmt_list,
4645                          octave_idx_type nr, octave_idx_type nc,
4646                          bool one_elt_size_spec,
4647                          octave_idx_type& conversion_count,
4648                          const std::string& who)
4649   {
4650     interpreter& interp = __get_interpreter__ ("base_stream::do_scanf");
4651 
4652     if (interp.interactive () && file_number () == 0)
4653       ::error ("%s: unable to read from stdin while running interactively",
4654                who.c_str ());
4655 
4656     octave_value retval = Matrix ();
4657 
4658     conversion_count = 0;
4659 
4660     octave_idx_type nconv = fmt_list.num_conversions ();
4661 
4662     octave_idx_type data_index = 0;
4663 
4664     if (nr == 0 || nc == 0)
4665       {
4666         if (one_elt_size_spec)
4667           nc = 0;
4668 
4669         return Matrix (nr, nc, 0.0);
4670       }
4671 
4672     std::istream *isp = input_stream ();
4673 
4674     bool all_char_conv = fmt_list.all_character_conversions ();
4675 
4676     Matrix mval;
4677     octave_idx_type max_size = 0;
4678     octave_idx_type max_conv = 0;
4679 
4680     octave_idx_type final_nr = 0;
4681     octave_idx_type final_nc = 0;
4682 
4683     if (all_char_conv)
4684       {
4685         // Any of these could be resized later (if we have %s conversions,
4686         // we may read more than one element for each conversion).
4687         if (one_elt_size_spec)
4688           {
4689             max_size = 512;
4690             mval.resize (1, max_size, 0.0);
4691 
4692             if (nr > 0)
4693               max_conv = nr;
4694           }
4695         else if (nr > 0)
4696           {
4697             if (nc > 0)
4698               {
4699                 mval.resize (nr, nc, 0.0);
4700                 max_size = max_conv = nr * nc;
4701               }
4702             else
4703               {
4704                 mval.resize (nr, 32, 0.0);
4705                 max_size = nr * 32;
4706               }
4707           }
4708         else
4709           panic_impossible ();
4710       }
4711     else if (nr > 0)
4712       {
4713         if (nc > 0)
4714           {
4715             // Will not resize later.
4716             mval.resize (nr, nc, 0.0);
4717             max_size = nr * nc;
4718             max_conv = max_size;
4719           }
4720         else
4721           {
4722             // Maybe resize later.
4723             mval.resize (nr, 32, 0.0);
4724             max_size = nr * 32;
4725           }
4726       }
4727     else
4728       {
4729         // Maybe resize later.
4730         mval.resize (32, 1, 0.0);
4731         max_size = 32;
4732       }
4733 
4734     double *data = mval.fortran_vec ();
4735 
4736     if (isp)
4737       {
4738         std::istream& is = *isp;
4739 
4740         const scanf_format_elt *elt = fmt_list.first ();
4741 
4742         std::ios::fmtflags flags = is.flags ();
4743 
4744         octave_idx_type trips = 0;
4745 
4746         octave_idx_type num_fmt_elts = fmt_list.length ();
4747 
4748         for (;;)
4749           {
4750             octave_quit ();
4751 
4752             if (elt)
4753               {
4754                 if (elt->type == scanf_format_elt::null
4755                     || (! (elt->type == scanf_format_elt::whitespace_conversion
4756                            || elt->type == scanf_format_elt::literal_conversion
4757                            || elt->type == '%')
4758                         && max_conv > 0 && conversion_count == max_conv))
4759                   {
4760                     // We are done, either because we have reached the end of the
4761                     // format string and are not cycling through the format again
4762                     // or because we've converted all the values that have been
4763                     // requested and the next format element is a conversion.
4764                     // Determine final array size and exit.
4765                     if (all_char_conv && one_elt_size_spec)
4766                       {
4767                         final_nr = 1;
4768                         final_nc = data_index;
4769                       }
4770                     else
4771                       {
4772                         final_nr = nr;
4773                         final_nc = (data_index - 1) / nr + 1;
4774                       }
4775 
4776                     break;
4777                   }
4778                 else if (data_index == max_size)
4779                   {
4780                     max_size *= 2;
4781 
4782                     if (all_char_conv)
4783                       {
4784                         if (one_elt_size_spec)
4785                           mval.resize (1, max_size, 0.0);
4786                         else if (nr > 0)
4787                           mval.resize (nr, max_size / nr, 0.0);
4788                         else
4789                           panic_impossible ();
4790                       }
4791                     else if (nr > 0)
4792                       mval.resize (nr, max_size / nr, 0.0);
4793                     else
4794                       mval.resize (max_size, 1, 0.0);
4795 
4796                     data = mval.fortran_vec ();
4797                   }
4798 
4799                 std::string fmt = elt->text;
4800 
4801                 bool discard = elt->discard;
4802 
4803                 switch (elt->type)
4804                   {
4805                   case scanf_format_elt::whitespace_conversion:
4806                     DO_WHITESPACE_CONVERSION ();
4807                     break;
4808 
4809                   case scanf_format_elt::literal_conversion:
4810                     DO_LITERAL_CONVERSION ();
4811                     break;
4812 
4813                   case '%':
4814                     DO_PCT_CONVERSION ();
4815                     break;
4816 
4817                   case 'd': case 'i':
4818                     {
4819                       switch (elt->modifier)
4820                         {
4821                         case 'h':
4822                           {
4823                             int16_t tmp;
4824                             do_scanf_conv (is, *elt, &tmp, mval, data,
4825                                            data_index, conversion_count,
4826                                            nr, max_size, discard);
4827                           }
4828                           break;
4829 
4830                         case 'l':
4831                           {
4832                             int64_t tmp;
4833                             do_scanf_conv (is, *elt, &tmp, mval, data,
4834                                            data_index, conversion_count,
4835                                            nr, max_size, discard);
4836                           }
4837                           break;
4838 
4839                         default:
4840                           {
4841                             int32_t tmp;
4842                             do_scanf_conv (is, *elt, &tmp, mval, data,
4843                                            data_index, conversion_count,
4844                                            nr, max_size, discard);
4845                           }
4846                           break;
4847                         }
4848                     }
4849                     break;
4850 
4851                   case 'o': case 'u': case 'x': case 'X':
4852                     {
4853                       switch (elt->modifier)
4854                         {
4855                         case 'h':
4856                           {
4857                             uint16_t tmp;
4858                             do_scanf_conv (is, *elt, &tmp, mval, data,
4859                                            data_index, conversion_count,
4860                                            nr, max_size, discard);
4861                           }
4862                           break;
4863 
4864                         case 'l':
4865                           {
4866                             uint64_t tmp;
4867                             do_scanf_conv (is, *elt, &tmp, mval, data,
4868                                            data_index, conversion_count,
4869                                            nr, max_size, discard);
4870                           }
4871                           break;
4872 
4873                         default:
4874                           {
4875                             uint32_t tmp;
4876                             do_scanf_conv (is, *elt, &tmp, mval, data,
4877                                            data_index, conversion_count,
4878                                            nr, max_size, discard);
4879                           }
4880                           break;
4881                         }
4882                     }
4883                     break;
4884 
4885                   case 'e': case 'f': case 'g':
4886                   case 'E': case 'G':
4887                     {
4888                       double tmp;
4889 
4890                       do_scanf_conv (is, *elt, &tmp, mval, data,
4891                                      data_index, conversion_count,
4892                                      nr, max_size, discard);
4893                     }
4894                     break;
4895 
4896                   case 'c':
4897                     {
4898                       BEGIN_C_CONVERSION ();
4899 
4900                       FINISH_CHARACTER_CONVERSION ();
4901 
4902                       is.setf (flags);
4903                     }
4904                     break;
4905 
4906                   case 's':
4907                     {
4908                       BEGIN_S_CONVERSION ();
4909 
4910                       FINISH_CHARACTER_CONVERSION ();
4911                     }
4912                     break;
4913 
4914                   case '[': case '^':
4915                     {
4916                       BEGIN_CHAR_CLASS_CONVERSION ();
4917 
4918                       FINISH_CHARACTER_CONVERSION ();
4919                     }
4920                     break;
4921 
4922                   case 'p':
4923                     error (who, "unsupported format specifier");
4924                     break;
4925 
4926                   default:
4927                     error (who, "internal format error");
4928                     break;
4929                   }
4930 
4931                 if (! ok ())
4932                   {
4933                     break;
4934                   }
4935                 else if (is.eof () || ! is)
4936                   {
4937                     if (all_char_conv)
4938                       {
4939                         if (one_elt_size_spec)
4940                           {
4941                             final_nr = 1;
4942                             final_nc = data_index;
4943                           }
4944                         else if (data_index > nr)
4945                           {
4946                             final_nr = nr;
4947                             final_nc = (data_index - 1) / nr + 1;
4948                           }
4949                         else
4950                           {
4951                             final_nr = data_index;
4952                             final_nc = 1;
4953                           }
4954                       }
4955                     else if (nr > 0)
4956                       {
4957                         if (data_index > nr)
4958                           {
4959                             final_nr = nr;
4960                             final_nc = (data_index - 1) / nr + 1;
4961                           }
4962                         else
4963                           {
4964                             final_nr = data_index;
4965                             final_nc = 1;
4966                           }
4967                       }
4968                     else
4969                       {
4970                         final_nr = data_index;
4971                         final_nc = 1;
4972                       }
4973 
4974                     // If it looks like we have a matching failure, then
4975                     // reset the failbit in the stream state.
4976                     if (is.rdstate () & std::ios::failbit)
4977                       {
4978                         error (who, "format failed to match");
4979                         is.clear (is.rdstate () & (~std::ios::failbit));
4980                       }
4981 
4982                     // FIXME: is this the right thing to do?
4983                     if (interp.interactive ()
4984                         && ! application::forced_interactive ()
4985                         && name () == "stdin")
4986                       {
4987                         is.clear ();
4988 
4989                         // Skip to end of line.
4990                         bool err;
4991                         do_gets (-1, err, false, who);
4992                       }
4993 
4994                     break;
4995                   }
4996               }
4997             else
4998               {
4999                 error (who, "internal format error");
5000                 break;
5001               }
5002 
5003             if (nconv == 0 && ++trips == num_fmt_elts)
5004               {
5005                 if (all_char_conv && one_elt_size_spec)
5006                   {
5007                     final_nr = 1;
5008                     final_nc = data_index;
5009                   }
5010                 else
5011                   {
5012                     final_nr = nr;
5013                     final_nc = (data_index - 1) / nr + 1;
5014                   }
5015 
5016                 break;
5017               }
5018             else
5019               {
5020                 // Cycle through the format list more than once if we have some
5021                 // conversions to make and we haven't reached the limit on the
5022                 // number of values to convert (possibly because there is no
5023                 // specified limit).
5024                 elt = fmt_list.next (nconv > 0
5025                                      && (max_conv == 0
5026                                          || conversion_count < max_conv));
5027               }
5028           }
5029       }
5030 
5031     mval.resize (final_nr, final_nc, 0.0);
5032 
5033     retval = mval;
5034 
5035     if (all_char_conv)
5036       retval = retval.convert_to_str (false, true);
5037 
5038     return retval;
5039   }
5040 
5041   octave_value
5042   base_stream::scanf (const std::string& fmt, const Array<double>& size,
5043                       octave_idx_type& conversion_count,
5044                       const std::string& who)
5045   {
5046     octave_value retval = Matrix ();
5047 
5048     conversion_count = 0;
5049 
5050     std::istream *isp = input_stream ();
5051 
5052     if (! isp)
5053       invalid_operation (who, "reading");
5054     else
5055       {
5056         scanf_format_list fmt_list (fmt);
5057 
5058         if (fmt_list.num_conversions () == -1)
5059           ::error ("%s: invalid format specified", who.c_str ());
5060 
5061         octave_idx_type nr = -1;
5062         octave_idx_type nc = -1;
5063 
5064         bool one_elt_size_spec;
5065 
5066         get_size (size, nr, nc, one_elt_size_spec, who);
5067 
5068         retval = do_scanf (fmt_list, nr, nc, one_elt_size_spec,
5069                            conversion_count, who);
5070       }
5071 
5072     return retval;
5073   }
5074 
5075   bool
5076   base_stream::do_oscanf (const scanf_format_elt *elt,
5077                           octave_value& retval, const std::string& who)
5078   {
5079     std::istream *isp = input_stream ();
5080 
5081     if (! isp)
5082       return false;
5083 
5084     bool quit = false;
5085 
5086     std::istream& is = *isp;
5087 
5088     std::ios::fmtflags flags = is.flags ();
5089 
5090     if (elt)
5091       {
5092         std::string fmt = elt->text;
5093 
5094         bool discard = elt->discard;
5095 
5096         switch (elt->type)
5097           {
5098           case scanf_format_elt::whitespace_conversion:
5099             DO_WHITESPACE_CONVERSION ();
5100             break;
5101 
5102           case scanf_format_elt::literal_conversion:
5103             DO_LITERAL_CONVERSION ();
5104             break;
5105 
5106           case '%':
5107             {
5108               DO_PCT_CONVERSION ();
5109 
5110               if (! is)
5111                 quit = true;
5112             }
5113             break;
5114 
5115           case 'd': case 'i':
5116             {
5117               switch (elt->modifier)
5118                 {
5119                 case 'h':
5120                   {
5121                     int16_t tmp;
5122                     if (octave_scan (is, *elt, &tmp))
5123                       {
5124                         if (! discard)
5125                           retval = tmp;
5126                       }
5127                     else
5128                       quit = true;
5129                   }
5130                   break;
5131 
5132                 case 'l':
5133                   {
5134                     int64_t tmp;
5135                     if (octave_scan (is, *elt, &tmp))
5136                       {
5137                         if (! discard)
5138                           retval = tmp;
5139                       }
5140                     else
5141                       quit = true;
5142                   }
5143                   break;
5144 
5145                 default:
5146                   {
5147                     int32_t tmp;
5148                     if (octave_scan (is, *elt, &tmp))
5149                       {
5150                         if (! discard)
5151                           retval = tmp;
5152                       }
5153                     else
5154                       quit = true;
5155                   }
5156                   break;
5157                 }
5158             }
5159             break;
5160 
5161           case 'o': case 'u': case 'x': case 'X':
5162             {
5163               switch (elt->modifier)
5164                 {
5165                 case 'h':
5166                   {
5167                     uint16_t tmp;
5168                     if (octave_scan (is, *elt, &tmp))
5169                       {
5170                         if (! discard)
5171                           retval = tmp;
5172                       }
5173                     else
5174                       quit = true;
5175                   }
5176                   break;
5177 
5178                 case 'l':
5179                   {
5180                     uint64_t tmp;
5181                     if (octave_scan (is, *elt, &tmp))
5182                       {
5183                         if (! discard)
5184                           retval = tmp;
5185                       }
5186                     else
5187                       quit = true;
5188                   }
5189                   break;
5190 
5191                 default:
5192                   {
5193                     uint32_t tmp;
5194                     if (octave_scan (is, *elt, &tmp))
5195                       {
5196                         if (! discard)
5197                           retval = tmp;
5198                       }
5199                     else
5200                       quit = true;
5201                   }
5202                   break;
5203                 }
5204             }
5205             break;
5206 
5207           case 'e': case 'f': case 'g':
5208           case 'E': case 'G':
5209             {
5210               double tmp;
5211 
5212               if (octave_scan (is, *elt, &tmp))
5213                 {
5214                   if (! discard)
5215                     retval = tmp;
5216                 }
5217               else
5218                 quit = true;
5219             }
5220             break;
5221 
5222           case 'c':
5223             {
5224               BEGIN_C_CONVERSION ();
5225 
5226               if (! discard)
5227                 retval = tmp;
5228 
5229               if (! is)
5230                 quit = true;
5231 
5232               is.setf (flags);
5233             }
5234             break;
5235 
5236           case 's':
5237             {
5238               BEGIN_S_CONVERSION ();
5239 
5240               if (! discard)
5241                 retval = tmp;
5242 
5243               if (! is)
5244                 quit = true;
5245             }
5246             break;
5247 
5248           case '[':
5249           case '^':
5250             {
5251               BEGIN_CHAR_CLASS_CONVERSION ();
5252 
5253               if (! discard)
5254                 retval = tmp;
5255 
5256               if (! is)
5257                 quit = true;
5258             }
5259             break;
5260 
5261           case 'p':
5262             error (who, "unsupported format specifier");
5263             break;
5264 
5265           default:
5266             error (who, "internal format error");
5267             break;
5268           }
5269       }
5270 
5271     if (ok () && is.fail ())
5272       {
5273         error ("%s: read error", who.c_str ());
5274 
5275         // FIXME: is this the right thing to do?
5276 
5277         interpreter& interp = __get_interpreter__ ("base_stream::do_oscanf");
5278 
5279         if (interp.interactive () && ! application::forced_interactive ()
5280             && name () == "stdin")
5281           {
5282             // Skip to end of line.
5283             bool err;
5284             do_gets (-1, err, false, who);
5285           }
5286       }
5287 
5288     return quit;
5289   }
5290 
5291   octave_value_list
5292   base_stream::oscanf (const std::string& fmt, const std::string& who)
5293   {
5294     octave_value_list retval;
5295 
5296     std::istream *isp = input_stream ();
5297 
5298     if (! isp)
5299       invalid_operation (who, "reading");
5300     else
5301       {
5302         std::istream& is = *isp;
5303 
5304         scanf_format_list fmt_list (fmt);
5305 
5306         octave_idx_type nconv = fmt_list.num_conversions ();
5307 
5308         if (nconv == -1)
5309           ::error ("%s: invalid format specified", who.c_str ());
5310 
5311         is.clear ();
5312 
5313         octave_idx_type len = fmt_list.length ();
5314 
5315         retval.resize (nconv+2, Matrix ());
5316 
5317         const scanf_format_elt *elt = fmt_list.first ();
5318 
5319         int num_values = 0;
5320 
5321         bool quit = false;
5322 
5323         for (octave_idx_type i = 0; i < len; i++)
5324           {
5325             octave_value tmp;
5326 
5327             quit = do_oscanf (elt, tmp, who);
5328 
5329             if (quit)
5330               break;
5331             else
5332               {
5333                 if (tmp.is_defined ())
5334                   retval(num_values++) = tmp;
5335 
5336                 if (! ok ())
5337                   break;
5338 
5339                 elt = fmt_list.next (nconv > 0);
5340               }
5341           }
5342 
5343         retval(nconv) = num_values;
5344 
5345         int err_num;
5346         retval(nconv+1) = error (false, err_num);
5347 
5348         if (! quit)
5349           {
5350             // Pick up any trailing stuff.
5351             if (ok () && len > nconv)
5352               {
5353                 octave_value tmp;
5354 
5355                 elt = fmt_list.next ();
5356 
5357                 do_oscanf (elt, tmp, who);
5358               }
5359           }
5360       }
5361 
5362     return retval;
5363   }
5364 
5365   octave_value
5366   base_stream::do_textscan (const std::string& fmt,
5367                             octave_idx_type ntimes,
5368                             const octave_value_list& options,
5369                             const std::string& who,
5370                             octave_idx_type& read_count)
5371   {
5372     interpreter& interp = __get_interpreter__ ("base_stream::do_textscan");
5373 
5374     if (interp.interactive () && file_number () == 0)
5375       ::error ("%s: unable to read from stdin while running interactively",
5376                who.c_str ());
5377 
5378     octave_value retval = Cell (dim_vector (1, 1), Matrix (0, 1));
5379 
5380     std::istream *isp = input_stream ();
5381 
5382     if (! isp)
5383       invalid_operation (who, "reading");
5384     else
5385       {
5386         textscan scanner (who, encoding ());
5387 
5388         retval = scanner.scan (*isp, fmt, ntimes, options, read_count);
5389       }
5390 
5391     return retval;
5392   }
5393 
5394   // Functions that are defined for all output streams
5395   // (output streams are those that define os).
5396 
5397   int
5398   base_stream::flush (void)
5399   {
5400     int retval = -1;
5401 
5402     std::ostream *os = output_stream ();
5403 
5404     if (! os)
5405       invalid_operation ("fflush", "writing");
5406     else
5407       {
5408         os->flush ();
5409 
5410         if (os->good ())
5411           retval = 0;
5412       }
5413 
5414     return retval;
5415   }
5416 
5417   class
5418   printf_value_cache
5419   {
5420   public:
5421 
5422     enum state { ok, conversion_error };
5423 
5424     printf_value_cache (const octave_value_list& args, const std::string& who)
5425       : values (args), val_idx (0), elt_idx (0),
5426         n_vals (values.length ()), n_elts (0), have_data (false),
5427         curr_state (ok)
5428     {
5429       for (octave_idx_type i = 0; i < values.length (); i++)
5430         {
5431           octave_value val = values(i);
5432 
5433           if (val.isstruct () || val.iscell () || val.isobject ())
5434             err_wrong_type_arg (who, val);
5435         }
5436     }
5437 
5438     // No copying!
5439 
5440     printf_value_cache (const printf_value_cache&) = delete;
5441 
5442     printf_value_cache& operator = (const printf_value_cache&) = delete;
5443 
5444     ~printf_value_cache (void) = default;
5445 
5446     // Get the current value as a double and advance the internal pointer.
5447     octave_value get_next_value (char type = 0);
5448 
5449     // Get the current value as an int and advance the internal
5450     // pointer.  Value before conversion to int must be >= 0 and less
5451     // than std::numeric_limits<int>::max ().
5452 
5453     int int_value (void);
5454 
5455     operator bool () const { return (curr_state == ok); }
5456 
5457     bool exhausted (void) { return (val_idx >= n_vals); }
5458 
5459   private:
5460 
5461     const octave_value_list values;
5462     int val_idx;
5463     int elt_idx;
5464     int n_vals;
5465     int n_elts;
5466     bool have_data;
5467     octave_value curr_val;
5468     state curr_state;
5469 
5470     // Must create value cache with values!
5471 
5472     printf_value_cache (void);
5473   };
5474 
5475   octave_value
5476   printf_value_cache::get_next_value (char type)
5477   {
5478     octave_value retval;
5479 
5480     if (exhausted ())
5481       curr_state = conversion_error;
5482 
5483     while (! exhausted ())
5484       {
5485         if (! have_data)
5486           {
5487             curr_val = values (val_idx);
5488 
5489             elt_idx = 0;
5490             n_elts = curr_val.numel ();
5491             have_data = true;
5492           }
5493 
5494         if (elt_idx < n_elts)
5495           {
5496             if (type == 's')
5497               {
5498                 if (curr_val.is_string ())
5499                   {
5500                     dim_vector dv (1, curr_val.numel ());
5501                     octave_value tmp = curr_val.reshape (dv);
5502 
5503                     std::string sval = tmp.string_value ();
5504 
5505                     retval = sval.substr (elt_idx);
5506 
5507                     // We've consumed the rest of the value.
5508                     elt_idx = n_elts;
5509                   }
5510                 else
5511                   {
5512                     // Convert to character string while values are
5513                     // integers in the range [0 : char max]
5514                     const NDArray val = curr_val.array_value ();
5515 
5516                     octave_idx_type idx = elt_idx;
5517 
5518                     for (; idx < n_elts; idx++)
5519                       {
5520                         double dval = val(idx);
5521 
5522                         if (math::x_nint (dval) != dval || dval < 0 || dval > 255)
5523                           break;
5524                       }
5525 
5526                     octave_idx_type n = idx - elt_idx;
5527 
5528                     if (n > 0)
5529                       {
5530                         std::string sval (n, '\0');
5531 
5532                         for (octave_idx_type i = 0; i < n; i++)
5533                           sval[i] = val(elt_idx++);
5534 
5535                         retval = sval;
5536                       }
5537                     else
5538                       retval = curr_val.fast_elem_extract (elt_idx++);
5539                   }
5540               }
5541             else
5542               {
5543                 retval = curr_val.fast_elem_extract (elt_idx++);
5544 
5545                 if (type == 'c' && ! retval.is_string ())
5546                   {
5547                     double dval = retval.double_value ();
5548 
5549                     if (math::x_nint (dval) == dval && dval >= 0 && dval < 256)
5550                       retval = static_cast<char> (dval);
5551                   }
5552               }
5553 
5554             if (elt_idx >= n_elts)
5555               {
5556                 elt_idx = 0;
5557                 val_idx++;
5558                 have_data = false;
5559               }
5560 
5561             break;
5562           }
5563         else
5564           {
5565             val_idx++;
5566             have_data = false;
5567 
5568             if (n_elts == 0)
5569               {
5570                 if (elt_idx == 0)
5571                   {
5572                     if (type == 's' || type == 'c')
5573                       retval = "";
5574                     else
5575                       retval = Matrix ();
5576 
5577                     break;
5578                   }
5579 
5580                 if (exhausted ())
5581                   curr_state = conversion_error;
5582               }
5583           }
5584       }
5585 
5586     return retval;
5587   }
5588 
5589   int
5590   printf_value_cache::int_value (void)
5591   {
5592     octave_value val = get_next_value ();
5593 
5594     double dval = val.double_value (true);
5595 
5596     if (dval < 0 || dval > std::numeric_limits<int>::max ()
5597         || math::x_nint (dval) != dval)
5598       {
5599         curr_state = conversion_error;
5600         return -1;
5601       }
5602 
5603     return math::nint (dval);
5604   }
5605 
5606   // Ugh again and again.
5607 
5608   template <typename T>
5609   int
5610   do_printf_conv (std::ostream& os, const std::string& encoding,
5611                   const char *fmt, int nsa, int sa_1, int sa_2, T arg,
5612                   const std::string& who)
5613   {
5614     int retval = 0;
5615 
5616     switch (nsa)
5617       {
5618       case 2:
5619         retval = format (os, encoding, fmt, sa_1, sa_2, arg);
5620         break;
5621 
5622       case 1:
5623         retval = format (os, encoding, fmt, sa_1, arg);
5624         break;
5625 
5626       case 0:
5627         retval = format (os, encoding, fmt, arg);
5628         break;
5629 
5630       default:
5631         ::error ("%s: internal error handling format", who.c_str ());
5632         break;
5633       }
5634 
5635     return retval;
5636   }
5637 
5638   static std::size_t
5639   do_printf_string (std::ostream& os, const printf_format_elt *elt,
5640                     int nsa, int sa_1, int sa_2, const std::string& arg,
5641                     const std::string& encoding, const std::string& who)
5642   {
5643     if (nsa > 2)
5644       ::error ("%s: internal error handling format", who.c_str ());
5645 
5646     std::string flags = elt->flags;
5647 
5648     bool left = flags.find ('-') != std::string::npos;
5649 
5650     std::size_t len = arg.length ();
5651 
5652     std::size_t prec = (nsa > 1 ? sa_2 : (elt->prec == -1 ? len : elt->prec));
5653 
5654     std::string print_str = prec < arg.length () ? arg.substr (0, prec) : arg;
5655     if (encoding.compare ("utf-8"))
5656       {
5657         std::size_t src_len = print_str.length ();
5658         print_str = string::u8_to_encoding (who, print_str, encoding);
5659         len -= src_len - print_str.length ();
5660       }
5661 
5662     std::size_t fw = (nsa > 0 ? sa_1 : (elt->fw == -1 ? len : elt->fw));
5663 
5664     os << std::setw (fw) << (left ? std::left : std::right) << print_str;
5665 
5666     return len > fw ? len : fw;
5667   }
5668 
5669   static bool
5670   is_nan_or_inf (const octave_value& val)
5671   {
5672     octave_value ov_isnan = val.isnan ();
5673     octave_value ov_isinf = val.isinf ();
5674 
5675     return (ov_isnan.is_true () || ov_isinf.is_true ());
5676   }
5677 
5678   static bool
5679   ok_for_signed_int_conv (const octave_value& val)
5680   {
5681     uint64_t limit = std::numeric_limits<int64_t>::max ();
5682 
5683     if (val.is_string ())
5684       return true;
5685     else if (val.isinteger ())
5686       {
5687         if (val.is_uint64_type ())
5688           {
5689             octave_uint64 ival = val.uint64_scalar_value ();
5690 
5691             if (ival.value () <= limit)
5692               return true;
5693           }
5694         else
5695           return true;
5696       }
5697     else
5698       {
5699         double dval = val.double_value (true);
5700 
5701         if (dval == math::fix (dval) && dval <= limit)
5702           return true;
5703       }
5704 
5705     return false;
5706   }
5707 
5708   static bool
5709   ok_for_unsigned_int_conv (const octave_value& val)
5710   {
5711     if (val.is_string ())
5712       return true;
5713     else if (val.isinteger ())
5714       {
5715         // Easier than dispatching here...
5716 
5717         octave_value ov_is_ge_zero
5718           = do_binary_op (octave_value::op_ge, val, octave_value (0.0));
5719 
5720         return ov_is_ge_zero.is_true ();
5721       }
5722     else
5723       {
5724         double dval = val.double_value (true);
5725 
5726         uint64_t limit = std::numeric_limits<uint64_t>::max ();
5727 
5728         if (dval == math::fix (dval) && dval >= 0 && dval <= limit)
5729           return true;
5730       }
5731 
5732     return false;
5733   }
5734 
5735   static std::string
5736   switch_to_g_format (const printf_format_elt *elt)
5737   {
5738     std::string tfmt = elt->text;
5739 
5740     tfmt.replace (tfmt.rfind (elt->type), 1, "g");
5741 
5742     return tfmt;
5743   }
5744 
5745   int
5746   base_stream::do_numeric_printf_conv (std::ostream& os,
5747                                        const printf_format_elt *elt,
5748                                        int nsa, int sa_1, int sa_2,
5749                                        const octave_value& val,
5750                                        const std::string& who)
5751   {
5752     int retval = 0;
5753 
5754     std::string tfmt = elt->text;
5755 
5756     if (is_nan_or_inf (val))
5757       {
5758         double dval = val.double_value ();
5759 
5760         std::string::size_type i1, i2;
5761 
5762         tfmt.replace ((i1 = tfmt.rfind (elt->type)), 1, 1, 's');
5763 
5764         if ((i2 = tfmt.rfind ('.')) != std::string::npos && i2 < i1)
5765           {
5766             tfmt.erase (i2, i1-i2);
5767             if (elt->prec == -2)
5768               nsa--;
5769           }
5770 
5771         const char *tval;
5772         if (lo_ieee_isinf (dval))
5773           {
5774             if (elt->flags.find ('+') != std::string::npos)
5775               tval = (dval < 0 ? "-Inf" : "+Inf");
5776             else
5777               tval = (dval < 0 ? "-Inf" : "Inf");
5778           }
5779         else
5780           {
5781             if (elt->flags.find ('+') != std::string::npos)
5782               tval = (lo_ieee_is_NA (dval) ? "+NA" : "+NaN");
5783             else
5784               tval = (lo_ieee_is_NA (dval) ? "NA" : "NaN");
5785           }
5786 
5787         retval += do_printf_conv (os, encoding (), tfmt.c_str (), nsa, sa_1,
5788                                   sa_2, tval, who);
5789       }
5790     else
5791       {
5792         static std::string llmod
5793           = (sizeof (long) == sizeof (int64_t) ? "l" : "ll");
5794 
5795         char type = elt->type;
5796 
5797         switch (type)
5798           {
5799           case 'd': case 'i': case 'c':
5800             if (ok_for_signed_int_conv (val))
5801               {
5802                 octave_int64 tval = val.int64_scalar_value ();
5803 
5804                 // Insert "long" modifier.
5805                 tfmt.replace (tfmt.rfind (type), 1, llmod + type);
5806 
5807                 retval += do_printf_conv (os, encoding (), tfmt.c_str (), nsa,
5808                                           sa_1, sa_2, tval.value (), who);
5809               }
5810             else
5811               {
5812                 tfmt = switch_to_g_format (elt);
5813 
5814                 double dval = val.double_value (true);
5815 
5816                 retval += do_printf_conv (os, encoding (), tfmt.c_str (), nsa,
5817                                           sa_1, sa_2, dval, who);
5818               }
5819             break;
5820 
5821           case 'o': case 'x': case 'X': case 'u':
5822             if (ok_for_unsigned_int_conv (val))
5823               {
5824                 octave_uint64 tval = val.uint64_scalar_value ();
5825 
5826                 // Insert "long" modifier.
5827                 tfmt.replace (tfmt.rfind (type), 1, llmod + type);
5828 
5829                 retval += do_printf_conv (os, encoding (), tfmt.c_str (), nsa,
5830                                           sa_1, sa_2, tval.value (), who);
5831               }
5832             else
5833               {
5834                 tfmt = switch_to_g_format (elt);
5835 
5836                 double dval = val.double_value (true);
5837 
5838                 retval += do_printf_conv (os, encoding (), tfmt.c_str (), nsa,
5839                                           sa_1, sa_2, dval, who);
5840               }
5841             break;
5842 
5843           case 'f': case 'e': case 'E':
5844           case 'g': case 'G':
5845             {
5846               double dval = val.double_value (true);
5847 
5848               retval += do_printf_conv (os, encoding (), tfmt.c_str (), nsa,
5849                                         sa_1, sa_2, dval, who);
5850             }
5851             break;
5852 
5853           default:
5854             // Note: error is member fcn from base_stream, not ::error.
5855             // This error does not halt execution so "return ..." must exist.
5856             error (who, "invalid format specifier");
5857             return -1;
5858             break;
5859           }
5860       }
5861 
5862     return retval;
5863   }
5864 
5865   void
5866   base_stream::field_width_error (const std::string& who) const
5867   {
5868     ::error ("%s: invalid field width, must be integer >= 0 and <= INT_MAX",
5869              who.c_str ());
5870   }
5871 
5872   int
5873   base_stream::do_printf (printf_format_list& fmt_list,
5874                           const octave_value_list& args,
5875                           const std::string& who)
5876   {
5877     int retval = 0;
5878 
5879     octave_idx_type nconv = fmt_list.num_conversions ();
5880 
5881     std::ostream *osp = output_stream ();
5882 
5883     if (! osp)
5884       invalid_operation (who, "writing");
5885     else
5886       {
5887         std::ostream& os = *osp;
5888 
5889         const printf_format_elt *elt = fmt_list.first ();
5890 
5891         printf_value_cache val_cache (args, who);
5892 
5893         for (;;)
5894           {
5895             octave_quit ();
5896 
5897             if (! elt)
5898               ::error ("%s: internal error handling format", who.c_str ());
5899 
5900             // NSA is the number of 'star' args to convert.
5901             int nsa = (elt->fw == -2) + (elt->prec == -2);
5902 
5903             int sa_1 = 0;
5904             int sa_2 = 0;
5905 
5906             if (nsa > 0)
5907               {
5908                 sa_1 = val_cache.int_value ();
5909 
5910                 if (! val_cache)
5911                   {
5912                     field_width_error (who);
5913                     break;
5914                   }
5915                 else
5916                   {
5917                     if (nsa > 1)
5918                       {
5919                         sa_2 = val_cache.int_value ();
5920 
5921                         if (! val_cache)
5922                           {
5923                             field_width_error (who);
5924                             break;
5925                           }
5926                       }
5927                   }
5928               }
5929 
5930             if (elt->type == '%')
5931               {
5932                 if (encoding ().compare ("utf-8"))
5933                   os << string::u8_to_encoding (who, "%", encoding ());
5934                 else
5935                   os << '%';
5936                 retval++;
5937               }
5938             else if (elt->args == 0 && ! elt->text.empty ())
5939               {
5940                 if (encoding ().compare ("utf-8"))
5941                   os << string::u8_to_encoding (who, elt->text, encoding ());
5942                 else
5943                   os << elt->text;
5944                 retval += (elt->text.length ());
5945               }
5946             else if (elt->type == 's' || elt->type == 'c')
5947               {
5948                 octave_value val = val_cache.get_next_value (elt->type);
5949 
5950                 if (val_cache)
5951                   {
5952                     if (val.is_string ())
5953                       {
5954                         std::string sval = val.string_value ();
5955 
5956                         retval += do_printf_string (os, elt, nsa, sa_1,
5957                                                     sa_2, sval, encoding (),
5958                                                     who);
5959                       }
5960                     else
5961                       retval += do_numeric_printf_conv (os, elt, nsa, sa_1,
5962                                                         sa_2, val, who);
5963                   }
5964                 else
5965                   break;
5966               }
5967             else
5968               {
5969                 octave_value val = val_cache.get_next_value ();
5970 
5971                 if (val_cache)
5972                   {
5973                     if (! val.isempty ())
5974                       retval += do_numeric_printf_conv (os, elt, nsa, sa_1,
5975                                                         sa_2, val, who);
5976                   }
5977                 else
5978                   break;
5979               }
5980 
5981             if (! os)
5982               {
5983                 error (who, "write error");
5984                 break;
5985               }
5986 
5987             elt = fmt_list.next (nconv > 0 && ! val_cache.exhausted ());
5988 
5989             if (! elt || (val_cache.exhausted () && elt->args > 0))
5990               break;
5991           }
5992       }
5993 
5994     return retval;
5995   }
5996 
5997   int
5998   base_stream::printf (const std::string& fmt,
5999                        const octave_value_list& args,
6000                        const std::string& who)
6001   {
6002     printf_format_list fmt_list (fmt);
6003 
6004     if (fmt_list.num_conversions () == -1)
6005       ::error ("%s: invalid format specified", who.c_str ());
6006 
6007     return do_printf (fmt_list, args, who);
6008   }
6009 
6010   int
6011   base_stream::puts (const std::string& s, const std::string& who)
6012   {
6013     int retval = -1;
6014 
6015     std::ostream *osp = output_stream ();
6016 
6017     if (! osp)
6018       invalid_operation (who, "writing");
6019     else
6020       {
6021         std::ostream& os = *osp;
6022 
6023         os << s;
6024 
6025         if (! os)
6026           error (who, "write error");
6027         else
6028           {
6029             // FIXME: why does this seem to be necessary?
6030             // Without it, output from a loop like
6031             //
6032             //   for i = 1:100, fputs (stdout, "foo\n"); endfor
6033             //
6034             // doesn't seem to go to the pager immediately.
6035             os.flush ();
6036 
6037             if (os)
6038               retval = 0;
6039             else
6040               error (who, "write error");
6041           }
6042       }
6043 
6044     return retval;
6045   }
6046 
6047   // Return current error message for this stream.
6048 
6049   std::string
6050   base_stream::error (bool clear_err, int& err_num)
6051   {
6052     err_num = (m_fail ? -1 : 0);
6053 
6054     std::string tmp = m_errmsg;
6055 
6056     if (clear_err)
6057       clear ();
6058 
6059     return tmp;
6060   }
6061 
6062   void
6063   base_stream::invalid_operation (const std::string& who, const char *rw)
6064   {
6065     // Note: This calls the member fcn error, not ::error from error.h.
6066     error (who, std::string ("stream not open for ") + rw);
6067   }
6068 
6069   int
6070   stream::flush (void)
6071   {
6072     int retval = -1;
6073 
6074     if (stream_ok ())
6075       retval = m_rep->flush ();
6076 
6077     return retval;
6078   }
6079 
6080   std::string
6081   stream::getl (octave_idx_type max_len, bool& err, const std::string& who)
6082   {
6083     std::string retval;
6084 
6085     if (stream_ok ())
6086       retval = m_rep->getl (max_len, err, who);
6087 
6088     return retval;
6089   }
6090 
6091   std::string
6092   stream::getl (const octave_value& tc_max_len, bool& err,
6093                 const std::string& who)
6094   {
6095     err = false;
6096 
6097     int conv_err = 0;
6098 
6099     int max_len = -1;
6100 
6101     if (tc_max_len.is_defined ())
6102       {
6103         max_len = convert_to_valid_int (tc_max_len, conv_err);
6104 
6105         if (conv_err || max_len < 0)
6106           {
6107             err = true;
6108             ::error ("%s: invalid maximum length specified", who.c_str ());
6109           }
6110       }
6111 
6112     return getl (max_len, err, who);
6113   }
6114 
6115   std::string
6116   stream::gets (octave_idx_type max_len, bool& err, const std::string& who)
6117   {
6118     std::string retval;
6119 
6120     if (stream_ok ())
6121       retval = m_rep->gets (max_len, err, who);
6122 
6123     return retval;
6124   }
6125 
6126   std::string
6127   stream::gets (const octave_value& tc_max_len, bool& err,
6128                 const std::string& who)
6129   {
6130     err = false;
6131 
6132     int conv_err = 0;
6133 
6134     int max_len = -1;
6135 
6136     if (tc_max_len.is_defined ())
6137       {
6138         max_len = convert_to_valid_int (tc_max_len, conv_err);
6139 
6140         if (conv_err || max_len < 0)
6141           {
6142             err = true;
6143             ::error ("%s: invalid maximum length specified", who.c_str ());
6144           }
6145       }
6146 
6147     return gets (max_len, err, who);
6148   }
6149 
6150   off_t
6151   stream::skipl (off_t count, bool& err, const std::string& who)
6152   {
6153     off_t retval = -1;
6154 
6155     if (stream_ok ())
6156       retval = m_rep->skipl (count, err, who);
6157 
6158     return retval;
6159   }
6160 
6161   off_t
6162   stream::skipl (const octave_value& tc_count, bool& err,
6163                  const std::string& who)
6164   {
6165     err = false;
6166 
6167     int conv_err = 0;
6168 
6169     int count = 1;
6170 
6171     if (tc_count.is_defined ())
6172       {
6173         if (tc_count.is_scalar_type ()
6174             && math::isinf (tc_count.scalar_value ()))
6175           count = -1;
6176         else
6177           {
6178             count = convert_to_valid_int (tc_count, conv_err);
6179 
6180             if (conv_err || count < 0)
6181               {
6182                 err = true;
6183                 ::error ("%s: invalid number of lines specified", who.c_str ());
6184               }
6185           }
6186       }
6187 
6188     return skipl (count, err, who);
6189   }
6190 
6191   int
6192   stream::seek (off_t offset, int origin)
6193   {
6194     int status = -1;
6195 
6196     if (stream_ok ())
6197       {
6198         clearerr ();
6199 
6200         // Find current position so we can return to it if needed.
6201         off_t orig_pos = m_rep->tell ();
6202 
6203         // Move to end of file.  If successful, find the offset of the end.
6204         status = m_rep->seek (0, SEEK_END);
6205 
6206         if (status == 0)
6207           {
6208             off_t eof_pos = m_rep->tell ();
6209 
6210             if (origin == SEEK_CUR)
6211               {
6212                 // Move back to original position, otherwise we will be seeking
6213                 // from the end of file which is probably not the original
6214                 // location.
6215                 m_rep->seek (orig_pos, SEEK_SET);
6216               }
6217 
6218             // Attempt to move to desired position; may be outside bounds of
6219             // existing file.
6220             status = m_rep->seek (offset, origin);
6221 
6222             if (status == 0)
6223               {
6224                 // Where are we after moving to desired position?
6225                 off_t desired_pos = m_rep->tell ();
6226 
6227                 // I don't think save_pos can be less than zero,
6228                 // but we'll check anyway...
6229                 if (desired_pos > eof_pos || desired_pos < 0)
6230                   {
6231                     // Seek outside bounds of file.
6232                     // Failure should leave position unchanged.
6233                     m_rep->seek (orig_pos, SEEK_SET);
6234 
6235                     status = -1;
6236                   }
6237               }
6238             else
6239               {
6240                 // Seeking to the desired position failed.
6241                 // Move back to original position and return failure status.
6242                 m_rep->seek (orig_pos, SEEK_SET);
6243 
6244                 status = -1;
6245               }
6246           }
6247       }
6248 
6249     return status;
6250   }
6251 
6252   int
6253   stream::seek (const octave_value& tc_offset,
6254                 const octave_value& tc_origin)
6255   {
6256     int retval = -1;
6257 
6258     // FIXME: should we have octave_value methods that handle off_t explicitly?
6259     octave_int64 val = tc_offset.xint64_scalar_value ("fseek: invalid value for offset");
6260     off_t xoffset = val.value ();
6261 
6262     int conv_err = 0;
6263 
6264     int origin = SEEK_SET;
6265 
6266     if (tc_origin.is_string ())
6267       {
6268         std::string xorigin = tc_origin.xstring_value ("fseek: invalid value for origin");
6269 
6270         if (xorigin == "bof")
6271           origin = SEEK_SET;
6272         else if (xorigin == "cof")
6273           origin = SEEK_CUR;
6274         else if (xorigin == "eof")
6275           origin = SEEK_END;
6276         else
6277           conv_err = -1;
6278       }
6279     else
6280       {
6281         int xorigin = convert_to_valid_int (tc_origin, conv_err);
6282 
6283         if (! conv_err)
6284           {
6285             if (xorigin == -1)
6286               origin = SEEK_SET;
6287             else if (xorigin == 0)
6288               origin = SEEK_CUR;
6289             else if (xorigin == 1)
6290               origin = SEEK_END;
6291             else
6292               conv_err = -1;
6293           }
6294       }
6295 
6296     if (conv_err)
6297       ::error ("fseek: invalid value for origin");
6298 
6299     retval = seek (xoffset, origin);
6300 
6301     if (retval != 0)
6302       // Note: error is member fcn from stream, not ::error.
6303       error ("fseek: failed to seek to requested position");
6304 
6305     return retval;
6306   }
6307 
6308   off_t
6309   stream::tell (void)
6310   {
6311     off_t retval = -1;
6312 
6313     if (stream_ok ())
6314       retval = m_rep->tell ();
6315 
6316     return retval;
6317   }
6318 
6319   int
6320   stream::rewind (void)
6321   {
6322     return seek (0, SEEK_SET);
6323   }
6324 
6325   bool
6326   stream::is_open (void) const
6327   {
6328     bool retval = false;
6329 
6330     if (stream_ok ())
6331       retval = m_rep->is_open ();
6332 
6333     return retval;
6334   }
6335 
6336   void
6337   stream::close (void)
6338   {
6339     if (stream_ok ())
6340       m_rep->close ();
6341   }
6342 
6343   // FIXME: maybe these should be defined in lo-ieee.h?
6344 
6345   template <typename T>
6346   static inline bool
6347   is_old_NA (T)
6348   {
6349     return false;
6350   }
6351 
6352   template <>
6353   inline bool
6354   is_old_NA<double> (double val)
6355   {
6356     return __lo_ieee_is_old_NA (val);
6357   }
6358 
6359   template <typename T>
6360   static inline T
6361   replace_old_NA (T val)
6362   {
6363     return val;
6364   }
6365 
6366   template <>
6367   inline double
6368   replace_old_NA<double> (double val)
6369   {
6370     return __lo_ieee_replace_old_NA (val);
6371   }
6372 
6373   template <typename SRC_T, typename DST_T>
6374   static octave_value
6375   convert_and_copy (std::list<void *>& input_buf_list,
6376                     octave_idx_type input_buf_elts,
6377                     octave_idx_type elts_read,
6378                     octave_idx_type nr, octave_idx_type nc, bool swap,
6379                     bool do_float_fmt_conv, bool do_NA_conv,
6380                     mach_info::float_format from_flt_fmt)
6381   {
6382     typedef typename DST_T::element_type dst_elt_type;
6383 
6384     DST_T conv (dim_vector (nr, nc));
6385 
6386     dst_elt_type *conv_data = conv.fortran_vec ();
6387 
6388     octave_idx_type j = 0;
6389 
6390     for (auto it = input_buf_list.cbegin (); it != input_buf_list.cend (); it++)
6391       {
6392         SRC_T *data = static_cast<SRC_T *> (*it);
6393 
6394         if (swap || do_float_fmt_conv)
6395           {
6396             if (do_NA_conv)
6397               {
6398                 for (octave_idx_type i = 0; i < input_buf_elts && j < elts_read;
6399                      i++, j++)
6400                   {
6401                     if (swap)
6402                       swap_bytes<sizeof (SRC_T)> (&data[i]);
6403                     else if (do_float_fmt_conv)
6404                       do_float_format_conversion (&data[i], sizeof (SRC_T),
6405                                                   1, from_flt_fmt,
6406                                                   mach_info::native_float_format ());
6407 
6408                     dst_elt_type tmp (data[i]);
6409 
6410                     if (is_old_NA (tmp))
6411                       tmp = replace_old_NA (tmp);
6412 
6413                     conv_data[j] = tmp;
6414                   }
6415               }
6416             else
6417               {
6418                 for (octave_idx_type i = 0; i < input_buf_elts && j < elts_read;
6419                      i++, j++)
6420                   {
6421                     if (swap)
6422                       swap_bytes<sizeof (SRC_T)> (&data[i]);
6423                     else if (do_float_fmt_conv)
6424                       do_float_format_conversion (&data[i], sizeof (SRC_T),
6425                                                   1, from_flt_fmt,
6426                                                   mach_info::native_float_format ());
6427 
6428                     conv_data[j] = data[i];
6429                   }
6430               }
6431           }
6432         else
6433           {
6434             if (do_NA_conv)
6435               {
6436                 for (octave_idx_type i = 0; i < input_buf_elts && j < elts_read;
6437                      i++, j++)
6438                   {
6439                     dst_elt_type tmp (data[i]);
6440 
6441                     if (is_old_NA (tmp))
6442                       tmp = replace_old_NA (tmp);
6443 
6444                     conv_data[j] = tmp;
6445                   }
6446               }
6447             else
6448               {
6449                 for (octave_idx_type i = 0; i < input_buf_elts && j < elts_read;
6450                      i++, j++)
6451                   conv_data[j] = data[i];
6452               }
6453           }
6454 
6455         delete [] data;
6456       }
6457 
6458     input_buf_list.clear ();
6459 
6460     for (octave_idx_type i = elts_read; i < nr * nc; i++)
6461       conv_data[i] = dst_elt_type (0);
6462 
6463     return conv;
6464   }
6465 
6466   typedef octave_value (*conv_fptr)
6467     (std::list<void *>& input_buf_list, octave_idx_type input_buf_elts,
6468      octave_idx_type elts_read, octave_idx_type nr, octave_idx_type nc,
6469      bool swap, bool do_float_fmt_conv, bool do_NA_conv,
6470      mach_info::float_format from_flt_fmt);
6471 
6472 #define TABLE_ELT(T, U, V, W)                                           \
6473   conv_fptr_table[oct_data_conv::T][oct_data_conv::U] = convert_and_copy<V, W>
6474 
6475 #define FILL_TABLE_ROW(T, V)                    \
6476   TABLE_ELT (T, dt_int8, V, int8NDArray);       \
6477   TABLE_ELT (T, dt_uint8, V, uint8NDArray);     \
6478   TABLE_ELT (T, dt_int16, V, int16NDArray);     \
6479   TABLE_ELT (T, dt_uint16, V, uint16NDArray);   \
6480   TABLE_ELT (T, dt_int32, V, int32NDArray);     \
6481   TABLE_ELT (T, dt_uint32, V, uint32NDArray);   \
6482   TABLE_ELT (T, dt_int64, V, int64NDArray);     \
6483   TABLE_ELT (T, dt_uint64, V, uint64NDArray);   \
6484   TABLE_ELT (T, dt_single, V, FloatNDArray);    \
6485   TABLE_ELT (T, dt_double, V, NDArray);         \
6486   TABLE_ELT (T, dt_char, V, charNDArray);       \
6487   TABLE_ELT (T, dt_schar, V, charNDArray);      \
6488   TABLE_ELT (T, dt_uchar, V, charNDArray);      \
6489   TABLE_ELT (T, dt_logical, V, boolNDArray);
6490 
6491   octave_value
6492   stream::finalize_read (std::list<void *>& input_buf_list,
6493                          octave_idx_type input_buf_elts,
6494                          octave_idx_type elts_read,
6495                          octave_idx_type nr, octave_idx_type nc,
6496                          oct_data_conv::data_type input_type,
6497                          oct_data_conv::data_type output_type,
6498                          mach_info::float_format ffmt)
6499   {
6500     octave_value retval;
6501 
6502     static bool initialized = false;
6503 
6504     // Table function pointers for return types x read types.
6505 
6506     static conv_fptr conv_fptr_table[oct_data_conv::dt_unknown][14];
6507 
6508     if (! initialized)
6509       {
6510         for (int i = 0; i < oct_data_conv::dt_unknown; i++)
6511           for (int j = 0; j < 14; j++)
6512             conv_fptr_table[i][j] = nullptr;
6513 
6514         FILL_TABLE_ROW (dt_int8, int8_t);
6515         FILL_TABLE_ROW (dt_uint8, uint8_t);
6516         FILL_TABLE_ROW (dt_int16, int16_t);
6517         FILL_TABLE_ROW (dt_uint16, uint16_t);
6518         FILL_TABLE_ROW (dt_int32, int32_t);
6519         FILL_TABLE_ROW (dt_uint32, uint32_t);
6520         FILL_TABLE_ROW (dt_int64, int64_t);
6521         FILL_TABLE_ROW (dt_uint64, uint64_t);
6522         FILL_TABLE_ROW (dt_single, float);
6523         FILL_TABLE_ROW (dt_double, double);
6524         FILL_TABLE_ROW (dt_char, char);
6525         FILL_TABLE_ROW (dt_schar, signed char);
6526         FILL_TABLE_ROW (dt_uchar, unsigned char);
6527         FILL_TABLE_ROW (dt_logical, bool);
6528 
6529         initialized = true;
6530       }
6531 
6532     bool swap = false;
6533 
6534     if (ffmt == mach_info::flt_fmt_unknown)
6535       ffmt = float_format ();
6536 
6537     if (mach_info::words_big_endian ())
6538       swap = (ffmt == mach_info::flt_fmt_ieee_little_endian);
6539     else
6540       swap = (ffmt == mach_info::flt_fmt_ieee_big_endian);
6541 
6542     bool do_float_fmt_conv = ((input_type == oct_data_conv::dt_double
6543                                || input_type == oct_data_conv::dt_single)
6544                               && ffmt != float_format ());
6545 
6546     bool do_NA_conv = (output_type == oct_data_conv::dt_double);
6547 
6548     switch (output_type)
6549       {
6550       case oct_data_conv::dt_int8:
6551       case oct_data_conv::dt_uint8:
6552       case oct_data_conv::dt_int16:
6553       case oct_data_conv::dt_uint16:
6554       case oct_data_conv::dt_int32:
6555       case oct_data_conv::dt_uint32:
6556       case oct_data_conv::dt_int64:
6557       case oct_data_conv::dt_uint64:
6558       case oct_data_conv::dt_single:
6559       case oct_data_conv::dt_double:
6560       case oct_data_conv::dt_char:
6561       case oct_data_conv::dt_schar:
6562       case oct_data_conv::dt_uchar:
6563       case oct_data_conv::dt_logical:
6564         {
6565           conv_fptr fptr = conv_fptr_table[input_type][output_type];
6566 
6567           retval = fptr (input_buf_list, input_buf_elts, elts_read,
6568                          nr, nc, swap, do_float_fmt_conv, do_NA_conv, ffmt);
6569         }
6570         break;
6571 
6572       default:
6573         ::error ("read: invalid type specification");
6574       }
6575 
6576     return retval;
6577   }
6578 
6579   octave_value
6580   stream::read (const Array<double>& size, octave_idx_type block_size,
6581                 oct_data_conv::data_type input_type,
6582                 oct_data_conv::data_type output_type,
6583                 octave_idx_type skip, mach_info::float_format ffmt,
6584                 octave_idx_type& count)
6585   {
6586     octave_value retval;
6587 
6588     if (! stream_ok ())
6589       return retval;
6590 
6591     octave_idx_type nr = -1;
6592     octave_idx_type nc = -1;
6593 
6594     bool one_elt_size_spec = false;
6595 
6596     // FIXME: We may eventually want to make this extensible.
6597 
6598     // FIXME: We need a better way to ensure that this numbering stays
6599     // consistent with the order of the elements in the data_type enum in the
6600     // oct_data_conv class.
6601 
6602     // Expose this in a future version?
6603     std::size_t char_count = 0;
6604 
6605     std::ptrdiff_t tmp_count = 0;
6606 
6607     try
6608       {
6609         get_size (size, nr, nc, one_elt_size_spec, "fread");
6610       }
6611     catch (const execution_exception&)
6612       {
6613         invalid_operation ("fread", "reading");
6614 
6615         return retval;
6616       }
6617 
6618     if (one_elt_size_spec)
6619       {
6620         // If NR == 0, Matlab returns [](0x0).
6621 
6622         // If NR > 0, the result will be a column vector with the given
6623         // number of rows.
6624 
6625         // If NR < 0, then we have Inf and the result will be a column
6626         // vector but we have to wait to see how big NR will be.
6627 
6628         if (nr == 0)
6629           nr = nc = 0;
6630         else
6631           nc = 1;
6632       }
6633     else
6634       {
6635         // Matlab returns [] even if there are two elements in the size
6636         // specification and one is nonzero.
6637 
6638         // If NC < 0 we have [NR, Inf] and we'll wait to decide how big NC
6639         // should be.
6640 
6641         if (nr == 0 || nc == 0)
6642           nr = nc = 0;
6643       }
6644 
6645     octave_idx_type elts_to_read = nr * nc;
6646 
6647     bool read_to_eof = elts_to_read < 0;
6648 
6649     octave_idx_type input_buf_elts = -1;
6650 
6651     if (skip == 0)
6652       {
6653         if (read_to_eof)
6654           input_buf_elts = 1024 * 1024;
6655         else
6656           input_buf_elts = elts_to_read;
6657       }
6658     else
6659       input_buf_elts = block_size;
6660 
6661     octave_idx_type input_elt_size
6662       = oct_data_conv::data_type_size (input_type);
6663 
6664     std::ptrdiff_t input_buf_size
6665       = static_cast<std::ptrdiff_t> (input_buf_elts) * input_elt_size;
6666 
6667     assert (input_buf_size >= 0);
6668 
6669     // Must also work and return correct type object for 0 elements to read.
6670     std::istream *isp = input_stream ();
6671 
6672     if (! isp)
6673       error ("fread: invalid input stream");
6674     else
6675       {
6676         std::istream& is = *isp;
6677 
6678         // Initialize eof_pos variable just once per function call
6679         off_t eof_pos = 0;
6680         off_t cur_pos = 0;
6681         if (skip != 0 && is && ! is.eof ())
6682           {
6683             cur_pos = is.tellg ();
6684             is.seekg (0, is.end);
6685             eof_pos = is.tellg ();
6686             is.seekg (cur_pos, is.beg);
6687           }
6688 
6689         std::list<void *> input_buf_list;
6690 
6691         while (is && ! is.eof ()
6692                && (read_to_eof || tmp_count < elts_to_read))
6693           {
6694             if (! read_to_eof)
6695               {
6696                 octave_idx_type remaining_elts = elts_to_read - tmp_count;
6697 
6698                 if (remaining_elts < input_buf_elts)
6699                   input_buf_size = remaining_elts * input_elt_size;
6700               }
6701 
6702             char *input_buf = new char [input_buf_size];
6703 
6704             is.read (input_buf, input_buf_size);
6705 
6706             std::size_t gcount = is.gcount ();
6707 
6708             char_count += gcount;
6709             cur_pos += gcount;
6710 
6711             octave_idx_type nel = gcount / input_elt_size;
6712 
6713             tmp_count += nel;
6714 
6715             input_buf_list.push_back (input_buf);
6716 
6717             if (skip != 0 && nel == block_size && is)
6718               {
6719                 // Attempt to skip.
6720                 // If skip would move past EOF, position at EOF.
6721                 off_t remaining = eof_pos - cur_pos;
6722 
6723                 if (remaining < skip)
6724                   {
6725                     is.seekg (0, is.end);
6726                     cur_pos = eof_pos;
6727                   }
6728                 else
6729                   {
6730                     is.seekg (skip, is.cur);
6731                     cur_pos += skip;
6732                   }
6733               }
6734           }
6735 
6736         if (read_to_eof)
6737           {
6738             if (nc < 0)
6739               {
6740                 nc = tmp_count / nr;
6741 
6742                 if (tmp_count % nr != 0)
6743                   nc++;
6744               }
6745             else
6746               nr = tmp_count;
6747           }
6748         else if (tmp_count == 0)
6749           {
6750             nr = 0;
6751             nc = 0;
6752           }
6753         else if (tmp_count != nr * nc)
6754           {
6755             if (tmp_count % nr != 0)
6756               nc = tmp_count / nr + 1;
6757             else
6758               nc = tmp_count / nr;
6759 
6760             if (tmp_count < nr)
6761               nr = tmp_count;
6762           }
6763 
6764         if (tmp_count > std::numeric_limits<octave_idx_type>::max ())
6765           error ("fread: number of elements read exceeds max index size");
6766         else
6767           count = static_cast<octave_idx_type> (tmp_count);
6768 
6769         retval = finalize_read (input_buf_list, input_buf_elts, count,
6770                                 nr, nc, input_type, output_type, ffmt);
6771       }
6772 
6773     return retval;
6774   }
6775 
6776   octave_idx_type
6777   stream::write (const octave_value& data, octave_idx_type block_size,
6778                  oct_data_conv::data_type output_type,
6779                  octave_idx_type skip, mach_info::float_format flt_fmt)
6780   {
6781     octave_idx_type retval = -1;
6782 
6783     if (! stream_ok ())
6784       invalid_operation ("fwrite", "writing");
6785     else
6786       {
6787         if (flt_fmt == mach_info::flt_fmt_unknown)
6788           flt_fmt = float_format ();
6789 
6790         octave_idx_type status = data.write (*this, block_size, output_type,
6791                                              skip, flt_fmt);
6792 
6793         if (status < 0)
6794           error ("fwrite: write error");
6795         else
6796           retval = status;
6797       }
6798 
6799     return retval;
6800   }
6801 
6802   template <typename T, typename V>
6803   static void
6804   convert_chars (const void *data, void *conv_data, octave_idx_type n_elts)
6805   {
6806     const T *tt_data = static_cast<const T *> (data);
6807 
6808     V *vt_data = static_cast<V *> (conv_data);
6809 
6810     for (octave_idx_type i = 0; i < n_elts; i++)
6811       vt_data[i] = tt_data[i];
6812   }
6813 
6814   template <typename T, typename V>
6815   static void
6816   convert_ints (const T *data, void *conv_data, octave_idx_type n_elts,
6817                 bool swap)
6818   {
6819     typedef typename V::val_type val_type;
6820 
6821     val_type *vt_data = static_cast<val_type *> (conv_data);
6822 
6823     for (octave_idx_type i = 0; i < n_elts; i++)
6824       {
6825         // Yes, we want saturation semantics when converting to an integer type.
6826         V val (data[i]);
6827 
6828         vt_data[i] = val.value ();
6829 
6830         if (swap)
6831           swap_bytes<sizeof (val_type)> (&vt_data[i]);
6832       }
6833   }
6834 
6835   template <typename T>
6836   class ultimate_element_type
6837   {
6838   public:
6839     typedef T type;
6840   };
6841 
6842   template <typename T>
6843   class ultimate_element_type<octave_int<T>>
6844   {
6845   public:
6846     typedef T type;
6847   };
6848 
6849   template <typename T>
6850   static bool
6851   convert_data (const T *data, void *conv_data, octave_idx_type n_elts,
6852                 oct_data_conv::data_type output_type,
6853                 mach_info::float_format flt_fmt)
6854   {
6855     bool retval = true;
6856 
6857     bool swap = false;
6858 
6859     if (mach_info::words_big_endian ())
6860       swap = (flt_fmt == mach_info::flt_fmt_ieee_little_endian);
6861     else
6862       swap = (flt_fmt == mach_info::flt_fmt_ieee_big_endian);
6863 
6864     bool do_float_conversion = flt_fmt != mach_info::float_format ();
6865 
6866     typedef typename ultimate_element_type<T>::type ult_elt_type;
6867 
6868     switch (output_type)
6869       {
6870       case oct_data_conv::dt_char:
6871         convert_chars<ult_elt_type, char> (data, conv_data, n_elts);
6872         break;
6873 
6874       case oct_data_conv::dt_schar:
6875         convert_chars<ult_elt_type, signed char> (data, conv_data, n_elts);
6876         break;
6877 
6878       case oct_data_conv::dt_uchar:
6879         convert_chars<ult_elt_type, unsigned char> (data, conv_data, n_elts);
6880         break;
6881 
6882       case oct_data_conv::dt_int8:
6883         convert_ints<T, octave_int8> (data, conv_data, n_elts, swap);
6884         break;
6885 
6886       case oct_data_conv::dt_uint8:
6887         convert_ints<T, octave_uint8> (data, conv_data, n_elts, swap);
6888         break;
6889 
6890       case oct_data_conv::dt_int16:
6891         convert_ints<T, octave_int16> (data, conv_data, n_elts, swap);
6892         break;
6893 
6894       case oct_data_conv::dt_uint16:
6895         convert_ints<T, octave_uint16> (data, conv_data, n_elts, swap);
6896         break;
6897 
6898       case oct_data_conv::dt_int32:
6899         convert_ints<T, octave_int32> (data, conv_data, n_elts, swap);
6900         break;
6901 
6902       case oct_data_conv::dt_uint32:
6903         convert_ints<T, octave_uint32> (data, conv_data, n_elts, swap);
6904         break;
6905 
6906       case oct_data_conv::dt_int64:
6907         convert_ints<T, octave_int64> (data, conv_data, n_elts, swap);
6908         break;
6909 
6910       case oct_data_conv::dt_uint64:
6911         convert_ints<T, octave_uint64> (data, conv_data, n_elts, swap);
6912         break;
6913 
6914       case oct_data_conv::dt_single:
6915         {
6916           float *vt_data = static_cast<float *> (conv_data);
6917 
6918           for (octave_idx_type i = 0; i < n_elts; i++)
6919             {
6920               vt_data[i] = data[i];
6921 
6922               if (do_float_conversion)
6923                 do_float_format_conversion (&vt_data[i], 1, flt_fmt);
6924             }
6925         }
6926         break;
6927 
6928       case oct_data_conv::dt_double:
6929         {
6930           double *vt_data = static_cast<double *> (conv_data);
6931 
6932           for (octave_idx_type i = 0; i < n_elts; i++)
6933             {
6934               vt_data[i] = data[i];
6935 
6936               if (do_float_conversion)
6937                 do_double_format_conversion (&vt_data[i], 1, flt_fmt);
6938             }
6939         }
6940         break;
6941 
6942       default:
6943         ::error ("write: invalid type specification");
6944       }
6945 
6946     return retval;
6947   }
6948 
6949   bool
6950   stream::write_bytes (const void *data, std::size_t nbytes)
6951   {
6952     bool status = false;
6953 
6954     std::ostream *osp = output_stream ();
6955 
6956     if (osp)
6957       {
6958         std::ostream& os = *osp;
6959 
6960         if (os)
6961           {
6962             os.write (static_cast<const char *> (data), nbytes);
6963 
6964             if (os)
6965               status = true;
6966           }
6967       }
6968 
6969     return status;
6970   }
6971 
6972   bool
6973   stream::skip_bytes (std::size_t skip)
6974   {
6975     bool status = false;
6976 
6977     std::ostream *osp = output_stream ();
6978 
6979     if (! osp)
6980       return false;
6981 
6982     std::ostream& os = *osp;
6983 
6984     // Seek to skip when inside bounds of existing file.
6985     // Otherwise, write NUL to skip.
6986     off_t orig_pos = tell ();
6987 
6988     seek (0, SEEK_END);
6989 
6990     off_t eof_pos = tell ();
6991 
6992     // Is it possible for this to fail to return us to the original position?
6993     seek (orig_pos, SEEK_SET);
6994 
6995     std::size_t remaining = eof_pos - orig_pos;
6996 
6997     if (remaining < skip)
6998       {
6999         seek (0, SEEK_END);
7000 
7001         // FIXME: probably should try to write larger blocks...
7002         unsigned char zero = 0;
7003         for (std::size_t j = 0; j < skip - remaining; j++)
7004           os.write (reinterpret_cast<const char *> (&zero), 1);
7005       }
7006     else
7007       seek (skip, SEEK_CUR);
7008 
7009     if (os)
7010       status = true;
7011 
7012     return status;
7013   }
7014 
7015   template <typename T>
7016   octave_idx_type
7017   stream::write (const Array<T>& data, octave_idx_type block_size,
7018                  oct_data_conv::data_type output_type,
7019                  octave_idx_type skip,
7020                  mach_info::float_format flt_fmt)
7021   {
7022     bool swap = false;
7023 
7024     if (mach_info::words_big_endian ())
7025       swap = (flt_fmt == mach_info::flt_fmt_ieee_little_endian);
7026     else
7027       swap = (flt_fmt == mach_info::flt_fmt_ieee_big_endian);
7028 
7029     bool do_data_conversion = (swap || ! is_equivalent_type<T> (output_type)
7030                                || flt_fmt != mach_info::float_format ());
7031 
7032     octave_idx_type nel = data.numel ();
7033 
7034     octave_idx_type chunk_size;
7035 
7036     if (skip != 0)
7037       chunk_size = block_size;
7038     else if (do_data_conversion)
7039       chunk_size = 1024 * 1024;
7040     else
7041       chunk_size = nel;
7042 
7043     octave_idx_type i = 0;
7044 
7045     const T *pdata = data.data ();
7046 
7047     while (i < nel)
7048       {
7049         if (skip != 0)
7050           {
7051             if (! skip_bytes (skip))
7052               return -1;
7053           }
7054 
7055         octave_idx_type remaining_nel = nel - i;
7056 
7057         if (chunk_size > remaining_nel)
7058           chunk_size = remaining_nel;
7059 
7060         bool status = false;
7061 
7062         if (do_data_conversion)
7063           {
7064             std::size_t output_size
7065               = chunk_size * oct_data_conv::data_type_size (output_type);
7066 
7067             OCTAVE_LOCAL_BUFFER (unsigned char, conv_data, output_size);
7068 
7069             status = convert_data (&pdata[i], conv_data, chunk_size,
7070                                    output_type, flt_fmt);
7071 
7072             if (status)
7073               status = write_bytes (conv_data, output_size);
7074           }
7075         else
7076           status = write_bytes (pdata, sizeof (T) * chunk_size);
7077 
7078         if (! status)
7079           return -1;
7080 
7081         i += chunk_size;
7082       }
7083 
7084     return nel;
7085   }
7086 
7087 #define INSTANTIATE_WRITE(T)                                            \
7088   template                                                              \
7089   octave_idx_type                                                       \
7090   stream::write (const Array<T>& data, octave_idx_type block_size,      \
7091                  oct_data_conv::data_type output_type,                  \
7092                  octave_idx_type skip,                                  \
7093                  mach_info::float_format flt_fmt)
7094 
7095   INSTANTIATE_WRITE (octave_int8);
7096   INSTANTIATE_WRITE (octave_uint8);
7097   INSTANTIATE_WRITE (octave_int16);
7098   INSTANTIATE_WRITE (octave_uint16);
7099   INSTANTIATE_WRITE (octave_int32);
7100   INSTANTIATE_WRITE (octave_uint32);
7101   INSTANTIATE_WRITE (octave_int64);
7102   INSTANTIATE_WRITE (octave_uint64);
7103   INSTANTIATE_WRITE (int8_t);
7104   INSTANTIATE_WRITE (uint8_t);
7105   INSTANTIATE_WRITE (int16_t);
7106   INSTANTIATE_WRITE (uint16_t);
7107   INSTANTIATE_WRITE (int32_t);
7108   INSTANTIATE_WRITE (uint32_t);
7109   INSTANTIATE_WRITE (int64_t);
7110   INSTANTIATE_WRITE (uint64_t);
7111   INSTANTIATE_WRITE (bool);
7112 #if defined (OCTAVE_HAVE_OVERLOAD_CHAR_INT8_TYPES)
7113   INSTANTIATE_WRITE (char);
7114 #endif
7115   INSTANTIATE_WRITE (float);
7116   INSTANTIATE_WRITE (double);
7117 
7118   octave_value
7119   stream::scanf (const std::string& fmt, const Array<double>& size,
7120                  octave_idx_type& count, const std::string& who)
7121   {
7122     octave_value retval;
7123 
7124     if (stream_ok ())
7125       retval = m_rep->scanf (fmt, size, count, who);
7126 
7127     return retval;
7128   }
7129 
7130   octave_value
7131   stream::scanf (const octave_value& fmt, const Array<double>& size,
7132                  octave_idx_type& count, const std::string& who)
7133   {
7134     octave_value retval = Matrix ();
7135 
7136     if (fmt.is_string ())
7137       {
7138         std::string sfmt = fmt.string_value ();
7139 
7140         if (fmt.is_sq_string ())
7141           sfmt = do_string_escapes (sfmt);
7142 
7143         retval = scanf (sfmt, size, count, who);
7144       }
7145     else
7146       {
7147         // Note: error is member fcn from stream, not ::error.
7148         error (who + ": format must be a string");
7149       }
7150 
7151     return retval;
7152   }
7153 
7154   octave_value_list
7155   stream::oscanf (const std::string& fmt, const std::string& who)
7156   {
7157     octave_value_list retval;
7158 
7159     if (stream_ok ())
7160       retval = m_rep->oscanf (fmt, who);
7161 
7162     return retval;
7163   }
7164 
7165   octave_value_list
7166   stream::oscanf (const octave_value& fmt, const std::string& who)
7167   {
7168     octave_value_list retval;
7169 
7170     if (fmt.is_string ())
7171       {
7172         std::string sfmt = fmt.string_value ();
7173 
7174         if (fmt.is_sq_string ())
7175           sfmt = do_string_escapes (sfmt);
7176 
7177         retval = oscanf (sfmt, who);
7178       }
7179     else
7180       {
7181         // Note: error is member fcn from stream, not ::error.
7182         error (who + ": format must be a string");
7183       }
7184 
7185     return retval;
7186   }
7187 
7188   octave_value
7189   stream::textscan (const std::string& fmt, octave_idx_type ntimes,
7190                     const octave_value_list& options,
7191                     const std::string& who, octave_idx_type& count)
7192   {
7193     return (stream_ok ()
7194             ? m_rep->do_textscan (fmt, ntimes, options, who, count)
7195             : octave_value ());
7196   }
7197 
7198   int
7199   stream::printf (const std::string& fmt, const octave_value_list& args,
7200                   const std::string& who)
7201   {
7202     int retval = -1;
7203 
7204     if (stream_ok ())
7205       retval = m_rep->printf (fmt, args, who);
7206 
7207     return retval;
7208   }
7209 
7210   int
7211   stream::printf (const octave_value& fmt, const octave_value_list& args,
7212                   const std::string& who)
7213   {
7214     int retval = 0;
7215 
7216     if (fmt.is_string ())
7217       {
7218         std::string sfmt = fmt.string_value ();
7219 
7220         if (fmt.is_sq_string ())
7221           sfmt = do_string_escapes (sfmt);
7222 
7223         retval = printf (sfmt, args, who);
7224       }
7225     else
7226       {
7227         // Note: error is member fcn from stream, not ::error.
7228         error (who + ": format must be a string");
7229       }
7230 
7231     return retval;
7232   }
7233 
7234   int
7235   stream::puts (const std::string& s, const std::string& who)
7236   {
7237     int retval = -1;
7238 
7239     if (stream_ok ())
7240       retval = m_rep->puts (s, who);
7241 
7242     return retval;
7243   }
7244 
7245   // FIXME: maybe this should work for string arrays too.
7246 
7247   int
7248   stream::puts (const octave_value& tc_s, const std::string& who)
7249   {
7250     int retval = -1;
7251 
7252     if (tc_s.is_string ())
7253       {
7254         std::string s = tc_s.string_value ();
7255         retval = puts (s, who);
7256       }
7257     else
7258       {
7259         // Note: error is member fcn from stream, not ::error.
7260         error (who + ": argument must be a string");
7261       }
7262 
7263     return retval;
7264   }
7265 
7266   bool
7267   stream::eof (void) const
7268   {
7269     int retval = -1;
7270 
7271     if (stream_ok ())
7272       retval = m_rep->eof ();
7273 
7274     return retval;
7275   }
7276 
7277   std::string
7278   stream::error (bool clear, int& err_num)
7279   {
7280     std::string retval = "invalid stream object";
7281 
7282     if (stream_ok (false))
7283       retval = m_rep->error (clear, err_num);
7284 
7285     return retval;
7286   }
7287 
7288   std::string
7289   stream::name (void) const
7290   {
7291     std::string retval;
7292 
7293     if (stream_ok ())
7294       retval = m_rep->name ();
7295 
7296     return retval;
7297   }
7298 
7299   int
7300   stream::mode (void) const
7301   {
7302     int retval = 0;
7303 
7304     if (stream_ok ())
7305       retval = m_rep->mode ();
7306 
7307     return retval;
7308   }
7309 
7310   mach_info::float_format
7311   stream::float_format (void) const
7312   {
7313     mach_info::float_format retval = mach_info::flt_fmt_unknown;
7314 
7315     if (stream_ok ())
7316       retval = m_rep->float_format ();
7317 
7318     return retval;
7319   }
7320 
7321   std::string
7322   stream::mode_as_string (int mode)
7323   {
7324     std::string retval = "???";
7325     std::ios::openmode in_mode = static_cast<std::ios::openmode> (mode);
7326 
7327     if (in_mode == std::ios::in)
7328       retval = "r";
7329     else if (in_mode == std::ios::out
7330              || in_mode == (std::ios::out | std::ios::trunc))
7331       retval = "w";
7332     else if (in_mode == (std::ios::out | std::ios::app))
7333       retval = "a";
7334     else if (in_mode == (std::ios::in | std::ios::out))
7335       retval = "r+";
7336     else if (in_mode == (std::ios::in | std::ios::out | std::ios::trunc))
7337       retval = "w+";
7338     else if (in_mode == (std::ios::in | std::ios::out | std::ios::ate))
7339       retval = "a+";
7340     else if (in_mode == (std::ios::in | std::ios::binary))
7341       retval = "rb";
7342     else if (in_mode == (std::ios::out | std::ios::binary)
7343              || in_mode == (std::ios::out | std::ios::trunc | std::ios::binary))
7344       retval = "wb";
7345     else if (in_mode == (std::ios::out | std::ios::app | std::ios::binary))
7346       retval = "ab";
7347     else if (in_mode == (std::ios::in | std::ios::out | std::ios::binary))
7348       retval = "r+b";
7349     else if (in_mode == (std::ios::in | std::ios::out | std::ios::trunc
7350                          | std::ios::binary))
7351       retval = "w+b";
7352     else if (in_mode == (std::ios::in | std::ios::out | std::ios::ate
7353                          | std::ios::binary))
7354       retval = "a+b";
7355 
7356     return retval;
7357   }
7358 
7359   stream_list::stream_list (interpreter& interp)
7360     : m_list (), m_lookup_cache (m_list.end ()), m_stdin_file (-1),
7361       m_stdout_file (-1), m_stderr_file (-1)
7362   {
7363     stream stdin_stream = octave_istream::create (&std::cin, "stdin");
7364 
7365     // This uses octave_stdout (see pager.h), not std::cout so that
7366     // Octave's standard output stream will pass through the pager.
7367 
7368     // FIXME: we should be accessing octave_stdout from the interpreter.
7369 
7370     output_system& output_sys = interp.get_output_system ();
7371 
7372     stream stdout_stream
7373       = octave_ostream::create (&(output_sys.__stdout__ ()), "stdout");
7374 
7375     stream stderr_stream = octave_ostream::create (&std::cerr, "stderr");
7376 
7377     m_stdin_file = insert (stdin_stream);
7378     m_stdout_file = insert (stdout_stream);
7379     m_stderr_file = insert (stderr_stream);
7380   }
7381 
7382   stream_list::~stream_list (void)
7383   {
7384     clear ();
7385   }
7386 
7387   int stream_list::insert (stream& os)
7388   {
7389     // Insert item with key corresponding to file-descriptor.
7390 
7391     int stream_number = os.file_number ();
7392 
7393     if (stream_number == -1)
7394       return stream_number;
7395 
7396     // Should we test for
7397     //
7398     //  (m_list.find (stream_number) != m_list.end ()
7399     //   && m_list[stream_number].is_open ())
7400     //
7401     // and respond with "error ("internal error: ...")"?  It should not
7402     // happen except for some bug or if the user has opened a stream with
7403     // an interpreted command, but closed it directly with a system call
7404     // in an oct-file; then the kernel knows the fd is free, but Octave
7405     // does not know.  If it happens, it should not do harm here to simply
7406     // overwrite this entry, although the wrong entry might have done harm
7407     // before.
7408 
7409     if (m_list.size () >= m_list.max_size ())
7410       ::error ("could not create file id");
7411 
7412     m_list[stream_number] = os;
7413 
7414     return stream_number;
7415   }
7416 
7417   OCTAVE_NORETURN static
7418   void
7419   err_invalid_file_id (int fid, const std::string& who)
7420   {
7421     if (who.empty ())
7422       ::error ("invalid stream number = %d", fid);
7423     else
7424       ::error ("%s: invalid stream number = %d", who.c_str (), fid);
7425   }
7426 
7427   stream stream_list::lookup (int fid, const std::string& who) const
7428   {
7429     stream retval;
7430 
7431     if (fid < 0)
7432       err_invalid_file_id (fid, who);
7433 
7434     if (m_lookup_cache != m_list.end () && m_lookup_cache->first == fid)
7435       retval = m_lookup_cache->second;
7436     else
7437       {
7438         ostrl_map::const_iterator iter = m_list.find (fid);
7439 
7440         if (iter == m_list.end ())
7441           err_invalid_file_id (fid, who);
7442 
7443         retval = iter->second;
7444         m_lookup_cache = iter;
7445       }
7446 
7447     return retval;
7448   }
7449 
7450   stream stream_list::lookup (const octave_value& fid,
7451                               const std::string& who) const
7452   {
7453     int i = get_file_number (fid);
7454 
7455     return lookup (i, who);
7456   }
7457 
7458   int stream_list::remove (int fid, const std::string& who)
7459   {
7460     // Can't remove stdin (std::cin), stdout (std::cout), or stderr (std::cerr).
7461     if (fid < 3)
7462       err_invalid_file_id (fid, who);
7463 
7464     auto iter = m_list.find (fid);
7465 
7466     if (iter == m_list.end ())
7467       err_invalid_file_id (fid, who);
7468 
7469     stream os = iter->second;
7470     m_list.erase (iter);
7471     m_lookup_cache = m_list.end ();
7472 
7473     // FIXME: is this check redundant?
7474     if (! os.is_valid ())
7475       err_invalid_file_id (fid, who);
7476 
7477     os.close ();
7478 
7479     return 0;
7480   }
7481 
7482   int stream_list::remove (const octave_value& fid, const std::string& who)
7483   {
7484     int retval = -1;
7485 
7486     if (fid.is_string () && fid.string_value () == "all")
7487       {
7488         clear (false);
7489 
7490         retval = 0;
7491       }
7492     else
7493       {
7494         int i = get_file_number (fid);
7495 
7496         retval = remove (i, who);
7497       }
7498 
7499     return retval;
7500   }
7501 
7502   void stream_list::clear (bool flush)
7503   {
7504     if (flush)
7505       {
7506         // Flush stdout and stderr.
7507         m_list[1].flush ();
7508         m_list[2].flush ();
7509       }
7510 
7511     for (auto iter = m_list.begin (); iter != m_list.end (); )
7512       {
7513         int fid = iter->first;
7514         if (fid < 3)  // Don't delete stdin, stdout, stderr
7515           {
7516             iter++;
7517             continue;
7518           }
7519 
7520         stream os = iter->second;
7521 
7522         std::string name = os.name ();
7523         std::transform (name.begin (), name.end (), name.begin (), tolower);
7524 
7525         // FIXME: This test for gnuplot is hardly foolproof.
7526         if (name.find ("gnuplot") != std::string::npos)
7527           {
7528             // Don't close down pipes to gnuplot
7529             iter++;
7530             continue;
7531           }
7532 
7533         // Normal file handle.  Close and delete from m_list.
7534         if (os.is_valid ())
7535           os.close ();
7536 
7537         m_list.erase (iter++);
7538       }
7539 
7540     m_lookup_cache = m_list.end ();
7541   }
7542 
7543   string_vector stream_list::get_info (int fid) const
7544   {
7545     string_vector retval (4);
7546 
7547     if (fid < 0)
7548       return retval;
7549 
7550     stream os;
7551     if (m_lookup_cache != m_list.end () && m_lookup_cache->first == fid)
7552       os = m_lookup_cache->second;
7553     else
7554       {
7555         ostrl_map::const_iterator iter = m_list.find (fid);
7556 
7557         if (iter == m_list.end ())
7558           return retval;
7559 
7560         os = iter->second;
7561         m_lookup_cache = iter;
7562       }
7563 
7564     if (! os.is_valid ())
7565       return retval;
7566 
7567     retval(0) = os.name ();
7568     retval(1) = stream::mode_as_string (os.mode ());
7569     retval(2) = mach_info::float_format_as_string (os.float_format ());
7570     retval(3) = os.encoding ();
7571 
7572     return retval;
7573   }
7574 
7575   string_vector stream_list::get_info (const octave_value& fid) const
7576   {
7577     int conv_err = 0;
7578 
7579     if (fid.is_single_type ())
7580       ::error ("file id must be a file object or integer value");
7581 
7582     int int_fid = convert_to_valid_int (fid, conv_err);
7583 
7584     if (conv_err)
7585       ::error ("file id must be a file object or integer value");
7586 
7587     return get_info (int_fid);
7588   }
7589 
7590   std::string stream_list::list_open_files (void) const
7591   {
7592     std::ostringstream buf;
7593 
7594     buf << "\n"
7595         << "  number  mode  arch       name\n"
7596         << "  ------  ----  ----       ----\n";
7597 
7598     for (const auto& fid_strm : m_list)
7599       {
7600         stream os = fid_strm.second;
7601 
7602         buf << "  "
7603             << std::setiosflags (std::ios::right)
7604             << std::setw (4) << fid_strm.first << "     "
7605           // reset necessary in addition to setiosflags since this is one stmt.
7606             << std::resetiosflags (std::ios::adjustfield)
7607             << std::setiosflags (std::ios::left)
7608             << std::setw (3)
7609             << stream::mode_as_string (os.mode ())
7610             << "  "
7611             << std::setw (9)
7612             << mach_info::float_format_as_string (os.float_format ())
7613             << "  "
7614             << os.name () << "\n";
7615       }
7616 
7617     buf << "\n";
7618 
7619     return buf.str ();
7620   }
7621 
7622   octave_value stream_list::open_file_numbers (void) const
7623   {
7624     Matrix retval (1, m_list.size (), 0.0);
7625 
7626     int num_open = 0;
7627 
7628     for (const auto& fid_strm : m_list)
7629       {
7630         // Skip stdin, stdout, and stderr.
7631         if (fid_strm.first > 2 && fid_strm.second)
7632           retval(0, num_open++) = fid_strm.first;
7633       }
7634 
7635     retval.resize ((num_open > 0), num_open);
7636 
7637     return retval;
7638   }
7639 
7640   int stream_list::get_file_number (const octave_value& fid) const
7641   {
7642     int retval = -1;
7643 
7644     if (fid.is_string ())
7645       {
7646         std::string nm = fid.string_value ();
7647 
7648         for (const auto& fid_strm : m_list)
7649           {
7650             // stdin, stdout, and stderr are unnamed.
7651             if (fid_strm.first > 2)
7652               {
7653                 stream os = fid_strm.second;
7654 
7655                 if (os && os.name () == nm)
7656                   {
7657                     retval = fid_strm.first;
7658                     break;
7659                   }
7660               }
7661           }
7662       }
7663     else if (fid.is_single_type ())
7664       ::error ("file id must be a file object, std::string, or integer value");
7665     else
7666       {
7667         int conv_err = 0;
7668 
7669         int int_fid = convert_to_valid_int (fid, conv_err);
7670 
7671         if (conv_err)
7672           ::error ("file id must be a file object, std::string, or integer value");
7673 
7674         retval = int_fid;
7675       }
7676 
7677     return retval;
7678   }
7679 
7680   octave_value stream_list::stdin_file (void) const
7681   {
7682     return octave_value (m_stdin_file);
7683   }
7684 
7685   octave_value stream_list::stdout_file (void) const
7686   {
7687     return octave_value (m_stdout_file);
7688   }
7689 
7690   octave_value stream_list::stderr_file (void) const
7691   {
7692     return octave_value (m_stderr_file);
7693   }
7694 }
7695