The Battle for Wesnoth  1.13.11+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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 http://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 "exceptions.hpp"
18 #include "filesystem.hpp"
19 #include "game_config.hpp" //for game_config::debug_lua
20 #include "game_errors.hpp"
21 #include "log.hpp"
22 #include "scripting/lua_common.hpp" // for chat_message, luaW_pcall
23 #include "scripting/push_check.hpp"
24 
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 {
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  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  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  size_t pos = filename.find("/..");
95  if(pos == std::string::npos) {
96  break;
97  }
98  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  }
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 {
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);
153  size_t ndirs = dirs.size();
154  std::copy(files.begin(), files.end(), std::back_inserter(dirs));
155  lua_push(L, dirs);
156  lua_pushnumber(L, ndirs);
157  lua_setfield(L, -2, "ndirs");
158  return 1;
159  }
160  const std::unique_ptr<std::istream> fs(filesystem::istream_file(p));
161  fs->exceptions(std::ios_base::goodbit);
162  size_t size = 0;
163  fs->seekg(0, std::ios::end);
164  if(!fs->good()) {
165  return luaL_error(L, "Error when reading file");
166  }
167  size = fs->tellg();
168  fs->seekg(0, std::ios::beg);
169  if(!fs->good()) {
170  return luaL_error(L, "Error when reading file");
171  }
172  luaL_Buffer b;
173  luaL_buffinit(L, &b);
174  //throws an exception if malloc failed.
175  char* out = luaL_prepbuffsize(&b, size);
176  fs->read(out, size);
177  if(fs->good()) {
178  luaL_addsize(&b, size);
179  }
180  luaL_pushresult(&b);
181  return 1;
182 }
183 
185 {
186 public:
188  : buff_()
190  {
191 
192  }
193 
194  static const char * lua_read_data(lua_State * /*L*/, void *data, size_t *size)
195  {
196  lua_filestream* lfs = static_cast<lua_filestream*>(data);
197 
198  //int startpos = lfs->pistream_->tellg();
199  lfs->pistream_->read(lfs->buff_, LUAL_BUFFERSIZE);
200  //int newpos = lfs->pistream_->tellg();
201  *size = lfs->pistream_->gcount();
202 #if 0
203  ERR_LUA << "read bytes from " << startpos << " to " << newpos << " in total " *size << " from steam\n";
204  ERR_LUA << "streamstate being "
205  << " goodbit:" << lfs->pistream_->good()
206  << " endoffile:" << lfs->pistream_->eof()
207  << " badbit:" << lfs->pistream_->bad()
208  << " failbit:" << lfs->pistream_->fail() << "\n";
209 #endif
210  return lfs->buff_;
211  }
212 
213  static int lua_loadfile(lua_State *L, const std::string& fname, const std::string& relativename)
214  {
215  lua_filestream lfs(fname);
216  //lua uses '@' to know that this is a file (as opposed to something loaded via loadstring )
217  std::string chunkname = '@' + relativename;
218  LOG_LUA << "starting to read from " << fname << "\n";
219  return lua_load(L, &lua_filestream::lua_read_data, &lfs, chunkname.c_str(), nullptr);
220  }
221 private:
223  const std::unique_ptr<std::istream> pistream_;
224 };
225 
226 /**
227  * Loads a Lua file and pushes the contents on the stack.
228  * - Arg 1: string containing the file name.
229  * - Ret 1: the loaded contents of the file
230  */
232 {
233  std::string p = luaL_checkstring(L, -1);
234  std::string rel;
235 
236  if(!resolve_filename(p, get_calling_file(L), &rel)) {
237  return luaL_argerror(L, -1, "file not found");
238  }
239 
240  try
241  {
242  if(lua_filestream::lua_loadfile(L, p, rel)) {
243  return lua_error(L);
244  }
245  }
246  catch(const std::exception & ex)
247  {
248  luaL_argerror(L, -1, ex.what());
249  }
250  lua_remove(L, -2); //remove the filename from the stack
251 
252  return 1;
253 }
254 
255 } // end namespace lua_fileops
#define luaL_addsize(B, s)
Definition: lauxlib.h:161
Definition: lua.h:441
std::vector< char_t > string
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:556
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
#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:295
#define ERR_LUA
Definition: lua_fileops.cpp:38
#define b
#define LUAL_BUFFERSIZE
Definition: luaconf.h:751
LUALIB_API int luaL_argerror(lua_State *L, int arg, const char *extramsg)
Definition: lauxlib.cpp:164
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error=true)
bool luaW_toboolean(lua_State *L, int n)
Definition: lua_common.cpp:858
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.
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)
size_t size(const utf8::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs=nullptr, file_name_option mode=FILE_NAME_ONLY, file_filter_option filter=NO_FILTER, file_reorder_option reorder=DONT_REORDER, file_tree_checksum *checksum=nullptr)
Populates 'files' with all the files and 'dirs' with all the directories in dir.
std::string get_wml_location(const std::string &filename, const std::string &current_dir=std::string())
Returns a complete path to the actual WML file or directory or an empty string if the file isn't pres...
LUA_API int lua_getinfo(lua_State *L, const char *what, lua_Debug *ar)
Definition: ldebug.cpp:309
mock_party p
static const char * lua_read_data(lua_State *, void *data, size_t *size)
LUA_API int lua_load(lua_State *L, lua_Reader reader, void *data, const char *chunkname, const char *mode)
Definition: lapi.cpp:991
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
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:1113
bool find(E event, F functor)
Tests whether an event handler is available.
Standard logging facilities (interface).
LUA_API void lua_setfield(lua_State *L, int idx, const char *k)
Definition: lapi.cpp:776
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
char buff_[LUAL_BUFFERSIZE]
#define luaL_checkstring(L, n)
Definition: lauxlib.h:124