1 /*- 2 * Copyright (c) 2014 Pedro Souza <pedrosouza@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28 #include <sys/param.h> 29 30 #include "lua.h" 31 #include "lauxlib.h" 32 #include "lstd.h" 33 #include "lutils.h" 34 #include "bootstrap.h" 35 36 /* 37 * Like loader.perform, except args are passed already parsed 38 * on the stack. 39 */ 40 static int 41 lua_command(lua_State *L) 42 { 43 int i; 44 int res = 1; 45 int argc = lua_gettop(L); 46 char **argv; 47 48 argv = malloc(sizeof(char *) * (argc + 1)); 49 if (argv == NULL) 50 return 0; 51 for (i = 0; i < argc; i++) 52 argv[i] = (char *)(intptr_t)luaL_checkstring(L, i + 1); 53 argv[argc] = NULL; 54 res = interp_builtin_cmd(argc, argv); 55 free(argv); 56 lua_pushinteger(L, res); 57 58 return 1; 59 } 60 61 static int 62 lua_has_command(lua_State *L) 63 { 64 const char *cmd; 65 66 cmd = luaL_checkstring(L, 1); 67 if (interp_has_builtin_cmd(cmd)) { 68 lua_pushboolean(L, 1); 69 return 1; 70 } 71 72 lua_pushnil(L); 73 lua_pushstring(L, "Builtin command not found"); 74 return 2; 75 } 76 77 static int 78 lua_has_feature(lua_State *L) 79 { 80 const char *feature; 81 char *msg; 82 83 feature = luaL_checkstring(L, 1); 84 85 if (feature_name_is_enabled(feature)) { 86 lua_pushboolean(L, 1); 87 return 1; 88 } 89 90 lua_pushnil(L); 91 lua_pushstring(L, "Feature not enabled"); 92 return 2; 93 } 94 95 96 static int 97 lua_perform(lua_State *L) 98 { 99 int argc; 100 char **argv; 101 int res = 1; 102 103 if (parse(&argc, &argv, luaL_checkstring(L, 1)) == 0) { 104 res = interp_builtin_cmd(argc, argv); 105 free(argv); 106 } 107 lua_pushinteger(L, res); 108 109 return 1; 110 } 111 112 static int 113 lua_command_error(lua_State *L) 114 { 115 116 lua_pushstring(L, command_errbuf); 117 return 1; 118 } 119 120 /* 121 * Accepts a space-delimited loader command and runs it through the standard 122 * loader parsing, as if it were executed at the loader prompt by the user. 123 */ 124 static int 125 lua_interpret(lua_State *L) 126 { 127 const char *interp_string; 128 129 if (lua_gettop(L) != 1) { 130 lua_pushnil(L); 131 return 1; 132 } 133 134 interp_string = luaL_checkstring(L, 1); 135 lua_pushinteger(L, interp_run(interp_string)); 136 return 1; 137 } 138 139 static int 140 lua_parse(lua_State *L) 141 { 142 int argc, nargc; 143 char **argv; 144 145 if (parse(&argc, &argv, luaL_checkstring(L, 1)) == 0) { 146 for (nargc = 0; nargc < argc; ++nargc) { 147 lua_pushstring(L, argv[nargc]); 148 } 149 free(argv); 150 return nargc; 151 } 152 153 lua_pushnil(L); 154 return 1; 155 } 156 157 static int 158 lua_getchar(lua_State *L) 159 { 160 161 lua_pushinteger(L, getchar()); 162 return 1; 163 } 164 165 static int 166 lua_ischar(lua_State *L) 167 { 168 169 lua_pushboolean(L, ischar()); 170 return 1; 171 } 172 173 static int 174 lua_gets(lua_State *L) 175 { 176 char buf[129]; 177 178 ngets(buf, 128); 179 lua_pushstring(L, buf); 180 return 1; 181 } 182 183 static int 184 lua_time(lua_State *L) 185 { 186 187 lua_pushinteger(L, time(NULL)); 188 return 1; 189 } 190 191 static int 192 lua_delay(lua_State *L) 193 { 194 195 delay((int)luaL_checknumber(L, 1)); 196 return 0; 197 } 198 199 static int 200 lua_getenv(lua_State *L) 201 { 202 lua_pushstring(L, getenv(luaL_checkstring(L, 1))); 203 204 return 1; 205 } 206 207 static int 208 lua_setenv(lua_State *L) 209 { 210 const char *key, *val; 211 212 key = luaL_checkstring(L, 1); 213 val = luaL_checkstring(L, 2); 214 lua_pushinteger(L, setenv(key, val, 1)); 215 216 return 1; 217 } 218 219 static int 220 lua_unsetenv(lua_State *L) 221 { 222 const char *ev; 223 224 ev = luaL_checkstring(L, 1); 225 lua_pushinteger(L, unsetenv(ev)); 226 227 return 1; 228 } 229 230 static int 231 lua_printc(lua_State *L) 232 { 233 ssize_t cur, l; 234 const char *s = luaL_checklstring(L, 1, &l); 235 236 for (cur = 0; cur < l; ++cur) 237 putchar((unsigned char)*(s++)); 238 239 return 1; 240 } 241 242 static int 243 lua_openfile(lua_State *L) 244 { 245 const char *mode, *str; 246 int nargs; 247 248 nargs = lua_gettop(L); 249 if (nargs < 1 || nargs > 2) { 250 lua_pushnil(L); 251 return 1; 252 } 253 str = lua_tostring(L, 1); 254 mode = "r"; 255 if (nargs > 1) { 256 mode = lua_tostring(L, 2); 257 if (mode == NULL) { 258 lua_pushnil(L); 259 return 1; 260 } 261 } 262 FILE * f = fopen(str, mode); 263 if (f != NULL) { 264 FILE ** ptr = (FILE**)lua_newuserdata(L, sizeof(FILE**)); 265 *ptr = f; 266 } else 267 lua_pushnil(L); 268 return 1; 269 } 270 271 static int 272 lua_closefile(lua_State *L) 273 { 274 FILE ** f; 275 if (lua_gettop(L) != 1) { 276 lua_pushboolean(L, 0); 277 return 1; 278 } 279 280 f = (FILE**)lua_touserdata(L, 1); 281 if (f != NULL && *f != NULL) { 282 lua_pushboolean(L, fclose(*f) == 0 ? 1 : 0); 283 *f = NULL; 284 } else 285 lua_pushboolean(L, 0); 286 287 return 1; 288 } 289 290 static int 291 lua_readfile(lua_State *L) 292 { 293 FILE **f; 294 size_t size, r; 295 char * buf; 296 297 if (lua_gettop(L) < 1 || lua_gettop(L) > 2) { 298 lua_pushnil(L); 299 lua_pushinteger(L, 0); 300 return 2; 301 } 302 303 f = (FILE**)lua_touserdata(L, 1); 304 305 if (f == NULL || *f == NULL) { 306 lua_pushnil(L); 307 lua_pushinteger(L, 0); 308 return 2; 309 } 310 311 if (lua_gettop(L) == 2) 312 size = (size_t)lua_tonumber(L, 2); 313 else 314 size = (*f)->size; 315 316 317 buf = (char*)malloc(size); 318 r = fread(buf, 1, size, *f); 319 lua_pushlstring(L, buf, r); 320 free(buf); 321 lua_pushinteger(L, r); 322 323 return 2; 324 } 325 326 /* 327 * Implements io.write(file, ...) 328 * Any number of string and number arguments may be passed to it, 329 * and it will return the number of bytes written, or nil, an error string, and 330 * the errno. 331 */ 332 static int 333 lua_writefile(lua_State *L) 334 { 335 FILE **f; 336 const char *buf; 337 int i, nargs; 338 size_t bufsz, w, wrsz; 339 340 buf = NULL; 341 bufsz = 0; 342 w = 0; 343 wrsz = 0; 344 nargs = lua_gettop(L); 345 if (nargs < 2) { 346 errno = EINVAL; 347 return luaL_fileresult(L, 0, NULL); 348 } 349 350 f = (FILE**)lua_touserdata(L, 1); 351 352 if (f == NULL || *f == NULL) { 353 errno = EINVAL; 354 return luaL_fileresult(L, 0, NULL); 355 } 356 357 /* Do a validation pass first */ 358 for (i = 0; i < nargs - 1; i++) { 359 /* 360 * With Lua's API, lua_isstring really checks if the argument 361 * is a string or a number. The latter will be implicitly 362 * converted to a string by our later call to lua_tolstring. 363 */ 364 if (!lua_isstring(L, i + 2)) { 365 errno = EINVAL; 366 return luaL_fileresult(L, 0, NULL); 367 } 368 } 369 for (i = 0; i < nargs - 1; i++) { 370 /* We've already validated; there's no chance of failure */ 371 buf = lua_tolstring(L, i + 2, &bufsz); 372 wrsz = fwrite(buf, 1, bufsz, *f); 373 if (wrsz < bufsz) 374 return luaL_fileresult(L, 0, NULL); 375 w += wrsz; 376 } 377 lua_pushinteger(L, w); 378 return 1; 379 } 380 381 #define REG_SIMPLE(n) { #n, lua_ ## n } 382 static const struct luaL_Reg loaderlib[] = { 383 REG_SIMPLE(delay), 384 REG_SIMPLE(command_error), 385 REG_SIMPLE(command), 386 REG_SIMPLE(interpret), 387 REG_SIMPLE(parse), 388 REG_SIMPLE(getenv), 389 REG_SIMPLE(has_command), 390 REG_SIMPLE(has_feature), 391 REG_SIMPLE(perform), 392 REG_SIMPLE(printc), /* Also registered as the global 'printc' */ 393 REG_SIMPLE(setenv), 394 REG_SIMPLE(time), 395 REG_SIMPLE(unsetenv), 396 { NULL, NULL }, 397 }; 398 399 static const struct luaL_Reg iolib[] = { 400 { "close", lua_closefile }, 401 REG_SIMPLE(getchar), 402 REG_SIMPLE(gets), 403 REG_SIMPLE(ischar), 404 { "open", lua_openfile }, 405 { "read", lua_readfile }, 406 { "write", lua_writefile }, 407 { NULL, NULL }, 408 }; 409 #undef REG_SIMPLE 410 411 static void 412 lua_add_feature(void *cookie, const char *name, const char *desc, bool enabled) 413 { 414 lua_State *L = cookie; 415 416 /* 417 * The feature table consists solely of features that are enabled, and 418 * their associated descriptions for debugging purposes. 419 */ 420 lua_pushstring(L, desc); 421 lua_setfield(L, -2, name); 422 } 423 424 static void 425 lua_add_features(lua_State *L) 426 { 427 428 lua_newtable(L); 429 feature_iter(&lua_add_feature, L); 430 431 /* 432 * We should still have just the table on the stack after we're done 433 * iterating. 434 */ 435 lua_setfield(L, -2, "features"); 436 } 437 438 int 439 luaopen_loader(lua_State *L) 440 { 441 luaL_newlib(L, loaderlib); 442 /* Add loader.machine and loader.machine_arch properties */ 443 lua_pushstring(L, MACHINE); 444 lua_setfield(L, -2, "machine"); 445 lua_pushstring(L, MACHINE_ARCH); 446 lua_setfield(L, -2, "machine_arch"); 447 lua_pushstring(L, LUA_PATH); 448 lua_setfield(L, -2, "lua_path"); 449 lua_pushinteger(L, bootprog_rev); 450 lua_setfield(L, -2, "version"); 451 lua_pushinteger(L, CMD_OK); 452 lua_setfield(L, -2, "CMD_OK"); 453 lua_pushinteger(L, CMD_WARN); 454 lua_setfield(L, -2, "CMD_WARN"); 455 lua_pushinteger(L, CMD_ERROR); 456 lua_setfield(L, -2, "CMD_ERROR"); 457 lua_pushinteger(L, CMD_CRIT); 458 lua_setfield(L, -2, "CMD_CRIT"); 459 lua_pushinteger(L, CMD_FATAL); 460 lua_setfield(L, -2, "CMD_FATAL"); 461 lua_add_features(L); 462 /* Set global printc to loader.printc */ 463 lua_register(L, "printc", lua_printc); 464 return 1; 465 } 466 467 int 468 luaopen_io(lua_State *L) 469 { 470 luaL_newlib(L, iolib); 471 return 1; 472 } 473