1 //////////////////////////////////////////////////////////////////////// 2 // 3 // Copyright (C) 2006-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 <algorithm> 31 32 #include "dir-ops.h" 33 #include "file-ops.h" 34 #include "file-stat.h" 35 #include "oct-env.h" 36 #include "pathsearch.h" 37 38 #include "defaults.h" 39 #include "defun.h" 40 #include "input.h" 41 #include "interpreter-private.h" 42 #include "interpreter.h" 43 #include "load-path.h" 44 #include "ov-usr-fcn.h" 45 #include "pager.h" 46 #include "parse.h" 47 #include "sysdep.h" 48 #include "unwind-prot.h" 49 #include "utils.h" 50 51 namespace octave 52 { 53 // Canonicalize file name (keeping the path relative) if it exists. 54 // Return it unmodified otherwise. 55 56 static std::string maybe_canonicalize(const std::string & dir_arg)57 maybe_canonicalize (const std::string& dir_arg) 58 { 59 bool is_absolute_path = sys::env::absolute_pathname (dir_arg); 60 61 std::string canonical_dir = sys::canonicalize_file_name (dir_arg); 62 std::string dir; 63 if (canonical_dir.empty ()) 64 dir = dir_arg; 65 else 66 { 67 dir = canonical_dir; 68 69 if (! is_absolute_path) 70 { 71 // Remove current path from absolute path generated by 72 // canonicalize_file_name. 73 std::string cwd = sys::canonicalize_file_name ("."); 74 if (dir.compare (0, cwd.length (), cwd) == 0) 75 dir.erase (0, cwd.length ()+1); 76 if (dir.empty ()) 77 dir = "."; 78 } 79 } 80 81 return dir; 82 } 83 84 static void maybe_add_path_elts(std::string & path,const std::string & dir)85 maybe_add_path_elts (std::string& path, const std::string& dir) 86 { 87 std::string tpath = genpath (maybe_canonicalize (dir)); 88 89 if (! tpath.empty ()) 90 { 91 if (path.empty ()) 92 path = tpath; 93 else 94 path += directory_path::path_sep_str () + tpath; 95 } 96 } 97 98 static std::list<std::string> split_path(const std::string & p)99 split_path (const std::string& p) 100 { 101 std::list<std::string> retval; 102 103 std::size_t beg = 0; 104 std::size_t end = p.find (directory_path::path_sep_char ()); 105 106 std::size_t len = p.length (); 107 108 while (end != std::string::npos) 109 { 110 std::string elt = p.substr (beg, end-beg); 111 112 if (! elt.empty ()) 113 retval.push_back (elt); 114 115 beg = end + 1; 116 117 if (beg == len) 118 break; 119 120 end = p.find (directory_path::path_sep_char (), beg); 121 } 122 123 std::string elt = p.substr (beg); 124 125 if (! elt.empty ()) 126 retval.push_back (elt); 127 128 return retval; 129 } 130 131 // Strip trailing directory separators. 132 133 static std::string strip_trailing_separators(const std::string & dir_arg)134 strip_trailing_separators (const std::string& dir_arg) 135 { 136 std::string dir = dir_arg; 137 138 std::size_t k = dir.length (); 139 140 while (k > 1 && sys::file_ops::is_dir_sep (dir[k-1])) 141 k--; 142 143 if (k < dir.length ()) 144 dir.resize (k); 145 146 return dir; 147 } 148 149 // Should we cache all files in private directories, or is it OK to just 150 // look them up each time as needed? 151 152 static std::string find_private_file(const std::string & fname)153 find_private_file (const std::string& fname) 154 { 155 std::string retval; 156 157 // Look in private directory corresponding to current function (if 158 // any). 159 160 symbol_scope scope = __get_current_scope__ ("find_private_file"); 161 162 octave_user_code *curr_code = scope ? scope.user_code () : nullptr; 163 164 if (curr_code) 165 { 166 // Even for private functions, dir_name doesn't contain the 167 // "private" directory component so we append it here in all 168 // cases. 169 170 std::string dir_name = curr_code->dir_name (); 171 172 if (! dir_name.empty ()) 173 { 174 std::string pfname = dir_name + sys::file_ops::dir_sep_str () 175 + "private" + sys::file_ops::dir_sep_str () 176 + fname; 177 178 sys::file_stat fs (pfname); 179 180 if (fs.exists () && fs.is_reg ()) 181 retval = pfname; 182 } 183 } 184 185 return retval; 186 } 187 188 // True if a path is contained in a path list separated by path_sep_char 189 190 static bool in_path_list(const std::string & path_list,const std::string & path)191 in_path_list (const std::string& path_list, const std::string& path) 192 { 193 std::size_t ps = path.size (); 194 std::size_t pls = path_list.size (); 195 std::size_t pos = path_list.find (path); 196 char psc = directory_path::path_sep_char (); 197 while (pos != std::string::npos) 198 { 199 if ((pos == 0 || path_list[pos-1] == psc) 200 && (pos + ps == pls || path_list[pos + ps] == psc)) 201 return true; 202 else 203 pos = path_list.find (path, pos + 1); 204 } 205 206 return false; 207 } 208 209 static void rehash_internal(void)210 rehash_internal (void) 211 { 212 load_path& lp = __get_load_path__ ("rehash_internal"); 213 214 lp.update (); 215 216 // Signal the GUI allowing updating the load path dialog 217 event_manager& evmgr = __get_event_manager__ ("rehash_internal"); 218 evmgr.update_path_dialog (); 219 220 // FIXME: maybe we should rename this variable since it is being 221 // used for more than keeping track of the prompt time. 222 223 // This will force updated functions to be found. 224 Vlast_prompt_time.stamp (); 225 } 226 227 std::string load_path::sys_path; 228 load_path::abs_dir_cache_type load_path::abs_dir_cache; 229 load_path(interpreter & interp)230 load_path::load_path (interpreter& interp) 231 : m_interpreter (interp), package_map (), top_level_package (), 232 dir_info_list (), init_dirs (), m_command_line_path (), 233 add_hook ([this] (const std::string& dir) { this->execute_pkg_add (dir); }), __anon29c60d5d0202(const std::string& dir) 234 remove_hook ([this] (const std::string& dir) { this->execute_pkg_del (dir); }) 235 { } 236 237 void initialize(bool set_initial_path)238 load_path::initialize (bool set_initial_path) 239 { 240 sys_path = ""; 241 242 if (set_initial_path) 243 { 244 maybe_add_path_elts (sys_path, config::local_ver_oct_file_dir ()); 245 maybe_add_path_elts (sys_path, config::local_api_oct_file_dir ()); 246 maybe_add_path_elts (sys_path, config::local_oct_file_dir ()); 247 maybe_add_path_elts (sys_path, config::local_ver_fcn_file_dir ()); 248 maybe_add_path_elts (sys_path, config::local_api_fcn_file_dir ()); 249 maybe_add_path_elts (sys_path, config::local_fcn_file_dir ()); 250 maybe_add_path_elts (sys_path, config::oct_file_dir ()); 251 maybe_add_path_elts (sys_path, config::fcn_file_dir ()); 252 maybe_add_path_elts (sys_path, config::oct_data_dir ()); 253 } 254 255 std::string tpath = load_path::m_command_line_path; 256 257 if (tpath.empty ()) 258 tpath = sys::env::getenv ("OCTAVE_PATH"); 259 260 std::string xpath; 261 262 if (! tpath.empty ()) 263 { 264 xpath = tpath; 265 266 if (! sys_path.empty ()) 267 xpath += directory_path::path_sep_str () + sys_path; 268 } 269 else 270 xpath = sys_path; 271 272 set (xpath, false, true); 273 } 274 275 void clear(void)276 load_path::clear (void) 277 { 278 dir_info_list.clear (); 279 280 top_level_package.clear (); 281 282 package_map.clear (); 283 } 284 285 void set(const std::string & p,bool warn,bool is_init)286 load_path::set (const std::string& p, bool warn, bool is_init) 287 { 288 // Use a list when we need to preserve order. 289 std::list<std::string> elts = split_path (p); 290 291 for (auto& elt : elts) 292 elt = maybe_canonicalize (elt); 293 294 // Use a set when we need to search and order is not important. 295 std::set<std::string> elts_set (elts.begin (), elts.end ()); 296 297 if (is_init) 298 init_dirs = elts_set; 299 else 300 { 301 for (const auto& init_dir : init_dirs) 302 { 303 if (elts_set.find (init_dir) == elts_set.end ()) 304 { 305 warning_with_id ("Octave:remove-init-dir", 306 "default load path altered. Some built-in functions may not be found. Try restoredefaultpath() to recover it."); 307 break; 308 } 309 } 310 } 311 312 // Temporarily disable add hook. 313 314 unwind_protect frame; 315 frame.protect_var (add_hook); 316 317 add_hook = nullptr; 318 319 clear (); 320 321 for (const auto& elt : elts) 322 append (elt, warn); 323 324 // Restore add hook and execute for all newly added directories. 325 frame.run_first (); 326 327 // FIXME: Shouldn't the test for add_hook be outside the for loop? 328 // Why not use const here? Does add_hook change dir_info_list? 329 for (auto& di : dir_info_list) 330 { 331 if (add_hook) 332 add_hook (di.dir_name); 333 } 334 335 // Always prepend current directory. 336 prepend (".", warn); 337 } 338 339 void append(const std::string & dir,bool warn)340 load_path::append (const std::string& dir, bool warn) 341 { 342 if (! dir.empty ()) 343 add (dir, true, warn); 344 } 345 346 void prepend(const std::string & dir,bool warn)347 load_path::prepend (const std::string& dir, bool warn) 348 { 349 if (! dir.empty ()) 350 add (dir, false, warn); 351 } 352 353 bool remove(const std::string & dir_arg)354 load_path::remove (const std::string& dir_arg) 355 { 356 bool retval = false; 357 358 if (! dir_arg.empty ()) 359 { 360 if (same_file (dir_arg, ".")) 361 { 362 warning (R"(rmpath: can't remove "." from path)"); 363 364 // Avoid additional warnings. 365 retval = true; 366 } 367 else 368 { 369 std::string dir = sys::file_ops::tilde_expand (dir_arg); 370 371 dir = strip_trailing_separators (dir); 372 373 auto i = find_dir_info (dir); 374 375 if (i != dir_info_list.end ()) 376 { 377 retval = true; 378 379 if (remove_hook) 380 remove_hook (dir); 381 382 dir_info& di = *i; 383 384 remove (di); 385 386 dir_info_list.erase (i); 387 } 388 } 389 } 390 391 return retval; 392 } 393 394 void update(void) const395 load_path::update (void) const 396 { 397 // I don't see a better way to do this because we need to 398 // preserve the correct directory ordering for new files that 399 // have appeared. 400 401 top_level_package.clear (); 402 403 package_map.clear (); 404 405 for (auto& di : dir_info_list) 406 { 407 bool ok = di.update (); 408 409 if (! ok) 410 warning ("load-path: update failed for '%s', removing from path", 411 di.dir_name.c_str ()); 412 else 413 add (di, true, "", true); 414 } 415 } 416 417 bool contains_canonical(const std::string & dir) const418 load_path::contains_canonical (const std::string& dir) const 419 { 420 bool retval = false; 421 422 for (const auto& d : dir_info_list) 423 { 424 if (same_file (dir, d.dir_name)) 425 { 426 retval = true; 427 break; 428 } 429 } 430 431 return retval; 432 } 433 434 bool contains_file_in_dir(const std::string & file,const std::string & dir)435 load_path::contains_file_in_dir (const std::string& file, 436 const std::string& dir) 437 { 438 bool ok = false; 439 bool addpath_option = true; 440 441 std::string curr_dir = sys::env::get_current_directory (); 442 443 if (same_file (curr_dir, dir)) 444 ok = true; 445 else 446 { 447 bool dir_in_load_path = contains_canonical (dir); 448 449 // get base name, allowing "@class/method.m" (bug #41514) 450 std::string base_file = (file.length () > dir.length ()) 451 ? file.substr (dir.length () + 1) 452 : sys::env::base_pathname (file); 453 454 std::string lp_file = find_file (base_file); 455 456 if (dir_in_load_path) 457 { 458 if (same_file (lp_file, file)) 459 ok = true; 460 } 461 else 462 { 463 // File directory is not in path. Is the file in the path in 464 // the current directory? If so, then changing the current 465 // directory will be needed. Adding directory to path is 466 // not enough because the file in the current directory would 467 // still be found. 468 469 if (same_file (lp_file, base_file)) 470 { 471 if (same_file (curr_dir, dir)) 472 ok = true; 473 else 474 addpath_option = false; 475 } 476 } 477 } 478 479 if (! ok) 480 { 481 event_manager& evmgr = m_interpreter.get_event_manager (); 482 483 int action 484 = evmgr.debug_cd_or_addpath_error (file, dir, addpath_option); 485 486 switch (action) 487 { 488 case 1: 489 m_interpreter.chdir (dir); 490 ok = true; 491 break; 492 493 case 2: 494 { 495 prepend (dir); 496 ok = true; 497 } 498 break; 499 500 default: 501 break; 502 } 503 } 504 505 return ok; 506 } 507 508 std::list<std::string> overloads(const std::string & meth) const509 load_path::overloads (const std::string& meth) const 510 { 511 std::list<std::string> retval; 512 513 // update (); 514 515 top_level_package.overloads (meth, retval); 516 517 for (const auto& nm_ldr : package_map) 518 nm_ldr.second.overloads (meth, retval); 519 520 return retval; 521 } 522 523 std::list<std::string> get_all_package_names(bool only_top_level) const524 load_path::get_all_package_names (bool only_top_level) const 525 { 526 std::list<std::string> retval; 527 528 for (const auto& dir_ldr : package_map) 529 { 530 if (! only_top_level || dir_ldr.first.find ('.') == std::string::npos) 531 retval.push_back (dir_ldr.first); 532 } 533 534 return retval; 535 } 536 537 std::string find_file(const std::string & file) const538 load_path::find_file (const std::string& file) const 539 { 540 std::string retval; 541 542 if (sys::env::absolute_pathname (file) 543 || sys::env::rooted_relative_pathname (file)) 544 { 545 sys::file_stat fs (file); 546 547 return fs.exists () ? file : retval; 548 } 549 else 550 { 551 std::string tfile = find_private_file (file); 552 553 if (! tfile.empty ()) 554 return tfile; 555 } 556 557 if (file.find_first_of (sys::file_ops::dir_sep_chars ()) 558 != std::string::npos) 559 { 560 // Given name has a directory separator, so append it to each 561 // element of the load path in turn. 562 for (const auto& di : dir_info_list) 563 { 564 std::string tfile = sys::file_ops::concat (di.abs_dir_name, file); 565 566 sys::file_stat fs (tfile); 567 568 if (fs.exists ()) 569 return tfile; 570 } 571 } 572 else 573 { 574 // Look in cache. 575 for (const auto & di : dir_info_list) 576 { 577 string_vector all_files = di.all_files; 578 579 octave_idx_type len = all_files.numel (); 580 581 for (octave_idx_type i = 0; i < len; i++) 582 { 583 if (all_files[i] == file) 584 return sys::file_ops::concat (di.abs_dir_name, file); 585 } 586 } 587 } 588 589 return retval; 590 } 591 592 std::string find_dir(const std::string & dir) const593 load_path::find_dir (const std::string& dir) const 594 { 595 std::string retval; 596 597 if (dir.find_first_of (sys::file_ops::dir_sep_chars ()) != std::string::npos 598 && (sys::env::absolute_pathname (dir) 599 || sys::env::rooted_relative_pathname (dir))) 600 { 601 sys::file_stat fs (dir); 602 603 if (fs.exists () && fs.is_dir ()) 604 return dir; 605 } 606 else 607 { 608 std::string canon_dir = maybe_canonicalize (dir); 609 for (const auto& di : dir_info_list) 610 { 611 std::string dname = di.abs_dir_name; 612 613 std::size_t dname_len = dname.length (); 614 615 if (dname.substr (dname_len - 1) 616 == sys::file_ops::dir_sep_str ()) 617 { 618 dname = dname.substr (0, dname_len - 1); 619 dname_len--; 620 } 621 622 std::size_t dir_len = canon_dir.length (); 623 624 if (dname_len > dir_len 625 && sys::file_ops::is_dir_sep (dname[dname_len - dir_len - 1]) 626 && canon_dir == dname.substr (dname_len - dir_len)) 627 { 628 sys::file_stat fs (di.dir_name); 629 630 if (fs.exists () && fs.is_dir ()) 631 return di.abs_dir_name; 632 } 633 } 634 } 635 636 return retval; 637 } 638 639 string_vector find_matching_dirs(const std::string & dir) const640 load_path::find_matching_dirs (const std::string& dir) const 641 { 642 std::list<std::string> retlist; 643 644 if (dir.find_first_of (sys::file_ops::dir_sep_chars ()) != std::string::npos 645 && (sys::env::absolute_pathname (dir) 646 || sys::env::rooted_relative_pathname (dir))) 647 { 648 sys::file_stat fs (dir); 649 650 if (fs.exists () && fs.is_dir ()) 651 retlist.push_back (dir); 652 } 653 else 654 { 655 std::string canon_dir = maybe_canonicalize (dir); 656 for (const auto& di : dir_info_list) 657 { 658 std::string dname = di.abs_dir_name; 659 660 std::size_t dname_len = dname.length (); 661 662 if (dname.substr (dname_len - 1) 663 == sys::file_ops::dir_sep_str ()) 664 { 665 dname = dname.substr (0, dname_len - 1); 666 dname_len--; 667 } 668 669 std::size_t dir_len = canon_dir.length (); 670 671 if (dname_len > dir_len 672 && sys::file_ops::is_dir_sep (dname[dname_len - dir_len - 1]) 673 && canon_dir == dname.substr (dname_len - dir_len)) 674 { 675 sys::file_stat fs (di.dir_name); 676 677 if (fs.exists () && fs.is_dir ()) 678 retlist.push_back (di.abs_dir_name); 679 } 680 } 681 } 682 683 return retlist; 684 } 685 686 std::string find_first_of(const string_vector & flist) const687 load_path::find_first_of (const string_vector& flist) const 688 { 689 std::string retval; 690 691 std::string dir_name; 692 std::string file_name; 693 694 octave_idx_type flen = flist.numel (); 695 octave_idx_type rel_flen = 0; 696 697 string_vector rel_flist (flen); 698 699 for (octave_idx_type i = 0; i < flen; i++) 700 { 701 std::string file = flist[i]; 702 703 if (file.find_first_of (sys::file_ops::dir_sep_chars ()) 704 != std::string::npos) 705 { 706 if (sys::env::absolute_pathname (file) 707 || sys::env::rooted_relative_pathname (file)) 708 { 709 sys::file_stat fs (file); 710 711 if (fs.exists ()) 712 return file; 713 } 714 else 715 { 716 for (const auto& di : dir_info_list) 717 { 718 std::string tfile; 719 tfile = sys::file_ops::concat (di.abs_dir_name, file); 720 721 sys::file_stat fs (tfile); 722 723 if (fs.exists ()) 724 return tfile; 725 } 726 } 727 } 728 else 729 rel_flist[rel_flen++] = file; 730 } 731 732 rel_flist.resize (rel_flen); 733 734 for (const auto& di : dir_info_list) 735 { 736 string_vector all_files = di.all_files; 737 738 octave_idx_type len = all_files.numel (); 739 740 for (octave_idx_type i = 0; i < len; i++) 741 { 742 for (octave_idx_type j = 0; j < rel_flen; j++) 743 { 744 if (all_files[i] == rel_flist[j]) 745 { 746 dir_name = di.abs_dir_name; 747 file_name = rel_flist[j]; 748 749 goto done; 750 } 751 } 752 } 753 } 754 755 done: 756 757 if (! dir_name.empty ()) 758 retval = sys::file_ops::concat (dir_name, file_name); 759 760 return retval; 761 } 762 763 string_vector find_all_first_of(const string_vector & flist) const764 load_path::find_all_first_of (const string_vector& flist) const 765 { 766 std::list<std::string> retlist; 767 768 std::string dir_name; 769 std::string file_name; 770 771 octave_idx_type flen = flist.numel (); 772 octave_idx_type rel_flen = 0; 773 774 string_vector rel_flist (flen); 775 776 for (octave_idx_type i = 0; i < flen; i++) 777 { 778 std::string file = flist[i]; 779 780 if (file.find_first_of (sys::file_ops::dir_sep_chars ()) 781 != std::string::npos) 782 { 783 if (sys::env::absolute_pathname (file) 784 || sys::env::rooted_relative_pathname (file)) 785 { 786 sys::file_stat fs (file); 787 788 if (fs.exists ()) 789 retlist.push_back (file); 790 } 791 else 792 { 793 for (const auto& di : dir_info_list) 794 { 795 std::string tfile; 796 tfile = sys::file_ops::concat (di.abs_dir_name, file); 797 798 sys::file_stat fs (tfile); 799 800 if (fs.exists ()) 801 retlist.push_back (tfile); 802 } 803 } 804 } 805 else 806 rel_flist[rel_flen++] = file; 807 } 808 809 rel_flist.resize (rel_flen); 810 811 for (const auto& di : dir_info_list) 812 { 813 string_vector all_files = di.all_files; 814 815 octave_idx_type len = all_files.numel (); 816 817 for (octave_idx_type i = 0; i < len; i++) 818 { 819 for (octave_idx_type j = 0; j < rel_flen; j++) 820 { 821 if (all_files[i] == rel_flist[j]) 822 retlist.push_back (sys::file_ops::concat (di.abs_dir_name, 823 rel_flist[j])); 824 } 825 } 826 } 827 828 return retlist; 829 } 830 831 string_vector dirs(void) const832 load_path::dirs (void) const 833 { 834 std::size_t len = dir_info_list.size (); 835 836 string_vector retval (len); 837 838 octave_idx_type k = 0; 839 840 for (const auto& di : dir_info_list) 841 retval[k++] = di.dir_name; 842 843 return retval; 844 } 845 846 std::list<std::string> dir_list(void) const847 load_path::dir_list (void) const 848 { 849 std::list<std::string> retval; 850 851 for (const auto& di : dir_info_list) 852 retval.push_back (di.dir_name); 853 854 return retval; 855 } 856 857 string_vector files(const std::string & dir,bool omit_exts) const858 load_path::files (const std::string& dir, bool omit_exts) const 859 { 860 string_vector retval; 861 862 const_dir_info_list_iterator p = find_dir_info (dir); 863 864 if (p != dir_info_list.end ()) 865 retval = p->fcn_files; 866 867 if (omit_exts) 868 { 869 octave_idx_type len = retval.numel (); 870 871 for (octave_idx_type i = 0; i < len; i++) 872 { 873 std::string fname = retval[i]; 874 875 std::size_t pos = fname.rfind ('.'); 876 877 if (pos != std::string::npos) 878 retval[i] = fname.substr (0, pos); 879 } 880 } 881 882 return retval; 883 } 884 885 string_vector fcn_names(void) const886 load_path::fcn_names (void) const 887 { 888 return top_level_package.fcn_names (); 889 } 890 891 std::string path(void) const892 load_path::path (void) const 893 { 894 std::string xpath; 895 896 string_vector xdirs = load_path::dirs (); 897 898 octave_idx_type len = xdirs.numel (); 899 900 if (len > 0) 901 xpath = xdirs[0]; 902 903 for (octave_idx_type i = 1; i < len; i++) 904 xpath += directory_path::path_sep_str () + xdirs[i]; 905 906 return xpath; 907 } 908 909 void display(std::ostream & os) const910 load_path::display (std::ostream& os) const 911 { 912 for (const auto& di : dir_info_list) 913 { 914 string_vector fcn_files = di.fcn_files; 915 916 if (! fcn_files.empty ()) 917 { 918 os << "\n*** function files in " << di.dir_name << ":\n\n"; 919 920 fcn_files.list_in_columns (os); 921 } 922 923 const dir_info::method_file_map_type& method_file_map 924 = di.method_file_map; 925 926 if (! method_file_map.empty ()) 927 { 928 for (const auto& cls_ci : method_file_map) 929 { 930 os << "\n*** methods in " << di.dir_name 931 << "/@" << cls_ci.first << ":\n\n"; 932 933 const dir_info::class_info& ci = cls_ci.second; 934 935 string_vector method_files = get_file_list (ci.method_file_map); 936 937 method_files.list_in_columns (os); 938 } 939 } 940 } 941 942 top_level_package.display (os); 943 944 for (const auto& nm_ldr : package_map) 945 nm_ldr.second.display (os); 946 } 947 948 void execute_pkg_add(const std::string & dir)949 load_path::execute_pkg_add (const std::string& dir) 950 { 951 execute_pkg_add_or_del (dir, "PKG_ADD"); 952 } 953 954 void execute_pkg_del(const std::string & dir)955 load_path::execute_pkg_del (const std::string& dir) 956 { 957 execute_pkg_add_or_del (dir, "PKG_DEL"); 958 } 959 execute_pkg_add_or_del(const std::string & dir,const std::string & script_file)960 void load_path::execute_pkg_add_or_del (const std::string& dir, 961 const std::string& script_file) 962 { 963 if (! octave_interpreter_ready) 964 return; 965 966 unwind_protect frame; 967 968 std::string file = sys::file_ops::concat (dir, script_file); 969 970 sys::file_stat fs (file); 971 972 if (fs.exists ()) 973 source_file (file, "base"); 974 } 975 976 // FIXME: maybe we should also maintain a map to speed up this method of access. 977 978 load_path::const_dir_info_list_iterator find_dir_info(const std::string & dir_arg) const979 load_path::find_dir_info (const std::string& dir_arg) const 980 { 981 std::string dir = sys::file_ops::tilde_expand (dir_arg); 982 983 dir = maybe_canonicalize (dir); 984 985 auto retval = dir_info_list.cbegin (); 986 987 while (retval != dir_info_list.cend ()) 988 { 989 if (retval->dir_name == dir) 990 break; 991 992 retval++; 993 } 994 995 return retval; 996 } 997 998 load_path::dir_info_list_iterator find_dir_info(const std::string & dir_arg)999 load_path::find_dir_info (const std::string& dir_arg) 1000 { 1001 std::string dir = sys::file_ops::tilde_expand (dir_arg); 1002 1003 dir = maybe_canonicalize (dir); 1004 1005 auto retval = dir_info_list.begin (); 1006 1007 while (retval != dir_info_list.end ()) 1008 { 1009 if (retval->dir_name == dir) 1010 break; 1011 1012 retval++; 1013 } 1014 1015 return retval; 1016 } 1017 1018 bool contains(const std::string & dir) const1019 load_path::contains (const std::string& dir) const 1020 { 1021 return find_dir_info (dir) != dir_info_list.end (); 1022 } 1023 1024 void move(dir_info_list_iterator i,bool at_end)1025 load_path::move (dir_info_list_iterator i, bool at_end) 1026 { 1027 if (dir_info_list.size () > 1) 1028 { 1029 dir_info di = *i; 1030 1031 dir_info_list.erase (i); 1032 1033 if (at_end) 1034 dir_info_list.push_back (di); 1035 else 1036 dir_info_list.push_front (di); 1037 1038 move (di, at_end); 1039 } 1040 } 1041 1042 void move(const dir_info & di,bool at_end,const std::string & pname)1043 load_path::move (const dir_info& di, bool at_end, const std::string& pname) 1044 { 1045 package_info& l = get_package (pname); 1046 1047 l.move (di, at_end); 1048 1049 dir_info::package_dir_map_type package_dir_map = di.package_dir_map; 1050 1051 for (const auto& pkg_di : package_dir_map) 1052 { 1053 std::string full_name = pkg_di.first; 1054 1055 if (! pname.empty ()) 1056 full_name = pname + '.' + full_name; 1057 1058 move (pkg_di.second, at_end, full_name); 1059 } 1060 } 1061 1062 void add(const std::string & dir_arg,bool at_end,bool warn)1063 load_path::add (const std::string& dir_arg, bool at_end, bool warn) 1064 { 1065 std::size_t len = dir_arg.length (); 1066 1067 if (len > 1 && dir_arg.substr (len-2) == "//") 1068 warning_with_id ("Octave:recursive-path-search", 1069 "trailing '//' is no longer special in search path elements"); 1070 1071 std::string dir = sys::file_ops::tilde_expand (dir_arg); 1072 1073 dir = strip_trailing_separators (dir); 1074 1075 dir = maybe_canonicalize (dir); 1076 1077 auto i = find_dir_info (dir); 1078 1079 if (i != dir_info_list.end ()) 1080 move (i, at_end); 1081 else 1082 { 1083 sys::file_stat fs (dir); 1084 1085 if (fs) 1086 { 1087 if (fs.is_dir ()) 1088 { 1089 dir_info di (dir); 1090 1091 if (at_end) 1092 dir_info_list.push_back (di); 1093 else 1094 dir_info_list.push_front (di); 1095 1096 add (di, at_end); 1097 1098 if (add_hook) 1099 add_hook (dir); 1100 } 1101 else if (warn) 1102 warning ("addpath: %s: not a directory", dir_arg.c_str ()); 1103 } 1104 else if (warn) 1105 { 1106 std::string msg = fs.error (); 1107 warning ("addpath: %s: %s", dir_arg.c_str (), msg.c_str ()); 1108 } 1109 } 1110 1111 // FIXME: is there a better way to do this? 1112 1113 i = find_dir_info ("."); 1114 1115 if (i != dir_info_list.end ()) 1116 move (i, false); 1117 } 1118 1119 void remove(const dir_info & di,const std::string & pname)1120 load_path::remove (const dir_info& di, const std::string& pname) 1121 { 1122 package_info& l = get_package (pname); 1123 1124 l.remove (di); 1125 1126 dir_info::package_dir_map_type package_dir_map = di.package_dir_map; 1127 1128 for (const auto& pkg_di : package_dir_map) 1129 { 1130 std::string full_name = pkg_di.first; 1131 1132 if (! pname.empty ()) 1133 full_name = pname + '.' + full_name; 1134 1135 remove (pkg_di.second, full_name); 1136 } 1137 } 1138 1139 bool is_package(const std::string & name) const1140 load_path::is_package (const std::string& name) const 1141 { 1142 for (const auto& di : dir_info_list) 1143 { 1144 if (di.is_package (name)) 1145 return true; 1146 } 1147 1148 return false; 1149 } 1150 1151 void add(const dir_info & di,bool at_end,const std::string & pname,bool updating) const1152 load_path::add (const dir_info& di, bool at_end, 1153 const std::string& pname, bool updating) const 1154 { 1155 package_info& l = get_package (pname); 1156 1157 l.add (di, at_end, updating); 1158 1159 dir_info::package_dir_map_type package_dir_map = di.package_dir_map; 1160 1161 for (const auto& pkg_di : package_dir_map) 1162 { 1163 std::string full_name = pkg_di.first; 1164 1165 if (! pname.empty ()) 1166 full_name = pname + '.' + full_name; 1167 1168 add (pkg_di.second, at_end, full_name); 1169 } 1170 } 1171 1172 string_vector get_file_list(const load_path::dir_info::fcn_file_map_type & lst) const1173 load_path::get_file_list (const load_path::dir_info::fcn_file_map_type& lst) const 1174 { 1175 octave_idx_type n = lst.size (); 1176 1177 string_vector retval (n); 1178 1179 octave_idx_type count = 0; 1180 1181 for (const auto& nm_typ : lst) 1182 { 1183 std::string nm = nm_typ.first; 1184 1185 int types = nm_typ.second; 1186 1187 if (types & load_path::OCT_FILE) 1188 nm += ".oct"; 1189 else if (types & load_path::MEX_FILE) 1190 nm += ".mex"; 1191 else 1192 nm += ".m"; 1193 1194 retval[count++] = nm; 1195 } 1196 1197 return retval; 1198 } 1199 1200 load_path::dir_info::fcn_file_map_type get_fcn_files(const std::string & d)1201 get_fcn_files (const std::string& d) 1202 { 1203 load_path::dir_info::fcn_file_map_type retval; 1204 1205 string_vector flist; 1206 std::string msg; 1207 1208 if (! sys::get_dirlist (d, flist, msg)) 1209 warning ("load_path: %s: %s", d.c_str (), msg.c_str ()); 1210 else 1211 { 1212 octave_idx_type len = flist.numel (); 1213 1214 for (octave_idx_type i = 0; i < len; i++) 1215 { 1216 std::string fname = flist[i]; 1217 1218 std::size_t pos = fname.rfind ('.'); 1219 1220 if (pos != std::string::npos) 1221 { 1222 std::string base = fname.substr (0, pos); 1223 std::string ext = fname.substr (pos); 1224 1225 if (valid_identifier (base)) 1226 { 1227 int t = 0; 1228 1229 if (ext == ".m") 1230 t = load_path::M_FILE; 1231 else if (ext == ".oct") 1232 t = load_path::OCT_FILE; 1233 else if (ext == ".mex") 1234 t = load_path::MEX_FILE; 1235 1236 if (t) 1237 { 1238 load_path::dir_info::fcn_file_map_iterator p 1239 = retval.find (base); 1240 1241 if (p == retval.end ()) 1242 retval[base] = t; 1243 else 1244 p->second |= t; 1245 } 1246 } 1247 } 1248 } 1249 } 1250 1251 return retval; 1252 } 1253 1254 bool update(void)1255 load_path::dir_info::update (void) 1256 { 1257 sys::file_stat fs (dir_name); 1258 1259 if (! fs) 1260 { 1261 std::string msg = fs.error (); 1262 warning ("load_path: %s: %s", dir_name.c_str (), msg.c_str ()); 1263 return false; 1264 } 1265 1266 sys::file_stat pfs (sys::file_ops::concat (dir_name, "private")); 1267 bool has_private_dir = pfs && pfs.is_dir (); 1268 1269 if (is_relative) 1270 { 1271 try 1272 { 1273 std::string abs_name = sys::canonicalize_file_name (dir_name); 1274 1275 const_abs_dir_cache_iterator p = abs_dir_cache.find (abs_name); 1276 1277 if (p != abs_dir_cache.end ()) 1278 { 1279 // The directory is in the cache of all directories we have 1280 // visited (indexed by absolute name). If it is out of date, 1281 // initialize it. Otherwise, copy the info from the cache. 1282 // By doing that, we avoid unnecessary calls to stat that can 1283 // slow things down tremendously for large directories. 1284 const dir_info& di = p->second; 1285 1286 if ((fs.mtime () + fs.time_resolution () 1287 > di.dir_time_last_checked) 1288 || (has_private_dir 1289 && (pfs.mtime () + pfs.time_resolution () 1290 > dir_time_last_checked))) 1291 initialize (); 1292 else 1293 { 1294 // Copy over info from cache, but leave dir_name and 1295 // is_relative unmodified. 1296 abs_dir_name = di.abs_dir_name; 1297 dir_mtime = di.dir_mtime; 1298 dir_time_last_checked = di.dir_time_last_checked; 1299 all_files = di.all_files; 1300 fcn_files = di.fcn_files; 1301 private_file_map = di.private_file_map; 1302 method_file_map = di.method_file_map; 1303 package_dir_map = di.package_dir_map; 1304 } 1305 } 1306 else 1307 { 1308 // We haven't seen this directory before. 1309 initialize (); 1310 } 1311 } 1312 catch (const execution_exception& ee) 1313 { 1314 // Skip updating if we don't know where we are, but don't 1315 // treat it as an error. 1316 1317 interpreter& interp 1318 = __get_interpreter__ ("load_path::dir_info::update"); 1319 1320 interp.recover_from_exception (); 1321 } 1322 } 1323 // Absolute path, check timestamp to see whether it requires re-caching 1324 else if (fs.mtime () + fs.time_resolution () > dir_time_last_checked 1325 || (has_private_dir 1326 && (pfs.mtime () + pfs.time_resolution () 1327 > dir_time_last_checked))) 1328 initialize (); 1329 1330 return true; 1331 } 1332 1333 bool is_package(const std::string & name) const1334 load_path::dir_info::is_package (const std::string& name) const 1335 { 1336 std::size_t pos = name.find ('.'); 1337 1338 if (pos == std::string::npos) 1339 return package_dir_map.find (name) != package_dir_map.end (); 1340 else 1341 { 1342 std::string name_head = name.substr (0, pos); 1343 std::string name_tail = name.substr (pos + 1); 1344 1345 const_package_dir_map_iterator it = package_dir_map.find (name_head); 1346 1347 if (it != package_dir_map.end ()) 1348 return it->second.is_package (name_tail); 1349 else 1350 return false; 1351 } 1352 } 1353 1354 void initialize(void)1355 load_path::dir_info::initialize (void) 1356 { 1357 is_relative = ! sys::env::absolute_pathname (dir_name); 1358 1359 dir_time_last_checked = sys::time (static_cast<time_t> (0)); 1360 1361 sys::file_stat fs (dir_name); 1362 1363 if (fs) 1364 { 1365 method_file_map.clear (); 1366 package_dir_map.clear (); 1367 1368 dir_mtime = fs.mtime (); 1369 dir_time_last_checked = sys::time (); 1370 1371 get_file_list (dir_name); 1372 1373 try 1374 { 1375 abs_dir_name = sys::canonicalize_file_name (dir_name); 1376 1377 // FIXME: nothing is ever removed from this cache of 1378 // directory information, so there could be some resource 1379 // problems. Perhaps it should be pruned from time to time. 1380 1381 abs_dir_cache[abs_dir_name] = *this; 1382 } 1383 catch (const execution_exception&) 1384 { 1385 // Skip updating if we don't know where we are but don't treat 1386 // it as an error. 1387 1388 interpreter& interp 1389 = __get_interpreter__ ("load_path::dir_info::initialize"); 1390 1391 interp.recover_from_exception (); 1392 } 1393 } 1394 else 1395 { 1396 std::string msg = fs.error (); 1397 warning ("load_path: %s: %s", dir_name.c_str (), msg.c_str ()); 1398 } 1399 } 1400 1401 void get_file_list(const std::string & d)1402 load_path::dir_info::get_file_list (const std::string& d) 1403 { 1404 string_vector flist; 1405 std::string msg; 1406 1407 if (! sys::get_dirlist (d, flist, msg)) 1408 { 1409 warning ("load_path: %s: %s", d.c_str (), msg.c_str ()); 1410 return; 1411 } 1412 1413 octave_idx_type len = flist.numel (); 1414 1415 all_files.resize (len); 1416 fcn_files.resize (len); 1417 1418 octave_idx_type all_files_count = 0; 1419 octave_idx_type fcn_files_count = 0; 1420 1421 for (octave_idx_type i = 0; i < len; i++) 1422 { 1423 std::string fname = flist[i]; 1424 1425 std::string full_name = sys::file_ops::concat (d, fname); 1426 1427 sys::file_stat fs (full_name); 1428 1429 if (fs) 1430 { 1431 if (fs.is_dir ()) 1432 { 1433 if (fname == "private") 1434 get_private_file_map (full_name); 1435 else if (fname[0] == '@') 1436 get_method_file_map (full_name, fname.substr (1)); 1437 else if (fname[0] == '+') 1438 get_package_dir (full_name, fname.substr (1)); 1439 } 1440 else 1441 { 1442 all_files[all_files_count++] = fname; 1443 1444 std::size_t pos = fname.rfind ('.'); 1445 1446 if (pos != std::string::npos) 1447 { 1448 std::string ext = fname.substr (pos); 1449 1450 if (ext == ".m" || ext == ".oct" || ext == ".mex") 1451 { 1452 std::string base = fname.substr (0, pos); 1453 1454 if (valid_identifier (base)) 1455 fcn_files[fcn_files_count++] = fname; 1456 } 1457 } 1458 } 1459 } 1460 } 1461 1462 all_files.resize (all_files_count); 1463 fcn_files.resize (fcn_files_count); 1464 } 1465 1466 void get_private_file_map(const std::string & d)1467 load_path::dir_info::get_private_file_map (const std::string& d) 1468 { 1469 private_file_map = get_fcn_files (d); 1470 } 1471 1472 void get_method_file_map(const std::string & d,const std::string & class_name)1473 load_path::dir_info::get_method_file_map (const std::string& d, 1474 const std::string& class_name) 1475 { 1476 method_file_map[class_name].method_file_map = get_fcn_files (d); 1477 1478 std::string pd = sys::file_ops::concat (d, "private"); 1479 1480 sys::file_stat fs (pd); 1481 1482 if (fs && fs.is_dir ()) 1483 method_file_map[class_name].private_file_map = get_fcn_files (pd); 1484 } 1485 1486 void get_package_dir(const std::string & d,const std::string & package_name)1487 load_path::dir_info::get_package_dir (const std::string& d, 1488 const std::string& package_name) 1489 { 1490 package_dir_map[package_name] = dir_info (d); 1491 } 1492 1493 void move(const dir_info & di,bool at_end)1494 load_path::package_info::move (const dir_info& di, bool at_end) 1495 { 1496 std::string dir_name = di.abs_dir_name; 1497 1498 auto s = std::find (dir_list.begin (), dir_list.end (), dir_name); 1499 1500 if (s != dir_list.end ()) 1501 { 1502 dir_list.erase (s); 1503 1504 if (at_end) 1505 dir_list.push_back (dir_name); 1506 else 1507 dir_list.push_front (dir_name); 1508 } 1509 1510 move_fcn_map (dir_name, di.fcn_files, at_end); 1511 1512 // No need to move elements of private function map. 1513 1514 move_method_map (dir_name, at_end); 1515 } 1516 1517 void remove(const dir_info & di)1518 load_path::package_info::remove (const dir_info& di) 1519 { 1520 std::string dir = di.abs_dir_name; 1521 1522 string_vector fcn_files = di.fcn_files; 1523 1524 dir_list.remove (dir); 1525 1526 remove_fcn_map (dir, fcn_files); 1527 1528 remove_private_fcn_map (dir); 1529 1530 remove_method_map (dir); 1531 } 1532 1533 void display(std::ostream & os) const1534 load_path::package_info::display (std::ostream& os) const 1535 { 1536 os << "*** package_info: " 1537 << (m_package_name.empty () ? "<top-level>" : m_package_name) 1538 << "\n\n"; 1539 1540 for (const auto& dir : dir_list) 1541 os << dir << "\n"; 1542 os << "\n"; 1543 1544 for (const auto& dir_fnlst : private_fcn_map) 1545 { 1546 os << "\n*** private functions in " 1547 << sys::file_ops::concat (dir_fnlst.first, "private") 1548 << ":\n\n"; 1549 1550 print_fcn_list (os, dir_fnlst.second); 1551 } 1552 1553 #if defined (DEBUG_LOAD_PATH) 1554 1555 for (const auto& nm_filst : fcn_map) 1556 { 1557 os << nm_filst.first << ":\n"; 1558 1559 const file_info_list_type& file_info_list = nm_filst.second; 1560 1561 for (const auto& finfo : file_info_list) 1562 { 1563 os << " " << finfo.dir_name << " ("; 1564 1565 print_types (os, finfo.types); 1566 1567 os << ")\n"; 1568 } 1569 } 1570 1571 for (const auto& cls_fnmap : method_map) 1572 { 1573 os << "CLASS " << cls_fnmap.first << ":\n"; 1574 1575 const fcn_map_type& fm = cls_fnmap.second; 1576 1577 for (const auto& nm_fnlst : fcn_map) 1578 { 1579 os << " " << nm_fnlst.first << ":\n"; 1580 1581 const file_info_list_type& file_info_list = nm_fnlst.second; 1582 1583 for (const auto& finfo : file_info_list) 1584 { 1585 os << " " << finfo.dir_name << " ("; 1586 1587 print_types (os, finfo.types); 1588 1589 os << ")\n"; 1590 } 1591 } 1592 } 1593 1594 os << "\n"; 1595 1596 #endif 1597 } 1598 1599 std::string find_fcn(const std::string & fcn,std::string & dir_name,int type) const1600 load_path::package_info::find_fcn (const std::string& fcn, 1601 std::string& dir_name, 1602 int type) const 1603 { 1604 std::string retval; 1605 1606 // update (); 1607 1608 if (fcn.length () > 0 && fcn[0] == '@') 1609 { 1610 std::size_t pos = fcn.find ('/'); 1611 1612 if (pos != std::string::npos) 1613 { 1614 std::string class_name = fcn.substr (1, pos-1); 1615 std::string meth = fcn.substr (pos+1); 1616 1617 retval = find_method (class_name, meth, dir_name); 1618 } 1619 else 1620 retval = ""; 1621 } 1622 else 1623 { 1624 dir_name = ""; 1625 1626 const_fcn_map_iterator p = fcn_map.find (fcn); 1627 1628 if (p != fcn_map.end ()) 1629 { 1630 const file_info_list_type& file_info_list = p->second; 1631 1632 for (const auto& fi : file_info_list) 1633 { 1634 retval = sys::file_ops::concat (fi.dir_name, fcn); 1635 1636 if (check_file_type (retval, type, fi.types, 1637 fcn, "load_path::find_fcn")) 1638 { 1639 dir_name = fi.dir_name; 1640 break; 1641 } 1642 else 1643 retval = ""; 1644 } 1645 } 1646 } 1647 1648 return retval; 1649 } 1650 1651 std::string find_private_fcn(const std::string & dir,const std::string & fcn,int type) const1652 load_path::package_info::find_private_fcn (const std::string& dir, 1653 const std::string& fcn, 1654 int type) const 1655 { 1656 std::string retval; 1657 1658 // update (); 1659 1660 const_private_fcn_map_iterator q = private_fcn_map.find (dir); 1661 1662 if (q != private_fcn_map.end ()) 1663 { 1664 const dir_info::fcn_file_map_type& fcn_file_map = q->second; 1665 1666 dir_info::const_fcn_file_map_iterator p = fcn_file_map.find (fcn); 1667 1668 if (p != fcn_file_map.end ()) 1669 { 1670 std::string fname 1671 = sys::file_ops::concat (sys::file_ops::concat (dir, "private"), fcn); 1672 1673 if (check_file_type (fname, type, p->second, fcn, 1674 "load_path::find_private_fcn")) 1675 retval = fname; 1676 } 1677 } 1678 1679 return retval; 1680 } 1681 1682 std::string find_method(const std::string & class_name,const std::string & meth,std::string & dir_name,int type) const1683 load_path::package_info::find_method (const std::string& class_name, 1684 const std::string& meth, 1685 std::string& dir_name, 1686 int type) const 1687 { 1688 std::string retval; 1689 1690 // update (); 1691 1692 dir_name = ""; 1693 1694 const_method_map_iterator q = method_map.find (class_name); 1695 1696 if (q != method_map.end ()) 1697 { 1698 const fcn_map_type& m = q->second; 1699 1700 const_fcn_map_iterator p = m.find (meth); 1701 1702 if (p != m.end ()) 1703 { 1704 const file_info_list_type& file_info_list = p->second; 1705 1706 for (const auto& fi : file_info_list) 1707 { 1708 retval = sys::file_ops::concat (fi.dir_name, meth); 1709 1710 bool found = check_file_type (retval, type, fi.types, 1711 meth, "load_path::find_method"); 1712 1713 if (found) 1714 { 1715 dir_name = fi.dir_name; 1716 break; 1717 } 1718 else 1719 retval = ""; 1720 } 1721 } 1722 } 1723 1724 return retval; 1725 } 1726 1727 std::list<std::string> methods(const std::string & class_name) const1728 load_path::package_info::methods (const std::string& class_name) const 1729 { 1730 std::list<std::string> retval; 1731 1732 // update (); 1733 1734 const_method_map_iterator mtd_map_it = method_map.find (class_name); 1735 1736 if (mtd_map_it != method_map.end ()) 1737 { 1738 for (const auto& nm_filst : mtd_map_it->second) 1739 retval.push_back (nm_filst.first); 1740 } 1741 1742 if (! retval.empty ()) 1743 retval.sort (); 1744 1745 return retval; 1746 } 1747 1748 void overloads(const std::string & meth,std::list<std::string> & l) const1749 load_path::package_info::overloads (const std::string& meth, 1750 std::list<std::string>& l) const 1751 { 1752 for (const auto& cls_fnmap : method_map) 1753 { 1754 const fcn_map_type& m = cls_fnmap.second; 1755 1756 if (m.find (meth) != m.end ()) 1757 { 1758 std::string class_name = cls_fnmap.first; 1759 1760 if (! m_package_name.empty ()) 1761 class_name = m_package_name + '.' + class_name; 1762 1763 l.push_back (class_name); 1764 } 1765 } 1766 } 1767 1768 string_vector fcn_names(void) const1769 load_path::package_info::fcn_names (void) const 1770 { 1771 std::size_t len = fcn_map.size (); 1772 1773 string_vector retval (len); 1774 1775 octave_idx_type count = 0; 1776 1777 for (const auto& nm_filst : fcn_map) 1778 retval[count++] = nm_filst.first; 1779 1780 return retval; 1781 } 1782 1783 void add_to_fcn_map(const dir_info & di,bool at_end,bool updating)1784 load_path::package_info::add_to_fcn_map (const dir_info& di, 1785 bool at_end, bool updating) 1786 { 1787 std::string dir_name = di.abs_dir_name; 1788 1789 string_vector fcn_files = di.fcn_files; 1790 1791 octave_idx_type len = fcn_files.numel (); 1792 1793 for (octave_idx_type i = 0; i < len; i++) 1794 { 1795 std::string fname = fcn_files[i]; 1796 1797 std::string ext; 1798 std::string base = fname; 1799 1800 std::size_t pos = fname.rfind ('.'); 1801 1802 if (pos != std::string::npos) 1803 { 1804 base = fname.substr (0, pos); 1805 ext = fname.substr (pos); 1806 } 1807 1808 file_info_list_type& file_info_list = fcn_map[base]; 1809 1810 auto p = file_info_list.begin (); 1811 1812 while (p != file_info_list.end ()) 1813 { 1814 if (p->dir_name == dir_name) 1815 break; 1816 1817 p++; 1818 } 1819 1820 int t = 0; 1821 if (ext == ".m") 1822 t = load_path::M_FILE; 1823 else if (ext == ".oct") 1824 t = load_path::OCT_FILE; 1825 else if (ext == ".mex") 1826 t = load_path::MEX_FILE; 1827 1828 if (p == file_info_list.end ()) 1829 { 1830 // Warn if a built-in or library function is being shadowed, 1831 // but not if we are just updating (rehashing) the list. 1832 1833 if (! updating) 1834 { 1835 if (file_info_list.empty ()) 1836 { 1837 symbol_table& symtab 1838 = __get_symbol_table__ ("load_path::package_info::add_to_fcn_map"); 1839 1840 if (symtab.is_built_in_function_name (base)) 1841 { 1842 std::string fcn_path = sys::file_ops::concat (dir_name, fname); 1843 1844 warning_with_id ("Octave:shadowed-function", 1845 "function %s shadows a built-in function", 1846 fcn_path.c_str ()); 1847 } 1848 } 1849 else if (! at_end) 1850 { 1851 file_info& old = file_info_list.front (); 1852 1853 // FIXME: do we need to be more careful about the 1854 // way we look for old.dir_name in sys_path to avoid 1855 // partial matches? 1856 1857 // Don't warn about Contents.m files since we expect 1858 // more than one to exist in the load path. 1859 1860 if (fname != "Contents.m" 1861 && sys_path.find (old.dir_name) != std::string::npos 1862 && in_path_list (sys_path, old.dir_name)) 1863 { 1864 std::string fcn_path = sys::file_ops::concat (dir_name, fname); 1865 1866 warning_with_id ("Octave:shadowed-function", 1867 "function %s shadows a core library function", 1868 fcn_path.c_str ()); 1869 } 1870 } 1871 } 1872 1873 file_info fi (dir_name, t); 1874 1875 if (at_end) 1876 file_info_list.push_back (fi); 1877 else 1878 file_info_list.push_front (fi); 1879 } 1880 else 1881 { 1882 file_info& fi = *p; 1883 1884 fi.types |= t; 1885 } 1886 } 1887 } 1888 1889 void add_to_private_fcn_map(const dir_info & di)1890 load_path::package_info::add_to_private_fcn_map (const dir_info& di) 1891 { 1892 dir_info::fcn_file_map_type private_file_map = di.private_file_map; 1893 1894 if (! private_file_map.empty ()) 1895 private_fcn_map[di.abs_dir_name] = private_file_map; 1896 } 1897 1898 void add_to_method_map(const dir_info & di,bool at_end)1899 load_path::package_info::add_to_method_map (const dir_info& di, bool at_end) 1900 { 1901 std::string dir_name = di.abs_dir_name; 1902 1903 // <CLASS_NAME, CLASS_INFO> 1904 dir_info::method_file_map_type method_file_map = di.method_file_map; 1905 1906 for (const auto& cls_ci : method_file_map) 1907 { 1908 std::string class_name = cls_ci.first; 1909 1910 fcn_map_type& fm = method_map[class_name]; 1911 1912 std::string full_dir_name 1913 = sys::file_ops::concat (dir_name, '@' + class_name); 1914 1915 const dir_info::class_info& ci = cls_ci.second; 1916 1917 // <FCN_NAME, TYPES> 1918 const dir_info::fcn_file_map_type& m = ci.method_file_map; 1919 1920 for (const auto& nm_typ : m) 1921 { 1922 std::string base = nm_typ.first; 1923 int types = nm_typ.second; 1924 1925 file_info_list_type& file_info_list = fm[base]; 1926 1927 auto p2 = file_info_list.begin (); 1928 while (p2 != file_info_list.end ()) 1929 { 1930 if (p2->dir_name == full_dir_name) 1931 break; 1932 1933 p2++; 1934 } 1935 1936 if (p2 == file_info_list.end ()) 1937 { 1938 file_info fi (full_dir_name, types); 1939 1940 if (at_end) 1941 file_info_list.push_back (fi); 1942 else 1943 file_info_list.push_front (fi); 1944 } 1945 else 1946 { 1947 // FIXME: is this possible? 1948 file_info& fi = *p2; 1949 1950 fi.types = types; 1951 } 1952 } 1953 1954 // <FCN_NAME, TYPES> 1955 dir_info::fcn_file_map_type private_file_map = ci.private_file_map; 1956 1957 if (! private_file_map.empty ()) 1958 private_fcn_map[full_dir_name] = private_file_map; 1959 } 1960 } 1961 1962 void move_fcn_map(const std::string & dir_name,const string_vector & fcn_files,bool at_end)1963 load_path::package_info::move_fcn_map (const std::string& dir_name, 1964 const string_vector& fcn_files, bool at_end) 1965 { 1966 octave_idx_type len = fcn_files.numel (); 1967 1968 for (octave_idx_type k = 0; k < len; k++) 1969 { 1970 std::string fname = fcn_files[k]; 1971 1972 std::string ext; 1973 std::string base = fname; 1974 1975 std::size_t pos = fname.rfind ('.'); 1976 1977 if (pos != std::string::npos) 1978 { 1979 base = fname.substr (0, pos); 1980 ext = fname.substr (pos); 1981 } 1982 1983 file_info_list_type& file_info_list = fcn_map[base]; 1984 1985 if (file_info_list.size () == 1) 1986 continue; 1987 else 1988 { 1989 for (auto fi_it = file_info_list.begin (); 1990 fi_it != file_info_list.end (); 1991 fi_it++) 1992 { 1993 if (fi_it->dir_name == dir_name) 1994 { 1995 file_info fi_tmp = *fi_it; 1996 1997 file_info_list.erase (fi_it); 1998 1999 if (at_end) 2000 file_info_list.push_back (fi_tmp); 2001 else 2002 file_info_list.push_front (fi_tmp); 2003 2004 break; 2005 } 2006 } 2007 } 2008 } 2009 } 2010 2011 void move_method_map(const std::string & dir_name,bool at_end)2012 load_path::package_info::move_method_map (const std::string& dir_name, 2013 bool at_end) 2014 { 2015 for (auto& cls_fnmap : method_map) 2016 { 2017 std::string class_name = cls_fnmap.first; 2018 2019 fcn_map_type& fn_map = cls_fnmap.second; 2020 2021 std::string full_dir_name 2022 = sys::file_ops::concat (dir_name, '@' + class_name); 2023 2024 for (auto& nm_filst : fn_map) 2025 { 2026 file_info_list_type& file_info_list = nm_filst.second; 2027 2028 if (file_info_list.size () == 1) 2029 continue; 2030 else 2031 { 2032 for (auto fi_it = file_info_list.begin (); 2033 fi_it != file_info_list.end (); fi_it++) 2034 { 2035 if (fi_it->dir_name == full_dir_name) 2036 { 2037 file_info fi_tmp = *fi_it; 2038 2039 file_info_list.erase (fi_it); 2040 2041 if (at_end) 2042 file_info_list.push_back (fi_tmp); 2043 else 2044 file_info_list.push_front (fi_tmp); 2045 2046 break; 2047 } 2048 } 2049 } 2050 } 2051 } 2052 } 2053 2054 void remove_fcn_map(const std::string & dir,const string_vector & fcn_files)2055 load_path::package_info::remove_fcn_map (const std::string& dir, 2056 const string_vector& fcn_files) 2057 { 2058 octave_idx_type len = fcn_files.numel (); 2059 2060 for (octave_idx_type k = 0; k < len; k++) 2061 { 2062 std::string fname = fcn_files[k]; 2063 2064 std::string ext; 2065 std::string base = fname; 2066 2067 std::size_t pos = fname.rfind ('.'); 2068 2069 if (pos != std::string::npos) 2070 { 2071 base = fname.substr (0, pos); 2072 ext = fname.substr (pos); 2073 } 2074 2075 file_info_list_type& file_info_list = fcn_map[base]; 2076 2077 for (auto fi_it = file_info_list.begin (); 2078 fi_it != file_info_list.end (); 2079 fi_it++) 2080 { 2081 if (fi_it->dir_name == dir) 2082 { 2083 file_info_list.erase (fi_it); 2084 2085 if (file_info_list.empty ()) 2086 fcn_map.erase (fname); 2087 2088 break; 2089 } 2090 } 2091 } 2092 } 2093 2094 void remove_private_fcn_map(const std::string & dir)2095 load_path::package_info::remove_private_fcn_map (const std::string& dir) 2096 { 2097 auto p = private_fcn_map.find (dir); 2098 2099 if (p != private_fcn_map.end ()) 2100 private_fcn_map.erase (p); 2101 } 2102 2103 void remove_method_map(const std::string & dir)2104 load_path::package_info::remove_method_map (const std::string& dir) 2105 { 2106 for (auto& cls_fnmap : method_map) 2107 { 2108 std::string class_name = cls_fnmap.first; 2109 2110 fcn_map_type& fn_map = cls_fnmap.second; 2111 2112 std::string full_dir_name 2113 = sys::file_ops::concat (dir, '@' + class_name); 2114 2115 for (auto& nm_filst : fn_map) 2116 { 2117 file_info_list_type& file_info_list = nm_filst.second; 2118 2119 if (file_info_list.size () == 1) 2120 continue; 2121 else 2122 { 2123 for (auto fi_it = file_info_list.begin (); 2124 fi_it != file_info_list.end (); fi_it++) 2125 { 2126 if (fi_it->dir_name == full_dir_name) 2127 { 2128 file_info_list.erase (fi_it); 2129 // FIXME: if there are no other elements, we 2130 // should remove this element of fn_map but calling 2131 // erase here would invalidate the iterator fi_it. 2132 2133 break; 2134 } 2135 } 2136 } 2137 } 2138 } 2139 } 2140 2141 bool check_file_type(std::string & fname,int type,int possible_types,const std::string & fcn,const char * who) const2142 load_path::package_info::check_file_type (std::string& fname, int type, 2143 int possible_types, 2144 const std::string& fcn, 2145 const char *who) const 2146 { 2147 bool retval = false; 2148 2149 if (type == load_path::OCT_FILE) 2150 { 2151 if ((type & possible_types) == load_path::OCT_FILE) 2152 { 2153 fname += ".oct"; 2154 retval = true; 2155 } 2156 } 2157 else if (type == load_path::M_FILE) 2158 { 2159 if ((type & possible_types) == load_path::M_FILE) 2160 { 2161 fname += ".m"; 2162 retval = true; 2163 } 2164 } 2165 else if (type == load_path::MEX_FILE) 2166 { 2167 if ((type & possible_types) == load_path::MEX_FILE) 2168 { 2169 fname += ".mex"; 2170 retval = true; 2171 } 2172 } 2173 else if (type == (load_path::M_FILE | load_path::OCT_FILE)) 2174 { 2175 if (possible_types & load_path::OCT_FILE) 2176 { 2177 fname += ".oct"; 2178 retval = true; 2179 } 2180 else if (possible_types & load_path::M_FILE) 2181 { 2182 fname += ".m"; 2183 retval = true; 2184 } 2185 } 2186 else if (type == (load_path::M_FILE | load_path::MEX_FILE)) 2187 { 2188 if (possible_types & load_path::MEX_FILE) 2189 { 2190 fname += ".mex"; 2191 retval = true; 2192 } 2193 else if (possible_types & load_path::M_FILE) 2194 { 2195 fname += ".m"; 2196 retval = true; 2197 } 2198 } 2199 else if (type == (load_path::OCT_FILE | load_path::MEX_FILE)) 2200 { 2201 if (possible_types & load_path::OCT_FILE) 2202 { 2203 fname += ".oct"; 2204 retval = true; 2205 } 2206 else if (possible_types & load_path::MEX_FILE) 2207 { 2208 fname += ".mex"; 2209 retval = true; 2210 } 2211 } 2212 else if (type == (load_path::M_FILE | load_path::OCT_FILE 2213 | load_path::MEX_FILE)) 2214 { 2215 if (possible_types & load_path::OCT_FILE) 2216 { 2217 fname += ".oct"; 2218 retval = true; 2219 } 2220 else if (possible_types & load_path::MEX_FILE) 2221 { 2222 fname += ".mex"; 2223 retval = true; 2224 } 2225 else if (possible_types & load_path::M_FILE) 2226 { 2227 fname += ".m"; 2228 retval = true; 2229 } 2230 } 2231 else 2232 error ("%s: %s: invalid type code = %d", who, fcn.c_str (), type); 2233 2234 return retval; 2235 } 2236 2237 void print_types(std::ostream & os,int types) const2238 load_path::package_info::print_types (std::ostream& os, int types) const 2239 { 2240 bool printed_type = false; 2241 2242 if (types & load_path::OCT_FILE) 2243 { 2244 os << "oct"; 2245 printed_type = true; 2246 } 2247 2248 if (types & load_path::MEX_FILE) 2249 { 2250 if (printed_type) 2251 os << '|'; 2252 os << "mex"; 2253 printed_type = true; 2254 } 2255 2256 if (types & load_path::M_FILE) 2257 { 2258 if (printed_type) 2259 os << '|'; 2260 os << 'm'; 2261 printed_type = true; 2262 } 2263 } 2264 2265 void print_fcn_list(std::ostream & os,const load_path::dir_info::fcn_file_map_type & lst) const2266 load_path::package_info::print_fcn_list (std::ostream& os, 2267 const load_path::dir_info::fcn_file_map_type& lst) const 2268 { 2269 for (const auto& nm_typ : lst) 2270 { 2271 os << " " << nm_typ.first << " ("; 2272 2273 print_types (os, nm_typ.second); 2274 2275 os << ")\n"; 2276 } 2277 } 2278 2279 std::string genpath(const std::string & dirname,const string_vector & skip)2280 genpath (const std::string& dirname, const string_vector& skip) 2281 { 2282 std::string retval; 2283 string_vector dirlist; 2284 std::string msg; 2285 2286 if (! sys::get_dirlist (dirname, dirlist, msg)) 2287 return retval; 2288 2289 retval = dirname; 2290 2291 dirlist = dirlist.sort (false); 2292 2293 octave_idx_type len = dirlist.numel (); 2294 2295 for (octave_idx_type i = 0; i < len; i++) 2296 { 2297 std::string elt = dirlist[i]; 2298 2299 bool skip_p = (elt == "." || elt == ".." || elt[0] == '@' 2300 || elt[0] == '+'); 2301 2302 if (! skip_p) 2303 { 2304 for (octave_idx_type j = 0; j < skip.numel (); j++) 2305 { 2306 skip_p = (elt == skip[j]); 2307 if (skip_p) 2308 break; 2309 } 2310 2311 if (! skip_p) 2312 { 2313 std::string nm = sys::file_ops::concat (dirname, elt); 2314 2315 sys::file_stat fs (nm); 2316 2317 if (fs && fs.is_dir ()) 2318 retval += directory_path::path_sep_str () + genpath (nm, skip); 2319 } 2320 } 2321 } 2322 2323 return retval; 2324 } 2325 } 2326 2327 DEFUN (genpath, args, , 2328 doc: /* -*- texinfo -*- 2329 @deftypefn {} {} genpath (@var{dir}) 2330 @deftypefnx {} {} genpath (@var{dir}, @var{skip}, @dots{}) 2331 Return a path constructed from @var{dir} and all its subdirectories. 2332 2333 The path does not include package directories (beginning with @samp{+}), 2334 old-style class directories (beginning with @samp{@@}), @file{private} 2335 directories, or any subdirectories of these types. 2336 2337 If additional string parameters are given, the resulting path will exclude 2338 directories with those names. 2339 @seealso{path, addpath} 2340 @end deftypefn */) 2341 { 2342 int nargin = args.length (); 2343 2344 if (nargin == 0) 2345 print_usage (); 2346 2347 octave_value retval; 2348 2349 if (nargin == 1) 2350 { 2351 std::string dirname = args(0).xstring_value ("genpath: DIR must be a string"); 2352 2353 retval = octave::genpath (dirname); 2354 } 2355 else 2356 { 2357 std::string dirname = args(0).xstring_value ("genpath: all arguments must be strings"); 2358 2359 string_vector skip (nargin - 1); 2360 2361 for (octave_idx_type i = 1; i < nargin; i++) 2362 skip[i-1] = args(i).xstring_value ("genpath: all arguments must be strings"); 2363 2364 retval = octave::genpath (dirname, skip); 2365 } 2366 2367 return retval; 2368 } 2369 2370 DEFUN (rehash, , , 2371 doc: /* -*- texinfo -*- 2372 @deftypefn {} {} rehash () 2373 Reinitialize Octave's load path directory cache. 2374 @end deftypefn */) 2375 { 2376 octave::rehash_internal (); 2377 2378 return ovl (); 2379 } 2380 2381 DEFMETHOD (command_line_path, interp, args, , 2382 doc: /* -*- texinfo -*- 2383 @deftypefn {} {} command_line_path () 2384 Return the command line path variable. 2385 2386 @seealso{path, addpath, rmpath, genpath, pathdef, savepath, pathsep} 2387 @end deftypefn */) 2388 { 2389 if (! args.empty ()) 2390 print_usage (); 2391 2392 octave::load_path& lp = interp.get_load_path (); 2393 2394 return ovl (lp.get_command_line_path ()); 2395 } 2396 2397 DEFMETHOD (restoredefaultpath, interp, args, , 2398 doc: /* -*- texinfo -*- 2399 @deftypefn {} {} restoredefaultpath () 2400 Restore Octave's path to its initial state at startup. 2401 2402 @seealso{path, addpath, rmpath, genpath, pathdef, savepath, pathsep} 2403 @end deftypefn */) 2404 { 2405 if (! args.empty ()) 2406 print_usage (); 2407 2408 octave::load_path& lp = interp.get_load_path (); 2409 2410 lp.initialize (true); 2411 2412 return ovl (lp.system_path ()); 2413 } 2414 2415 // Return Octave's original default list of directories in which to 2416 // search for function files. This corresponds to the path that 2417 // exists prior to running the system's octaverc file or the user's 2418 // ~/.octaverc file 2419 2420 DEFMETHOD (__pathorig__, interp, , , 2421 doc: /* -*- texinfo -*- 2422 @deftypefn {} {@var{val} =} __pathorig__ () 2423 Undocumented internal function. 2424 @end deftypefn */) 2425 { 2426 octave::load_path& lp = interp.get_load_path (); 2427 2428 return ovl (lp.system_path ()); 2429 } 2430 2431 DEFMETHOD (path, interp, args, nargout, 2432 doc: /* -*- texinfo -*- 2433 @deftypefn {} {} path () 2434 @deftypefnx {} {@var{str} =} path () 2435 @deftypefnx {} {@var{str} =} path (@var{path1}, @dots{}) 2436 Modify or display Octave's load path. 2437 2438 If @var{nargin} and @var{nargout} are zero, display the elements of 2439 Octave's load path in an easy to read format. 2440 2441 If @var{nargin} is zero and nargout is greater than zero, return the 2442 current load path. 2443 2444 If @var{nargin} is greater than zero, concatenate the arguments, 2445 separating them with @code{pathsep}. Set the internal search path 2446 to the result and return it. 2447 2448 No checks are made for duplicate elements. 2449 @seealso{addpath, rmpath, genpath, pathdef, savepath, pathsep} 2450 @end deftypefn */) 2451 { 2452 int nargin = args.length (); 2453 2454 string_vector argv = args.make_argv ("path"); 2455 2456 octave::load_path& lp = interp.get_load_path (); 2457 2458 if (nargin > 0) 2459 { 2460 std::string path = argv[1]; 2461 2462 for (int i = 2; i <= nargin; i++) 2463 path += octave::directory_path::path_sep_str () + argv[i]; 2464 2465 lp.set (path, true); 2466 2467 octave::rehash_internal (); 2468 } 2469 2470 if (nargout > 0) 2471 return ovl (lp.path ()); 2472 else if (nargin == 0 && nargout == 0) 2473 { 2474 octave_stdout << 2475 "\nOctave's search path contains the following directories:\n\n"; 2476 2477 string_vector dirs = lp.dirs (); 2478 2479 dirs.list_in_columns (octave_stdout); 2480 2481 octave_stdout << "\n"; 2482 } 2483 2484 return ovl (); 2485 } 2486 2487 DEFMETHOD (addpath, interp, args, nargout, 2488 doc: /* -*- texinfo -*- 2489 @deftypefn {} {} addpath (@var{dir1}, @dots{}) 2490 @deftypefnx {} {} addpath (@var{dir1}, @dots{}, @var{option}) 2491 Add named directories to the function search path. 2492 2493 If @var{option} is @qcode{"-begin"} or 0 (the default), prepend the directory 2494 name(s) to the current path. If @var{option} is @qcode{"-end"} or 1, append 2495 the directory name(s) to the current path. Directories added to the path must 2496 exist. 2497 2498 In addition to accepting individual directory arguments, lists of 2499 directory names separated by @code{pathsep} are also accepted. For example: 2500 2501 @example 2502 addpath ("dir1:/dir2:~/dir3") 2503 @end example 2504 2505 The newly added paths appear in the load path in the same order that they 2506 appear in the arguments of @code{addpath}. When extending the load path to 2507 the front, the last path in the list of arguments is added first. When 2508 extending the load path to the end, the first path in the list of arguments 2509 is added first. 2510 2511 For each directory that is added, and that was not already in the path, 2512 @code{addpath} checks for the existence of a file named @file{PKG_ADD} 2513 (note lack of .m extension) and runs it if it exists. 2514 2515 @seealso{path, rmpath, genpath, pathdef, savepath, pathsep} 2516 @end deftypefn */) 2517 { 2518 // Originally written by Bill Denney and Etienne Grossman. 2519 // Heavily modified and translated to C++ by jwe. 2520 2521 int nargin = args.length (); 2522 2523 if (nargin == 0) 2524 print_usage (); 2525 2526 octave::load_path& lp = interp.get_load_path (); 2527 2528 octave_value retval; 2529 2530 if (nargout > 0) 2531 retval = lp.path (); 2532 2533 bool append = false; 2534 2535 octave_value option_arg = args(nargin-1); 2536 2537 if (option_arg.is_string ()) 2538 { 2539 std::string option = option_arg.string_value (); 2540 2541 if (option == "-end") 2542 { 2543 append = true; 2544 nargin--; 2545 } 2546 else if (option == "-begin") 2547 nargin--; 2548 } 2549 else if (option_arg.isnumeric ()) 2550 { 2551 int val = option_arg.xint_value ("addpath: OPTION must be '-begin'/0 or '-end'/1"); 2552 2553 if (val == 0) 2554 nargin--; 2555 else if (val == 1) 2556 { 2557 append = true; 2558 nargin--; 2559 } 2560 else 2561 error ("addpath: OPTION must be '-begin'/0 or '-end'/1"); 2562 } 2563 2564 bool need_to_update = false; 2565 2566 octave_value_list arglist (args.slice (0, nargin)); 2567 if (! append) 2568 arglist.reverse (); 2569 2570 for (int i = 0; i < arglist.length (); i++) 2571 { 2572 std::string arg = arglist(i).xstring_value ("addpath: all arguments must be strings"); 2573 2574 std::list<std::string> dir_elts = octave::split_path (arg); 2575 2576 if (! append) 2577 std::reverse (dir_elts.begin (), dir_elts.end ()); 2578 2579 for (auto dir : dir_elts) 2580 { 2581 // Remove duplicate directory separators 2582 auto it_start = dir.begin (); 2583 #if defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) 2584 // In Windows, start check at second character (for UNC paths). 2585 it_start++; 2586 #endif 2587 dir.erase (std::unique 2588 (it_start, dir.end (), 2589 [] (char l, char r) __anon29c60d5d0302(char l, char r) 2590 { 2591 return l == r && octave::sys::file_ops::is_dir_sep (l); 2592 }), 2593 dir.end ()); 2594 2595 auto pos = dir.find_last_of (octave::sys::file_ops::dir_sep_chars ()); 2596 if (pos == std::string::npos) 2597 { 2598 if (! dir.empty () && dir[0] == '+') 2599 warning_with_id ("Octave:addpath-pkg", 2600 "addpath: package directories should not be " 2601 "added to path: %s\n", dir.c_str ()); 2602 } 2603 else 2604 { 2605 if (pos + 1 < dir.length () && dir[pos+1] == '+') 2606 warning_with_id ("Octave:addpath-pkg", 2607 "addpath: package directories should not be " 2608 "added to path: %s\n", dir.c_str ()); 2609 } 2610 2611 if (append) 2612 lp.append (dir, true); 2613 else 2614 lp.prepend (dir, true); 2615 2616 need_to_update = true; 2617 } 2618 } 2619 2620 if (need_to_update) 2621 octave::rehash_internal (); 2622 2623 return retval; 2624 } 2625 2626 DEFMETHOD (rmpath, interp, args, nargout, 2627 doc: /* -*- texinfo -*- 2628 @deftypefn {} {} rmpath (@var{dir1}, @dots{}) 2629 Remove @var{dir1}, @dots{} from the current function search path. 2630 2631 In addition to accepting individual directory arguments, lists of 2632 directory names separated by @code{pathsep} are also accepted. For example: 2633 2634 @example 2635 rmpath ("dir1:/dir2:~/dir3") 2636 @end example 2637 2638 For each directory that is removed, @code{rmpath} checks for the 2639 existence of a file named @file{PKG_DEL} (note lack of .m extension) 2640 and runs it if it exists. 2641 2642 @seealso{path, addpath, genpath, pathdef, savepath, pathsep} 2643 @end deftypefn */) 2644 { 2645 // Originally written by Etienne Grossmann. Heavily modified and translated 2646 // to C++ by jwe. 2647 2648 int nargin = args.length (); 2649 2650 if (nargin == 0) 2651 print_usage (); 2652 2653 octave_value retval; 2654 2655 octave::load_path& lp = interp.get_load_path (); 2656 2657 if (nargout > 0) 2658 retval = lp.path (); 2659 2660 bool need_to_update = false; 2661 2662 for (int i = 0; i < nargin; i++) 2663 { 2664 std::string arg = args(i).xstring_value ("rmpath: all arguments must be strings"); 2665 std::list<std::string> dir_elts = octave::split_path (arg); 2666 2667 for (const auto& dir : dir_elts) 2668 { 2669 //dir = regexprep (dir_elts{j}, '//+', "/"); 2670 //dir = regexprep (dir, '/$', ""); 2671 2672 if (! lp.remove (dir)) 2673 warning ("rmpath: %s: not found", dir.c_str ()); 2674 else 2675 need_to_update = true; 2676 } 2677 } 2678 2679 if (need_to_update) 2680 octave::rehash_internal (); 2681 2682 return retval; 2683 } 2684 2685 DEFMETHOD (__dump_load_path__, interp, , , 2686 doc: /* -*- texinfo -*- 2687 @deftypefn {} {} __dump_load_path__ () 2688 Undocumented internal function. 2689 @end deftypefn */) 2690 { 2691 octave::load_path& lp = interp.get_load_path (); 2692 2693 lp.display (octave_stdout); 2694 2695 return ovl (); 2696 } 2697