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