ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

liolib.cpp

Go to the documentation of this file.
00001 /*
00002 ** Standard I/O (and system) library
00003 ** See Copyright Notice in lua.h
00004 */
00005 
00006 #define FORBIDDEN_SYMBOL_EXCEPTION_setjmp
00007 #define FORBIDDEN_SYMBOL_EXCEPTION_longjmp
00008 #define FORBIDDEN_SYMBOL_EXCEPTION_fread
00009 #define FORBIDDEN_SYMBOL_EXCEPTION_fwrite
00010 #define FORBIDDEN_SYMBOL_EXCEPTION_fseek
00011 #define FORBIDDEN_SYMBOL_EXCEPTION_stderr
00012 #define FORBIDDEN_SYMBOL_EXCEPTION_stdin
00013 #define FORBIDDEN_SYMBOL_EXCEPTION_stdout
00014 #define FORBIDDEN_SYMBOL_EXCEPTION_exit
00015 
00016 #include "common/savefile.h"
00017 #include "common/fs.h"
00018 #include "common/system.h"
00019 
00020 #include "engines/grim/lua/lauxlib.h"
00021 #include "engines/grim/lua/lua.h"
00022 #include "engines/grim/lua/luadebug.h"
00023 #include "engines/grim/lua/lualib.h"
00024 
00025 #include "base/commandLine.h"
00026 
00027 #include "engines/grim/resource.h"
00028 
00029 namespace Grim {
00030 
00031 #define CLOSEDTAG   2
00032 #define IOTAG       1
00033 
00034 #define FIRSTARG      3  // 1st and 2nd are upvalues
00035 
00036 #define FINPUT      "_INPUT"
00037 #define FOUTPUT     "_OUTPUT"
00038 
00039 LuaFile *g_stderr;
00040 
00041 static int32 s_id = 0;
00042 Common::HashMap<int32, LuaFile *> *g_files;
00043 
00044 LuaFile::LuaFile() : _in(nullptr), _out(nullptr), _stdin(false), _stdout(false), _stderr(false) {
00045 }
00046 
00047 LuaFile::~LuaFile() {
00048     close();
00049 }
00050 
00051 void LuaFile::close() {
00052     delete _in;
00053     delete _out;
00054     _in = nullptr;
00055     _out = nullptr;
00056     _stdin = _stdout = _stderr = false;
00057 }
00058 
00059 bool LuaFile::isOpen() const {
00060     return _in || _out || _stdin || stdout || stderr;
00061 }
00062 
00063 uint32 LuaFile::read(void *buf, uint32 len) {
00064     if (_stdin) {
00065         return fread(buf, len, 1, stdin);
00066     } else if (_in) {
00067         return _in->read(buf, len);
00068     } else
00069         assert(0);
00070     return 0;
00071 }
00072 
00073 uint32 LuaFile::write(const char *buf, uint32 len) {
00074     if (_stdin)
00075         error("LuaFile::write() not allowed on stdin");
00076     if (_in)
00077         error("LuaFile::write() not allowed on in");
00078     if (_stdout) {
00079         return fwrite(buf, len, 1, stdout);
00080     } else if (_stderr) {
00081         return fwrite(buf, len, 1, stderr);
00082     } else if (_out) {
00083         return _out->write(buf, len);
00084     } else
00085         assert(0);
00086     return 0;
00087 }
00088 
00089 void LuaFile::seek(int32 pos, int whence) {
00090     if (_stdin) {
00091         fseek(stdin, pos, whence);
00092     } else if (_in) {
00093         _in->seek(pos, whence);
00094     } else
00095         assert(0);
00096 }
00097 
00098 static int32 gettag(int32 i) {
00099     return (int32)lua_getnumber(lua_getparam(i));
00100 }
00101 
00102 static void pushresult(int32 i) {
00103     if (i)
00104         lua_pushuserdata(0);
00105     else {
00106         lua_pushnil();
00107         lua_pushstring("File I/O error.");
00108     }
00109 }
00110 
00111 static int32 ishandler(lua_Object f) {
00112     if (lua_isuserdata(f)) {
00113         if (lua_tag(f) == gettag(CLOSEDTAG))
00114             lua_error("cannot access a closed file");
00115         return lua_tag(f) == gettag(IOTAG);
00116     }
00117     else return 0;
00118 }
00119 
00120 static LuaFile *getfile(int32 id) {
00121     if (g_files->contains(id)) {
00122         return (*g_files)[id];
00123     }
00124 
00125     return nullptr;
00126 }
00127 
00128 static LuaFile *getfile(const char *name) {
00129     lua_Object f = lua_getglobal(name);
00130     if (!ishandler(f))
00131         luaL_verror("global variable `%.50s' is not a file handle", name);
00132     return getfile(lua_getuserdata(f));
00133 }
00134 
00135 static LuaFile *getfileparam(const char *name, int32 *arg) {
00136     lua_Object f = lua_getparam(*arg);
00137     if (ishandler(f)) {
00138         (*arg)++;
00139         return getfile(lua_getuserdata(f));
00140     } else
00141         return getfile(name);
00142 }
00143 
00144 static void closefile(const char *name) {
00145     LuaFile *f = getfile(name);
00146     f->close();
00147     lua_pushobject(lua_getglobal(name));
00148     lua_settag(gettag(CLOSEDTAG));
00149 }
00150 
00151 static void setfile(int32 id, const char *name, int32 tag) {
00152     lua_pushusertag(id, tag);
00153     lua_setglobal(name);
00154 }
00155 
00156 static void setreturn(int32 id, const char *name) {
00157     int32 tag = gettag(IOTAG);
00158     setfile(id, name, tag);
00159     lua_pushusertag(id, tag);
00160 }
00161 
00162 static int32 addfile(LuaFile *f) {
00163     ++s_id;
00164     (*g_files)[s_id] = f;
00165 
00166     return s_id;
00167 }
00168 
00169 static void io_readfrom() {
00170     lua_Object f = lua_getparam(FIRSTARG);
00171     if (f == LUA_NOOBJECT) {
00172         if (getfile(FINPUT) != getfile(1)) {
00173             closefile(FINPUT);
00174             setreturn(1, FINPUT);
00175         }
00176     } else if (lua_tag(f) == gettag(IOTAG)) {
00177         int32 id = lua_getuserdata(f);
00178         LuaFile *current = getfile(id);
00179         if (!current) {
00180             pushresult(0);
00181             return;
00182         }
00183         setreturn(id, FINPUT);
00184     } else {
00185         const char *s = luaL_check_string(FIRSTARG);
00186         Common::String fileName = Common::lastPathComponent(s, '\\');
00187         Common::SeekableReadStream *inFile = nullptr;
00188         Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
00189         inFile = saveFileMan->openForLoading(fileName);
00190         if (!inFile) {
00191             inFile = g_resourceloader->openNewStreamFile(s);
00192         }
00193         if (inFile) {
00194             LuaFile *current = new LuaFile();
00195             current->_in = inFile;
00196             current->_filename = s;
00197             setreturn(addfile(current), FINPUT);
00198         } else {
00199             warning("liolib.cpp, io_readfrom(): Could not open file %s", s);
00200             pushresult(0);
00201         }
00202     }
00203 }
00204 
00205 static void io_writeto() {
00206     lua_Object f = lua_getparam(FIRSTARG);
00207     if (f == LUA_NOOBJECT) {
00208         if (getfile(FOUTPUT) != getfile(2)) {
00209             closefile(FOUTPUT);
00210             setreturn(2, FOUTPUT);
00211         }
00212     } else if (lua_tag(f) == gettag(IOTAG)) {
00213         int32 id = lua_getuserdata(f);
00214         LuaFile *current = getfile(id);
00215         if (!current->isOpen()) {
00216             pushresult(0);
00217             return;
00218         }
00219         setreturn(id, FOUTPUT);
00220     } else {
00221         Common::String s = Common::lastPathComponent(luaL_check_string(FIRSTARG), '\\');
00222         LuaFile *current;
00223         Common::WriteStream *outFile = nullptr;
00224         Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
00225         outFile = saveFileMan->openForSaving(s, false);
00226         if (!outFile) {
00227             pushresult(0);
00228             return;
00229         }
00230         current = new LuaFile();
00231         current->_out = outFile;
00232         current->_filename = s;
00233         setreturn(addfile(current), FOUTPUT);
00234     }
00235 }
00236 
00237 static void io_appendto() {
00238     Common::String s = Common::lastPathComponent(luaL_check_string(FIRSTARG), '\\');
00239     Common::SeekableReadStream *inFile = nullptr;
00240     Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
00241     inFile = saveFileMan->openForLoading(s);
00242     if (!inFile) {
00243         pushresult(0);
00244         return;
00245     }
00246     int size = inFile->size();
00247     byte *buf = new byte[size];
00248     inFile->read(buf, size);
00249     delete inFile;
00250 
00251     Common::WriteStream *outFile = nullptr;
00252     outFile = saveFileMan->openForSaving(s);
00253     if (!outFile)
00254         pushresult(0);
00255     else {
00256         outFile->write(buf, size);
00257         LuaFile *current = new LuaFile();
00258         current->_out = outFile;
00259         current->_filename = s;
00260         setreturn(addfile(current), FOUTPUT);
00261     }
00262     delete[] buf;
00263 }
00264 
00265 #define NEED_OTHER (EOF - 1)  // just some flag different from EOF
00266 
00267 static void io_read() {
00268     int32 arg = FIRSTARG;
00269     LuaFile *f = (LuaFile *)getfileparam(FINPUT, &arg);
00270     char *buff;
00271     const char *p = luaL_opt_string(arg, "[^\n]*{\n}");
00272     int inskip = 0;  // to control {skips}
00273     int c = NEED_OTHER;
00274     luaL_resetbuffer();
00275     while (*p) {
00276         if (*p == '{') {
00277             inskip++;
00278             p++;
00279         } else if (*p == '}') {
00280             if (inskip == 0)
00281                 lua_error("unbalanced braces in read pattern");
00282             inskip--;
00283             p++;
00284         } else {
00285             const char *ep;  // get what is next
00286             int m;  // match result
00287             if (c == NEED_OTHER) {
00288                 char z;
00289                 if (f->read(&z, 1) != 1)
00290                     c = EOF;
00291                 else
00292                     c = z;
00293             }
00294             m = luaI_singlematch((c == EOF) ? 0 : (char)c, p, &ep);
00295             if (m) {
00296                 if (inskip == 0)
00297                     luaL_addchar(c);
00298                 c = NEED_OTHER;
00299             }
00300             switch (*ep) {
00301             case '*':  // repetition
00302                 if (!m)
00303                     p = ep + 1;  // else stay in (repeat) the same item
00304                 break;
00305             case '?':  // optional
00306                 p = ep + 1;  // continues reading the pattern
00307                 break;
00308             default:
00309                 if (m)
00310                     p = ep;  // continues reading the pattern
00311                 else
00312                     goto break_while;   // pattern fails
00313             }
00314         }
00315     }
00316 break_while:
00317     if (c >= 0) // not EOF nor NEED_OTHER?
00318         f->seek(-1, SEEK_CUR);
00319     luaL_addchar(0);
00320     buff = luaL_buffer();
00321     if (*buff != 0 || *p == 0)  // read something or did not fail?
00322         lua_pushstring(buff);
00323 }
00324 
00325 static void io_write() {
00326     int32 arg = FIRSTARG;
00327     LuaFile *f = (LuaFile *)getfileparam(FOUTPUT, &arg);
00328     int32 status = 1;
00329     const char *s;
00330     while ((s = luaL_opt_string(arg++, nullptr)) != nullptr)
00331         status = status && ((int32)f->write(s, strlen(s)) != EOF);
00332     pushresult(status);
00333 }
00334 
00335 static void io_date() {
00336     TimeDate t;
00337     char b[BUFSIZ];
00338 
00339     g_system->getTimeAndDate(t);
00340     sprintf(b, "%02d.%02d.%d %02d:%02d.%02d", t.tm_mday, t.tm_mon + 1, 1900 + t.tm_year, t.tm_hour, t.tm_min, t.tm_sec);
00341     lua_pushstring(b);
00342 }
00343 
00344 static void io_exit() {
00345     lua_Object o = lua_getparam(1);
00346     exit((int)lua_isnumber(o) ? (int)lua_getnumber(o) : 1);
00347 }
00348 
00349 static void lua_printstack() {
00350     int32 level = 1;  // skip level 0 (it's this function)
00351     lua_Object func;
00352     char buf[256];
00353     while ((func = lua_stackedfunction(level++)) != LUA_NOOBJECT) {
00354         const char *name;
00355         int32 currentline;
00356         const char *filename;
00357         int32 linedefined;
00358         lua_funcinfo(func, &filename, &linedefined);
00359         sprintf(buf, (level == 2) ? "Active Stack:\n\t" : "\t");
00360         g_stderr->write(buf, strlen(buf));
00361         switch (*lua_getobjname(func, &name)) {
00362         case 'g':
00363             sprintf(buf, "function %s", name);
00364             break;
00365         case 't':
00366             sprintf(buf, "`%s' tag method", name);
00367             break;
00368         default:
00369             {
00370                 if (linedefined == 0)
00371                     sprintf(buf, "main of %s", filename);
00372                 else if (linedefined < 0)
00373                     sprintf(buf, "%s", filename);
00374                 else
00375                     sprintf(buf, "function (%s:%d)", filename, (int)linedefined);
00376                 filename = nullptr;
00377             }
00378         }
00379         g_stderr->write(buf, strlen(buf));
00380 
00381         if ((currentline = lua_currentline(func)) > 0) {
00382             sprintf(buf, " at line %d", (int)currentline);
00383             g_stderr->write(buf, strlen(buf));
00384         }
00385         if (filename) {
00386             sprintf(buf, " [in file %s]", filename);
00387             g_stderr->write(buf, strlen(buf));
00388         }
00389         sprintf(buf, "\n");
00390         g_stderr->write(buf, strlen(buf));
00391     }
00392 }
00393 
00394 static void errorfb() {
00395     char buf[256];
00396     sprintf(buf, "lua: %s\n", lua_getstring(lua_getparam(1)));
00397     g_stderr->write(buf, strlen(buf));
00398     lua_printstack();
00399 }
00400 
00401 static struct luaL_reg iolib[] = {
00402     { "date",           io_date },
00403     { "exit",           io_exit },
00404     { "print_stack",    errorfb }
00405 };
00406 
00407 static struct luaL_reg iolibtag[] = {
00408     { "readfrom",   io_readfrom },
00409     { "writeto",    io_writeto },
00410     { "appendto",   io_appendto },
00411     { "read",       io_read },
00412     { "write",      io_write }
00413 };
00414 
00415 static void openwithtags() {
00416     int32 iotag = lua_newtag();
00417     int32 closedtag = lua_newtag();
00418     uint32 i;
00419     for (i = 0; i < sizeof(iolibtag) / sizeof(iolibtag[0]); i++) {
00420         // put both tags as upvalues for these functions
00421         lua_pushnumber(iotag);
00422         lua_pushnumber(closedtag);
00423         lua_pushCclosure(iolibtag[i].func, 2);
00424         lua_setglobal(iolibtag[i].name);
00425     }
00426 
00427     LuaFile* f;
00428     f = new LuaFile();
00429     f->_stdin = true;
00430     setfile(addfile(f), FINPUT, iotag);
00431 
00432     f = new LuaFile();
00433     f->_stdout = true;
00434     setfile(addfile(f), FOUTPUT, iotag);
00435 
00436     f = new LuaFile();
00437     f->_stdin = true;
00438     setfile(addfile(f), "_STDIN", iotag);
00439 
00440     f = new LuaFile();
00441     f->_stdout = true;
00442     setfile(addfile(f), "_STDOUT", iotag);
00443 
00444     g_stderr = new LuaFile();
00445     g_stderr->_stderr = true;
00446     setfile(addfile(g_stderr), "_STDERR", iotag);
00447 }
00448 
00449 void lua_iolibopen() {
00450     g_files = new Common::HashMap<int32, LuaFile *>();
00451 
00452     luaL_openlib(iolib, (sizeof(iolib) / sizeof(iolib[0])));
00453     luaL_addlibtolist(iolibtag, (sizeof(iolibtag) / sizeof(iolibtag[0])));
00454     openwithtags();
00455     lua_pushcfunction(errorfb);
00456     lua_seterrormethod();
00457 }
00458 
00459 void lua_iolibclose() {
00460     for (Common::HashMap<int32, LuaFile *>::iterator it = g_files->begin(); it != g_files->end(); ++it) {
00461         delete it->_value;
00462     }
00463     delete g_files;
00464 }
00465 
00466 } // end of namespace Grim


Generated on Sat Oct 19 2019 05:01:04 for ResidualVM by doxygen 1.7.1
curved edge   curved edge