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