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