xref: /freebsd/stand/liblua/lutils.c (revision 1edb7116)
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