The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
lua_fileops.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2014 - 2025
3 by Chris Beck <render787@gmail.com>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
17
18#include "filesystem.hpp"
19#include "log.hpp"
20#include "scripting/lua_common.hpp" // for chat_message, luaW_pcall
22#include "picture.hpp"
23#include "sdl/point.hpp"
24
25#include <algorithm>
26#include <exception>
27#include <string>
28
29static lg::log_domain log_scripting_lua("scripting/lua");
30#define DBG_LUA LOG_STREAM(debug, log_scripting_lua)
31#define LOG_LUA LOG_STREAM(info, log_scripting_lua)
32#define WRN_LUA LOG_STREAM(warn, log_scripting_lua)
33#define ERR_LUA LOG_STREAM(err, log_scripting_lua)
34
35/**
36* Gets the dimension of an image.
37* - Arg 1: string.
38* - Ret 1: width.
39* - Ret 2: height.
40*/
41static int intf_get_image_size(lua_State *L)
42{
43 char const *m = luaL_checkstring(L, 1);
44 image::locator img(m);
45 if(!image::exists(img)) return 0;
46 const point s = get_size(img);
47 lua_pushinteger(L, s.x);
48 lua_pushinteger(L, s.y);
49 return 2;
50}
51
52/**
53 * Returns true if an asset with the given path can be found in the binary paths.
54 * - Arg 1: asset type (generally one of images, sounds, music, maps)
55 * - Arg 2: relative path
56 */
57static int intf_have_asset(lua_State* L)
58{
59 std::string type = luaL_checkstring(L, 1), name = luaL_checkstring(L, 2);
60 lua_pushboolean(L, filesystem::get_binary_file_location(type, name).has_value());
61 return 1;
62}
63
64/**
65 * Given an asset path relative to binary paths, resolves to an absolute
66 * asset path starting from data/
67 * - Arg 1: asset type
68 * - Arg 2: relative path
69 */
70static int intf_resolve_asset(lua_State* L)
71{
72 std::string type = luaL_checkstring(L, 1), name = luaL_checkstring(L, 2);
73 lua_push(L, filesystem::get_independent_binary_file_path(type, name).value_or(""));
74 return 1;
75}
76
77namespace lua_fileops {
78static std::string get_calling_file(lua_State* L)
79{
80 std::string currentdir;
81 lua_Debug ar;
82 if(lua_getstack(L, 1, &ar)) {
83 lua_getinfo(L, "S", &ar);
84 if(ar.source[0] == '@') {
85 std::string calling_file(ar.source + 1);
86 for(int stack_pos = 2; calling_file == "lua/package.lua"; stack_pos++) {
87 if(!lua_getstack(L, stack_pos, &ar)) {
88 return currentdir;
89 }
90 lua_getinfo(L, "S", &ar);
91 if(ar.source[0] == '@') {
92 calling_file.assign(ar.source + 1);
93 }
94 }
95 currentdir = filesystem::directory_name(calling_file);
96 }
97 }
98 return currentdir;
99}
100/**
101 * resolves @a filename to an absolute path
102 * @returns true if the filename was successfully resolved.
103 */
104static bool canonical_path(std::string& filename, const std::string& currentdir)
105{
106 if(filename.size() < 2) {
107 return false;
108 }
109 if(filename[0] == '.' && filename[1] == '/') {
110 filename = currentdir + filename.substr(1);
111 }
112 if(std::find(filename.begin(), filename.end(), '\\') != filename.end()) {
113 return false;
114 }
115 //resolve /./
116 while(true) {
117 std::size_t pos = filename.find("/./");
118 if(pos == std::string::npos) {
119 break;
120 }
121 filename = filename.replace(pos, 2, "");
122 }
123 //resolve //
124 while(true) {
125 std::size_t pos = filename.find("//");
126 if(pos == std::string::npos) {
127 break;
128 }
129 filename = filename.replace(pos, 1, "");
130 }
131 //resolve /../
132 while(true) {
133 std::size_t pos = filename.find("/..");
134 if(pos == std::string::npos) {
135 break;
136 }
137 std::size_t pos2 = filename.find_last_of('/', pos - 1);
138 if(pos2 == std::string::npos || pos2 >= pos) {
139 return false;
140 }
141 filename = filename.replace(pos2, pos- pos2 + 3, "");
142 }
143 if(filename.find("..") != std::string::npos) {
144 return false;
145 }
146 return true;
147}
148
149/**
150 * resolves @a filename to an absolute path
151 * @returns true if the filename was successfully resolved.
152 */
153static bool resolve_filename(std::string& filename, const std::string& currentdir, std::string* rel = nullptr)
154{
155 if(!canonical_path(filename, currentdir)) {
156 return false;
157 }
159 if(!p) {
160 return false;
161 }
162 if(rel) {
163 *rel = filename;
164 }
165 filename = p.value();
166 return true;
167}
168
169int intf_canonical_path(lua_State *L)
170{
171 std::string m = luaL_checkstring(L, 1);
173 lua_push(L, m);
174 return 1;
175 } else {
176 return luaL_argerror(L, 1, "invalid path");
177 }
178}
179/**
180 * Checks if a file exists (not necessarily a Lua script).
181 * - Arg 1: string containing the file name.
182 * - Arg 2: if true, the file must be a real file and not a directory
183 * - Ret 1: boolean
184 */
185int intf_have_file(lua_State *L)
186{
187 std::string m = luaL_checkstring(L, 1);
189 lua_pushboolean(L, false);
190 } else if(luaW_toboolean(L, 2)) {
191 lua_pushboolean(L, !filesystem::is_directory(m));
192 } else {
193 lua_pushboolean(L, true);
194 }
195 return 1;
196}
197
198/**
199 * Reads a file into a string, or a directory into a list of files therein.
200 * - Arg 1: string containing the file name.
201 * - Ret 1: string
202 */
203int intf_read_file(lua_State *L)
204{
205 std::string p = luaL_checkstring(L, 1);
206
208 return luaL_argerror(L, -1, "file not found");
209 }
210
212 std::vector<std::string> files, dirs;
213 filesystem::get_files_in_dir(p, &files, &dirs);
215 std::size_t ndirs = dirs.size();
216 std::copy(files.begin(), files.end(), std::back_inserter(dirs));
217 lua_push(L, dirs);
218 lua_pushnumber(L, ndirs);
219 lua_setfield(L, -2, "ndirs");
220 return 1;
221 }
222 const std::unique_ptr<std::istream> fs(filesystem::istream_file(p));
223 fs->exceptions(std::ios_base::goodbit);
224 std::size_t size = 0;
225 fs->seekg(0, std::ios::end);
226 if(!fs->good()) {
227 return luaL_error(L, "Error when reading file");
228 }
229 size = fs->tellg();
230 fs->seekg(0, std::ios::beg);
231 if(!fs->good()) {
232 return luaL_error(L, "Error when reading file");
233 }
234 luaL_Buffer b;
235 luaL_buffinit(L, &b);
236 //throws an exception if malloc failed.
237 char* out = luaL_prepbuffsize(&b, size);
238 fs->read(out, size);
239 if(fs->good()) {
240 luaL_addsize(&b, size);
241 }
242 luaL_pushresult(&b);
243 return 1;
244}
245
247{
248public:
249 lua_filestream(const std::string& fname)
250 : buff_()
251 , pistream_(filesystem::istream_file(fname))
252 {
253
254 }
255
256 static const char * lua_read_data(lua_State * /*L*/, void *data, std::size_t *size)
257 {
258 lua_filestream* lfs = static_cast<lua_filestream*>(data);
259
260 //int startpos = lfs->pistream_->tellg();
261 lfs->pistream_->read(lfs->buff_, luaL_buffersize);
262 //int newpos = lfs->pistream_->tellg();
263 *size = lfs->pistream_->gcount();
264#if 0
265 ERR_LUA << "read bytes from " << startpos << " to " << newpos << " in total " *size << " from steam";
266 ERR_LUA << "streamstate being "
267 << " goodbit:" << lfs->pistream_->good()
268 << " endoffile:" << lfs->pistream_->eof()
269 << " badbit:" << lfs->pistream_->bad()
270 << " failbit:" << lfs->pistream_->fail();
271#endif
272 return lfs->buff_;
273 }
274
275 static int lua_loadfile(lua_State *L, const std::string& fname, const std::string& relativename)
276 {
277 lua_filestream lfs(fname);
278 //lua uses '@' to know that this is a file (as opposed to something loaded via loadstring )
279 std::string chunkname = '@' + relativename;
280 LOG_LUA << "starting to read from " << fname;
281 return lua_load(L, &lua_filestream::lua_read_data, &lfs, chunkname.c_str(), "t");
282 }
283private:
285 const std::unique_ptr<std::istream> pistream_;
286};
287
288/**
289 * Loads a Lua file and pushes the contents on the stack.
290 * - Arg 1: string containing the file name.
291 * - Ret 1: the loaded contents of the file
292 */
293int load_file(lua_State *L)
294{
295 std::string p = luaL_checkstring(L, -1);
296 std::string rel;
297
298 if(!resolve_filename(p, get_calling_file(L), &rel)) {
299 return luaL_argerror(L, -1, "file not found");
300 }
301
302 try
303 {
304 if(lua_filestream::lua_loadfile(L, p, rel)) {
305 return lua_error(L);
306 }
307 }
308 catch(const std::exception & ex)
309 {
310 luaL_argerror(L, -1, ex.what());
311 }
312 lua_remove(L, -2); //remove the filename from the stack
313
314 return 1;
315}
316
317int luaW_open(lua_State* L)
318{
319 static luaL_Reg const callbacks[] {
320 { "have_file", &lua_fileops::intf_have_file },
321 { "read_file", &lua_fileops::intf_read_file },
322 { "canonical_path", &lua_fileops::intf_canonical_path },
323 { "image_size", &intf_get_image_size },
324 { "have_asset", &intf_have_asset },
325 { "resolve_asset", &intf_resolve_asset },
326 { nullptr, nullptr }
327 };
328 lua_newtable(L);
329 luaL_setfuncs(L, callbacks, 0);
330 return 1;
331}
332
333} // end namespace lua_fileops
#define ERR_LUA
#define LOG_LUA
void remove_blacklisted_files_and_dirs(std::vector< std::string > &files, std::vector< std::string > &directories) const
Generic locator abstracting the location of an image.
Definition picture.hpp:59
static const char * lua_read_data(lua_State *, void *data, std::size_t *size)
lua_filestream(const std::string &fname)
static int lua_loadfile(lua_State *L, const std::string &fname, const std::string &relativename)
char buff_[luaL_buffersize]
const std::unique_ptr< std::istream > pistream_
Declarations for File-IO.
Standard logging facilities (interface).
bool luaW_toboolean(lua_State *L, int n)
static lg::log_domain log_scripting_lua("scripting/lua")
static int intf_have_asset(lua_State *L)
Returns true if an asset with the given path can be found in the binary paths.
static int intf_resolve_asset(lua_State *L)
Given an asset path relative to binary paths, resolves to an absolute asset path starting from data/.
static int intf_get_image_size(lua_State *L)
Gets the dimension of an image.
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs=nullptr, name_mode mode=name_mode::FILE_NAME_ONLY, filter_mode filter=filter_mode::NO_FILTER, reorder_mode reorder=reorder_mode::DONT_REORDER, file_tree_checksum *checksum=nullptr)
Get a list of all files and/or directories in a given directory.
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
utils::optional< std::string > get_wml_location(const std::string &path, const utils::optional< std::string > &current_dir=utils::nullopt)
Returns a translated path to the actual file or directory, if it exists.
utils::optional< std::string > get_binary_file_location(const std::string &type, const std::string &filename)
Returns a complete path to the actual file of a given type, if it exists.
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
utils::optional< std::string > get_independent_binary_file_path(const std::string &type, const std::string &filename)
Returns an asset path to filename for binary path-independent use in saved games.
const blacklist_pattern_list default_blacklist
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error=true)
bool exists(const image::locator &i_locator)
Returns true if the given image actually exists, without loading it.
Definition picture.cpp:855
int luaW_open(lua_State *L)
static bool canonical_path(std::string &filename, const std::string &currentdir)
resolves filename to an absolute path
int load_file(lua_State *L)
Loads a Lua file and pushes the contents on the stack.
int intf_read_file(lua_State *L)
Reads a file into a string, or a directory into a list of files therein.
int intf_have_file(lua_State *L)
Checks if a file exists (not necessarily a Lua script).
static std::string get_calling_file(lua_State *L)
int intf_canonical_path(lua_State *L)
static bool resolve_filename(std::string &filename, const std::string &currentdir, std::string *rel=nullptr)
resolves filename to an absolute path
std::string_view data
Definition picture.cpp:188
void lua_push(lua_State *L, const T &val)
std::string filename
Filename.
Holds a 2D point.
Definition point.hpp:25
mock_party p
static map_location::direction s
constexpr int luaL_buffersize
#define b