GNU Octave  6.2.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-2021 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-passwd.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 
92 unsigned int kpse_debug = 0;
93 
94 void
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 
113 void
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 
131 static std::string
132 kpse_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  size_t m_len = name.length ();
140 
141  for (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 
169 static inline bool
170 READABLE (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 
186  if (octave_access_wrapper (t, octave_access_r_ok ()) == 0)
187  {
188  octave::sys::file_stat fs (fn);
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 
204 static std::string
205 kpse_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 
245 static bool
246 kpse_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. */
259 static bool first_search = true;
260 
261 /* This function is called after every search. */
262 
263 static void
264 log_search (const std::list<std::string>& filenames)
265 {
267  {
268  for (const auto& filename : filenames)
269  {
270  octave::sys::time now;
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 
286 static std::list<std::string>
287 dir_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 
310 static std::list<std::string>
311 absolute_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 
326 static std::list<std::string>
327 path_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  else
350  {
351  /* We never want to search the whole disk. */
352  while (elt.length () > 1
353  && IS_DIR_SEP (elt[0]) && IS_DIR_SEP (elt[1]))
354  elt = elt.substr (1);
355  }
356 
357  /* Our caller (search), also tests first_search, and does
358  the resetting. */
359  if (first_search)
360  found = std::list<std::string> ();
361 
362  /* Search the filesystem. */
363 
364  if (found.empty ())
365  {
366  std::string dir = kpse_element_dir (elt);
367 
368  if (! dir.empty ())
369  found = dir_search (dir, name, all);
370  }
371 
372  /* Did we find anything anywhere? */
373  if (! found.empty ())
374  {
375  if (all)
376  ret_list.splice (ret_list.end (), found);
377  else
378  {
379  ret_list.push_back (found.front ());
380  done = true;
381  }
382  }
383  }
384 
385  return ret_list;
386 }
387 
388 /* If NAME has a leading ~ or ~user, Unix-style, expand it to the user's
389  home directory, and return a new malloced string. If no ~, or no
390  <pwd.h>, just return NAME. */
391 
392 static std::string
393 kpse_tilde_expand (const std::string& name)
394 {
395  std::string expansion;
396 
397  /* If no leading tilde, do nothing. */
398  if (name.empty () || name[0] != '~')
399  {
400  expansion = name;
401 
402  /* If a bare tilde, return the home directory or '.'. (Very
403  unlikely that the directory name will do anyone any good, but
404  ... */
405  }
406  else if (name.length () == 1)
407  {
409 
410  if (expansion.empty ())
411  expansion = ".";
412 
413  /* If '~/', remove any trailing / or replace leading // in $HOME.
414  Should really check for doubled intermediate slashes, too. */
415  }
416  else if (IS_DIR_SEP (name[1]))
417  {
418  unsigned c = 1;
419  std::string home = octave::sys::env::get_home_directory ();
420 
421  if (home.empty ())
422  home = ".";
423 
424  size_t home_len = home.length ();
425 
426  /* handle leading // */
427  if (home_len > 1 && IS_DIR_SEP (home[0]) && IS_DIR_SEP (home[1]))
428  home = home.substr (1);
429 
430  /* omit / after ~ */
431  if (IS_DIR_SEP (home[home_len - 1]))
432  c++;
433 
434  expansion = home + name.substr (c);
435 
436  /* If '~user' or '~user/', look up user in the passwd database (but
437  OS/2 doesn't have this concept. */
438  }
439  else
440 #if defined (HAVE_PWD_H)
441  {
442  unsigned c = 2;
443 
444  /* find user name */
445  while (name.length () > c && ! IS_DIR_SEP (name[c]))
446  c++;
447 
448  std::string user = name.substr (1, c-1);
449 
450  /* We only need the cast here for (deficient) systems
451  which do not declare 'getpwnam' in <pwd.h>. */
453 
454  /* If no such user, just use '.'. */
455  std::string home = (p ? p.dir () : ".");
456 
457  if (home.empty ())
458  home = ".";
459 
460  /* handle leading // */
461  if (home.length () > 1 && IS_DIR_SEP (home[0]) && IS_DIR_SEP (home[1]))
462  home = home.substr (1);
463 
464  /* If HOME ends in /, omit the / after ~user. */
465  if (name.length () > c && IS_DIR_SEP (home.back ()))
466  c++;
467 
468  expansion = (name.length () > c ? home : home + name.substr (c));
469  }
470 #else /* not HAVE_PWD_H */
471  expansion = name;
472 #endif /* not HAVE_PWD_H */
473 
474  return expansion;
475 }
476 
477 /* Search PATH for ORIGINAL_NAME. If ALL is false, or ORIGINAL_NAME is
478  absolute_p, check ORIGINAL_NAME itself. Otherwise, look at each
479  element of PATH for the first readable ORIGINAL_NAME.
480 
481  Always return a list; if no files are found, the list will
482  contain just NULL. If ALL is true, the list will be
483  terminated with NULL. */
484 
485 static std::list<std::string>
486 search (const std::string& path, const std::string& original_name,
487  bool all)
488 {
489  std::list<std::string> ret_list;
490  bool absolute_p;
491 
492  /* Make a leading ~ count as an absolute filename. */
493  std::string name = kpse_tilde_expand (original_name);
494 
495  /* If the first name is absolute or explicitly relative, no need to
496  consider PATH at all. */
497  absolute_p = kpse_absolute_p (name, true);
498 
500  std::cerr << "kdebug: start search (file=" << name
501  << ", find_all=" << all << ", path=" << path << ")."
502  << std::endl;
503 
504  /* Find the file(s). */
505  ret_list = (absolute_p
507  : path_search (path, name, all));
508 
509  /* The very first search is for texmf.cnf. We can't log that, since
510  we want to allow setting TEXMFLOG in texmf.cnf. */
511  if (first_search)
512  {
513  first_search = false;
514  }
515  else
516  {
517  /* Record the filenames we found, if desired. And wrap them in a
518  debugging line if we're doing that. */
519 
521  std::cerr << "kdebug: search (" << original_name << ") =>";
522 
523  log_search (ret_list);
524 
526  std::cerr << std::endl;
527  }
528 
529  return ret_list;
530 }
531 
532 /* Search PATH for the first NAME. */
533 
534 /* Perform tilde expansion on NAME. If the result is an absolute or
535  explicitly relative filename, check whether it is a readable
536  (regular) file.
537 
538  Otherwise, look in each of the directories specified in PATH (also do
539  tilde and variable expansion on elements in PATH).
540 
541  The caller must expand PATH. This is because it makes more sense to
542  do this once, in advance, instead of for every search using it.
543 
544  In any case, return the complete filename if found, otherwise NULL. */
545 
546 std::string
547 kpse_path_search (const std::string& path, const std::string& name)
548 {
549  std::list<std::string> ret_list = search (path, name, false);
550 
551  return ret_list.empty () ? "" : ret_list.front ();
552 }
553 
554 /* Like 'kpse_path_search' with MUST_EXIST true, but return a list of
555  all the filenames (or NULL if none), instead of taking the first. */
556 
557 std::list<std::string>
558 kpse_all_path_search (const std::string& path, const std::string& name)
559 {
560  return search (path, name, true);
561 }
562 
563 /* This is the hard case -- look in each element of PATH for each
564  element of NAMES. If ALL is false, return the first file found.
565  Otherwise, search all elements of PATH. */
566 
567 std::list<std::string>
568 path_find_first_of (const std::string& path,
569  const std::list<std::string>& names, bool all)
570 {
571  std::list<std::string> ret_list;
572  bool done = false;
573 
574  for (kpse_path_iterator pi (path); ! done && pi != std::string::npos; pi++)
575  {
576  std::string elt = *pi;
577 
578  std::string dir;
579  std::list<std::string> found;
580 
581  /* Do not touch the device if present */
582 
583  if (NAME_BEGINS_WITH_DEVICE (elt))
584  {
585  while (elt.length () > 3
586  && IS_DIR_SEP (elt[2]) && IS_DIR_SEP (elt[3]))
587  {
588  elt[2] = elt[1];
589  elt[1] = elt[0];
590  elt = elt.substr (1);
591  }
592  }
593  else
594  {
595  /* We never want to search the whole disk. */
596  while (elt.length () > 1
597  && IS_DIR_SEP (elt[0]) && IS_DIR_SEP (elt[1]))
598  elt = elt.substr (1);
599  }
600 
601  /* We have to search one directory at a time. */
602  dir = kpse_element_dir (elt);
603 
604  if (! dir.empty ())
605  {
606  for (auto it = names.cbegin (); it != names.cend () && ! done; it++)
607  {
608  std::string name = *it;
609 
610  /* Our caller (find_first_of), also tests first_search,
611  and does the resetting. */
612  if (first_search)
613  found = std::list<std::string> ();
614 
615  /* Search the filesystem. */
616 
617  if (found.empty ())
618  found = dir_search (dir, name, all);
619 
620  /* Did we find anything anywhere? */
621  if (! found.empty ())
622  {
623  if (all)
624  ret_list.splice (ret_list.end (), found);
625  else
626  {
627  ret_list.push_back (found.front ());
628  done = true;
629  }
630  }
631  }
632  }
633  }
634 
635  return ret_list;
636 }
637 
638 static std::list<std::string>
639 find_first_of (const std::string& path, const std::list<std::string>& names,
640  bool all)
641 {
642  std::list<std::string> ret_list;
643 
645  {
646  std::cerr << "kdebug: start find_first_of (";
647 
648  for (auto p = names.cbegin (); p != names.cend (); p++)
649  {
650  if (p == names.cbegin ())
651  std::cerr << *p;
652  else
653  std::cerr << ", " << *p;
654  }
655 
656  std::cerr << "), path=" << path << '.' << std::endl;
657  }
658 
659  for (const auto& name : names)
660  {
661  if (kpse_absolute_p (name, true))
662  {
663  /* If the name is absolute or explicitly relative, no need
664  to consider PATH at all. If we find something, then we
665  are done. */
666 
667  ret_list = absolute_search (name);
668 
669  if (! ret_list.empty ())
670  return ret_list;
671  }
672  }
673 
674  /* Find the file. */
675  ret_list = path_find_first_of (path, names, all);
676 
677  /* The very first search is for texmf.cnf. We can't log that, since
678  we want to allow setting TEXMFLOG in texmf.cnf. */
679  if (first_search)
680  {
681  first_search = false;
682  }
683  else
684  {
685  /* Record the filenames we found, if desired. And wrap them in a
686  debugging line if we're doing that. */
687 
689  {
690  std::cerr << "kdebug: find_first_of (";
691 
692  for (auto p = names.cbegin (); p != names.cend (); p++)
693  {
694  if (p == names.cbegin ())
695  std::cerr << *p;
696  else
697  std::cerr << ", " << *p;
698  }
699 
700  std::cerr << ") =>";
701  }
702 
703  log_search (ret_list);
704 
706  std::cerr << std::endl;
707  }
708 
709  return ret_list;
710 }
711 
712 /* Search each element of PATH for each element of NAMES. Return the
713  first one found. */
714 
715 /* Search each element of PATH for each element in the list of NAMES.
716  Return the first one found. */
717 
718 std::string
719 kpse_path_find_first_of (const std::string& path,
720  const std::list<std::string>& names)
721 {
722  std::list<std::string> ret_list = find_first_of (path, names, false);
723 
724  return ret_list.empty () ? "" : ret_list.front ();
725 }
726 
727 /* Search each element of PATH for each element of NAMES and return a
728  list containing everything found, in the order found. */
729 
730 /* Like 'kpse_path_find_first_of' with MUST_EXIST true, but return a
731  list of all the filenames (or NULL if none), instead of taking the
732  first. */
733 
734 std::list<std::string>
735 kpse_all_path_find_first_of (const std::string& path,
736  const std::list<std::string>& names)
737 {
738  return find_first_of (path, names, true);
739 }
740 
741 /* Perform tilde expansion on each element of the path, and include
742  canonical directory names for only the the actually existing
743  directories in the result. */
744 
745 std::string
746 kpse_path_expand (const std::string& path)
747 {
748  std::string ret;
749  unsigned len = 0;
750 
751  /* Now expand each of the path elements, printing the results */
752  for (kpse_path_iterator pi (path); pi != std::string::npos; pi++)
753  {
754  std::string elt = kpse_tilde_expand (*pi);
755 
756  std::string dir;
757 
758  /* Do not touch the device if present */
759  if (NAME_BEGINS_WITH_DEVICE (elt))
760  {
761  while (elt.length () > 3
762  && IS_DIR_SEP (elt[2]) && IS_DIR_SEP (elt[3]))
763  {
764  elt[2] = elt[1];
765  elt[1] = elt[0];
766  elt = elt.substr (1);
767  }
768  }
769  else
770  {
771  /* We never want to search the whole disk. */
772  while (elt.length () > 1
773  && IS_DIR_SEP (elt[0]) && IS_DIR_SEP (elt[1]))
774  elt = elt.substr (1);
775  }
776 
777  /* Search the disk for all dirs in the component specified.
778  Be faster to check the database, but this is more reliable. */
779  dir = kpse_element_dir (elt);
780 
781  size_t dirlen = dir.length ();
782 
783  if (dirlen > 0)
784  {
785  ret += dir;
786  len += dirlen;
787 
788  /* Retain trailing slash if that's the root directory. */
789  if (dirlen == 1
790  || (dirlen == 3 && NAME_BEGINS_WITH_DEVICE (dir)
791  && IS_DIR_SEP (dir[2])))
792  {
793  ret += ENV_SEP_STRING;
794  len++;
795  }
796 
797  ret[len-1] = ENV_SEP;
798  }
799  }
800 
801  if (! ret.empty ())
802  ret.pop_back ();
803 
804  return ret;
805 }
806 
807 /* braces.c -- code for doing word expansion in curly braces. Taken from
808  bash 1.14.5. [And subsequently modified for kpatshea.]
809 
810  Copyright (C) 1987,1991 Free Software Foundation, Inc. */
811 
812 #define brace_whitespace(c) (! (c) || (c) == ' ' || (c) == '\t' || (c) == '\n')
813 
814 /* Basic idea:
815 
816  Segregate the text into 3 sections: preamble (stuff before an open brace),
817  postamble (stuff after the matching close brace) and amble (stuff after
818  preamble, and before postamble). Expand amble, and then tack on the
819  expansions to preamble. Expand postamble, and tack on the expansions to
820  the result so far. */
821 
822 /* Return a new array of strings which is the result of appending each
823  string in ARR2 to each string in ARR1. The resultant array is
824  len (arr1) * len (arr2) long. For convenience, ARR1 (and its contents)
825  are free ()'ed. ARR1 can be NULL, in that case, a new version of ARR2
826  is returned. */
827 
828 static std::list<std::string>
829 array_concat (const std::list<std::string>& arr1,
830  const std::list<std::string>& arr2)
831 {
832  std::list<std::string> result;
833 
834  if (arr1.empty ())
835  result = arr2;
836  else if (arr2.empty ())
837  result = arr1;
838  else
839  {
840  for (const auto& elt_2 : arr2)
841  for (const auto& elt_1 : arr1)
842  result.push_back (elt_1 + elt_2);
843  }
844 
845  return result;
846 }
847 
848 static int brace_gobbler (const std::string&, int&, int);
849 static std::list<std::string> expand_amble (const std::string&);
850 
851 /* Return an array of strings; the brace expansion of TEXT. */
852 static std::list<std::string>
853 brace_expand (const std::string& text)
854 {
855  /* Find the text of the preamble. */
856  int i = 0;
857  int c = brace_gobbler (text, i, '{');
858 
859  std::string preamble = text.substr (0, i);
860 
861  std::list<std::string> result (1, preamble);
862 
863  if (c == '{')
864  {
865  /* Find the amble. This is the stuff inside this set of braces. */
866  int start = ++i;
867  c = brace_gobbler (text, i, '}');
868 
869  /* What if there isn't a matching close brace? */
870  if (! c)
871  {
872  (*current_liboctave_warning_with_id_handler)
873  ("Octave:pathsearch-syntax",
874  "%s: Unmatched {", text.c_str ());
875 
876  result = std::list<std::string> (1, text);
877  }
878  else
879  {
880  std::string amble = text.substr (start, i-start);
881  result = array_concat (result, expand_amble (amble));
882 
883  std::string postamble = text.substr (i+1);
884  result = array_concat (result, brace_expand (postamble));
885  }
886  }
887 
888  return result;
889 }
890 
891 /* The character which is used to separate arguments. */
892 static int brace_arg_separator = ',';
893 
894 /* Expand the text found inside of braces. We simply try to split the
895  text at BRACE_ARG_SEPARATORs into separate strings. We then brace
896  expand each slot which needs it, until there are no more slots which
897  need it. */
898 static std::list<std::string>
899 expand_amble (const std::string& text)
900 {
901  std::list<std::string> result;
902 
903  size_t text_len = text.length ();
904  size_t start;
905  int i, c;
906 
907  for (start = 0, i = 0, c = 1; c && start < text_len; start = ++i)
908  {
909  int i0 = i;
910  int c0 = brace_gobbler (text, i0, brace_arg_separator);
911  int i1 = i;
912  int c1 = brace_gobbler (text, i1, ENV_SEP);
913  c = c0 | c1;
914  i = (i0 < i1 ? i0 : i1);
915 
916  std::string tem = text.substr (start, i-start);
917 
918  std::list<std::string> partial = brace_expand (tem);
919 
920  if (result.empty ())
921  result = partial;
922  else
923  result.splice (result.end (), partial);
924  }
925 
926  return result;
927 }
928 
929 /* Start at INDEX, and skip characters in TEXT. Set INDEX to the
930  index of the character matching SATISFY. This understands about
931  quoting. Return the character that caused us to stop searching;
932  this is either the same as SATISFY, or 0. */
933 static int
934 brace_gobbler (const std::string& text, int& indx, int satisfy)
935 {
936  int c = 0;
937  int level = 0;
938  int quoted = 0;
939  int pass_next = 0;
940 
941  size_t text_len = text.length ();
942 
943  size_t i = indx;
944 
945  for (; i < text_len; i++)
946  {
947  c = text[i];
948 
949  if (pass_next)
950  {
951  pass_next = 0;
952  continue;
953  }
954 
955  /* A backslash escapes the next character. This allows backslash to
956  escape the quote character in a double-quoted string. */
957  if (c == '\\' && (quoted == 0 || quoted == '"' || quoted == '`'))
958  {
959  pass_next = 1;
960  continue;
961  }
962 
963  if (quoted)
964  {
965  if (c == quoted)
966  quoted = 0;
967  continue;
968  }
969 
970  if (c == '"' || c == '\'' || c == '`')
971  {
972  quoted = c;
973  continue;
974  }
975 
976  if (c == satisfy && ! level && ! quoted)
977  {
978  /* We ignore an open brace surrounded by whitespace, and also
979  an open brace followed immediately by a close brace, that
980  was preceded with whitespace. */
981  if (c == '{'
982  && ((i == 0 || brace_whitespace (text[i-1]))
983  && (i+1 < text_len
984  && (brace_whitespace (text[i+1]) || text[i+1] == '}'))))
985  continue;
986  /* If this is being compiled as part of bash, ignore the '{'
987  in a '${ }' construct */
988  if ((c != '{') || i == 0 || (text[i-1] != '$'))
989  break;
990  }
991 
992  if (c == '{')
993  level++;
994  else if (c == '}' && level)
995  level--;
996  }
997 
998  indx = i;
999  c = (c == satisfy) ? c : 0;
1000  return c;
1001 }
1002 
1003 /* Return true if FN is a directory or a symlink to a directory,
1004  false if not. */
1005 
1006 static bool
1007 dir_p (const std::string& fn)
1008 {
1009  octave::sys::file_stat fs (fn);
1010 
1011  return (fs && fs.is_dir ());
1012 }
1013 
1014 /* Given a path element ELT, return a the element with a trailing slash
1015  or an empty string if the element is not a directory.
1016 
1017  It's up to the caller to expand ELT. This is because this routine is
1018  most likely only useful to be called from 'kpse_path_search', which
1019  has already assumed expansion has been done. */
1020 
1021 std::string
1022 kpse_element_dir (const std::string& elt)
1023 {
1024  std::string ret;
1025 
1026  /* If given nothing, return nothing. */
1027  if (elt.empty ())
1028  return ret;
1029 
1030  if (dir_p (elt))
1031  {
1032  ret = elt;
1033 
1034  char last_char = ret.back ();
1035 
1036  if (! (IS_DIR_SEP (last_char) || IS_DEVICE_SEP (last_char)))
1037  ret += DIR_SEP_STRING;
1038  }
1039 
1040  return ret;
1041 }
const std::string & m_path
Definition: kpse.h:68
size_t m_b
Definition: kpse.h:69
size_t m_len
Definition: kpse.h:71
size_t m_e
Definition: kpse.h:70
void next(void)
Definition: kpse.cc:114
void set_end(void)
Definition: kpse.cc:95
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:119
static std::string get_home_directory(void)
Definition: oct-env.cc:147
static bool absolute_pathname(const std::string &s)
Definition: oct-env.cc:112
std::string dir(void) const
Definition: oct-passwd.cc:99
static password getpwnam(const std::string &nm)
Definition: oct-passwd.cc:157
time_t unix_time(void) const
Definition: oct-time.h:113
QString path
QString name
std::string kpse_path_find_first_of(const std::string &path, const std::list< std::string > &names)
Definition: kpse.cc:719
#define DIR_SEP_STRING
Definition: kpse.cc:68
#define NAME_BEGINS_WITH_DEVICE(name)
Definition: kpse.cc:65
static std::list< std::string > brace_expand(const std::string &text)
Definition: kpse.cc:853
static void log_search(const std::list< std::string > &filenames)
Definition: kpse.cc:264
#define KPSE_DEBUG_SEARCH
Definition: kpse.cc:86
static std::string kpse_tilde_expand(const std::string &name)
Definition: kpse.cc:393
std::string kpse_element_dir(const std::string &elt)
Definition: kpse.cc:1022
static std::list< std::string > dir_search(const std::string &dir, const std::string &name, bool search_all)
Definition: kpse.cc:287
static int brace_arg_separator
Definition: kpse.cc:892
static std::list< std::string > absolute_search(const std::string &name)
Definition: kpse.cc:311
static std::list< std::string > expand_amble(const std::string &)
Definition: kpse.cc:899
#define ENV_SEP_STRING
Definition: kpse.cc:72
static int brace_gobbler(const std::string &, int &, int)
Definition: kpse.cc:934
std::string kpse_path_expand(const std::string &path)
Definition: kpse.cc:746
std::list< std::string > kpse_all_path_find_first_of(const std::string &path, const std::list< std::string > &names)
Definition: kpse.cc:735
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::list< std::string > find_first_of(const std::string &path, const std::list< std::string > &names, bool all)
Definition: kpse.cc:639
static std::string kpse_readable_file(const std::string &name)
Definition: kpse.cc:205
unsigned int kpse_debug
Definition: kpse.cc:92
std::list< std::string > kpse_all_path_search(const std::string &path, const std::string &name)
Definition: kpse.cc:558
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:812
static bool dir_p(const std::string &fn)
Definition: kpse.cc:1007
std::list< std::string > path_find_first_of(const std::string &path, const std::list< std::string > &names, bool all)
Definition: kpse.cc:568
#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 > search(const std::string &path, const std::string &original_name, bool all)
Definition: kpse.cc:486
#define ENV_SEP
Definition: kpse.cc:71
std::string kpse_path_search(const std::string &path, const std::string &name)
Definition: kpse.cc:547
static std::list< std::string > array_concat(const std::list< std::string > &arr1, const std::list< std::string > &arr2)
Definition: kpse.cc:829
static const double pi
Definition: lo-specfun.cc:1995
std::wstring u8_to_wstring(const std::string &utf8_string)
Definition: lo-sysdep.cc:468
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
int octave_access_r_ok(void)
int octave_access_wrapper(const char *nm, int mode)
F77_RET_T len
Definition: xerbla.cc:61