The Battle for Wesnoth  1.13.4+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 - 2016 by Chris Beck <[email protected]>
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 
15 #include "lua_fileops.hpp"
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_api.hpp" // for chat_message, luaW_pcall
23 #include "scripting/lua_common.hpp" // for chat_message, luaW_pcall
24 
25 #include <exception>
26 #include <string>
27 
28 #include <boost/scoped_ptr.hpp>
29 #include <boost/algorithm/string/predicate.hpp>
30 
31 #include "lua/lauxlib.h"
32 #include "lua/lua.h"
33 #include "lua/luaconf.h" // for LUAL_BUFFERSIZE
34 
35 static lg::log_domain log_scripting_lua("scripting/lua");
36 #define DBG_LUA LOG_STREAM(debug, log_scripting_lua)
37 #define LOG_LUA LOG_STREAM(info, log_scripting_lua)
38 #define WRN_LUA LOG_STREAM(warn, log_scripting_lua)
39 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
40 
41 namespace lua_fileops {
42 /// resolves @a filename where @a currentdir is the current directory, note that @a currentdir
43 /// is no absolute directory
44 /// @returns true iff the filename was sucesful resolved.
45 static bool resolve_filename(std::string& filename, const std::string& currentdir)
46 {
47  if(filename.size() < 2) {
48  return false;
49  }
50  if(filename[0] == '.' && filename[1] == '/') {
51  filename = currentdir + filename.substr(1);
52  }
53  if(std::find(filename.begin(), filename.end(), '\\') != filename.end()) {
54  return false;
55  }
56  //resolve /./
57  while(true) {
58  size_t pos = filename.find("/./");
59  if(pos == std::string::npos) {
60  break;
61  }
62  filename = filename.replace(pos, 2, "");
63  }
64  //resolve //
65  while(true) {
66  size_t pos = filename.find("//");
67  if(pos == std::string::npos) {
68  break;
69  }
70  filename = filename.replace(pos, 1, "");
71  }
72  //resolve /../
73  while(true) {
74  size_t pos = filename.find("/..");
75  if(pos == std::string::npos) {
76  break;
77  }
78  size_t pos2 = filename.find_last_of('/', pos - 1);
79  if(pos == std::string::npos || pos2 >= pos) {
80  return false;
81  }
82  filename = filename.replace(pos2, pos- pos2 + 3, "");
83  }
84  if(filename.find("..") != std::string::npos) {
85  return false;
86  }
87  return true;
88 }
89 
90 /**
91  * Checks if a file exists (not necessarily a Lua script).
92  * - Arg 1: string containing the file name.
93  * - Ret 1: boolean
94  */
96 {
97  char const *m = luaL_checkstring(L, 1);
99  if (p.empty()) { lua_pushboolean(L, false); }
100  else { lua_pushboolean(L, true); }
101  return 1;
102 }
103 /**
104  * Checks if a file exists (not necessarily a Lua script).
105  * - Arg 1: string containing the file name.
106  * - Ret 1: string
107  */
109 {
111 
112  std::string current_dir = "";
113  lua_Debug ar;
114  if(lua_getstack(L, 1, &ar)) {
115  lua_getinfo(L, "S", &ar);
116  if(ar.source[0] == '@') {
117  current_dir = filesystem::directory_name(std::string(ar.source + 1));
118  }
119  }
120  if(!resolve_filename(m, current_dir)) {
121  return luaL_argerror(L, -1, "file not found");
122  }
124  if(p.empty()) {
125  return luaL_argerror(L, -1, "file not found");
126  }
127  boost::scoped_ptr<std::istream> fs(filesystem::istream_file(p));
128  fs->exceptions(std::ios_base::goodbit);
129  size_t size = 0;
130  fs->seekg(0, std::ios::end);
131  if(!fs->good()) {
132  return luaL_error(L, "Error when reading file");
133  }
134  size = fs->tellg();
135  fs->seekg(0, std::ios::beg);
136  if(!fs->good()) {
137  return luaL_error(L, "Error when reading file");
138  }
139  luaL_Buffer b;
140  luaL_buffinit(L, &b);
141  //throws an exception if malloc failed.
142  char* out = luaL_prepbuffsize(&b, size);
143  fs->read(out, size);
144  if(fs->good()) {
145  luaL_addsize(&b, size);
146  }
147  luaL_pushresult(&b);
148  return 1;
149 }
150 
152 {
153 public:
156  {
157 
158  }
159 
160  static const char * lua_read_data(lua_State * /*L*/, void *data, size_t *size)
161  {
162  lua_filestream* lfs = static_cast<lua_filestream*>(data);
163 
164  //int startpos = lfs->pistream_->tellg();
165  lfs->pistream_->read(lfs->buff_, LUAL_BUFFERSIZE);
166  //int newpos = lfs->pistream_->tellg();
167  *size = lfs->pistream_->gcount();
168 #if 0
169  ERR_LUA << "read bytes from " << startpos << " to " << newpos << " in total " *size << " from steam\n";
170  ERR_LUA << "streamstate beeing "
171  << " goodbit:" << lfs->pistream_->good()
172  << " endoffile:" << lfs->pistream_->eof()
173  << " badbit:" << lfs->pistream_->bad()
174  << " failbit:" << lfs->pistream_->fail() << "\n";
175 #endif
176  return lfs->buff_;
177  }
178 
179  static int lua_loadfile(lua_State *L, const std::string& fname, const std::string& relativename)
180  {
181  lua_filestream lfs(fname);
182  //lua uses '@' to know that this is a file (as opposed to something loaded via loadstring )
183  std::string chunkname = '@' + relativename;
184  LOG_LUA << "starting to read from " << fname << "\n";
185  return lua_load(L, &lua_filestream::lua_read_data, &lfs, chunkname.c_str(), nullptr);
186  }
187 private:
189  boost::scoped_ptr<std::istream> pistream_;
190 };
191 
192 /**
193  * Loads a Lua file and pushes the contents on the stack.
194  * - Arg 1: string containing the file name.
195  * - Ret 1: the loaded contents of the file
196  */
198 {
199  std::string m = luaL_checkstring(L, -1);
200 
201  std::string current_dir = "";
202  lua_Debug ar;
203  if(lua_getstack(L, 1, &ar)) {
204  lua_getinfo(L, "S", &ar);
205  if(ar.source[0] == '@') {
206  current_dir = filesystem::directory_name(std::string(ar.source + 1));
207  }
208  }
209  if(!resolve_filename(m, current_dir)) {
210  return luaL_argerror(L, -1, "file not found");
211  }
213  if (p.empty()) {
214  return luaL_argerror(L, -1, "file not found");
215  }
216  try
217  {
218  if(lua_filestream::lua_loadfile(L, p, m)) {
219  return lua_error(L);
220  }
221  }
222  catch(const std::exception & ex)
223  {
224  luaL_argerror(L, -1, ex.what());
225  }
226  lua_remove(L, -2); //remove the filename from the stack
227 
228  return 1;
229 }
230 
231 } // end namespace lua_fileops
#define luaL_addsize(B, s)
Definition: lauxlib.h:156
Definition: lua.h:398
int intf_read_file(lua_State *L)
Checks if a file exists (not necessarily a Lua script).
int intf_have_file(lua_State *L)
Checks if a file exists (not necessarily a Lua script).
Definition: lua_fileops.cpp:95
LUALIB_API char * luaL_prepbuffsize(luaL_Buffer *B, size_t sz)
Definition: lauxlib.cpp:439
boost::scoped_ptr< std::istream > pistream_
lua_filestream(const std::string &fname)
int pos
Definition: formula.cpp:800
LUA_API void lua_pushboolean(lua_State *L, int b)
Definition: lapi.cpp:571
LUALIB_API void luaL_pushresult(luaL_Buffer *B)
Definition: lauxlib.cpp:473
const char * source
Definition: lua.h:403
#define LOG_LUA
Definition: lua_fileops.cpp:37
LUA_API int lua_getstack(lua_State *L, int level, lua_Debug *ar)
Definition: ldebug.cpp:82
GLint GLenum GLsizei GLint GLsizei const GLvoid * data
Definition: glew.h:1347
#define ERR_LUA
Definition: lua_fileops.cpp:39
GLdouble GLdouble GLdouble b
Definition: glew.h:6966
#define LUAL_BUFFERSIZE
Definition: luaconf.h:382
GLuint GLuint end
Definition: glew.h:1221
std::istream * istream_file(const std::string &fname, bool treat_failure_as_error=true)
LUALIB_API void luaL_buffinit(lua_State *L, luaL_Buffer *B)
Definition: lauxlib.cpp:498
LUA_API void lua_remove(lua_State *L, int idx)
Definition: lapi.cpp:176
GLfloat GLfloat p
Definition: glew.h:12766
static int lua_loadfile(lua_State *L, const std::string &fname, const std::string &relativename)
static bool resolve_filename(std::string &filename, const std::string &currentdir)
resolves filename where currentdir is the current directory, note that currentdir is no absolute dire...
Definition: lua_fileops.cpp:45
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:265
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:967
LUALIB_API int luaL_argerror(lua_State *L, int narg, const char *extramsg)
Definition: lauxlib.cpp:152
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:198
GLsizeiptr size
Definition: glew.h:1649
LUA_API int lua_error(lua_State *L)
Definition: lapi.cpp:1099
const GLdouble * m
Definition: glew.h:6968
bool find(E event, F functor)
Tests whether an event handler is available.
Standard logging facilities (interface).
GLsizei const GLcharARB ** string
Definition: glew.h:4503
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:115