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