xref: /freebsd/stand/liblua/lutils.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
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/cdefs.h>
29 #include <sys/param.h>
30 
31 #include "lua.h"
32 #include "lauxlib.h"
33 #include "lstd.h"
34 #include "lutils.h"
35 #include "bootstrap.h"
36 #include <gfx_fb.h>
37 #include <pnglite.h>
38 
39 /*
40  * Like loader.perform, except args are passed already parsed
41  * on the stack.
42  */
43 static int
44 lua_command(lua_State *L)
45 {
46 	int	i;
47 	int	res = 1;
48 	int 	argc = lua_gettop(L);
49 	char	**argv;
50 
51 	argv = malloc(sizeof(char *) * (argc + 1));
52 	if (argv == NULL)
53 		return 0;
54 	for (i = 0; i < argc; i++)
55 		argv[i] = (char *)(intptr_t)luaL_checkstring(L, i + 1);
56 	argv[argc] = NULL;
57 	res = interp_builtin_cmd(argc, argv);
58 	free(argv);
59 	lua_pushinteger(L, res);
60 
61 	return 1;
62 }
63 
64 static int
65 lua_has_command(lua_State *L)
66 {
67 	const char	*cmd;
68 
69 	if (lua_gettop(L) != 1) {
70 		lua_pushnil(L);
71 		return 1;
72 	}
73 	cmd = luaL_checkstring(L, 1);
74 	lua_pushinteger(L, interp_has_builtin_cmd(cmd));
75 
76 	return 1;
77 }
78 
79 static int
80 lua_perform(lua_State *L)
81 {
82 	int	argc;
83 	char	**argv;
84 	int	res = 1;
85 
86 	if (parse(&argc, &argv, luaL_checkstring(L, 1)) == 0) {
87 		res = interp_builtin_cmd(argc, argv);
88 		free(argv);
89 	}
90 	lua_pushinteger(L, res);
91 
92 	return 1;
93 }
94 
95 static int
96 lua_command_error(lua_State *L)
97 {
98 
99 	lua_pushstring(L, command_errbuf);
100 	return 1;
101 }
102 
103 /*
104  * Accepts a space-delimited loader command and runs it through the standard
105  * loader parsing, as if it were executed at the loader prompt by the user.
106  */
107 static int
108 lua_interpret(lua_State *L)
109 {
110 	const char	*interp_string;
111 
112 	if (lua_gettop(L) != 1) {
113 		lua_pushnil(L);
114 		return 1;
115 	}
116 
117 	interp_string = luaL_checkstring(L, 1);
118 	lua_pushinteger(L, interp_run(interp_string));
119 	return 1;
120 }
121 
122 static int
123 lua_parse(lua_State *L)
124 {
125 	int	argc, nargc;
126 	char	**argv;
127 
128 	if (parse(&argc, &argv, luaL_checkstring(L, 1)) == 0) {
129 		for (nargc = 0; nargc < argc; ++nargc) {
130 			lua_pushstring(L, argv[nargc]);
131 		}
132 		free(argv);
133 		return nargc;
134 	}
135 
136 	lua_pushnil(L);
137 	return 1;
138 }
139 
140 static int
141 lua_getchar(lua_State *L)
142 {
143 
144 	lua_pushinteger(L, getchar());
145 	return 1;
146 }
147 
148 static int
149 lua_ischar(lua_State *L)
150 {
151 
152 	lua_pushboolean(L, ischar());
153 	return 1;
154 }
155 
156 static int
157 lua_gets(lua_State *L)
158 {
159 	char	buf[129];
160 
161 	ngets(buf, 128);
162 	lua_pushstring(L, buf);
163 	return 1;
164 }
165 
166 static int
167 lua_time(lua_State *L)
168 {
169 
170 	lua_pushinteger(L, time(NULL));
171 	return 1;
172 }
173 
174 static int
175 lua_delay(lua_State *L)
176 {
177 
178 	delay((int)luaL_checknumber(L, 1));
179 	return 0;
180 }
181 
182 static int
183 lua_getenv(lua_State *L)
184 {
185 	lua_pushstring(L, getenv(luaL_checkstring(L, 1)));
186 
187 	return 1;
188 }
189 
190 static int
191 lua_setenv(lua_State *L)
192 {
193 	const char *key, *val;
194 
195 	key = luaL_checkstring(L, 1);
196 	val = luaL_checkstring(L, 2);
197 	lua_pushinteger(L, setenv(key, val, 1));
198 
199 	return 1;
200 }
201 
202 static int
203 lua_unsetenv(lua_State *L)
204 {
205 	const char	*ev;
206 
207 	ev = luaL_checkstring(L, 1);
208 	lua_pushinteger(L, unsetenv(ev));
209 
210 	return 1;
211 }
212 
213 static int
214 lua_printc(lua_State *L)
215 {
216 	ssize_t cur, l;
217 	const char *s = luaL_checklstring(L, 1, &l);
218 
219 	for (cur = 0; cur < l; ++cur)
220 		putchar((unsigned char)*(s++));
221 
222 	return 1;
223 }
224 
225 static int
226 lua_openfile(lua_State *L)
227 {
228 	const char	*mode, *str;
229 	int	nargs;
230 
231 	nargs = lua_gettop(L);
232 	if (nargs < 1 || nargs > 2) {
233 		lua_pushnil(L);
234 		return 1;
235 	}
236 	str = lua_tostring(L, 1);
237 	mode = "r";
238 	if (nargs > 1) {
239 		mode = lua_tostring(L, 2);
240 		if (mode == NULL) {
241 			lua_pushnil(L);
242 			return 1;
243 		}
244 	}
245 	FILE * f = fopen(str, mode);
246 	if (f != NULL) {
247 		FILE ** ptr = (FILE**)lua_newuserdata(L, sizeof(FILE**));
248 		*ptr = f;
249 	} else
250 		lua_pushnil(L);
251 	return 1;
252 }
253 
254 static int
255 lua_closefile(lua_State *L)
256 {
257 	FILE ** f;
258 	if (lua_gettop(L) != 1) {
259 		lua_pushboolean(L, 0);
260 		return 1;
261 	}
262 
263 	f = (FILE**)lua_touserdata(L, 1);
264 	if (f != NULL && *f != NULL) {
265 		lua_pushboolean(L, fclose(*f) == 0 ? 1 : 0);
266 		*f = NULL;
267 	} else
268 		lua_pushboolean(L, 0);
269 
270 	return 1;
271 }
272 
273 static int
274 lua_readfile(lua_State *L)
275 {
276 	FILE	**f;
277 	size_t	size, r;
278 	char * buf;
279 
280 	if (lua_gettop(L) < 1 || lua_gettop(L) > 2) {
281 		lua_pushnil(L);
282 		lua_pushinteger(L, 0);
283 		return 2;
284 	}
285 
286 	f = (FILE**)lua_touserdata(L, 1);
287 
288 	if (f == NULL || *f == NULL) {
289 		lua_pushnil(L);
290 		lua_pushinteger(L, 0);
291 		return 2;
292 	}
293 
294 	if (lua_gettop(L) == 2)
295 		size = (size_t)lua_tonumber(L, 2);
296 	else
297 		size = (*f)->size;
298 
299 
300 	buf = (char*)malloc(size);
301 	r = fread(buf, 1, size, *f);
302 	lua_pushlstring(L, buf, r);
303 	free(buf);
304 	lua_pushinteger(L, r);
305 
306 	return 2;
307 }
308 
309 /*
310  * Implements io.write(file, ...)
311  * Any number of string and number arguments may be passed to it,
312  * and it will return the number of bytes written, or nil, an error string, and
313  * the errno.
314  */
315 static int
316 lua_writefile(lua_State *L)
317 {
318 	FILE	**f;
319 	const char	*buf;
320 	int	i, nargs;
321 	size_t	bufsz, w, wrsz;
322 
323 	buf = NULL;
324 	bufsz = 0;
325 	w = 0;
326 	wrsz = 0;
327 	nargs = lua_gettop(L);
328 	if (nargs < 2) {
329 		errno = EINVAL;
330 		return luaL_fileresult(L, 0, NULL);
331 	}
332 
333 	f = (FILE**)lua_touserdata(L, 1);
334 
335 	if (f == NULL || *f == NULL) {
336 		errno = EINVAL;
337 		return luaL_fileresult(L, 0, NULL);
338 	}
339 
340 	/* Do a validation pass first */
341 	for (i = 0; i < nargs - 1; i++) {
342 		/*
343 		 * With Lua's API, lua_isstring really checks if the argument
344 		 * is a string or a number.  The latter will be implicitly
345 		 * converted to a string by our later call to lua_tolstring.
346 		 */
347 		if (!lua_isstring(L, i + 2)) {
348 			errno = EINVAL;
349 			return luaL_fileresult(L, 0, NULL);
350 		}
351 	}
352 	for (i = 0; i < nargs - 1; i++) {
353 		/* We've already validated; there's no chance of failure */
354 		buf = lua_tolstring(L, i + 2, &bufsz);
355 		wrsz = fwrite(buf, 1, bufsz, *f);
356 		if (wrsz < bufsz)
357 			return luaL_fileresult(L, 0, NULL);
358 		w += wrsz;
359 	}
360 	lua_pushinteger(L, w);
361 	return 1;
362 }
363 
364 /*
365  * put image using terminal coordinates.
366  */
367 static int
368 lua_term_putimage(lua_State *L)
369 {
370 	const char *name;
371 	png_t png;
372 	uint32_t x1, y1, x2, y2, f;
373 	int nargs, ret = 0, error;
374 
375 	nargs = lua_gettop(L);
376 	if (nargs != 6) {
377 		lua_pushboolean(L, 0);
378 		return 1;
379 	}
380 
381 	name = luaL_checkstring(L, 1);
382 	x1 = luaL_checknumber(L, 2);
383 	y1 = luaL_checknumber(L, 3);
384 	x2 = luaL_checknumber(L, 4);
385 	y2 = luaL_checknumber(L, 5);
386 	f = luaL_checknumber(L, 6);
387 
388 	x1 = gfx_state.tg_origin.tp_col + x1 * gfx_state.tg_font.vf_width;
389 	y1 = gfx_state.tg_origin.tp_row + y1 * gfx_state.tg_font.vf_height;
390 	if (x2 != 0) {
391 		x2 = gfx_state.tg_origin.tp_col +
392 		    x2 * gfx_state.tg_font.vf_width;
393 	}
394 	if (y2 != 0) {
395 		y2 = gfx_state.tg_origin.tp_row +
396 		    y2 * gfx_state.tg_font.vf_height;
397 	}
398 
399 	if ((error = png_open(&png, name)) != PNG_NO_ERROR) {
400 		if (f & FL_PUTIMAGE_DEBUG)
401 			printf("%s\n", png_error_string(error));
402 	} else {
403 		if (gfx_fb_putimage(&png, x1, y1, x2, y2, f) == 0)
404 			ret = 1;
405 		(void) png_close(&png);
406 	}
407 	lua_pushboolean(L, ret);
408 	return 1;
409 }
410 
411 static int
412 lua_fb_putimage(lua_State *L)
413 {
414 	const char *name;
415 	png_t png;
416 	uint32_t x1, y1, x2, y2, f;
417 	int nargs, ret = 0, error;
418 
419 	nargs = lua_gettop(L);
420 	if (nargs != 6) {
421 		lua_pushboolean(L, 0);
422 		return 1;
423 	}
424 
425 	name = luaL_checkstring(L, 1);
426 	x1 = luaL_checknumber(L, 2);
427 	y1 = luaL_checknumber(L, 3);
428 	x2 = luaL_checknumber(L, 4);
429 	y2 = luaL_checknumber(L, 5);
430 	f = luaL_checknumber(L, 6);
431 
432 	if ((error = png_open(&png, name)) != PNG_NO_ERROR) {
433 		if (f & FL_PUTIMAGE_DEBUG)
434 			printf("%s\n", png_error_string(error));
435 	} else {
436 		if (gfx_fb_putimage(&png, x1, y1, x2, y2, f) == 0)
437 			ret = 1;
438 		(void) png_close(&png);
439 	}
440 	lua_pushboolean(L, ret);
441 	return 1;
442 }
443 
444 static int
445 lua_fb_setpixel(lua_State *L)
446 {
447 	uint32_t x, y;
448 	int nargs;
449 
450 	nargs = lua_gettop(L);
451 	if (nargs != 2) {
452 		lua_pushnil(L);
453 		return 1;
454 	}
455 
456 	x = luaL_checknumber(L, 1);
457 	y = luaL_checknumber(L, 2);
458         gfx_fb_setpixel(x, y);
459 	return 0;
460 }
461 
462 static int
463 lua_fb_line(lua_State *L)
464 {
465 	uint32_t x0, y0, x1, y1, wd;
466 	int nargs;
467 
468 	nargs = lua_gettop(L);
469 	if (nargs != 5) {
470 		lua_pushnil(L);
471 		return 1;
472 	}
473 
474 	x0 = luaL_checknumber(L, 1);
475 	y0 = luaL_checknumber(L, 2);
476 	x1 = luaL_checknumber(L, 3);
477 	y1 = luaL_checknumber(L, 4);
478 	wd = luaL_checknumber(L, 5);
479         gfx_fb_line(x0, y0, x1, y1, wd);
480 	return 0;
481 }
482 
483 static int
484 lua_fb_bezier(lua_State *L)
485 {
486 	uint32_t x0, y0, x1, y1, x2, y2, width;
487 	int nargs;
488 
489 	nargs = lua_gettop(L);
490 	if (nargs != 7) {
491 		lua_pushnil(L);
492 		return 1;
493 	}
494 
495 	x0 = luaL_checknumber(L, 1);
496 	y0 = luaL_checknumber(L, 2);
497 	x1 = luaL_checknumber(L, 3);
498 	y1 = luaL_checknumber(L, 4);
499 	x2 = luaL_checknumber(L, 5);
500 	y2 = luaL_checknumber(L, 6);
501 	width = luaL_checknumber(L, 7);
502         gfx_fb_bezier(x0, y0, x1, y1, x2, y2, width);
503 	return 0;
504 }
505 
506 static int
507 lua_fb_drawrect(lua_State *L)
508 {
509 	uint32_t x0, y0, x1, y1, fill;
510 	int nargs;
511 
512 	nargs = lua_gettop(L);
513 	if (nargs != 5) {
514 		lua_pushnil(L);
515 		return 1;
516 	}
517 
518 	x0 = luaL_checknumber(L, 1);
519 	y0 = luaL_checknumber(L, 2);
520 	x1 = luaL_checknumber(L, 3);
521 	y1 = luaL_checknumber(L, 4);
522 	fill = luaL_checknumber(L, 5);
523         gfx_fb_drawrect(x0, y0, x1, y1, fill);
524 	return 0;
525 }
526 
527 static int
528 lua_term_drawrect(lua_State *L)
529 {
530 	uint32_t x0, y0, x1, y1;
531 	int nargs;
532 
533 	nargs = lua_gettop(L);
534 	if (nargs != 4) {
535 		lua_pushnil(L);
536 		return 1;
537 	}
538 
539 	x0 = luaL_checknumber(L, 1);
540 	y0 = luaL_checknumber(L, 2);
541 	x1 = luaL_checknumber(L, 3);
542 	y1 = luaL_checknumber(L, 4);
543         gfx_term_drawrect(x0, y0, x1, y1);
544 	return 0;
545 }
546 
547 #define REG_SIMPLE(n)	{ #n, lua_ ## n }
548 static const struct luaL_Reg loaderlib[] = {
549 	REG_SIMPLE(delay),
550 	REG_SIMPLE(command_error),
551 	REG_SIMPLE(command),
552 	REG_SIMPLE(interpret),
553 	REG_SIMPLE(parse),
554 	REG_SIMPLE(getenv),
555 	REG_SIMPLE(has_command),
556 	REG_SIMPLE(perform),
557 	REG_SIMPLE(printc),	/* Also registered as the global 'printc' */
558 	REG_SIMPLE(setenv),
559 	REG_SIMPLE(time),
560 	REG_SIMPLE(unsetenv),
561 	REG_SIMPLE(fb_bezier),
562 	REG_SIMPLE(fb_drawrect),
563 	REG_SIMPLE(fb_line),
564 	REG_SIMPLE(fb_putimage),
565 	REG_SIMPLE(fb_setpixel),
566 	REG_SIMPLE(term_drawrect),
567 	REG_SIMPLE(term_putimage),
568 	{ NULL, NULL },
569 };
570 
571 static const struct luaL_Reg iolib[] = {
572 	{ "close", lua_closefile },
573 	REG_SIMPLE(getchar),
574 	REG_SIMPLE(gets),
575 	REG_SIMPLE(ischar),
576 	{ "open", lua_openfile },
577 	{ "read", lua_readfile },
578 	{ "write", lua_writefile },
579 	{ NULL, NULL },
580 };
581 #undef REG_SIMPLE
582 
583 int
584 luaopen_loader(lua_State *L)
585 {
586 	luaL_newlib(L, loaderlib);
587 	/* Add loader.machine and loader.machine_arch properties */
588 	lua_pushstring(L, MACHINE);
589 	lua_setfield(L, -2, "machine");
590 	lua_pushstring(L, MACHINE_ARCH);
591 	lua_setfield(L, -2, "machine_arch");
592 	lua_pushstring(L, LUA_PATH);
593 	lua_setfield(L, -2, "lua_path");
594 	lua_pushinteger(L, bootprog_rev);
595 	lua_setfield(L, -2, "version");
596 	/* Set global printc to loader.printc */
597 	lua_register(L, "printc", lua_printc);
598 	return 1;
599 }
600 
601 int
602 luaopen_io(lua_State *L)
603 {
604 	luaL_newlib(L, iolib);
605 	return 1;
606 }
607