aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/functions-filesystem.cxx
blob: 665a0f39829f24667379afbe9eddc43578e31294 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// file      : libbuild2/functions-filesystem.cxx -*- C++ -*-
// license   : MIT; see accompanying LICENSE file

#include <libbutl/filesystem.hxx>

#include <libbuild2/function.hxx>
#include <libbuild2/variable.hxx>

using namespace std;
using namespace butl;

namespace build2
{
  // Return paths of filesystem entries that match the pattern. See
  // path_search() overloads (below) for details.
  //
  static names
  path_search (const path& pattern, const optional<dir_path>& start)
  {
    names r;
    auto add = [&r] (path&& p, const std::string&, bool interm) -> bool
    {
      // Canonicalizing paths seems to be the right thing to do. Otherwise, we
      // can end up with different separators in the same path on Windows.
      //
      if (!interm)
        r.emplace_back (
          value_traits<path>::reverse (move (p.canonicalize ())));

      return true;
    };

    auto dangling = [] (const dir_entry& de)
    {
      bool sl (de.ltype () == entry_type::symlink);

      warn << "skipping "
           << (sl ? "dangling symlink" : "inaccessible entry") << ' '
           << de.base () / de.path ();

      return true;
    };

    // Print paths "as is" in the diagnostics.
    //
    try
    {
      if (pattern.absolute ())
        path_search (pattern,
                     add,
                     dir_path () /* start */,
                     path_match_flags::follow_symlinks,
                     dangling);
      else
      {
        // An absolute start directory must be specified for the relative
        // pattern.
        //
        if (!start || start->relative ())
        {
          diag_record dr (fail);

          if (!start)
            dr << "start directory is not specified";
          else
            dr << "start directory '" << start->representation ()
               << "' is relative";

          dr << info << "pattern '" << pattern.representation ()
             << "' is relative";
        }

        path_search (pattern,
                     add,
                     *start,
                     path_match_flags::follow_symlinks,
                     dangling);
      }
    }
    catch (const system_error& e)
    {
      diag_record d (fail);
      d << "unable to scan";

      // If the pattern is absolute, then the start directory is not used, and
      // so printing it would be misleading.
      //
      if (start && pattern.relative ())
        d << " '" << start->representation () << "'";

      d << ": " << e
        << info << "pattern: '" << pattern.representation () << "'";
    }

    return r;
  }

  void
  filesystem_functions (function_map& m)
  {
    // @@ Maybe we should have the ability to mark the whole family as not
    //    pure?

    function_family f (m, "filesystem");

    // $path_search(<pattern>[, <start-dir>])
    //
    // Return filesystem paths that match the shell-like wildcard pattern. If
    // the pattern is an absolute path, then the start directory is ignored
    // (if present). Otherwise, the start directory must be specified and be
    // absolute.
    //
    // Note that this function is not pure.
    //

    // @@ In the future we may want to add a flag that controls the
    //    dangling/inaccessible treatment.
    //
    {
      auto e (f.insert ("path_search", false));

      e += [](path pattern, optional<dir_path> start)
      {
        return path_search (pattern, start);
      };

      e += [](path pattern, names start)
      {
        return path_search (pattern, convert<dir_path> (move (start)));
      };

      e += [](names pattern, optional<dir_path> start)
      {
        return path_search (convert<path> (move (pattern)), start);
      };

      e += [](names pattern, names start)
      {
        return path_search (convert<path>     (move (pattern)),
                            convert<dir_path> (move (start)));
      };
    }
  }
}