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