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