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