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