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