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