The Battle for Wesnoth  1.15.0-dev
lua_fileops.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2018 by Chris Beck <render787@gmail.com>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
16 
17 #include "filesystem.hpp"
18 #include "game_config.hpp" //for game_config::debug_lua
19 #include "game_errors.hpp"
20 #include "log.hpp"
21 #include "scripting/lua_common.hpp" // for chat_message, luaW_pcall
22 #include "scripting/push_check.hpp"
23 
24 #include <algorithm>
25 #include <exception>
26 #include <string>
27 
28 #include <boost/algorithm/string/predicate.hpp>
29 
30 #include "lua/lauxlib.h"
31 #include "lua/lua.h"
32 #include "lua/luaconf.h" // for LUAL_BUFFERSIZE
33 
34 static lg::log_domain log_scripting_lua("scripting/lua");
35 #define DBG_LUA LOG_STREAM(debug, log_scripting_lua)
36 #define LOG_LUA LOG_STREAM(info, log_scripting_lua)
37 #define WRN_LUA LOG_STREAM(warn, log_scripting_lua)
38 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
39 
40 namespace lua_fileops {
41 static std::string get_calling_file(lua_State* L)
42 {
43  std::string currentdir;
44  lua_Debug ar;
45  if(lua_getstack(L, 1, &ar)) {
46  lua_getinfo(L, "S", &ar);
47  if(ar.source[0] == '@') {
48  std::string calling_file(ar.source + 1);
49  for(int stack_pos = 2; calling_file == "lua/package.lua"; stack_pos++) {
50  if(!lua_getstack(L, stack_pos, &ar)) {
51  return currentdir;
52  }
53  lua_getinfo(L, "S", &ar);
54  if(ar.source[0] == '@') {
55  calling_file.assign(ar.source + 1);
56  }
57  }
58  currentdir = filesystem::directory_name(calling_file);
59  }
60  }
61  return currentdir;
62 }
63 /// resolves @a filename to an absolute path
64 /// @returns true if the filename was successfully resolved.
65 static bool resolve_filename(std::string& filename, std::string currentdir, std::string* rel = nullptr)
66 {
67  if(filename.size() < 2) {
68  return false;
69  }
70  if(filename[0] == '.' && filename[1] == '/') {
71  filename = currentdir + filename.substr(1);
72  }
73  if(std::find(filename.begin(), filename.end(), '\\') != filename.end()) {
74  return false;
75  }
76  //resolve /./
77  while(true) {
78  std::size_t pos = filename.find("/./");
79  if(pos == std::string::npos) {
80  break;
81  }
82  filename = filename.replace(pos, 2, "");
83  }
84  //resolve //
85  while(true) {
86  std::size_t pos = filename.find("//");
87  if(pos == std::string::npos) {
88  break;
89  }
90  filename = filename.replace(pos, 1, "");
91  }
92  //resolve /../
93  while(true) {
94  std::size_t pos = filename.find("/..");
95  if(pos == std::string::npos) {
96  break;
97  }
98  std::size_t pos2 = filename.find_last_of('/', pos - 1);
99  if(pos2 == std::string::npos || pos2 >= pos) {
100  return false;
101  }
102  filename = filename.replace(pos2, pos- pos2 + 3, "");
103  }
104  if(filename.find("..") != std::string::npos) {
105  return false;
106  }
107  std::string p = filesystem::get_wml_location(filename);
108  if(p.empty()) {
109  return false;
110  }
111  if(rel) {
112  *rel = filename;
113  }
114  filename = p;
115  return true;
116 }
117 
118 /**
119  * Checks if a file exists (not necessarily a Lua script).
120  * - Arg 1: string containing the file name.
121  * - Arg 2: if true, the file must be a real file and not a directory
122  * - Ret 1: boolean
123  */
125 {
126  std::string m = luaL_checkstring(L, 1);
127  if(!resolve_filename(m, get_calling_file(L))) {
128  lua_pushboolean(L, false);
129  } else if(luaW_toboolean(L, 2)) {
131  } else {
132  lua_pushboolean(L, true);
133  }
134  return 1;
135 }
136 
137 /**
138  * Reads a file into a string, or a directory into a list of files therein.
139  * - Arg 1: string containing the file name.
140  * - Ret 1: string
141  */
143 {
144  std::string p = luaL_checkstring(L, 1);
145 
146  if(!resolve_filename(p, get_calling_file(L))) {
147  return luaL_argerror(L, -1, "file not found");
148  }
149 
150  if(filesystem::is_directory(p)) {
151  std::vector<std::string> files, dirs;
152  filesystem::get_files_in_dir(p, &files, &dirs);
154  std::size_t ndirs = dirs.size();
155  std::copy(files.begin(), files.end(), std::back_inserter(dirs));
156  lua_push(L, dirs);
157  lua_pushnumber(L, ndirs);
158  lua_setfield(L, -2, "ndirs");
159  return 1;
160  }
161  const std::unique_ptr<std::istream> fs(filesystem::istream_file(p));
162  fs->exceptions(std::ios_base::goodbit);
163  std::size_t size = 0;
164  fs->seekg(0, std::ios::end);
165  if(!fs->good()) {
166  return luaL_error(L, "Error when reading file");
167  }
168  size = fs->tellg();
169  fs->seekg(0, std::ios::beg);
170  if(!fs->good()) {
171  return luaL_error(L, "Error when reading file");
172  }
173  luaL_Buffer b;
174  luaL_buffinit(L, &b);
175  //throws an exception if malloc failed.
176  char* out = luaL_prepbuffsize(&b, size);
177  fs->read(out, size);
178  if(fs->good()) {
179  luaL_addsize(&b, size);
180  }
181  luaL_pushresult(&b);
182  return 1;
183 }
184 
186 {
187 public:
188  lua_filestream(const std::string& fname)
189  : buff_()
191  {
192 
193  }
194 
195  static const char * lua_read_data(lua_State * /*L*/, void *data, std::size_t *size)
196  {
197  lua_filestream* lfs = static_cast<lua_filestream*>(data);
198 
199  //int startpos = lfs->pistream_->tellg();
200  lfs->pistream_->read(lfs->buff_, LUAL_BUFFERSIZE);
201  //int newpos = lfs->pistream_->tellg();
202  *size = lfs->pistream_->gcount();
203 #if 0
204  ERR_LUA << "read bytes from " << startpos << " to " << newpos << " in total " *size << " from steam\n";
205  ERR_LUA << "streamstate being "
206  << " goodbit:" << lfs->pistream_->good()
207  << " endoffile:" << lfs->pistream_->eof()
208  << " badbit:" << lfs->pistream_->bad()
209  << " failbit:" << lfs->pistream_->fail() << "\n";
210 #endif
211  return lfs->buff_;
212  }
213 
214  static int lua_loadfile(lua_State *L, const std::string& fname, const std::string& relativename)
215  {
216  lua_filestream lfs(fname);
217  //lua uses '@' to know that this is a file (as opposed to something loaded via loadstring )
218  std::string chunkname = '@' + relativename;
219  LOG_LUA << "starting to read from " << fname << "\n";
220  return lua_load(L, &lua_filestream::lua_read_data, &lfs, chunkname.c_str(), "t");
221  }
222 private:
224  const std::unique_ptr<std::istream> pistream_;
225 };
226 
227 /**
228  * Loads a Lua file and pushes the contents on the stack.
229  * - Arg 1: string containing the file name.
230  * - Ret 1: the loaded contents of the file
231  */
233 {
234  std::string p = luaL_checkstring(L, -1);
235  std::string rel;
236 
237  if(!resolve_filename(p, get_calling_file(L), &rel)) {
238  return luaL_argerror(L, -1, "file not found");
239  }
240 
241  try
242  {
243  if(lua_filestream::lua_loadfile(L, p, rel)) {
244  return lua_error(L);
245  }
246  }
247  catch(const std::exception & ex)
248  {
249  luaL_argerror(L, -1, ex.what());
250  }
251  lua_remove(L, -2); //remove the filename from the stack
252 
253  return 1;
254 }
255 
256 } // end namespace lua_fileops
#define luaL_addsize(B, s)
Definition: lauxlib.h:161
Definition: lua.h:441
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).
LUALIB_API char * luaL_prepbuffsize(luaL_Buffer *B, size_t sz)
Definition: lauxlib.cpp:505
lua_filestream(const std::string &fname)
LUA_API void lua_pushboolean(lua_State *L, int b)
Definition: lapi.cpp:557
const std::unique_ptr< std::istream > pistream_
LUALIB_API void luaL_pushresult(luaL_Buffer *B)
Definition: lauxlib.cpp:542
static bool resolve_filename(std::string &filename, std::string currentdir, std::string *rel=nullptr)
resolves filename to an absolute path
Definition: lua_fileops.cpp:65
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
Definition: filesystem.cpp:894
#define lua_remove(L, idx)
Definition: lua.h:371
const char * source
Definition: lua.h:446
#define LOG_LUA
Definition: lua_fileops.cpp:36
LUA_API int lua_getstack(lua_State *L, int level, lua_Debug *ar)
Definition: ldebug.cpp:110
void lua_push(lua_State *L, const T &val)
Definition: push_check.hpp:293
static const char * lua_read_data(lua_State *, void *data, std::size_t *size)
void remove_blacklisted_files_and_dirs(std::vector< std::string > &files, std::vector< std::string > &directories) const
#define ERR_LUA
Definition: lua_fileops.cpp:38
#define b
#define LUAL_BUFFERSIZE
Definition: luaconf.h:758
LUALIB_API int luaL_argerror(lua_State *L, int arg, const char *extramsg)
Definition: lauxlib.cpp:164
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
bool luaW_toboolean(lua_State *L, int n)
Definition: lua_common.cpp:870
LUALIB_API void luaL_buffinit(lua_State *L, luaL_Buffer *B)
Definition: lauxlib.cpp:569
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
Definition: filesystem.cpp:984
LUA_API void lua_pushnumber(lua_State *L, lua_Number n)
Definition: lapi.cpp:458
static int lua_loadfile(lua_State *L, const std::string &fname, const std::string &relativename)
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs, file_name_option mode, file_filter_option filter, file_reorder_option reorder, file_tree_checksum *checksum)
Populates &#39;files&#39; with all the files and &#39;dirs&#39; with all the directories in dir.
Definition: filesystem.cpp:386
std::string get_wml_location(const std::string &filename, const std::string &current_dir)
Returns a complete path to the actual WML file or directory or an empty string if the file isn&#39;t pres...
LUA_API int lua_getinfo(lua_State *L, const char *what, lua_Debug *ar)
Definition: ldebug.cpp:309
mock_party p
LUA_API int lua_load(lua_State *L, lua_Reader reader, void *data, const char *chunkname, const char *mode)
Definition: lapi.cpp:992
Declarations for File-IO.
int load_file(lua_State *L)
Loads a Lua file and pushes the contents on the stack.
static lg::log_domain log_scripting_lua("scripting/lua")
LUALIB_API int luaL_error(lua_State *L, const char *fmt,...)
Definition: lauxlib.cpp:223
The paths manager is responsible for recording the various paths that binary files may be located at...
Definition: fs_commit.cpp:37
static std::string get_calling_file(lua_State *L)
Definition: lua_fileops.cpp:41
LUA_API int lua_error(lua_State *L)
Definition: lapi.cpp:1114
Standard logging facilities (interface).
LUA_API void lua_setfield(lua_State *L, int idx, const char *k)
Definition: lapi.cpp:777
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
char buff_[LUAL_BUFFERSIZE]
static const blacklist_pattern_list default_blacklist
Definition: filesystem.hpp:99
#define luaL_checkstring(L, n)
Definition: lauxlib.h:124