GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
kpse.cc
Go to the documentation of this file.
1// This file is not compiled to a separate object file.
2// It is included in pathsearch.cc.
3
4//////////////////////////////////////////////////////////////////////////
5//
6// Copyright (C) 1991-2025 The Octave Project Developers
7//
8// See the file COPYRIGHT.md in the top-level directory of this
9// distribution or <https://octave.org/copyright/>.
10//
11// This file is part of Octave.
12//
13// Octave is free software: you can redistribute it and/or modify it
14// under the terms of the GNU General Public License as published by
15// the Free Software Foundation, either version 3 of the License, or
16// (at your option) any later version.
17//
18// Octave is distributed in the hope that it will be useful, but
19// WITHOUT ANY WARRANTY; without even the implied warranty of
20// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21// GNU General Public License for more details.
22//
23// You should have received a copy of the GNU General Public License
24// along with Octave; see the file COPYING. If not, see
25// <https://www.gnu.org/licenses/>.
26//
27////////////////////////////////////////////////////////////////////////
28
29// Look up a filename in a path.
30
31#if defined (HAVE_CONFIG_H)
32# include "config.h"
33#endif
34
35#include <cctype>
36#include <cerrno>
37#include <cstdlib>
38
39#include <map>
40#include <fstream>
41#include <iostream>
42#include <string>
43
44#include "dir-ops.h"
45#include "file-ops.h"
46#include "file-stat.h"
47#include "kpse.h"
48#include "lo-sysdep.h"
49#include "oct-env.h"
50#include "oct-password.h"
51#include "oct-time.h"
52#include "pathsearch.h"
53#include "unistd-wrappers.h"
54
55#if defined (OCTAVE_USE_WINDOWS_API)
56# define WIN32_LEAN_AND_MEAN 1
57# include <windows.h>
58#endif
59
60// Define the characters which separate components of filenames and
61// environment variable paths.
62
63#define IS_DEVICE_SEP(ch) octave::sys::file_ops::is_dev_sep (ch)
64#define NAME_BEGINS_WITH_DEVICE(name) \
65 (name.length () > 0 && IS_DEVICE_SEP ((name)[1]))
66
67#define DIR_SEP_STRING octave::sys::file_ops::dir_sep_str ()
68#define IS_DIR_SEP(ch) octave::sys::file_ops::is_dir_sep (ch)
69
70#define ENV_SEP octave::directory_path::path_sep_char ()
71#define ENV_SEP_STRING octave::directory_path::path_sep_str ()
72#define IS_ENV_SEP(ch) octave::directory_path::is_path_sep (ch)
73
74// If NO_DEBUG is defined (not recommended), skip all this.
75#if ! defined (NO_DEBUG)
76
77// OK, we'll have tracing support.
78# define KPSE_DEBUG
79
80// Test if a bit is on.
81# define KPSE_DEBUG_P(bit) (kpse_debug & (1 << (bit)))
82
83# define KPSE_DEBUG_STAT 0 // stat calls
84# define KPSE_DEBUG_EXPAND 1 // path element expansion
85# define KPSE_DEBUG_SEARCH 2 // searches
86# define KPSE_DEBUG_VARS 3 // variable values
87# define KPSE_LAST_DEBUG KPSE_DEBUG_VARS
88
89#endif
90
91unsigned int kpse_debug = 0;
92
93void
94kpse_path_iterator::set_end ()
95{
96 m_e = m_b + 1;
97
98 if (m_e == m_len)
99 ; // OK, we have found the last element.
100 else if (m_e > m_len)
101 m_b = m_e = std::string::npos;
102 else
103 {
104 // Find the next colon not enclosed by braces (or the end of the
105 // path).
106
107 while (m_e < m_len && ! octave::directory_path::is_path_sep (m_path[m_e]))
108 m_e++;
109 }
110}
111
112void
113kpse_path_iterator::next ()
114{
115 m_b = m_e + 1;
116
117 // Skip any consecutive colons.
118 while (m_b < m_len && octave::directory_path::is_path_sep (m_path[m_b]))
119 m_b++;
120
121 if (m_b >= m_len)
122 m_b = m_e = std::string::npos;
123 else
124 set_end ();
125}
126
127/* Truncate any too-long components in NAME, returning the result. It's
128 too bad this is necessary. See comments in readable.c for why. */
129
130static std::string
131kpse_truncate_filename (const std::string& name)
132{
133 unsigned c_len = 0; /* Length of current component. */
134 unsigned ret_len = 0; /* Length of constructed result. */
135
136 std::string ret = name;
137
138 std::size_t m_len = name.length ();
139
140 for (std::size_t i = 0; i < m_len; i++)
141 {
142 if (IS_DIR_SEP (name[i]) || IS_DEVICE_SEP (name[i]))
143 {
144 /* At a directory delimiter, reset component length. */
145 c_len = 0;
146 }
147 else if (c_len > octave::sys::dir_entry::max_name_length ())
148 {
149 /* If past the max for a component, ignore this character. */
150 continue;
151 }
152
153 /* Copy this character. */
154 ret[ret_len++] = name[i];
155 c_len++;
156 }
157
158 ret.resize (ret_len);
159
160 return ret;
161}
162
163/* If access can read FN, run stat (assigning to stat buffer ST) and
164 check that fn is not a directory. Don't check for just being a
165 regular file, as it is potentially useful to read fifo's or some
166 kinds of devices. */
167
168static inline bool
169READABLE (const std::string& fn)
170{
171#if defined (OCTAVE_USE_WINDOWS_API)
172
173 std::wstring w_fn = octave::sys::u8_to_wstring (fn);
174
175 DWORD f_attr = GetFileAttributesW (w_fn.c_str ());
176
177 return (f_attr != 0xFFFFFFFF && ! (f_attr & FILE_ATTRIBUTE_DIRECTORY));
178
179#else
180
181 bool retval = false;
182
183 const char *t = fn.c_str ();
184
186 {
187 octave::sys::file_stat fs (fn);
188
189 retval = fs && ! fs.is_dir ();
190 }
191
192 return retval;
193
194#endif
195}
196
197/* POSIX invented the brain-damage of not necessarily truncating
198 filename components; the system's behavior is defined by the value of
199 the symbol _POSIX_NO_TRUNC, but you can't change it dynamically!
200
201 Generic const return warning. See extend-fname.c. */
202
203static std::string
204kpse_readable_file (const std::string& name)
205{
206 std::string ret;
207
208 if (READABLE (name))
209 {
210 ret = name;
211
212#if defined (ENAMETOOLONG)
213 }
214 else if (errno == ENAMETOOLONG)
215 {
216 ret = kpse_truncate_filename (name);
217
218 /* Perhaps some other error will occur with the truncated name,
219 so let's call access again. */
220
221 if (! READABLE (ret))
222 {
223 /* Failed. */
224 ret = "";
225 }
226#endif /* ENAMETOOLONG */
227
228 }
229 else
230 {
231 /* Some other error. */
232 if (errno == EACCES)
233 {
234 /* Maybe warn them if permissions are bad. */
235 perror (name.c_str ());
236 }
237
238 ret = "";
239 }
240
241 return ret;
242}
243
244static bool
245kpse_absolute_p (const std::string& filename, int relative_ok)
246{
247 return (octave::sys::env::absolute_pathname (filename)
248 || (relative_ok
249 && octave::sys::env::rooted_relative_pathname (filename)));
250}
251
252/* The very first search is for texmf.cnf, called when someone tries to
253 initialize the TFM path or whatever. init_path calls kpse_cnf_get
254 which calls kpse_all_path_search to find all the texmf.cnf's. We
255 need to do various special things in this case, since we obviously
256 don't yet have the configuration files when we're searching for the
257 configuration files. */
258static bool first_search = true;
259
260/* This function is called after every search. */
261
262static void
263log_search (const std::list<std::string>& filenames)
264{
266 {
267 for (const auto& filename : filenames)
268 {
269 octave::sys::time now;
270 std::cerr << now.unix_time () << ' ' << filename << std::endl;
271 }
272 }
273}
274
275/* Concatenate each element in DIRS with NAME (assume each ends with a
276 /, to save time). If SEARCH_ALL is false, return the first readable
277 regular file. Else continue to search for more. In any case, if
278 none, return a list containing just NULL.
279
280 We keep a single buffer for the potential filenames and reallocate
281 only when necessary. I'm not sure it's noticeably faster, but it
282 does seem cleaner. (We do waste a bit of space in the return
283 value, though, since we don't shrink it to the final size returned.) */
284
285static std::list<std::string>
286dir_search (const std::string& dir, const std::string& name,
287 bool search_all)
288{
289 std::list<std::string> ret;
290
291 std::string potential = dir + name;
292
293 std::string tmp = kpse_readable_file (potential);
294
295 if (! tmp.empty ())
296 {
297 ret.push_back (potential);
298
299 if (! search_all)
300 return ret;
301 }
302
303 return ret;
304}
305
306/* This is called when NAME is absolute or explicitly relative; if it's
307 readable, return (a list containing) it; otherwise, return NULL. */
308
309static std::list<std::string>
310absolute_search (const std::string& name)
311{
312 std::list<std::string> ret_list;
313 std::string found = kpse_readable_file (name);
314
315 /* Add 'found' to the return list even if it's null; that tells
316 the caller we didn't find anything. */
317 ret_list.push_back (found);
318
319 return ret_list;
320}
321
322/* This is the hard case -- look for NAME in PATH. If ALL is false,
323 return the first file found. Otherwise, search all elements of PATH. */
324
325static std::list<std::string>
326path_search (const std::string& path, const std::string& name, bool all)
327{
328 std::list<std::string> ret_list;
329 bool done = false;
330
331 for (kpse_path_iterator pi (path); ! done && pi != std::string::npos; pi++)
332 {
333 std::string elt = *pi;
334
335 std::list<std::string> found;
336
337 /* Do not touch the device if present */
338 if (NAME_BEGINS_WITH_DEVICE (elt))
339 {
340 while (elt.length () > 3
341 && IS_DIR_SEP (elt[2]) && IS_DIR_SEP (elt[3]))
342 {
343 elt[2] = elt[1];
344 elt[1] = elt[0];
345 elt = elt.substr (1);
346 }
347 }
348#if (! defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) \
349 && ! defined (DOUBLE_SLASH_IS_DISTINCT_ROOT))
350 else
351 {
352 /* We never want to search the whole disk. */
353 while (elt.length () > 1
354 && IS_DIR_SEP (elt[0]) && IS_DIR_SEP (elt[1]))
355 elt = elt.substr (1);
356 }
357#endif
358
359 /* Our caller (search), also tests first_search, and does
360 the resetting. */
361 if (first_search)
362 found = std::list<std::string> ();
363
364 /* Search the filesystem. */
365
366 if (found.empty ())
367 {
368 std::string dir = kpse_element_dir (elt);
369
370 if (! dir.empty ())
371 found = dir_search (dir, name, all);
372 }
373
374 /* Did we find anything anywhere? */
375 if (! found.empty ())
376 {
377 if (all)
378 ret_list.splice (ret_list.end (), found);
379 else
380 {
381 ret_list.push_back (found.front ());
382 done = true;
383 }
384 }
385 }
386
387 return ret_list;
388}
389
390/* If NAME has a leading ~ or ~user, Unix-style, expand it to the user's
391 home directory, and return a new malloced string. If no ~, or no
392 <pwd.h>, just return NAME. */
393
394static std::string
395kpse_tilde_expand (const std::string& name)
396{
397 std::string expansion;
398
399 /* If no leading tilde, do nothing. */
400 if (name.empty () || name[0] != '~')
401 {
402 expansion = name;
403
404 /* If a bare tilde, return the home directory or '.'. (Very
405 unlikely that the directory name will do anyone any good, but
406 ... */
407 }
408 else if (name.length () == 1)
409 {
410 expansion = octave::sys::env::get_home_directory ();
411
412 if (expansion.empty ())
413 expansion = ".";
414
415 /* If '~/', remove any trailing / or replace leading // in $HOME.
416 Should really check for doubled intermediate slashes, too. */
417 }
418 else if (IS_DIR_SEP (name[1]))
419 {
420 unsigned c = 1;
421 std::string home = octave::sys::env::get_home_directory ();
422
423 if (home.empty ())
424 home = ".";
425
426 std::size_t home_len = home.length ();
427
428#if (! defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) \
429 && ! defined (DOUBLE_SLASH_IS_DISTINCT_ROOT))
430 /* handle leading // */
431 if (home_len > 1 && IS_DIR_SEP (home[0]) && IS_DIR_SEP (home[1]))
432 home = home.substr (1);
433#endif
434
435 /* omit / after ~ */
436 if (IS_DIR_SEP (home[home_len - 1]))
437 c++;
438
439 expansion = home + name.substr (c);
440
441 /* If '~user' or '~user/', look up user in the passwd database (but
442 OS/2 doesn't have this concept. */
443 }
444 else
445#if defined (HAVE_PWD_H)
446 {
447 unsigned c = 2;
448
449 /* find user name */
450 while (name.length () > c && ! IS_DIR_SEP (name[c]))
451 c++;
452
453 std::string user = name.substr (1, c-1);
454
455 /* We only need the cast here for (deficient) systems
456 which do not declare 'getpwnam' in <pwd.h>. */
457 octave::sys::password p = octave::sys::password::getpwnam (user);
458
459 /* If no such user, just use '.'. */
460 std::string home = (p ? p.dir () : ".");
461
462 if (home.empty ())
463 home = ".";
464
465# if (! defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) \
466 && ! defined (DOUBLE_SLASH_IS_DISTINCT_ROOT))
467 /* handle leading // */
468 if (home.length () > 1 && IS_DIR_SEP (home[0]) && IS_DIR_SEP (home[1]))
469 home = home.substr (1);
470# endif
471
472 /* If HOME ends in /, omit the / after ~user. */
473 if (name.length () > c && IS_DIR_SEP (home.back ()))
474 c++;
475
476 expansion = (name.length () > c ? home : home + name.substr (c));
477 }
478#else /* not HAVE_PWD_H */
479 expansion = name;
480#endif /* not HAVE_PWD_H */
481
482 return expansion;
483}
484
485/* Search PATH for ORIGINAL_NAME. If ALL is false, or ORIGINAL_NAME is
486 absolute_p, check ORIGINAL_NAME itself. Otherwise, look at each
487 element of PATH for the first readable ORIGINAL_NAME.
488
489 Always return a list; if no files are found, the list will
490 contain just NULL. If ALL is true, the list will be
491 terminated with NULL. */
492
493static std::list<std::string>
494search (const std::string& path, const std::string& original_name,
495 bool all)
496{
497 std::list<std::string> ret_list;
498 bool absolute_p;
499
500 /* Make a leading ~ count as an absolute filename. */
501 std::string name = kpse_tilde_expand (original_name);
502
503 /* If the first name is absolute or explicitly relative, no need to
504 consider PATH at all. */
505 absolute_p = kpse_absolute_p (name, true);
506
508 std::cerr << "kdebug: start search (file=" << name
509 << ", find_all=" << all << ", path=" << path << ")."
510 << std::endl;
511
512 /* Find the file(s). */
513 ret_list = (absolute_p
514 ? absolute_search (name)
515 : path_search (path, name, all));
516
517 /* The very first search is for texmf.cnf. We can't log that, since
518 we want to allow setting TEXMFLOG in texmf.cnf. */
519 if (first_search)
520 {
521 first_search = false;
522 }
523 else
524 {
525 /* Record the filenames we found, if desired. And wrap them in a
526 debugging line if we're doing that. */
527
529 std::cerr << "kdebug: search (" << original_name << ") =>";
530
531 log_search (ret_list);
532
534 std::cerr << std::endl;
535 }
536
537 return ret_list;
538}
539
540/* Search PATH for the first NAME. */
541
542/* Perform tilde expansion on NAME. If the result is an absolute or
543 explicitly relative filename, check whether it is a readable
544 (regular) file.
545
546 Otherwise, look in each of the directories specified in PATH (also do
547 tilde and variable expansion on elements in PATH).
548
549 The caller must expand PATH. This is because it makes more sense to
550 do this once, in advance, instead of for every search using it.
551
552 In any case, return the complete filename if found, otherwise NULL. */
553
554std::string
555kpse_path_search (const std::string& path, const std::string& name)
556{
557 std::list<std::string> ret_list = search (path, name, false);
558
559 return ret_list.empty () ? "" : ret_list.front ();
560}
561
562/* Like 'kpse_path_search' with MUST_EXIST true, but return a list of
563 all the filenames (or NULL if none), instead of taking the first. */
564
565std::list<std::string>
566kpse_all_path_search (const std::string& path, const std::string& name)
567{
568 return search (path, name, true);
569}
570
571/* This is the hard case -- look in each element of PATH for each
572 element of NAMES. If ALL is false, return the first file found.
573 Otherwise, search all elements of PATH. */
574
575std::list<std::string>
576path_find_first_of (const std::string& path,
577 const std::list<std::string>& names, bool all)
578{
579 std::list<std::string> ret_list;
580 bool done = false;
581
582 for (kpse_path_iterator pi (path); ! done && pi != std::string::npos; pi++)
583 {
584 std::string elt = *pi;
585
586 std::string dir;
587 std::list<std::string> found;
588
589 /* Do not touch the device if present */
590
591 if (NAME_BEGINS_WITH_DEVICE (elt))
592 {
593 while (elt.length () > 3
594 && IS_DIR_SEP (elt[2]) && IS_DIR_SEP (elt[3]))
595 {
596 elt[2] = elt[1];
597 elt[1] = elt[0];
598 elt = elt.substr (1);
599 }
600 }
601#if (! defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) \
602 && ! defined (DOUBLE_SLASH_IS_DISTINCT_ROOT))
603 else
604 {
605 /* We never want to search the whole disk. */
606 while (elt.length () > 1
607 && IS_DIR_SEP (elt[0]) && IS_DIR_SEP (elt[1]))
608 elt = elt.substr (1);
609 }
610#endif
611
612 /* We have to search one directory at a time. */
613 dir = kpse_element_dir (elt);
614
615 if (! dir.empty ())
616 {
617 for (auto it = names.cbegin (); it != names.cend () && ! done; it++)
618 {
619 std::string name = *it;
620
621 /* Our caller (find_first_of), also tests first_search,
622 and does the resetting. */
623 if (first_search)
624 found = std::list<std::string> ();
625
626 /* Search the filesystem. */
627
628 if (found.empty ())
629 found = dir_search (dir, name, all);
630
631 /* Did we find anything anywhere? */
632 if (! found.empty ())
633 {
634 if (all)
635 ret_list.splice (ret_list.end (), found);
636 else
637 {
638 ret_list.push_back (found.front ());
639 done = true;
640 }
641 }
642 }
643 }
644 }
645
646 return ret_list;
647}
648
649static std::list<std::string>
650find_first_of (const std::string& path, const std::list<std::string>& names,
651 bool all)
652{
653 std::list<std::string> ret_list;
654
656 {
657 std::cerr << "kdebug: start find_first_of (";
658
659 for (auto p = names.cbegin (); p != names.cend (); p++)
660 {
661 if (p == names.cbegin ())
662 std::cerr << *p;
663 else
664 std::cerr << ", " << *p;
665 }
666
667 std::cerr << "), path=" << path << '.' << std::endl;
668 }
669
670 for (const auto& name : names)
671 {
672 if (kpse_absolute_p (name, true))
673 {
674 /* If the name is absolute or explicitly relative, no need
675 to consider PATH at all. If we find something, then we
676 are done. */
677
678 ret_list = absolute_search (name);
679
680 if (! ret_list.empty ())
681 return ret_list;
682 }
683 }
684
685 /* Find the file. */
686 ret_list = path_find_first_of (path, names, all);
687
688 /* The very first search is for texmf.cnf. We can't log that, since
689 we want to allow setting TEXMFLOG in texmf.cnf. */
690 if (first_search)
691 {
692 first_search = false;
693 }
694 else
695 {
696 /* Record the filenames we found, if desired. And wrap them in a
697 debugging line if we're doing that. */
698
700 {
701 std::cerr << "kdebug: find_first_of (";
702
703 for (auto p = names.cbegin (); p != names.cend (); p++)
704 {
705 if (p == names.cbegin ())
706 std::cerr << *p;
707 else
708 std::cerr << ", " << *p;
709 }
710
711 std::cerr << ") =>";
712 }
713
714 log_search (ret_list);
715
717 std::cerr << std::endl;
718 }
719
720 return ret_list;
721}
722
723/* Search each element of PATH for each element of NAMES. Return the
724 first one found. */
725
726/* Search each element of PATH for each element in the list of NAMES.
727 Return the first one found. */
728
729std::string
730kpse_path_find_first_of (const std::string& path,
731 const std::list<std::string>& names)
732{
733 std::list<std::string> ret_list = find_first_of (path, names, false);
734
735 return ret_list.empty () ? "" : ret_list.front ();
736}
737
738/* Search each element of PATH for each element of NAMES and return a
739 list containing everything found, in the order found. */
740
741/* Like 'kpse_path_find_first_of' with MUST_EXIST true, but return a
742 list of all the filenames (or NULL if none), instead of taking the
743 first. */
744
745std::list<std::string>
746kpse_all_path_find_first_of (const std::string& path,
747 const std::list<std::string>& names)
748{
749 return find_first_of (path, names, true);
750}
751
752/* Perform tilde expansion on each element of the path, and include
753 canonical directory names for only the the actually existing
754 directories in the result. */
755
756std::string
757kpse_path_expand (const std::string& path)
758{
759 std::string ret;
760 unsigned len = 0;
761
762 /* Now expand each of the path elements, printing the results */
763 for (kpse_path_iterator pi (path); pi != std::string::npos; pi++)
764 {
765 std::string elt = kpse_tilde_expand (*pi);
766
767 std::string dir;
768
769 /* Do not touch the device if present */
770 if (NAME_BEGINS_WITH_DEVICE (elt))
771 {
772 while (elt.length () > 3
773 && IS_DIR_SEP (elt[2]) && IS_DIR_SEP (elt[3]))
774 {
775 elt[2] = elt[1];
776 elt[1] = elt[0];
777 elt = elt.substr (1);
778 }
779 }
780#if (! defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) \
781 && ! defined (DOUBLE_SLASH_IS_DISTINCT_ROOT))
782 else
783 {
784 /* We never want to search the whole disk. */
785 while (elt.length () > 1
786 && IS_DIR_SEP (elt[0]) && IS_DIR_SEP (elt[1]))
787 elt = elt.substr (1);
788 }
789#endif
790
791 /* Search the disk for all dirs in the component specified.
792 Be faster to check the database, but this is more reliable. */
793 dir = kpse_element_dir (elt);
794
795 std::size_t dirlen = dir.length ();
796
797 if (dirlen > 0)
798 {
799 ret += dir;
800 len += dirlen;
801
802 /* Retain trailing slash if that's the root directory. */
803 if (dirlen == 1
804 || (dirlen == 3 && NAME_BEGINS_WITH_DEVICE (dir)
805 && IS_DIR_SEP (dir[2])))
806 {
807 ret += ENV_SEP_STRING;
808 len++;
809 }
810
811 ret[len-1] = ENV_SEP;
812 }
813 }
814
815 if (! ret.empty ())
816 ret.pop_back ();
817
818 return ret;
819}
820
821/* braces.c -- code for doing word expansion in curly braces. Taken from
822 bash 1.14.5. [And subsequently modified for kpatshea.]
823
824 Copyright (C) 1987,1991 Free Software Foundation, Inc. */
825
826#define brace_whitespace(c) (! (c) || (c) == ' ' || (c) == '\t' || (c) == '\n')
827
828/* Basic idea:
829
830 Segregate the text into 3 sections: preamble (stuff before an open brace),
831 postamble (stuff after the matching close brace) and amble (stuff after
832 preamble, and before postamble). Expand amble, and then tack on the
833 expansions to preamble. Expand postamble, and tack on the expansions to
834 the result so far. */
835
836/* Return a new array of strings which is the result of appending each
837 string in ARR2 to each string in ARR1. The resultant array is
838 len (arr1) * len (arr2) long. For convenience, ARR1 (and its contents)
839 are free ()'ed. ARR1 can be NULL, in that case, a new version of ARR2
840 is returned. */
841
842static std::list<std::string>
843array_concat (const std::list<std::string>& arr1,
844 const std::list<std::string>& arr2)
845{
846 std::list<std::string> result;
847
848 if (arr1.empty ())
849 result = arr2;
850 else if (arr2.empty ())
851 result = arr1;
852 else
853 {
854 for (const auto& elt_2 : arr2)
855 for (const auto& elt_1 : arr1)
856 result.push_back (elt_1 + elt_2);
857 }
858
859 return result;
860}
861
862static int brace_gobbler (const std::string&, int&, int);
863static std::list<std::string> expand_amble (const std::string&);
864
865/* Return an array of strings; the brace expansion of TEXT. */
866static std::list<std::string>
867brace_expand (const std::string& text)
868{
869 /* Find the text of the preamble. */
870 int i = 0;
871 int c = brace_gobbler (text, i, '{');
872
873 std::string preamble = text.substr (0, i);
874
875 std::list<std::string> result (1, preamble);
876
877 if (c == '{')
878 {
879 /* Find the amble. This is the stuff inside this set of braces. */
880 int start = ++i;
881 c = brace_gobbler (text, i, '}');
882
883 /* What if there isn't a matching close brace? */
884 if (! c)
885 {
886 (*current_liboctave_warning_with_id_handler)
887 ("Octave:pathsearch-syntax",
888 "%s: Unmatched {", text.c_str ());
889
890 result = std::list<std::string> (1, text);
891 }
892 else
893 {
894 std::string amble = text.substr (start, i-start);
895 result = array_concat (result, expand_amble (amble));
896
897 std::string postamble = text.substr (i+1);
898 result = array_concat (result, brace_expand (postamble));
899 }
900 }
901
902 return result;
903}
904
905/* The character which is used to separate arguments. */
906static int brace_arg_separator = ',';
907
908/* Expand the text found inside of braces. We simply try to split the
909 text at BRACE_ARG_SEPARATORs into separate strings. We then brace
910 expand each slot which needs it, until there are no more slots which
911 need it. */
912static std::list<std::string>
913expand_amble (const std::string& text)
914{
915 std::list<std::string> result;
916
917 std::size_t text_len = text.length ();
918 std::size_t start;
919 int i, c;
920
921 for (start = 0, i = 0, c = 1; c && start < text_len; start = ++i)
922 {
923 int i0 = i;
924 int c0 = brace_gobbler (text, i0, brace_arg_separator);
925 int i1 = i;
926 int c1 = brace_gobbler (text, i1, ENV_SEP);
927 c = c0 | c1;
928 i = (i0 < i1 ? i0 : i1);
929
930 std::string tem = text.substr (start, i-start);
931
932 std::list<std::string> partial = brace_expand (tem);
933
934 if (result.empty ())
935 result = partial;
936 else
937 result.splice (result.end (), partial);
938 }
939
940 return result;
941}
942
943/* Start at INDEX, and skip characters in TEXT. Set INDEX to the
944 index of the character matching SATISFY. This understands about
945 quoting. Return the character that caused us to stop searching;
946 this is either the same as SATISFY, or 0. */
947static int
948brace_gobbler (const std::string& text, int& indx, int satisfy)
949{
950 int c = 0;
951 int level = 0;
952 int quoted = 0;
953 int pass_next = 0;
954
955 std::size_t text_len = text.length ();
956
957 std::size_t i = indx;
958
959 for (; i < text_len; i++)
960 {
961 c = text[i];
962
963 if (pass_next)
964 {
965 pass_next = 0;
966 continue;
967 }
968
969 /* A backslash escapes the next character. This allows backslash to
970 escape the quote character in a double-quoted string. */
971 if (c == '\\' && (quoted == 0 || quoted == '"' || quoted == '`'))
972 {
973 pass_next = 1;
974 continue;
975 }
976
977 if (quoted)
978 {
979 if (c == quoted)
980 quoted = 0;
981 continue;
982 }
983
984 if (c == '"' || c == '\'' || c == '`')
985 {
986 quoted = c;
987 continue;
988 }
989
990 if (c == satisfy && ! level && ! quoted)
991 {
992 /* We ignore an open brace surrounded by whitespace, and also
993 an open brace followed immediately by a close brace, that
994 was preceded with whitespace. */
995 if (c == '{'
996 && ((i == 0 || brace_whitespace (text[i-1]))
997 && (i+1 < text_len
998 && (brace_whitespace (text[i+1]) || text[i+1] == '}'))))
999 continue;
1000 /* If this is being compiled as part of bash, ignore the '{'
1001 in a '${ }' construct */
1002 if ((c != '{') || i == 0 || (text[i-1] != '$'))
1003 break;
1004 }
1005
1006 if (c == '{')
1007 level++;
1008 else if (c == '}' && level)
1009 level--;
1010 }
1011
1012 indx = i;
1013 c = (c == satisfy) ? c : 0;
1014 return c;
1015}
1016
1017/* Given a path element ELT, return a the element with a trailing slash
1018 or an empty string if the element is not a directory.
1019
1020 It's up to the caller to expand ELT. This is because this routine is
1021 most likely only useful to be called from 'kpse_path_search', which
1022 has already assumed expansion has been done. */
1023
1024std::string
1025kpse_element_dir (const std::string& elt)
1026{
1027 std::string ret;
1028
1029 /* If given nothing, return nothing. */
1030 if (elt.empty ())
1031 return ret;
1032
1033 if (octave::sys::dir_exists (elt))
1034 {
1035 ret = elt;
1036
1037 char last_char = ret.back ();
1038
1039 if (! (IS_DIR_SEP (last_char) || IS_DEVICE_SEP (last_char)))
1040 ret += DIR_SEP_STRING;
1041 }
1042
1043 return ret;
1044}
std::string kpse_path_find_first_of(const std::string &path, const std::list< std::string > &names)
Definition kpse.cc:730
#define DIR_SEP_STRING
Definition kpse.cc:67
#define NAME_BEGINS_WITH_DEVICE(name)
Definition kpse.cc:64
#define KPSE_DEBUG_SEARCH
Definition kpse.cc:85
std::string kpse_element_dir(const std::string &elt)
Definition kpse.cc:1025
#define ENV_SEP_STRING
Definition kpse.cc:71
std::string kpse_path_expand(const std::string &path)
Definition kpse.cc:757
unsigned int kpse_debug
Definition kpse.cc:91
std::list< std::string > kpse_all_path_find_first_of(const std::string &path, const std::list< std::string > &names)
Definition kpse.cc:746
std::list< std::string > kpse_all_path_search(const std::string &path, const std::string &name)
Definition kpse.cc:566
#define KPSE_DEBUG_P(bit)
Definition kpse.cc:81
#define IS_DIR_SEP(ch)
Definition kpse.cc:68
#define brace_whitespace(c)
Definition kpse.cc:826
std::list< std::string > path_find_first_of(const std::string &path, const std::list< std::string > &names, bool all)
Definition kpse.cc:576
#define IS_DEVICE_SEP(ch)
Definition kpse.cc:63
#define ENV_SEP
Definition kpse.cc:70
std::string kpse_path_search(const std::string &path, const std::string &name)
Definition kpse.cc:555
int octave_access_r_ok(void)
int octave_access_wrapper(const char *nm, int mode)
F77_RET_T len
Definition xerbla.cc:61