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