beta-0.89.2
[luatex.git] / source / texk / web2c / luatexdir / lua / liolibext.c
blob630562228d95f77d643b3fe97fc5454b29c3a3ea
1 /* liolibext.c
3 Copyright 2014 Taco Hoekwater <taco@luatex.org>
5 This file is part of LuaTeX.
7 LuaTeX is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2 of the License, or (at your
10 option) any later version.
12 LuaTeX is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 License for more details.
17 You should have received a copy of the GNU General Public License along
18 with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
20 /* This extension only replaces one function: io.popen() and two
21 metatable entries: f:read() and f:lines() but unfortunately
22 it has to copy quite a bunch of lines from the original liolib.c
23 to do so.
26 #include "ptexlib.h"
27 #include "lua/luatex-api.h"
30 #ifdef LuajitTeX
31 #include "lua/lauxlib_bridge.h"
32 #else
33 #include "lauxlib.h"
34 #endif
35 #include "lualib.h"
40 ** {======================================================
41 ** lua_popen spawns a new process connected to the current
42 ** one through the file streams.
43 ** =======================================================
46 #if defined(_WIN32)
48 #ifdef _MSC_VER
49 #define lua_popen(L,c,m) ((void)L, win32_popen(c,m))
50 #define lua_pclose(L,file) ((void)L, win32_pclose(file))
51 #else
52 #define lua_popen(L,c,m) ((void)L, _popen(c,m))
53 #define lua_pclose(L,file) ((void)L, _pclose(file))
54 #endif
56 #else
58 #define lua_popen(L,c,m) ((void)L, fflush(NULL), popen(c,m))
59 #define lua_pclose(L,file) ((void)L, pclose(file))
61 #endif
63 /* }====================================================== */
66 #if defined(LUA_USE_POSIX)
68 #define l_fseek(f,o,w) fseeko(f,o,w)
69 #define l_ftell(f) ftello(f)
70 #define l_seeknum off_t
72 #elif defined(LUA_WIN) && !defined(_CRTIMP_TYPEINFO) \
73 && defined(_MSC_VER) && (_MSC_VER >= 1400)
74 /* Windows (but not DDK) and Visual C++ 2005 or higher */
76 #define l_fseek(f,o,w) _fseeki64(f,o,w)
77 #define l_ftell(f) _ftelli64(f)
78 #define l_seeknum __int64
80 #elif defined(__MINGW32__)
82 #define l_fseek(f,o,w) fseeko64(f,o,w)
83 #define l_ftell(f) ftello64(f)
84 #define l_seeknum int64_t
86 #else
88 #define l_fseek(f,o,w) fseek(f,o,w)
89 #define l_ftell(f) ftell(f)
90 #define l_seeknum long
92 #endif
94 #define IO_PREFIX "_IO_"
95 #define IO_INPUT (IO_PREFIX "input")
96 #define IO_OUTPUT (IO_PREFIX "output")
98 typedef luaL_Stream LStream;
100 #define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE))
102 #define isclosed(p) ((p)->closef == NULL)
104 static int io_type (lua_State *L) {
105 LStream *p;
106 luaL_checkany(L, 1);
107 p = (LStream *)luaL_testudata(L, 1, LUA_FILEHANDLE);
108 if (p == NULL)
109 lua_pushnil(L); /* not a file */
110 else if (isclosed(p))
111 lua_pushliteral(L, "closed file");
112 else
113 lua_pushliteral(L, "file");
114 return 1;
118 static int f_tostring (lua_State *L) {
119 LStream *p = tolstream(L);
120 if (isclosed(p))
121 lua_pushliteral(L, "file (closed)");
122 else
123 lua_pushfstring(L, "file (%p)", p->f);
124 return 1;
127 static FILE *tofile (lua_State *L) {
128 LStream *p = tolstream(L);
129 if (isclosed(p))
130 luaL_error(L, "attempt to use a closed file");
131 lua_assert(p->f);
132 return p->f;
137 ** When creating file handles, always creates a `closed' file handle
138 ** before opening the actual file; so, if there is a memory error, the
139 ** file is not left opened.
141 static LStream *newprefile (lua_State *L) {
142 LStream *p = (LStream *)lua_newuserdata(L, sizeof(LStream));
143 p->closef = NULL; /* mark file handle as 'closed' */
144 luaL_setmetatable(L, LUA_FILEHANDLE);
145 return p;
149 static int aux_close (lua_State *L) {
150 LStream *p = tolstream(L);
151 lua_CFunction cf = p->closef;
152 p->closef = NULL; /* mark stream as closed */
153 return (*cf)(L); /* close it */
157 static int io_close (lua_State *L) {
158 if (lua_isnone(L, 1)) /* no argument? */
159 lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use standard output */
160 tofile(L); /* make sure argument is an open stream */
161 return aux_close(L);
164 static int f_gc (lua_State *L) {
165 LStream *p = tolstream(L);
166 if (!isclosed(p) && p->f != NULL)
167 aux_close(L); /* ignore closed and incompletely open files */
168 return 0;
173 ** function to close regular files
175 static int io_fclose (lua_State *L) {
176 LStream *p = tolstream(L);
177 int res = fclose(p->f);
178 return luaL_fileresult(L, (res == 0), NULL);
182 static LStream *newfile (lua_State *L) {
183 LStream *p = newprefile(L);
184 p->f = NULL;
185 p->closef = &io_fclose;
186 return p;
190 static void opencheck (lua_State *L, const char *fname, const char *mode) {
191 LStream *p = newfile(L);
192 p->f = fopen(fname, mode);
193 if (p->f == NULL) {
194 luaL_error(L, "cannot open file " LUA_QS " (%s)", fname, strerror(errno));
195 } else {
196 #ifdef WIN32
197 _setmode (fileno (p->f), _O_BINARY);
198 #endif
199 if (mode[0]=='r')
200 recorder_record_input(fname);
201 else
202 recorder_record_output(fname);
207 static int io_open (lua_State *L) {
208 const char *filename = luaL_checkstring(L, 1);
209 const char *mode = luaL_optstring(L, 2, "r");
210 LStream *p = newfile(L);
211 int i = 0;
212 /* check whether 'mode' matches '[rwa]%+?b?' */
213 if (!(mode[i] != '\0' && strchr("rwa", mode[i++]) != NULL &&
214 (mode[i] != '+' || ++i) && /* skip if char is '+' */
215 (mode[i] != 'b' || ++i) && /* skip if char is 'b' */
216 (mode[i] == '\0')))
217 return luaL_error(L, "invalid mode " LUA_QS
218 " (should match " LUA_QL("[rwa]%%+?b?") ")", mode);
219 p->f = fopen(filename, mode);
220 if (p->f == NULL) {
221 return luaL_fileresult(L, 0, filename) ;
222 } else {
223 #ifdef WIN32
224 _setmode (fileno (p->f), _O_BINARY);
225 #endif
226 if (mode[0]=='r')
227 recorder_record_input(filename);
228 else
229 recorder_record_output(filename);
230 return 1;
235 ** function to close 'popen' files
237 static int io_pclose (lua_State *L) {
238 LStream *p = tolstream(L);
239 return luaL_execresult(L, lua_pclose(L, p->f));
242 static int io_popen(lua_State * L)
244 int ret = 1;
245 char *safecmd = NULL;
246 char *cmdname = NULL;
247 int allow = 0;
248 const char *filename = luaL_checkstring(L, 1);
249 const char *mode = luaL_optstring(L, 2, "r");
250 LStream *p = newprefile(L);
252 if (shellenabledp <= 0) {
253 lua_pushnil(L);
254 lua_pushliteral(L, "All command execution is disabled");
255 return 2;
257 /* If restrictedshell == 0, any command is allowed. */
258 if (restrictedshell == 0)
259 allow = 1;
260 else
261 allow = shell_cmd_is_allowed(filename, &safecmd, &cmdname);
263 if (allow == 1) {
264 p->f = lua_popen(L, filename, mode);
265 p->closef = &io_pclose;
266 ret = (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
267 } else if (allow == 2) {
268 p->f = lua_popen(L, safecmd, mode);
269 p->closef = &io_pclose;
270 ret = (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
271 } else if (allow == 0) {
272 lua_pushnil(L);
273 lua_pushliteral(L, "Command execution disabled via shell_escape='p'");
274 ret = 2;
275 } else {
276 lua_pushnil(L);
277 lua_pushliteral(L, "Bad command line quoting");
278 ret = 2;
280 return ret;
284 static int io_tmpfile (lua_State *L) {
285 LStream *p = newfile(L);
286 p->f = tmpfile();
287 return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1;
291 static FILE *getiofile (lua_State *L, const char *findex) {
292 LStream *p;
293 lua_getfield(L, LUA_REGISTRYINDEX, findex);
294 p = (LStream *)lua_touserdata(L, -1);
295 if (isclosed(p))
296 luaL_error(L, "standard %s file is closed", findex + strlen(IO_PREFIX));
297 return p->f;
301 static int g_iofile (lua_State *L, const char *f, const char *mode) {
302 if (!lua_isnoneornil(L, 1)) {
303 const char *filename = lua_tostring(L, 1);
304 if (filename)
305 opencheck(L, filename, mode);
306 else {
307 tofile(L); /* check that it's a valid file handle */
308 lua_pushvalue(L, 1);
310 lua_setfield(L, LUA_REGISTRYINDEX, f);
312 /* return current value */
313 lua_getfield(L, LUA_REGISTRYINDEX, f);
314 return 1;
318 static int io_input (lua_State *L) {
319 return g_iofile(L, IO_INPUT, "r");
323 static int io_output (lua_State *L) {
324 return g_iofile(L, IO_OUTPUT, "w");
328 static int io_readline (lua_State *L);
331 static void aux_lines (lua_State *L, int toclose) {
332 int i;
333 int n = lua_gettop(L) - 1; /* number of arguments to read */
334 /* ensure that arguments will fit here and into 'io_readline' stack */
335 luaL_argcheck(L, n <= LUA_MINSTACK - 3, LUA_MINSTACK - 3, "too many options");
336 lua_pushvalue(L, 1); /* file handle */
337 lua_pushinteger(L, n); /* number of arguments to read */
338 lua_pushboolean(L, toclose); /* close/not close file when finished */
339 for (i = 1; i <= n; i++) lua_pushvalue(L, i + 1); /* copy arguments */
340 lua_pushcclosure(L, io_readline, 3 + n);
344 static int f_lines (lua_State *L) {
345 tofile(L); /* check that it's a valid file handle */
346 aux_lines(L, 0);
347 return 1;
351 static int io_lines (lua_State *L) {
352 int toclose;
353 if (lua_isnone(L, 1)) lua_pushnil(L); /* at least one argument */
354 if (lua_isnil(L, 1)) { /* no file name? */
355 lua_getfield(L, LUA_REGISTRYINDEX, IO_INPUT); /* get default input */
356 lua_replace(L, 1); /* put it at index 1 */
357 tofile(L); /* check that it's a valid file handle */
358 toclose = 0; /* do not close it after iteration */
360 else { /* open a new file */
361 const char *filename = luaL_checkstring(L, 1);
362 opencheck(L, filename, "r");
363 lua_replace(L, 1); /* put file at index 1 */
364 toclose = 1; /* close it after iteration */
366 aux_lines(L, toclose);
367 return 1;
371 ** {======================================================
372 ** READ
373 ** =======================================================
377 static int read_number (lua_State *L, FILE *f) {
378 lua_Number d;
379 if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) {
380 lua_pushnumber(L, d); /* integer or float */
381 return 1;
383 else {
384 lua_pushnil(L); /* "result" to be removed */
385 return 0; /* read fails */
390 static int test_eof (lua_State *L, FILE *f) {
391 int c = getc(f);
392 ungetc(c, f);
393 lua_pushlstring(L, NULL, 0);
394 return (c != EOF);
397 /* this version does not care wether the file has
398 line endings using an 'alien' convention */
400 static int read_line(lua_State * L, FILE * f, int chop)
402 luaL_Buffer buf;
403 int c, d;
404 luaL_buffinit(L, &buf);
405 while (1) {
406 c = fgetc(f);
407 if (c == EOF) {
408 luaL_pushresult(&buf); /* close buffer */
409 return (lua_rawlen(L, -1) > 0); /* check whether read something */
411 if (c == '\n') {
412 if (!chop)
413 luaL_addchar(&buf, c);
414 break;
415 } else if (c == '\r') {
416 if (!chop)
417 luaL_addchar(&buf, c);
418 d = fgetc(f);
419 if (d != EOF && d != '\n') {
420 ungetc(d, f);
421 } else if (!chop) {
422 luaL_addchar(&buf, d);
424 break;
425 } else {
426 luaL_addchar(&buf, c);
429 luaL_pushresult(&buf); /* close buffer */
430 return 1;
433 #define MAX_SIZE_T (~(size_t)0)
435 static void read_all (lua_State *L, FILE *f) {
436 size_t rlen = LUAL_BUFFERSIZE; /* how much to read in each cycle */
437 l_seeknum old, nrlen = 0; /* for testing file size */
438 luaL_Buffer b;
439 luaL_buffinit(L, &b);
440 /* speed up loading of not too large files: */
441 old = l_ftell(f);
442 if ((l_fseek(f, 0, SEEK_END) >= 0) &&
443 ((nrlen = l_ftell(f)) > 0) && nrlen < 1000 * 1000 * 100) {
444 rlen = nrlen;
446 l_fseek(f, old, SEEK_SET);
447 for (;;) {
448 char *p = luaL_prepbuffsize(&b, rlen);
449 size_t nr = fread(p, sizeof(char), rlen, f);
450 luaL_addsize(&b, nr);
451 if (nr < rlen) break; /* eof? */
452 else if (rlen <= (MAX_SIZE_T / 4)) /* avoid buffers too large */
453 rlen *= 2; /* double buffer size at each iteration */
455 luaL_pushresult(&b); /* close buffer */
459 static int read_chars (lua_State *L, FILE *f, size_t n) {
460 size_t nr; /* number of chars actually read */
461 char *p;
462 luaL_Buffer b;
463 luaL_buffinit(L, &b);
464 p = luaL_prepbuffsize(&b, n); /* prepare buffer to read whole block */
465 nr = fread(p, sizeof(char), n, f); /* try to read 'n' chars */
466 luaL_addsize(&b, nr);
467 luaL_pushresult(&b); /* close buffer */
468 return (nr > 0); /* true iff read something */
472 static int g_read (lua_State *L, FILE *f, int first) {
473 int nargs = lua_gettop(L) - 1;
474 int success;
475 int n;
476 clearerr(f);
477 if (nargs == 0) { /* no arguments? */
478 success = read_line(L, f, 1);
479 n = first+1; /* to return 1 result */
481 else { /* ensure stack space for all results and for auxlib's buffer */
482 luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
483 success = 1;
484 for (n = first; nargs-- && success; n++) {
485 if (lua_type(L, n) == LUA_TNUMBER) {
486 size_t l = (size_t)lua_tointeger(L, n);
487 success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
489 else {
490 const char *p = lua_tostring(L, n);
491 luaL_argcheck(L, p && p[0] == '*', n, "invalid option");
492 switch (p[1]) {
493 case 'n': /* number */
494 success = read_number(L, f);
495 break;
496 case 'l': /* line */
497 success = read_line(L, f, 1);
498 break;
499 case 'L': /* line with end-of-line */
500 success = read_line(L, f, 0);
501 break;
502 case 'a': /* file */
503 read_all(L, f); /* read entire file */
504 success = 1; /* always success */
505 break;
506 default:
507 return luaL_argerror(L, n, "invalid format");
512 if (ferror(f))
513 return luaL_fileresult(L, 0, NULL);
514 if (!success) {
515 lua_pop(L, 1); /* remove last result */
516 lua_pushnil(L); /* push nil instead */
518 return n - first;
522 static int io_read (lua_State *L) {
523 return g_read(L, getiofile(L, IO_INPUT), 1);
526 static int f_read (lua_State *L) {
527 return g_read(L, tofile(L), 2);
531 static int io_readline (lua_State *L) {
532 LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1));
533 int i;
534 int n = (int)lua_tointeger(L, lua_upvalueindex(2));
535 if (isclosed(p)) /* file is already closed? */
536 return luaL_error(L, "file is already closed");
537 lua_settop(L , 1);
538 for (i = 1; i <= n; i++) /* push arguments to 'g_read' */
539 lua_pushvalue(L, lua_upvalueindex(3 + i));
540 n = g_read(L, p->f, 2); /* 'n' is number of results */
541 lua_assert(n > 0); /* should return at least a nil */
542 if (!lua_isnil(L, -n)) /* read at least one value? */
543 return n; /* return them */
544 else { /* first result is nil: EOF or error */
545 if (n > 1) { /* is there error information? */
546 /* 2nd result is error message */
547 return luaL_error(L, "%s", lua_tostring(L, -n + 1));
549 if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */
550 lua_settop(L, 0);
551 lua_pushvalue(L, lua_upvalueindex(1));
552 aux_close(L); /* close it */
554 return 0;
558 /* }====================================================== */
561 static int g_write (lua_State *L, FILE *f, int arg) {
562 int nargs = lua_gettop(L) - arg;
563 int status = 1;
564 for (; nargs--; arg++) {
565 if (lua_type(L, arg) == LUA_TNUMBER) {
566 /* optimization: could be done exactly as for strings */
567 status = status &&
568 fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0;
570 else {
571 size_t l;
572 const char *s = luaL_checklstring(L, arg, &l);
573 status = status && (fwrite(s, sizeof(char), l, f) == l);
576 if (status) return 1; /* file handle already on stack top */
577 else return luaL_fileresult(L, status, NULL);
581 static int io_write (lua_State *L) {
582 return g_write(L, getiofile(L, IO_OUTPUT), 1);
586 static int f_write (lua_State *L) {
587 FILE *f = tofile(L);
588 lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */
589 return g_write(L, f, 2);
593 static int f_seek (lua_State *L) {
594 static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
595 static const char *const modenames[] = {"set", "cur", "end", NULL};
596 FILE *f = tofile(L);
597 int op = luaL_checkoption(L, 2, "cur", modenames);
598 lua_Number p3 = luaL_optnumber(L, 3, 0);
599 l_seeknum offset = (l_seeknum)p3;
600 luaL_argcheck(L, (lua_Number)offset == p3, 3,
601 "not an integer in proper range");
602 op = l_fseek(f, offset, mode[op]);
603 if (op)
604 return luaL_fileresult(L, 0, NULL); /* error */
605 else {
606 lua_pushinteger(L, (lua_Number)l_ftell(f));
607 return 1;
612 static int f_setvbuf (lua_State *L) {
613 int res;
614 static const int mode[] = {_IONBF, _IOFBF, _IOLBF};
615 static const char *const modenames[] = {"no", "full", "line", NULL};
616 FILE *f = tofile(L);
617 int op = luaL_checkoption(L, 2, NULL, modenames);
618 lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
619 if(sz == 0)
620 res = setvbuf(f, NULL, _IONBF, sz);
621 else
622 res = setvbuf(f, NULL, mode[op], sz);
623 return luaL_fileresult(L, res == 0, NULL);
628 static int io_flush (lua_State *L) {
629 return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL);
633 static int f_flush (lua_State *L) {
634 return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL);
639 ** functions for 'io' library
641 static const luaL_Reg iolib[] = {
642 {"close", io_close},
643 {"flush", io_flush},
644 {"input", io_input},
645 {"lines", io_lines},
646 {"open", io_open},
647 {"output", io_output},
648 {"popen", io_popen},
649 {"read", io_read},
650 {"tmpfile", io_tmpfile},
651 {"type", io_type},
652 {"write", io_write},
653 {NULL, NULL}
658 ** methods for file handles
660 static const luaL_Reg flib[] = {
661 {"close", io_close},
662 {"flush", f_flush},
663 {"lines", f_lines},
664 {"read", f_read},
665 {"seek", f_seek},
666 {"setvbuf", f_setvbuf},
667 {"write", f_write},
668 {"__gc", f_gc},
669 {"__tostring", f_tostring},
670 {NULL, NULL}
674 #ifndef LuajitTeX
675 static void createmeta (lua_State *L) {
676 luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */
677 lua_pushvalue(L, -1); /* push metatable */
678 lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
679 luaL_setfuncs(L, flib, 0); /* add file methods to new metatable */
680 lua_pop(L, 1); /* pop new metatable */
685 ** function to (not) close the standard files stdin, stdout, and stderr
687 static int io_noclose (lua_State *L) {
688 LStream *p = tolstream(L);
689 p->closef = &io_noclose; /* keep file opened */
690 lua_pushnil(L);
691 lua_pushliteral(L, "cannot close standard file");
692 return 2;
696 static void createstdfile (lua_State *L, FILE *f, const char *k,
697 const char *fname) {
698 LStream *p = newprefile(L);
699 p->f = f;
700 p->closef = &io_noclose;
701 if (k != NULL) {
702 lua_pushvalue(L, -1);
703 lua_setfield(L, LUA_REGISTRYINDEX, k); /* add file to registry */
705 lua_setfield(L, -2, fname); /* add file to module */
707 #endif
709 int open_iolibext (lua_State *L) {
710 #ifdef LuajitTeX
711 return luaopen_io(L);
712 #else
713 luaL_newlib(L, iolib); /* new module */
714 createmeta(L);
715 /* create (and set) default files */
716 createstdfile(L, stdin, IO_INPUT, "stdin");
717 createstdfile(L, stdout, IO_OUTPUT, "stdout");
718 createstdfile(L, stderr, NULL, "stderr");
719 return 1;
720 #endif