Line data Source code
1 : // Copyright (c) 2019-2022 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/snapdev
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 : #pragma once
20 :
21 : /** \file
22 : * \brief Create a list of files using glob().
23 : *
24 : * This template allows you to insert filenames from the output of a glob()
25 : * call to an STL container.
26 : */
27 :
28 : // snapdev lib
29 : //
30 : #include "snapdev/raii_generic_deleter.h"
31 :
32 :
33 : // C++ lib
34 : //
35 : #include <list>
36 : #include <memory>
37 : #include <set>
38 :
39 :
40 : // C lib
41 : //
42 : #include <glob.h>
43 : #include <limits.h>
44 : #include <stdlib.h>
45 : #include <sys/stat.h>
46 :
47 :
48 : namespace snapdev
49 : {
50 :
51 :
52 : /** \brief An object that holds the information about the file being loaded.
53 : *
54 : * In order to only read certain types of files (such as directories),
55 : * we have to get the lstat()'s. This object represents one file found
56 : * in the directory with it's lstat()'s.
57 : *
58 : * Note that since we use lstat() you get the statistics about the very
59 : * file named in the object, not the target of the symbolic link.
60 : */
61 78 : class file
62 : {
63 : public:
64 26 : file(std::string const & filename)
65 26 : : f_filename(filename)
66 : {
67 26 : }
68 :
69 15 : std::string const & filename() const
70 : {
71 15 : return f_filename;
72 : }
73 :
74 64 : bool exists() const
75 : {
76 64 : load_stats();
77 64 : return f_stat_loaded;
78 : }
79 :
80 26 : bool is_symbolic_link() const
81 : {
82 26 : if(exists())
83 : {
84 26 : return S_ISLNK(f_stat.st_mode);
85 : }
86 0 : return false;
87 : }
88 :
89 : bool is_regular_file() const
90 : {
91 : if(exists())
92 : {
93 : return S_ISREG(f_stat.st_mode);
94 : }
95 : return false;
96 : }
97 :
98 12 : bool is_directory() const
99 : {
100 12 : if(exists())
101 : {
102 12 : return S_ISDIR(f_stat.st_mode);
103 : }
104 0 : return false;
105 : }
106 :
107 : bool is_character() const
108 : {
109 : if(exists())
110 : {
111 : return S_ISCHR(f_stat.st_mode);
112 : }
113 : return false;
114 : }
115 :
116 : bool is_block() const
117 : {
118 : if(exists())
119 : {
120 : return S_ISBLK(f_stat.st_mode);
121 : }
122 : return false;
123 : }
124 :
125 : bool is_fifo() const
126 : {
127 : if(exists())
128 : {
129 : return S_ISFIFO(f_stat.st_mode);
130 : }
131 : return false;
132 : }
133 :
134 : bool is_socket() const
135 : {
136 : if(exists())
137 : {
138 : return S_ISSOCK(f_stat.st_mode);
139 : }
140 : return false;
141 : }
142 :
143 : int is_uid() const
144 : {
145 : if(exists())
146 : {
147 : return (f_stat.st_mode & S_ISUID) != 0;
148 : }
149 : return 0;
150 : }
151 :
152 : int is_gid() const
153 : {
154 : if(exists())
155 : {
156 : return (f_stat.st_mode & S_ISGID) != 0;
157 : }
158 : return 0;
159 : }
160 :
161 : int is_vtx() const
162 : {
163 : if(exists())
164 : {
165 : return (f_stat.st_mode & S_ISVTX) != 0;
166 : }
167 : return 0;
168 : }
169 :
170 : int permissions() const
171 : {
172 : if(exists())
173 : {
174 : return f_stat.st_mode & 0777;
175 : }
176 : return 0;
177 : }
178 :
179 : uid_t uid() const
180 : {
181 : if(exists())
182 : {
183 : return f_stat.st_uid;
184 : }
185 : return -1;
186 : }
187 :
188 : gid_t gid() const
189 : {
190 : if(exists())
191 : {
192 : return f_stat.st_gid;
193 : }
194 : return -1;
195 : }
196 :
197 : gid_t size() const
198 : {
199 : if(exists())
200 : {
201 : return f_stat.st_size;
202 : }
203 : return -1;
204 : }
205 :
206 5 : bool target_exists() const
207 : {
208 5 : load_target_stats();
209 5 : return f_target_stat_loaded;
210 : }
211 :
212 : bool is_target_symbolic_link() const
213 : {
214 : if(target_exists())
215 : {
216 : return S_ISLNK(f_target_stat.st_mode);
217 : }
218 : return false;
219 : }
220 :
221 : bool is_target_regular_file() const
222 : {
223 : if(target_exists())
224 : {
225 : return S_ISREG(f_target_stat.st_mode);
226 : }
227 : return false;
228 : }
229 :
230 5 : bool is_target_directory() const
231 : {
232 5 : if(target_exists())
233 : {
234 5 : return S_ISDIR(f_target_stat.st_mode);
235 : }
236 0 : return false;
237 : }
238 :
239 : bool is_target_character() const
240 : {
241 : if(target_exists())
242 : {
243 : return S_ISCHR(f_target_stat.st_mode);
244 : }
245 : return false;
246 : }
247 :
248 : bool is_target_block() const
249 : {
250 : if(target_exists())
251 : {
252 : return S_ISBLK(f_target_stat.st_mode);
253 : }
254 : return false;
255 : }
256 :
257 : bool is_target_fifo() const
258 : {
259 : if(target_exists())
260 : {
261 : return S_ISFIFO(f_target_stat.st_mode);
262 : }
263 : return false;
264 : }
265 :
266 : bool is_target_socket() const
267 : {
268 : if(target_exists())
269 : {
270 : return S_ISSOCK(f_target_stat.st_mode);
271 : }
272 : return false;
273 : }
274 :
275 : int is_target_uid() const
276 : {
277 : if(target_exists())
278 : {
279 : return (f_target_stat.st_mode & S_ISUID) != 0;
280 : }
281 : return 0;
282 : }
283 :
284 : int is_target_gid() const
285 : {
286 : if(target_exists())
287 : {
288 : return (f_target_stat.st_mode & S_ISGID) != 0;
289 : }
290 : return 0;
291 : }
292 :
293 : int is_target_vtx() const
294 : {
295 : if(target_exists())
296 : {
297 : return (f_target_stat.st_mode & S_ISVTX) != 0;
298 : }
299 : return 0;
300 : }
301 :
302 : int target_permissions() const
303 : {
304 : if(target_exists())
305 : {
306 : return f_target_stat.st_mode & 0777;
307 : }
308 : return 0;
309 : }
310 :
311 : uid_t target_uid() const
312 : {
313 : if(target_exists())
314 : {
315 : return f_target_stat.st_uid;
316 : }
317 : return -1;
318 : }
319 :
320 : gid_t target_gid() const
321 : {
322 : if(target_exists())
323 : {
324 : return f_target_stat.st_gid;
325 : }
326 : return -1;
327 : }
328 :
329 : gid_t target_size() const
330 : {
331 : if(target_exists())
332 : {
333 : return f_target_stat.st_size;
334 : }
335 : return -1;
336 : }
337 :
338 : private:
339 64 : void load_stats() const
340 : {
341 64 : if(f_stat_loaded)
342 : {
343 38 : return;
344 : }
345 :
346 26 : if(lstat(f_filename.c_str(), &f_stat) != 0)
347 : {
348 0 : return;
349 : }
350 :
351 26 : f_stat_loaded = true;
352 : }
353 :
354 5 : void load_target_stats() const
355 : {
356 5 : if(f_target_stat_loaded)
357 : {
358 0 : return;
359 : }
360 :
361 5 : if(stat(f_filename.c_str(), &f_target_stat) != 0)
362 : {
363 0 : return;
364 : }
365 :
366 5 : f_target_stat_loaded = true;
367 : }
368 :
369 : std::string f_filename = std::string();
370 : mutable struct stat f_stat = {};
371 : mutable struct stat f_target_stat = {};
372 : mutable bool f_stat_loaded = false;
373 : mutable bool f_target_stat_loaded = false;
374 : };
375 :
376 :
377 : /** \brief A smart pointer to auto-delete glob results.
378 : *
379 : * This type defines a smart pointer which automatically frees all
380 : * the data allocated by glob() and this pointer too.
381 : *
382 : * Usage:
383 : *
384 : * \code
385 : * snap::glob_to_list<std::vector<std::string>> glob;
386 : * if(glob.read_path<
387 : * snap::glob_to_list_flag_t::GLOB_FLAG_IGNORE_ERRORS,
388 : * snap::glob_to_list_flag_t::GLOB_FLAG_PERIOD>(pattern))
389 : * {
390 : * if(!glob.empty())
391 : * {
392 : * // handle file names
393 : *
394 : * }
395 : * else
396 : * {
397 : * // no files case
398 : * }
399 : * }
400 : * else {
401 : * // handle error
402 : * }
403 : * \endcode
404 : *
405 : * Note that the glob() function always gets called with the GLOB_NOSORT
406 : * flag set. If you want sorted results, use a container which returns
407 : * the data sorted such as the `std::set<>`.
408 : *
409 : * \warning
410 : * glob() is not thread safe and we currently do not add any additional
411 : * support to make it thread safe. Especially, the glob() function
412 : * makes use of a global function for errors and that uses global
413 : * pointers.
414 : * \warning
415 : * If you use our cppthread project, you can use a guard to lock a global
416 : * mutex. Remember that if you may get called before main() you must first
417 : * lock the `g_system_mutex`. Otherwise the initialization process may
418 : * not work correctly and your mutex may get initialized after you hit
419 : * your `cppthread::guard` statement (i.e. your g_mutex must be a pointer
420 : * that you allocate the first time you use it and to make that thread
421 : * safe you need to first lock the `g_system_mutex`).
422 : *
423 : * \code
424 : * cppthread::mutex g_mutex;
425 : * {
426 : * cppthread::guard lock(g_mutex);
427 : *
428 : * ...glob.read_path<...>(pattern);...
429 : * }
430 : * \endcode
431 : */
432 26 : typedef std::unique_ptr<glob_t, raii_pointer_deleter<glob_t, decltype(&::globfree), &::globfree>> glob_pointer_t;
433 :
434 :
435 : enum class glob_to_list_flag_t
436 : {
437 : GLOB_FLAG_NONE, // not a flag, useful in case you need a value for ?:
438 : GLOB_FLAG_BRACE, // allow {a,b,c}...
439 : GLOB_FLAG_IGNORE_ERRORS, // read as much as possible
440 : GLOB_FLAG_MARK_DIRECTORY, // add "/" to directory names
441 : GLOB_FLAG_NO_ESCAPE, // ignore '\'
442 : GLOB_FLAG_ONLY_DIRECTORIES, // only return directories
443 : GLOB_FLAG_PERIOD, // allow period at the start (i.e. pattern ".*")
444 : GLOB_FLAG_TILDE, // transform "~/..." with "$HOME/..."
445 : GLOB_FLAG_RECURSIVE, // when a directory is found, read it too
446 : GLOB_FLAG_FOLLOW_SYMLINK, // in recursive mode, do or do not follow symlinks
447 : };
448 :
449 :
450 : /** \brief Manage the results of glob() calls.
451 : *
452 : * This template is able to call glob() and insert the results to your
453 : * container object. If the type of the container is std::string, then
454 : * only the filenames are returned. If the type is set to a snap::file,
455 : * then the function returns a set of snap::file objects.
456 : *
457 : * The supported flags allow for selecting which files to ignore. By
458 : * default, files that start with a period (.) are ignored.
459 : *
460 : * Here is an example of usage:
461 : *
462 : * \code
463 : * glob_to_list<std::vector<std::string>> dir;
464 : * if(!dir.read_path<snap::glob_to_list_flag_t::GLOB_FLAG_ONLY_DIRECTORIES>("/proc/[0-9]*"))
465 : * {
466 : * ...handle error...
467 : * return;
468 : * }
469 : * for(auto f : dir)
470 : * {
471 : * ...f is std::string with filename...
472 : * }
473 : * \endcode
474 : *
475 : * \warning
476 : * The class is not multithread safe. The glob() function makes use of a
477 : * global variable to report errors so there is no way at this point to
478 : * make it safe (without a \em service like implementation).
479 : *
480 : * \tparam C The type of the container where to add the filenames.
481 : * \tparam T The type used for the filenames (C<T>).
482 : */
483 : template<typename C>
484 40 : class glob_to_list
485 : : public C
486 : {
487 : private:
488 : typedef std::set<std::string> directories_t;
489 :
490 : public:
491 : typedef C container_t;
492 :
493 : /** \brief Read files at given path.
494 : *
495 : * This function runs the glob() function with the given path.
496 : *
497 : * The \p path variable is expected to include a path with glob() like
498 : * patterns (i.e. `*.txt`, `0?.mp3`, etc.)
499 : *
500 : * Note that the glob()-ing is done on the entire path. However, only
501 : * the last part (after the last slash) is used in case you use the
502 : * GLOB_FLAG_RECURSIVE. Note that in recursive mode, the directories
503 : * will always be read since we have to recurse through them.
504 : *
505 : * \remarks
506 : * This implementation is not multithread safe. Make sure to use this
507 : * function in a part of your code which is locked.
508 : *
509 : * \todo
510 : * Add a read_path() which supports dynamic flags.
511 : *
512 : * \tparam args A list of one or more glob to list flags.
513 : * \param[in] path The path with glob patterns.
514 : *
515 : * \return true if no error occurred.
516 : */
517 : template<glob_to_list_flag_t ...args>
518 20 : bool read_path(std::string const & path)
519 : {
520 20 : f_recursive = false;
521 20 : f_follow_symlinks = false;
522 20 : int const flags = GLOB_NOSORT | flags_merge<args...>();
523 :
524 20 : if(!f_recursive)
525 : {
526 16 : return read_directory(path, flags);
527 : }
528 :
529 : // in recursive mode we want to collect the list of directories
530 : // along whatever the user wants to collect and then process
531 : // those directories as well; this also requires us to retrieve
532 : // the pattern (last segment) first as the actual pattern
533 : //
534 8 : directories_t visited;
535 4 : std::string::size_type const pos(path.rfind('/'));
536 4 : if(pos == std::string::npos)
537 : {
538 : // no directory in the path, only a pattern
539 : //
540 0 : return recursive_read_path(
541 : get_real_path(".")
542 : , path
543 : , flags
544 0 : , visited);
545 : }
546 : else
547 : {
548 12 : return recursive_read_path(
549 : get_real_path(path.substr(0, pos))
550 : , path.substr(pos + 1)
551 : , flags
552 12 : , visited);
553 : }
554 : }
555 :
556 : /** \brief Convert the input \p path in a canonicalized path.
557 : *
558 : * This function goes through the specified \p path and canonicalize
559 : * it. This means:
560 : *
561 : * * removing any "/./"
562 : * * removing any "/../"
563 : * * replacing softlinks with the target path
564 : *
565 : * The resulting path is likely going to be a full path.
566 : *
567 : * \note
568 : * If the input path is an empty string (equivalent to ".") then the
569 : * result may also be the empty string even though no errors would have
570 : * happened.
571 : *
572 : * \param[in] path The path to canonicalize.
573 : *
574 : * \return The canonicalized version of \p path or an empty string on error.
575 : */
576 42 : std::string get_real_path(std::string const & path)
577 : {
578 42 : char buf[PATH_MAX + 1];
579 42 : buf[PATH_MAX] = '\0';
580 42 : if(realpath(path.c_str(), buf) != buf)
581 : {
582 : // it failed
583 : //
584 0 : f_last_error_errno = errno;
585 0 : f_last_error_path = path;
586 0 : switch(f_last_error_errno)
587 : {
588 0 : case EACCES:
589 0 : f_last_error_message = "realpath() is missing permission to read or search a component of the path.";
590 0 : break;
591 :
592 0 : case EIO:
593 0 : f_last_error_message = "realpath() had I/O issues while searching.";
594 0 : break;
595 :
596 0 : case ELOOP:
597 0 : f_last_error_message = "realpath() found too many symbolic links.";
598 0 : break;
599 :
600 0 : case ENAMETOOLONG:
601 0 : f_last_error_message = "realpath() output buffer too small for path.";
602 0 : break;
603 :
604 0 : case ENOENT:
605 0 : f_last_error_message = "realpath() could not find the specified file.";
606 0 : break;
607 :
608 0 : case ENOMEM:
609 0 : f_last_error_message = "realpath() could not allocate necessary memory.";
610 0 : break;
611 :
612 0 : case ENOTDIR:
613 0 : f_last_error_message = "realpath() found a file instead of a directory within the path.";
614 0 : break;
615 :
616 0 : default:
617 0 : f_last_error_message = "realpath() failed.";
618 0 : break;
619 :
620 : }
621 0 : return std::string();
622 : }
623 42 : return buf;
624 : }
625 :
626 : /** \brief The last error message.
627 : *
628 : * Whenever a call fails, it saves an error message here.
629 : *
630 : * The error message is whatever represents the error best from our
631 : * point of view.
632 : *
633 : * \note
634 : * Error messages get overwritten so you must call this function
635 : * before calling another function to not lose intermediate messages.
636 : *
637 : * \return The last error message.
638 : */
639 0 : std::string get_last_error_message() const
640 : {
641 0 : return f_last_error_message;
642 : }
643 :
644 : /** \brief The last error path.
645 : *
646 : * The path that generated the error. In most cases, this is the input
647 : * path you specified, with the pattern still intact. When using the
648 : * recursive feature, the path will be the path of the directory that
649 : * is currently being handled.
650 : *
651 : * \return The path that generated the error.
652 : */
653 0 : std::string get_last_error_path() const
654 : {
655 0 : return f_last_error_path;
656 : }
657 :
658 : /** \brief Retrieve the last error number.
659 : *
660 : * Whenever an error occurs with a system function, the errno value
661 : * gets saved in the "last error errno" variable which can then be
662 : * retrieved using this function. This should be used instead of
663 : * trying to understand the error message which is expected to only
664 : * be used for human consumption.
665 : *
666 : * \return The last error errno value.
667 : */
668 2 : int get_last_error_errno() const
669 : {
670 2 : return f_last_error_errno;
671 : }
672 :
673 : private:
674 : template<class none = void>
675 20 : constexpr int flags_merge()
676 : {
677 20 : return 0;
678 : }
679 :
680 : template<glob_to_list_flag_t flag, glob_to_list_flag_t ...args>
681 21 : constexpr int flags_merge()
682 : {
683 : switch(flag)
684 : {
685 : case glob_to_list_flag_t::GLOB_FLAG_NONE:
686 : return GLOB_ONLYDIR | flags_merge<args...>();
687 :
688 : case glob_to_list_flag_t::GLOB_FLAG_BRACE:
689 : return GLOB_BRACE | flags_merge<args...>();
690 :
691 : case glob_to_list_flag_t::GLOB_FLAG_IGNORE_ERRORS:
692 0 : return GLOB_ERR | flags_merge<args...>();
693 :
694 : case glob_to_list_flag_t::GLOB_FLAG_MARK_DIRECTORY:
695 : return GLOB_MARK | flags_merge<args...>();
696 :
697 : case glob_to_list_flag_t::GLOB_FLAG_NO_ESCAPE:
698 : return GLOB_NOESCAPE | flags_merge<args...>();
699 :
700 : case glob_to_list_flag_t::GLOB_FLAG_ONLY_DIRECTORIES:
701 16 : return GLOB_ONLYDIR | flags_merge<args...>();
702 :
703 : case glob_to_list_flag_t::GLOB_FLAG_PERIOD:
704 : return GLOB_PERIOD | flags_merge<args...>();
705 :
706 : case glob_to_list_flag_t::GLOB_FLAG_TILDE:
707 : return GLOB_TILDE_CHECK | flags_merge<args...>();
708 :
709 : case glob_to_list_flag_t::GLOB_FLAG_RECURSIVE:
710 4 : f_recursive = true;
711 4 : return flags_merge<args...>();
712 :
713 : case glob_to_list_flag_t::GLOB_FLAG_FOLLOW_SYMLINK:
714 1 : f_follow_symlinks = true;
715 1 : return flags_merge<args...>();
716 :
717 : }
718 :
719 : throw std::logic_error("unimplemented GLOB_FLAG_... in flags_merge()");
720 : }
721 :
722 0 : static int glob_err_callback(char const * p, int e)
723 : {
724 0 : g_self->f_last_error_message = "caught an error while reading a directory.";
725 0 : g_self->f_last_error_path = p;
726 0 : g_self->f_last_error_errno = e;
727 :
728 0 : return 0;
729 : }
730 :
731 32 : bool read_directory(std::string const & path, int const flags)
732 : {
733 32 : glob_t dir = glob_t();
734 32 : g_self = this;
735 32 : int const r(glob(path.c_str(), flags, glob_err_callback, &dir));
736 32 : g_self = nullptr; // to detect if glob_err_callback gets called improperly
737 32 : if(r == 0)
738 : {
739 52 : glob_pointer_t auto_release_dir(&dir);
740 79 : for(size_t idx(0); idx < dir.gl_pathc; ++idx)
741 : {
742 53 : C::insert(C::end(), typename glob_to_list::value_type(std::string(dir.gl_pathv[idx])));
743 : }
744 : }
745 : else
746 : {
747 : // do nothing when errors occur
748 : //
749 12 : std::string err_msg;
750 6 : switch(r)
751 : {
752 0 : case GLOB_NOSPACE:
753 0 : f_last_error_message =
754 : "glob(\""
755 : + path
756 : + "\") did not have enough memory to allocate its buffers.";
757 0 : break;
758 :
759 0 : case GLOB_ABORTED:
760 0 : f_last_error_message =
761 : "glob(\""
762 : + path
763 : + "\") was aborted after a read error.";
764 0 : break;
765 :
766 6 : case GLOB_NOMATCH:
767 6 : f_last_error_message = "glob(\""
768 : + path
769 : + "\") could not find any files matching the pattern.";
770 6 : f_last_error_errno = ENOENT;
771 6 : break;
772 :
773 0 : default:
774 0 : f_last_error_message = "unknown glob(\""
775 : + path
776 : + "\") error code: "
777 : + std::to_string(r)
778 : + ".";
779 0 : break;
780 :
781 : }
782 :
783 6 : return false;
784 : }
785 :
786 26 : return true;
787 : }
788 :
789 16 : bool recursive_read_path(
790 : std::string const & path
791 : , std::string const & pattern
792 : , int flags
793 : , directories_t & visited)
794 : {
795 : // (1) read client's files in path
796 : //
797 16 : if(!read_directory(path + '/' + pattern, flags))
798 : {
799 4 : if(f_last_error_errno != ENOENT)
800 : {
801 0 : return false;
802 : }
803 : }
804 :
805 : // (2) find child directories
806 : //
807 : typedef std::list<file> dir_list_t;
808 32 : glob_to_list<dir_list_t> sub_dirs;
809 16 : bool success(false);
810 16 : if((flags & GLOB_ERR) != 0)
811 : {
812 0 : success = sub_dirs.read_path<
813 : glob_to_list_flag_t::GLOB_FLAG_IGNORE_ERRORS
814 0 : , glob_to_list_flag_t::GLOB_FLAG_ONLY_DIRECTORIES>(path + "/*");
815 : }
816 : else
817 : {
818 32 : success = sub_dirs.read_path<
819 32 : glob_to_list_flag_t::GLOB_FLAG_ONLY_DIRECTORIES>(path + "/*");
820 : }
821 32 : if(!success
822 16 : && sub_dirs.get_last_error_errno() != ENOENT)
823 : {
824 0 : f_last_error_message = sub_dirs.get_last_error_message();
825 0 : f_last_error_path = sub_dirs.get_last_error_path();
826 0 : f_last_error_errno = sub_dirs.get_last_error_errno();
827 0 : return f_last_error_errno != ENOENT;
828 : }
829 :
830 : // (3) read the sub-directories
831 : //
832 42 : for(auto const & d : sub_dirs)
833 : {
834 26 : if(!d.exists())
835 : {
836 0 : continue;
837 : }
838 26 : if(d.is_symbolic_link())
839 : {
840 28 : if(!f_follow_symlinks
841 14 : || !d.is_target_directory())
842 : {
843 11 : continue;
844 : }
845 : }
846 : else
847 : {
848 12 : if(!d.is_directory())
849 : {
850 0 : continue;
851 : }
852 : }
853 :
854 : // because we use a real-path, we may find that some paths are
855 : // duplicates (most certainly because of a softlink)
856 : //
857 30 : std::string const p(get_real_path(d.filename()));
858 15 : if(visited.insert(p).second)
859 : {
860 12 : if(!recursive_read_path(
861 : p
862 : , pattern
863 : , flags
864 : , visited))
865 : {
866 0 : return false;
867 : }
868 : }
869 : }
870 :
871 16 : return true;
872 : }
873 :
874 : static thread_local glob_to_list *
875 : g_self;
876 :
877 : std::string f_last_error_message = std::string();
878 : std::string f_last_error_path = std::string();
879 : int f_last_error_errno = 0;
880 : bool f_recursive = false;
881 : bool f_follow_symlinks = false;
882 : };
883 :
884 :
885 : template <typename C>
886 : thread_local glob_to_list<C> * glob_to_list<C>::g_self = nullptr;
887 :
888 :
889 : } // namespace snapdev
890 : // vim: ts=4 sw=4 et
|