1 /* $NetBSD: liolib.c,v 1.1.1.2 2012/03/15 00:08:04 alnsn Exp $ */ 2 3 /* 4 ** $Id: liolib.c,v 1.1.1.2 2012/03/15 00:08:04 alnsn Exp $ 5 ** Standard I/O (and system) library 6 ** See Copyright Notice in lua.h 7 */ 8 9 10 #include <errno.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 15 #define liolib_c 16 #define LUA_LIB 17 18 #include "lua.h" 19 20 #include "lauxlib.h" 21 #include "lualib.h" 22 23 24 25 #define IO_INPUT 1 26 #define IO_OUTPUT 2 27 28 29 static const char *const fnames[] = {"input", "output"}; 30 31 32 static int pushresult (lua_State *L, int i, const char *filename) { 33 int en = errno; /* calls to Lua API may change this value */ 34 if (i) { 35 lua_pushboolean(L, 1); 36 return 1; 37 } 38 else { 39 lua_pushnil(L); 40 if (filename) 41 lua_pushfstring(L, "%s: %s", filename, strerror(en)); 42 else 43 lua_pushfstring(L, "%s", strerror(en)); 44 lua_pushinteger(L, en); 45 return 3; 46 } 47 } 48 49 50 static void fileerror (lua_State *L, int arg, const char *filename) { 51 lua_pushfstring(L, "%s: %s", filename, strerror(errno)); 52 luaL_argerror(L, arg, lua_tostring(L, -1)); 53 } 54 55 56 #define tofilep(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE)) 57 58 59 static int io_type (lua_State *L) { 60 void *ud; 61 luaL_checkany(L, 1); 62 ud = lua_touserdata(L, 1); 63 lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE); 64 if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1)) 65 lua_pushnil(L); /* not a file */ 66 else if (*((FILE **)ud) == NULL) 67 lua_pushliteral(L, "closed file"); 68 else 69 lua_pushliteral(L, "file"); 70 return 1; 71 } 72 73 74 static FILE *tofile (lua_State *L) { 75 FILE **f = tofilep(L); 76 if (*f == NULL) 77 luaL_error(L, "attempt to use a closed file"); 78 return *f; 79 } 80 81 82 83 /* 84 ** When creating file handles, always creates a `closed' file handle 85 ** before opening the actual file; so, if there is a memory error, the 86 ** file is not left opened. 87 */ 88 static FILE **newfile (lua_State *L) { 89 FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *)); 90 *pf = NULL; /* file handle is currently `closed' */ 91 luaL_getmetatable(L, LUA_FILEHANDLE); 92 lua_setmetatable(L, -2); 93 return pf; 94 } 95 96 97 /* 98 ** function to (not) close the standard files stdin, stdout, and stderr 99 */ 100 static int io_noclose (lua_State *L) { 101 lua_pushnil(L); 102 lua_pushliteral(L, "cannot close standard file"); 103 return 2; 104 } 105 106 107 /* 108 ** function to close 'popen' files 109 */ 110 static int io_pclose (lua_State *L) { 111 FILE **p = tofilep(L); 112 int ok = lua_pclose(L, *p); 113 *p = NULL; 114 return pushresult(L, ok, NULL); 115 } 116 117 118 /* 119 ** function to close regular files 120 */ 121 static int io_fclose (lua_State *L) { 122 FILE **p = tofilep(L); 123 int ok = (fclose(*p) == 0); 124 *p = NULL; 125 return pushresult(L, ok, NULL); 126 } 127 128 129 static int aux_close (lua_State *L) { 130 lua_getfenv(L, 1); 131 lua_getfield(L, -1, "__close"); 132 return (lua_tocfunction(L, -1))(L); 133 } 134 135 136 static int io_close (lua_State *L) { 137 if (lua_isnone(L, 1)) 138 lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT); 139 tofile(L); /* make sure argument is a file */ 140 return aux_close(L); 141 } 142 143 144 static int io_gc (lua_State *L) { 145 FILE *f = *tofilep(L); 146 /* ignore closed files */ 147 if (f != NULL) 148 aux_close(L); 149 return 0; 150 } 151 152 153 static int io_tostring (lua_State *L) { 154 FILE *f = *tofilep(L); 155 if (f == NULL) 156 lua_pushliteral(L, "file (closed)"); 157 else 158 lua_pushfstring(L, "file (%p)", f); 159 return 1; 160 } 161 162 163 static int io_open (lua_State *L) { 164 const char *filename = luaL_checkstring(L, 1); 165 const char *mode = luaL_optstring(L, 2, "r"); 166 FILE **pf = newfile(L); 167 *pf = fopen(filename, mode); 168 return (*pf == NULL) ? pushresult(L, 0, filename) : 1; 169 } 170 171 172 /* 173 ** this function has a separated environment, which defines the 174 ** correct __close for 'popen' files 175 */ 176 static int io_popen (lua_State *L) { 177 const char *filename = luaL_checkstring(L, 1); 178 const char *mode = luaL_optstring(L, 2, "r"); 179 FILE **pf = newfile(L); 180 *pf = lua_popen(L, filename, mode); 181 return (*pf == NULL) ? pushresult(L, 0, filename) : 1; 182 } 183 184 185 static int io_tmpfile (lua_State *L) { 186 FILE **pf = newfile(L); 187 *pf = tmpfile(); 188 return (*pf == NULL) ? pushresult(L, 0, NULL) : 1; 189 } 190 191 192 static FILE *getiofile (lua_State *L, int findex) { 193 FILE *f; 194 lua_rawgeti(L, LUA_ENVIRONINDEX, findex); 195 f = *(FILE **)lua_touserdata(L, -1); 196 if (f == NULL) 197 luaL_error(L, "standard %s file is closed", fnames[findex - 1]); 198 return f; 199 } 200 201 202 static int g_iofile (lua_State *L, int f, const char *mode) { 203 if (!lua_isnoneornil(L, 1)) { 204 const char *filename = lua_tostring(L, 1); 205 if (filename) { 206 FILE **pf = newfile(L); 207 *pf = fopen(filename, mode); 208 if (*pf == NULL) 209 fileerror(L, 1, filename); 210 } 211 else { 212 tofile(L); /* check that it's a valid file handle */ 213 lua_pushvalue(L, 1); 214 } 215 lua_rawseti(L, LUA_ENVIRONINDEX, f); 216 } 217 /* return current value */ 218 lua_rawgeti(L, LUA_ENVIRONINDEX, f); 219 return 1; 220 } 221 222 223 static int io_input (lua_State *L) { 224 return g_iofile(L, IO_INPUT, "r"); 225 } 226 227 228 static int io_output (lua_State *L) { 229 return g_iofile(L, IO_OUTPUT, "w"); 230 } 231 232 233 static int io_readline (lua_State *L); 234 235 236 static void aux_lines (lua_State *L, int idx, int toclose) { 237 lua_pushvalue(L, idx); 238 lua_pushboolean(L, toclose); /* close/not close file when finished */ 239 lua_pushcclosure(L, io_readline, 2); 240 } 241 242 243 static int f_lines (lua_State *L) { 244 tofile(L); /* check that it's a valid file handle */ 245 aux_lines(L, 1, 0); 246 return 1; 247 } 248 249 250 static int io_lines (lua_State *L) { 251 if (lua_isnoneornil(L, 1)) { /* no arguments? */ 252 /* will iterate over default input */ 253 lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT); 254 return f_lines(L); 255 } 256 else { 257 const char *filename = luaL_checkstring(L, 1); 258 FILE **pf = newfile(L); 259 *pf = fopen(filename, "r"); 260 if (*pf == NULL) 261 fileerror(L, 1, filename); 262 aux_lines(L, lua_gettop(L), 1); 263 return 1; 264 } 265 } 266 267 268 /* 269 ** {====================================================== 270 ** READ 271 ** ======================================================= 272 */ 273 274 275 static int read_number (lua_State *L, FILE *f) { 276 lua_Number d; 277 if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) { 278 lua_pushnumber(L, d); 279 return 1; 280 } 281 else { 282 lua_pushnil(L); /* "result" to be removed */ 283 return 0; /* read fails */ 284 } 285 } 286 287 288 static int test_eof (lua_State *L, FILE *f) { 289 int c = getc(f); 290 ungetc(c, f); 291 lua_pushlstring(L, NULL, 0); 292 return (c != EOF); 293 } 294 295 296 static int read_line (lua_State *L, FILE *f) { 297 luaL_Buffer b; 298 luaL_buffinit(L, &b); 299 for (;;) { 300 size_t l; 301 char *p = luaL_prepbuffer(&b); 302 if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */ 303 luaL_pushresult(&b); /* close buffer */ 304 return (lua_objlen(L, -1) > 0); /* check whether read something */ 305 } 306 l = strlen(p); 307 if (l == 0 || p[l-1] != '\n') 308 luaL_addsize(&b, l); 309 else { 310 luaL_addsize(&b, l - 1); /* do not include `eol' */ 311 luaL_pushresult(&b); /* close buffer */ 312 return 1; /* read at least an `eol' */ 313 } 314 } 315 } 316 317 318 static int read_chars (lua_State *L, FILE *f, size_t n) { 319 size_t rlen; /* how much to read */ 320 size_t nr; /* number of chars actually read */ 321 luaL_Buffer b; 322 luaL_buffinit(L, &b); 323 rlen = LUAL_BUFFERSIZE; /* try to read that much each time */ 324 do { 325 char *p = luaL_prepbuffer(&b); 326 if (rlen > n) rlen = n; /* cannot read more than asked */ 327 nr = fread(p, sizeof(char), rlen, f); 328 luaL_addsize(&b, nr); 329 n -= nr; /* still have to read `n' chars */ 330 } while (n > 0 && nr == rlen); /* until end of count or eof */ 331 luaL_pushresult(&b); /* close buffer */ 332 return (n == 0 || lua_objlen(L, -1) > 0); 333 } 334 335 336 static int g_read (lua_State *L, FILE *f, int first) { 337 int nargs = lua_gettop(L) - 1; 338 int success; 339 int n; 340 clearerr(f); 341 if (nargs == 0) { /* no arguments? */ 342 success = read_line(L, f); 343 n = first+1; /* to return 1 result */ 344 } 345 else { /* ensure stack space for all results and for auxlib's buffer */ 346 luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); 347 success = 1; 348 for (n = first; nargs-- && success; n++) { 349 if (lua_type(L, n) == LUA_TNUMBER) { 350 size_t l = (size_t)lua_tointeger(L, n); 351 success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); 352 } 353 else { 354 const char *p = lua_tostring(L, n); 355 luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); 356 switch (p[1]) { 357 case 'n': /* number */ 358 success = read_number(L, f); 359 break; 360 case 'l': /* line */ 361 success = read_line(L, f); 362 break; 363 case 'a': /* file */ 364 read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */ 365 success = 1; /* always success */ 366 break; 367 default: 368 return luaL_argerror(L, n, "invalid format"); 369 } 370 } 371 } 372 } 373 if (ferror(f)) 374 return pushresult(L, 0, NULL); 375 if (!success) { 376 lua_pop(L, 1); /* remove last result */ 377 lua_pushnil(L); /* push nil instead */ 378 } 379 return n - first; 380 } 381 382 383 static int io_read (lua_State *L) { 384 return g_read(L, getiofile(L, IO_INPUT), 1); 385 } 386 387 388 static int f_read (lua_State *L) { 389 return g_read(L, tofile(L), 2); 390 } 391 392 393 static int io_readline (lua_State *L) { 394 FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1)); 395 int sucess; 396 if (f == NULL) /* file is already closed? */ 397 luaL_error(L, "file is already closed"); 398 sucess = read_line(L, f); 399 if (ferror(f)) 400 return luaL_error(L, "%s", strerror(errno)); 401 if (sucess) return 1; 402 else { /* EOF */ 403 if (lua_toboolean(L, lua_upvalueindex(2))) { /* generator created file? */ 404 lua_settop(L, 0); 405 lua_pushvalue(L, lua_upvalueindex(1)); 406 aux_close(L); /* close it */ 407 } 408 return 0; 409 } 410 } 411 412 /* }====================================================== */ 413 414 415 static int g_write (lua_State *L, FILE *f, int arg) { 416 int nargs = lua_gettop(L) - 1; 417 int status = 1; 418 for (; nargs--; arg++) { 419 if (lua_type(L, arg) == LUA_TNUMBER) { 420 /* optimization: could be done exactly as for strings */ 421 status = status && 422 fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0; 423 } 424 else { 425 size_t l; 426 const char *s = luaL_checklstring(L, arg, &l); 427 status = status && (fwrite(s, sizeof(char), l, f) == l); 428 } 429 } 430 return pushresult(L, status, NULL); 431 } 432 433 434 static int io_write (lua_State *L) { 435 return g_write(L, getiofile(L, IO_OUTPUT), 1); 436 } 437 438 439 static int f_write (lua_State *L) { 440 return g_write(L, tofile(L), 2); 441 } 442 443 444 static int f_seek (lua_State *L) { 445 static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; 446 static const char *const modenames[] = {"set", "cur", "end", NULL}; 447 FILE *f = tofile(L); 448 int op = luaL_checkoption(L, 2, "cur", modenames); 449 long offset = luaL_optlong(L, 3, 0); 450 op = fseek(f, offset, mode[op]); 451 if (op) 452 return pushresult(L, 0, NULL); /* error */ 453 else { 454 lua_pushinteger(L, ftell(f)); 455 return 1; 456 } 457 } 458 459 460 static int f_setvbuf (lua_State *L) { 461 static const int mode[] = {_IONBF, _IOFBF, _IOLBF}; 462 static const char *const modenames[] = {"no", "full", "line", NULL}; 463 FILE *f = tofile(L); 464 int op = luaL_checkoption(L, 2, NULL, modenames); 465 lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); 466 int res = setvbuf(f, NULL, mode[op], sz); 467 return pushresult(L, res == 0, NULL); 468 } 469 470 471 472 static int io_flush (lua_State *L) { 473 return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); 474 } 475 476 477 static int f_flush (lua_State *L) { 478 return pushresult(L, fflush(tofile(L)) == 0, NULL); 479 } 480 481 482 static const luaL_Reg iolib[] = { 483 {"close", io_close}, 484 {"flush", io_flush}, 485 {"input", io_input}, 486 {"lines", io_lines}, 487 {"open", io_open}, 488 {"output", io_output}, 489 {"popen", io_popen}, 490 {"read", io_read}, 491 {"tmpfile", io_tmpfile}, 492 {"type", io_type}, 493 {"write", io_write}, 494 {NULL, NULL} 495 }; 496 497 498 static const luaL_Reg flib[] = { 499 {"close", io_close}, 500 {"flush", f_flush}, 501 {"lines", f_lines}, 502 {"read", f_read}, 503 {"seek", f_seek}, 504 {"setvbuf", f_setvbuf}, 505 {"write", f_write}, 506 {"__gc", io_gc}, 507 {"__tostring", io_tostring}, 508 {NULL, NULL} 509 }; 510 511 512 static void createmeta (lua_State *L) { 513 luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */ 514 lua_pushvalue(L, -1); /* push metatable */ 515 lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ 516 luaL_register(L, NULL, flib); /* file methods */ 517 } 518 519 520 static void createstdfile (lua_State *L, FILE *f, int k, const char *fname) { 521 *newfile(L) = f; 522 if (k > 0) { 523 lua_pushvalue(L, -1); 524 lua_rawseti(L, LUA_ENVIRONINDEX, k); 525 } 526 lua_pushvalue(L, -2); /* copy environment */ 527 lua_setfenv(L, -2); /* set it */ 528 lua_setfield(L, -3, fname); 529 } 530 531 532 static void newfenv (lua_State *L, lua_CFunction cls) { 533 lua_createtable(L, 0, 1); 534 lua_pushcfunction(L, cls); 535 lua_setfield(L, -2, "__close"); 536 } 537 538 539 LUALIB_API int luaopen_io (lua_State *L) { 540 createmeta(L); 541 /* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */ 542 newfenv(L, io_fclose); 543 lua_replace(L, LUA_ENVIRONINDEX); 544 /* open library */ 545 luaL_register(L, LUA_IOLIBNAME, iolib); 546 /* create (and set) default files */ 547 newfenv(L, io_noclose); /* close function for default files */ 548 createstdfile(L, stdin, IO_INPUT, "stdin"); 549 createstdfile(L, stdout, IO_OUTPUT, "stdout"); 550 createstdfile(L, stderr, 0, "stderr"); 551 lua_pop(L, 1); /* pop environment for default files */ 552 lua_getfield(L, -1, "popen"); 553 newfenv(L, io_pclose); /* create environment for 'popen' */ 554 lua_setfenv(L, -2); /* set fenv for 'popen' */ 555 lua_pop(L, 1); /* pop 'popen' */ 556 return 1; 557 } 558 559