GCC Code Coverage Report


Directory: ./
File: lua/lua_io.cpp
Date: 2026-09-27 03:24:14
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