GCC Code Coverage Report


Directory: ./
File: lib/geogram/lua/lua_io.cpp
Date: 2026-09-07 02:37:58
Exec Total Coverage
Lines: 0 155 0.0%
Functions: 0 17 0.0%
Branches: 0 248 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #include <geogram/lua/lua_io.h>
41 #include <geogram/lua/lua_wrap.h>
42 #include <geogram/basic/file_system.h>
43 #include <geogram/basic/logger.h>
44 #include <geogram/bibliography/bibliography.h>
45 #include <map>
46 #include <sstream>
47
48 namespace {
49 using namespace GEO;
50
51 std::map<std::string, const char*> embedded_files;
52
53 /**
54 * \brief Calls LUA's "require" function, that
55 * was previously saved in the "GEO" table in
56 * the registry.
57 * \param[in] L a pointer to the LUA state.
58 * \return the number of return values pushed onto
59 * the stack.
60 */
61 int call_lua_require(lua_State* L) {
62 lua_getfield(L,LUA_REGISTRYINDEX,"GEO");
63 lua_getfield(L, -1, "lua_require_func");
64 lua_pushvalue(L,-3);
65 lua_call(L,1,1);
66 return 1;
67 }
68
69 /**
70 * \brief Our own implementation of "require".
71 * \details First looks-up the file within the
72 * embedded LUA files, then calls LUA's
73 * require function if not found.
74 * \param[in] L a pointer to the LUA state.
75 * \return the number of return values pushed onto
76 * the stack.
77 */
78 int require(lua_State* L) {
79 if(lua_gettop(L) != 1) {
80 return luaL_error(
81 L, "'require()' invalid number of arguments"
82 );
83 }
84 if(!lua_isstring(L,1)) {
85 return luaL_error(
86 L, "'require()' argument is not a string"
87 );
88 }
89 auto it = embedded_files.find(
90 std::string("lib/") + std::string(lua_tostring(L,1)) + ".lua"
91 );
92 if(it == embedded_files.end()) {
93 return call_lua_require(L);
94 }
95 const char* source = it->second;
96 if(luaL_dostring(L,source)) {
97 const char* msg = lua_tostring(L,-1);
98 GEO::Logger::err("LUA") << msg << std::endl;
99 }
100 return 0;
101 }
102
103 /************************************************************************/
104
105 /**
106 * \brief Calls LUA's "tostring" function, that
107 * was previously saved in the "GEO" table in
108 * the registry.
109 * \param[in] L a pointer to the LUA state.
110 * \return the number of return values pushed onto
111 * the stack.
112 */
113 int call_lua_tostring(lua_State* L) {
114 lua_getfield(L,LUA_REGISTRYINDEX,"GEO");
115 lua_getfield(L, -1, "lua_tostring_func");
116 lua_pushvalue(L,-3);
117 lua_call(L,1,1);
118 return 1;
119 }
120
121 inline void append(std::string& s, const char* c) {
122 s += (c==nullptr) ? "nil" : c;
123 }
124
125 int tostring(lua_State* L) {
126 if(lua_gettop(L) != 1) {
127 return luaL_error(
128 L, "'tostring()' invalid number of arguments"
129 );
130 }
131 if(lua_type(L,1) == LUA_TTABLE) {
132 std::string result = "{";
133 // Traverse the table (see LUA API doc. example).
134 lua_pushnil(L); // first key
135 bool first = true;
136 while (lua_next(L, 1) != 0) {
137 if(!first) {
138 result += ", ";
139 }
140
141 // We need to copy the key before calling
142 // tostring(), else this may modify the
143 // table in-place, which we do not want to do !
144 lua_pushvalue(L,-2);
145
146 append(result,lua_tostring(L,-1));
147
148 lua_pop(L,1);
149
150 result += "=";
151
152 // We need to copy the value before calling
153 // tostring(), else this may modify the
154 // table in-place, which we do not want to do !
155 lua_pushvalue(L,-1);
156 append(result,lua_tostring(L,-1));
157 lua_pop(L,1);
158
159 lua_pop(L,1);
160 first = false;
161 }
162 result += "}";
163 lua_pushstring(L,result.c_str());
164 return 1;
165 }
166 return call_lua_tostring(L);
167 }
168
169 /************************************************************************/
170
171 /**
172 * \brief Our own implementation of "print".
173 * \details Redirects all outputs to geogram outputs.
174 * \param[in] L a pointer to the LUA state.
175 * \return the number of return values pushed onto
176 * the stack.
177 */
178 int print(lua_State* L) {
179 std::ostringstream out;
180 int nargs = lua_gettop(L);
181 lua_getglobal(L, "tostring");
182 for(int i=1; i<=nargs; ++i) {
183 const char *s;
184 size_t l;
185 lua_pushvalue(L, -1); // function to be called.
186 lua_pushvalue(L, i); // value to print.
187 lua_call(L, 1, 1);
188 s = lua_tolstring(L, -1, &l); // get result.
189 if (s == nullptr) {
190 return luaL_error(
191 L, "'tostring' must return a string to 'print'"
192 );
193 }
194 if (i>1) {
195 out << "\t";
196 }
197 out << s;
198 lua_pop(L, 1); // pop result.
199 }
200 Logger::out("LUA") << out.str() << std::endl;
201 return 0;
202 }
203
204 namespace LUAFileSystemImpl {
205
206 // These three functions needed adaptation, because in the FileSystem
207 // API they take the returned vector as a reference argument.
208 // The following function return it, so that the generic LUA wrapper
209 // mechanism (lua_wrap.h) understands it and sends it back to LUA.
210 // Note: recursive mode not implemented yet (it is bugged in
211 // FileSystem, to be fixed...)
212
213
214 static std::vector<std::string> get_directory_entries(
215 const std::string& path
216 ) {
217 std::vector<std::string> result;
218 FileSystem::get_directory_entries(path,result);
219 return result;
220 }
221
222 static std::vector<std::string> get_files(const std::string& path) {
223 std::vector<std::string> result;
224 FileSystem::get_files(path,result);
225 return result;
226 }
227
228 static std::vector<std::string> get_subdirectories(
229 const std::string& path
230 ) {
231 std::vector<std::string> result;
232 FileSystem::get_subdirectories(path,result);
233 return result;
234 }
235
236 static const char* os_name() {
237 const char* result = "unknown";
238 #if defined(GEO_OS_LINUX)
239 result = "Linux";
240 #elif defined(GEO_OS_APPLE)
241 result = "Apple";
242 #elif defined(GEO_OS_WINDOWS)
243 result = "Windows";
244 #elif defined(GEO_OS_ANDROID)
245 result = "Android";
246 #elif defined(GEO_OS_UNIX)
247 result = "Generic Unix";
248 #endif
249 return result;
250 }
251
252 }
253 }
254
255
256 void register_embedded_lua_file(
257 const char* filename, const char* data
258 ) {
259 embedded_files[std::string(filename)] = data;
260 }
261
262 void list_embedded_lua_files(
263 std::vector<std::string>& filenames
264 ) {
265 filenames.clear();
266 for(auto& it : embedded_files) {
267 filenames.push_back(it.first);
268 }
269 }
270
271 void get_embedded_lua_file(
272 const char* filename, const char** data
273 ) {
274 auto it = embedded_files.find(filename);
275 *data = (it == embedded_files.end()) ? nullptr : it->second;
276 }
277
278 void init_lua_io(lua_State* L) {
279
280 geo_cite_with_info(
281 "WEB:lua",
282 "GEOGRAM has an embedded LUA interpreter "
283 "(LUA is worth one thousand times the few kilobytes it uses !)."
284 );
285
286 lua_newtable(L);
287
288 lua_bindwrapper(L, FileSystem::is_file);
289 lua_bindwrapper(L, FileSystem::is_directory);
290 lua_bindwrapper(L, FileSystem::create_directory);
291 lua_bindwrapper(L, FileSystem::delete_directory);
292 lua_bindwrapper(L, FileSystem::delete_file);
293 lua_bindwrapper(L, FileSystem::get_current_working_directory);
294 lua_bindwrapper(L, FileSystem::set_current_working_directory);
295 lua_bindwrapper(L, FileSystem::rename_file);
296 lua_bindwrapper(L, FileSystem::get_time_stamp);
297 lua_bindwrapper(L, FileSystem::extension);
298 lua_bindwrapper(L, FileSystem::base_name);
299 lua_bindwrapper(L, FileSystem::dir_name);
300 lua_bindwrapper(L, FileSystem::copy_file);
301 lua_bindwrapper(L, FileSystem::set_executable_flag);
302 lua_bindwrapper(L, FileSystem::touch);
303 lua_bindwrapper(L, FileSystem::normalized_path);
304 lua_bindwrapper(L, FileSystem::home_directory);
305 lua_bindwrapper(L, FileSystem::documents_directory);
306
307 lua_bindwrapper(L, LUAFileSystemImpl::get_directory_entries);
308 lua_bindwrapper(L, LUAFileSystemImpl::get_files);
309 lua_bindwrapper(L, LUAFileSystemImpl::get_subdirectories);
310
311 lua_bindwrapper(L, LUAFileSystemImpl::os_name);
312
313 lua_setglobal(L, "FileSystem");
314
315 // Save a copy of LUA's built-in "require"
316 // and "print" functions in the "GEO" table declared to
317 // the registry.
318 lua_newtable(L);
319 lua_getglobal(L, "require");
320 lua_setfield(L, -2, "lua_require_func");
321 lua_getglobal(L, "print");
322 lua_setfield(L, -2, "lua_print_func");
323 lua_getglobal(L, "tostring");
324 lua_setfield(L, -2, "lua_tostring_func");
325 lua_setfield(L, LUA_REGISTRYINDEX, "GEO");
326
327 // Replace require(),print() and tostring() with our own versions.
328 lua_bindwrapperglobal(L, require);
329 lua_bindwrapperglobal(L, print);
330 lua_bindwrapperglobal(L, tostring);
331 }
332
333 /************************************************************************/
334
335 // In lua 5.3, lua_replace, lua_insert and lua_remove are macros,
336 // whereas they are functions in lua 5.2.
337 // I redeclare them as functions here, so that we can load extension
338 // modules initially meant for use with lua 5.2 (such as "lgi", that
339 // allows loading libraries with gobject introspection, such as cairo
340 // and Poppler).
341
342 #ifdef lua_replace
343 #undef lua_replace
344 extern "C" int GEOGRAM_API lua_replace(lua_State* L, int idx);
345 extern "C" int GEOGRAM_API lua_replace(lua_State* L, int idx) {
346 lua_copy(L, -1, idx);
347 lua_pop(L,1);
348 return 0;
349 }
350 #endif
351
352 #ifdef lua_insert
353 #undef lua_insert
354 extern "C" int GEOGRAM_API lua_insert(lua_State* L, int idx);
355 extern "C" int GEOGRAM_API lua_insert(lua_State* L, int idx) {
356 lua_rotate(L, idx, 1);
357 return 0;
358 }
359 #endif
360
361 #ifdef lua_remove
362 #undef lua_remove
363 extern "C" int GEOGRAM_API lua_remove(lua_State* L, int idx);
364 extern "C" int GEOGRAM_API lua_remove(lua_State* L, int idx) {
365 lua_rotate(L, idx, -1);
366 lua_pop(L,1);
367 return 0;
368 }
369 #endif
370