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