xref: /minix3/external/bsd/kyua-cli/dist/utils/fs/lua_module.cpp (revision 11be35a165022172ed3cea20f2b5df0307540b0e)
1 // Copyright 2011 Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 //   notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 //   notice, this list of conditions and the following disclaimer in the
12 //   documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 //   may be used to endorse or promote products derived from this software
15 //   without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 #include "utils/fs/lua_module.hpp"
30 
31 extern "C" {
32 #include <dirent.h>
33 }
34 
35 #include <cerrno>
36 #include <cstring>
37 #include <stdexcept>
38 #include <string>
39 
40 #include <lutok/operations.hpp>
41 #include <lutok/state.ipp>
42 
43 #include "utils/format/macros.hpp"
44 #include "utils/fs/operations.hpp"
45 #include "utils/fs/path.hpp"
46 #include "utils/sanity.hpp"
47 
48 namespace fs = utils::fs;
49 
50 
51 namespace {
52 
53 
54 /// Lua binding for fs::path::basename.
55 ///
56 /// \pre stack(-1) The input path.
57 /// \post stack(-1) The basename of the input path.
58 ///
59 /// \param state The Lua state.
60 ///
61 /// \return The number of result values, i.e. 1.
62 static int
lua_fs_basename(lutok::state & state)63 lua_fs_basename(lutok::state& state)
64 {
65     if (!state.is_string())
66         throw std::runtime_error("Need a string parameter");
67     const fs::path path(state.to_string());
68 
69     state.push_string(path.leaf_name().c_str());
70     return 1;
71 }
72 
73 
74 /// Lua binding for fs::path::dirname.
75 ///
76 /// \pre stack(-1) The input path.
77 /// \post stack(-1) The directory part of the input path.
78 ///
79 /// \param state The Lua state.
80 ///
81 /// \return The number of result values, i.e. 1.
82 static int
lua_fs_dirname(lutok::state & state)83 lua_fs_dirname(lutok::state& state)
84 {
85     if (!state.is_string())
86         throw std::runtime_error("Need a string parameter");
87     const fs::path path(state.to_string());
88 
89     state.push_string(path.branch_path().c_str());
90     return 1;
91 }
92 
93 
94 /// Lua binding for fs::path::exists.
95 ///
96 /// \pre stack(-1) The input path.
97 /// \post stack(-1) Whether the input path exists or not.
98 ///
99 /// \param state The Lua state.
100 ///
101 /// \return The number of result values, i.e. 1.
102 static int
lua_fs_exists(lutok::state & state)103 lua_fs_exists(lutok::state& state)
104 {
105     if (!state.is_string())
106         throw std::runtime_error("Need a string parameter");
107     const fs::path path(state.to_string());
108 
109     state.push_boolean(fs::exists(path));
110     return 1;
111 }
112 
113 
114 /// Lua binding for the files iterator.
115 ///
116 /// This function takes an open directory from the closure of the iterator and
117 /// returns the next entry.  See lua_fs_files() for the iterator generator
118 /// function.
119 ///
120 /// \pre upvalue(1) The userdata containing an open DIR* object.
121 ///
122 /// \param state The lua state.
123 ///
124 /// \return The number of result values, i.e. 0 if there are no more entries or
125 /// 1 if an entry has been read.
126 static int
files_iterator(lutok::state & state)127 files_iterator(lutok::state& state)
128 {
129     DIR** dirp = state.to_userdata< DIR* >(state.upvalue_index(1));
130     const struct dirent* entry = ::readdir(*dirp);
131     if (entry == NULL)
132         return 0;
133     else {
134         state.push_string(entry->d_name);
135         return 1;
136     }
137 }
138 
139 
140 /// Lua binding for the destruction of the files iterator.
141 ///
142 /// This function takes an open directory and closes it.  See lua_fs_files() for
143 /// the iterator generator function.
144 ///
145 /// \pre stack(-1) The userdata containing an open DIR* object.
146 /// \post The DIR* object is closed.
147 ///
148 /// \param state The lua state.
149 ///
150 /// \return The number of result values, i.e. 0.
151 static int
files_gc(lutok::state & state)152 files_gc(lutok::state& state)
153 {
154     PRE(state.is_userdata());
155 
156     DIR** dirp = state.to_userdata< DIR* >();
157     // For some reason, this may be called more than once.  I don't know why
158     // this happens, but we must protect against it.
159     if (*dirp != NULL) {
160         ::closedir(*dirp);
161         *dirp = NULL;
162     }
163 
164     return 0;
165 }
166 
167 
168 /// Lua binding to create an iterator to scan the contents of a directory.
169 ///
170 /// \pre stack(-1) The input path.
171 /// \post stack(-1) The iterator function.
172 ///
173 /// \param state The Lua state.
174 ///
175 /// \return The number of result values, i.e. 1.
176 static int
lua_fs_files(lutok::state & state)177 lua_fs_files(lutok::state& state)
178 {
179     if (!state.is_string())
180         throw std::runtime_error("Need a string parameter");
181     const fs::path path(state.to_string());
182 
183     DIR** dirp = state.new_userdata< DIR* >();
184 
185     state.new_table();
186     state.push_string("__gc");
187     state.push_cxx_function(files_gc);
188     state.set_table();
189 
190     state.set_metatable();
191 
192     *dirp = ::opendir(path.c_str());
193     if (*dirp == NULL) {
194         const int original_errno = errno;
195         throw std::runtime_error(F("Failed to open directory: %s") %
196                                  std::strerror(original_errno));
197     }
198 
199     state.push_cxx_closure(files_iterator, 1);
200 
201     return 1;
202 }
203 
204 
205 /// Lua binding for fs::path::is_absolute.
206 ///
207 /// \pre stack(-1) The input path.
208 /// \post stack(-1) Whether the input path is absolute or not.
209 ///
210 /// \param state The Lua state.
211 ///
212 /// \return The number of result values, i.e. 1.
213 static int
lua_fs_is_absolute(lutok::state & state)214 lua_fs_is_absolute(lutok::state& state)
215 {
216     if (!state.is_string())
217         throw std::runtime_error("Need a string parameter");
218     const fs::path path(state.to_string());
219 
220     state.push_boolean(path.is_absolute());
221     return 1;
222 }
223 
224 
225 /// Lua binding for fs::path::operator/.
226 ///
227 /// \pre stack(-2) The first input path.
228 /// \pre stack(-1) The second input path.
229 /// \post stack(-1) The concatenation of the two paths.
230 ///
231 /// \param state The Lua state.
232 ///
233 /// \return The number of result values, i.e. 1.
234 static int
lua_fs_join(lutok::state & state)235 lua_fs_join(lutok::state& state)
236 {
237     if (!state.is_string(-2))
238         throw std::runtime_error("Need a string parameter");
239     const fs::path path1(state.to_string(-2));
240 
241     if (!state.is_string(-1))
242         throw std::runtime_error("Need a string parameter");
243     const fs::path path2(state.to_string(-1));
244 
245     state.push_string((path1 / path2).c_str());
246     return 1;
247 }
248 
249 
250 }  // anonymous namespace
251 
252 
253 /// Creates a Lua 'fs' module.
254 ///
255 /// \post The global 'fs' symbol is set to a table that contains functions to a
256 /// variety of utilites from the fs C++ module.
257 ///
258 /// \param s The Lua state.
259 void
open_fs(lutok::state & s)260 fs::open_fs(lutok::state& s)
261 {
262     std::map< std::string, lutok::cxx_function > members;
263     members["basename"] = lua_fs_basename;
264     members["dirname"] = lua_fs_dirname;
265     members["exists"] = lua_fs_exists;
266     members["files"] = lua_fs_files;
267     members["is_absolute"] = lua_fs_is_absolute;
268     members["join"] = lua_fs_join;
269     lutok::create_module(s, "fs", members);
270 }
271