xref: /illumos-gate/usr/src/common/ficl/loader.c (revision 41e0a469c3dbc14deb2b200f6ca6f6e00b5865d0)
1 /*
2  * Copyright (c) 2000 Daniel Capo Sobral
3  * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
4  * Copyright 2020 RackTop Systems, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 /*
30  * l o a d e r . c
31  * Additional FICL words designed for FreeBSD's loader
32  */
33 
34 #ifndef _STANDALONE
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <dirent.h>
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <unistd.h>
42 #include <strings.h>
43 #include <termios.h>
44 #else
45 #include <stand.h>
46 #include <gfx_fb.h>
47 #include <sys/tem_impl.h>
48 #include "bootstrap.h"
49 #endif
50 #ifdef _STANDALONE
51 #include <uuid.h>
52 #else
53 #include <uuid/uuid.h>
54 #endif
55 #include <string.h>
56 #include <gfx_fb.h>
57 #include "ficl.h"
58 
59 /*
60  *		FreeBSD's loader interaction words and extras
61  *
62  *		setenv      ( value n name n' -- )
63  *		setenv?     ( value n name n' flag -- )
64  *		getenv      ( addr n -- addr' n' | -1 )
65  *		unsetenv    ( addr n -- )
66  *		copyin      ( addr addr' len -- )
67  *		copyout     ( addr addr' len -- )
68  *		findfile    ( name len type len' -- addr )
69  *		ccall       ( [[...[p10] p9] ... p1] n addr -- result )
70  *		uuid-from-string ( addr n -- addr' )
71  *		uuid-to-string ( addr' -- addr n | -1 )
72  *		.#	    ( value -- )
73  */
74 
75 #ifdef _STANDALONE
76 /* Put image using terminal coordinates. ( flags x1 y1 x2 y2 -- flag ) */
77 void
78 ficl_term_putimage(ficlVm *pVM)
79 {
80 	char *namep, *name;
81 	ficlUnsigned names;
82 	ficlInteger ret = FICL_FALSE;
83 	uint32_t x1, y1, x2, y2, f;
84 	png_t png;
85 
86 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 7, 1);
87 
88 	names = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
89 	namep = (char *)ficlStackPopPointer(ficlVmGetDataStack(pVM));
90 	y2 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
91 	x2 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
92 	y1 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
93 	x1 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
94 	f = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
95 
96 	x1 = tems.ts_p_offset.x + x1 * tems.ts_font.vf_width;
97 	y1 = tems.ts_p_offset.y + y1 * tems.ts_font.vf_height;
98 	if (x2 != 0) {
99 		x2 = tems.ts_p_offset.x +
100 		    x2 * tems.ts_font.vf_width;
101 	}
102 	if (y2 != 0) {
103 		y2 = tems.ts_p_offset.y +
104 		    y2 * tems.ts_font.vf_height;
105 	}
106 
107 	name = ficlMalloc(names + 1);
108 	if (!name)
109 		ficlVmThrowError(pVM, "Error: out of memory");
110 	(void) strncpy(name, namep, names);
111 	name[names] = '\0';
112 
113 	if (png_open(&png, name) == PNG_NO_ERROR) {
114 		if (gfx_fb_putimage(&png, x1, y1, x2, y2, f) == 0)
115 			ret = FICL_TRUE;	/* success */
116 		(void) png_close(&png);
117 	}
118 	ficlFree(name);
119 	ficlStackPushInteger(ficlVmGetDataStack(pVM), ret);
120 }
121 #endif
122 
123 /* ( flags x1 y1 x2 y2 -- flag ) */
124 void
125 ficl_fb_putimage(ficlVm *pVM)
126 {
127 	char *namep, *name;
128 	ficlUnsigned names;
129 	ficlInteger ret = FICL_FALSE;
130 	uint32_t x1, y1, x2, y2, f;
131 	png_t png;
132 	int error;
133 
134 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 7, 1);
135 
136 	names = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
137 	namep = (char *)ficlStackPopPointer(ficlVmGetDataStack(pVM));
138 	y2 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
139 	x2 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
140 	y1 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
141 	x1 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
142 	f = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
143 
144 	name = ficlMalloc(names + 1);
145 	if (!name)
146 		ficlVmThrowError(pVM, "Error: out of memory");
147 	(void) strncpy(name, namep, names);
148 	name[names] = '\0';
149 
150 	if ((error = png_open(&png, name)) != PNG_NO_ERROR) {
151 		if (f & FL_PUTIMAGE_DEBUG)
152 			printf("%s\n", png_error_string(error));
153 	} else {
154 		if (gfx_fb_putimage(&png, x1, y1, x2, y2, f) == 0)
155 			ret = FICL_TRUE;	/* success */
156 		(void) png_close(&png);
157 	}
158 	ficlFree(name);
159 	ficlStackPushInteger(ficlVmGetDataStack(pVM), ret);
160 }
161 
162 void
163 ficl_fb_setpixel(ficlVm *pVM)
164 {
165 	ficlUnsigned x, y;
166 
167 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 2, 0);
168 
169 	y = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
170 	x = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
171 	gfx_fb_setpixel(x, y);
172 }
173 
174 void
175 ficl_fb_line(ficlVm *pVM)
176 {
177 	ficlUnsigned x0, y0, x1, y1, wd;
178 
179 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 5, 0);
180 
181 	wd = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
182 	y1 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
183 	x1 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
184 	y0 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
185 	x0 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
186 	gfx_fb_line(x0, y0, x1, y1, wd);
187 }
188 
189 void
190 ficl_fb_bezier(ficlVm *pVM)
191 {
192 	ficlUnsigned x0, y0, x1, y1, x2, y2, width;
193 
194 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 7, 0);
195 
196 	width = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
197 	y2 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
198 	x2 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
199 	y1 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
200 	x1 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
201 	y0 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
202 	x0 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
203 	gfx_fb_bezier(x0, y0, x1, y1, x2, y2, width);
204 }
205 
206 void
207 ficl_fb_drawrect(ficlVm *pVM)
208 {
209 	ficlUnsigned x1, x2, y1, y2, fill;
210 
211 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 5, 0);
212 
213 	fill = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
214 	y2 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
215 	x2 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
216 	y1 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
217 	x1 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
218 	gfx_fb_drawrect(x1, y1, x2, y2, fill);
219 }
220 
221 void
222 ficl_term_drawrect(ficlVm *pVM)
223 {
224 	ficlUnsigned x1, x2, y1, y2;
225 
226 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 4, 0);
227 
228 	y2 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
229 	x2 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
230 	y1 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
231 	x1 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
232 	gfx_term_drawrect(x1, y1, x2, y2);
233 }
234 
235 void
236 ficlSetenv(ficlVm *pVM)
237 {
238 	char *name, *value;
239 	char *namep, *valuep;
240 	int names, values;
241 
242 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 4, 0);
243 
244 	names = ficlStackPopInteger(ficlVmGetDataStack(pVM));
245 	namep = (char *)ficlStackPopPointer(ficlVmGetDataStack(pVM));
246 	values = ficlStackPopInteger(ficlVmGetDataStack(pVM));
247 	valuep = (char *)ficlStackPopPointer(ficlVmGetDataStack(pVM));
248 
249 	name = (char *)ficlMalloc(names+1);
250 	if (!name)
251 		ficlVmThrowError(pVM, "Error: out of memory");
252 	(void) strncpy(name, namep, names);
253 	name[names] = '\0';
254 	value = (char *)ficlMalloc(values+1);
255 	if (!value)
256 		ficlVmThrowError(pVM, "Error: out of memory");
257 	(void) strncpy(value, valuep, values);
258 	value[values] = '\0';
259 
260 	(void) setenv(name, value, 1);
261 	ficlFree(name);
262 	ficlFree(value);
263 }
264 
265 void
266 ficlSetenvq(ficlVm *pVM)
267 {
268 	char *name, *value;
269 	char *namep, *valuep;
270 	int names, values, overwrite;
271 
272 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 5, 0);
273 
274 	overwrite = ficlStackPopInteger(ficlVmGetDataStack(pVM));
275 	names = ficlStackPopInteger(ficlVmGetDataStack(pVM));
276 	namep = (char *)ficlStackPopPointer(ficlVmGetDataStack(pVM));
277 	values = ficlStackPopInteger(ficlVmGetDataStack(pVM));
278 	valuep = (char *)ficlStackPopPointer(ficlVmGetDataStack(pVM));
279 
280 	name = (char *)ficlMalloc(names+1);
281 	if (!name)
282 		(void) ficlVmThrowError(pVM, "Error: out of memory");
283 	(void) strncpy(name, namep, names);
284 	name[names] = '\0';
285 	value = (char *)ficlMalloc(values+1);
286 	if (!value)
287 		ficlVmThrowError(pVM, "Error: out of memory");
288 	(void) strncpy(value, valuep, values);
289 	value[values] = '\0';
290 
291 	(void) setenv(name, value, overwrite);
292 	ficlFree(name);
293 	ficlFree(value);
294 }
295 
296 void
297 ficlGetenv(ficlVm *pVM)
298 {
299 	char *name, *value;
300 	char *namep;
301 	int names;
302 
303 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 2, 2);
304 
305 	names = ficlStackPopInteger(ficlVmGetDataStack(pVM));
306 	namep = (char *)ficlStackPopPointer(ficlVmGetDataStack(pVM));
307 
308 	name = (char *)ficlMalloc(names+1);
309 	if (!name)
310 		ficlVmThrowError(pVM, "Error: out of memory");
311 	(void) strncpy(name, namep, names);
312 	name[names] = '\0';
313 
314 	value = getenv(name);
315 	ficlFree(name);
316 
317 	if (value != NULL) {
318 		ficlStackPushPointer(ficlVmGetDataStack(pVM), value);
319 		ficlStackPushInteger(ficlVmGetDataStack(pVM), strlen(value));
320 	} else
321 		ficlStackPushInteger(ficlVmGetDataStack(pVM), -1);
322 }
323 
324 void
325 ficlUnsetenv(ficlVm *pVM)
326 {
327 	char *name;
328 	char *namep;
329 	int names;
330 
331 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 2, 0);
332 
333 	names = ficlStackPopInteger(ficlVmGetDataStack(pVM));
334 	namep = (char *)ficlStackPopPointer(ficlVmGetDataStack(pVM));
335 
336 	name = (char *)ficlMalloc(names+1);
337 	if (!name)
338 		ficlVmThrowError(pVM, "Error: out of memory");
339 	(void) strncpy(name, namep, names);
340 	name[names] = '\0';
341 
342 	(void) unsetenv(name);
343 	ficlFree(name);
344 }
345 
346 void
347 ficlCopyin(ficlVm *pVM)
348 {
349 #ifdef _STANDALONE
350 	void*		src;
351 	vm_offset_t	dest;
352 	size_t		len;
353 #endif
354 
355 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 3, 0);
356 
357 #ifdef _STANDALONE
358 	len = ficlStackPopInteger(ficlVmGetDataStack(pVM));
359 	dest = ficlStackPopInteger(ficlVmGetDataStack(pVM));
360 	src = ficlStackPopPointer(ficlVmGetDataStack(pVM));
361 	archsw.arch_copyin(src, dest, len);
362 #else
363 	(void) ficlStackPopInteger(ficlVmGetDataStack(pVM));
364 	(void) ficlStackPopInteger(ficlVmGetDataStack(pVM));
365 	(void) ficlStackPopPointer(ficlVmGetDataStack(pVM));
366 #endif
367 }
368 
369 void
370 ficlCopyout(ficlVm *pVM)
371 {
372 #ifdef _STANDALONE
373 	void*		dest;
374 	vm_offset_t	src;
375 	size_t		len;
376 #endif
377 
378 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 3, 0);
379 
380 #ifdef _STANDALONE
381 	len = ficlStackPopInteger(ficlVmGetDataStack(pVM));
382 	dest = ficlStackPopPointer(ficlVmGetDataStack(pVM));
383 	src = ficlStackPopInteger(ficlVmGetDataStack(pVM));
384 	archsw.arch_copyout(src, dest, len);
385 #else
386 	(void) ficlStackPopInteger(ficlVmGetDataStack(pVM));
387 	(void) ficlStackPopPointer(ficlVmGetDataStack(pVM));
388 	(void) ficlStackPopInteger(ficlVmGetDataStack(pVM));
389 #endif
390 }
391 
392 void
393 ficlFindfile(ficlVm *pVM)
394 {
395 #ifdef _STANDALONE
396 	char	*name, *type;
397 	char	*namep, *typep;
398 	int	names, types;
399 #endif
400 	struct	preloaded_file *fp;
401 
402 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 4, 1);
403 
404 #ifdef _STANDALONE
405 	types = ficlStackPopInteger(ficlVmGetDataStack(pVM));
406 	typep = (char *)ficlStackPopPointer(ficlVmGetDataStack(pVM));
407 	names = ficlStackPopInteger(ficlVmGetDataStack(pVM));
408 	namep = (char *)ficlStackPopPointer(ficlVmGetDataStack(pVM));
409 
410 	name = (char *)ficlMalloc(names+1);
411 	if (!name)
412 		ficlVmThrowError(pVM, "Error: out of memory");
413 	strncpy(name, namep, names);
414 	name[names] = '\0';
415 	type = (char *)ficlMalloc(types+1);
416 	if (!type)
417 		ficlVmThrowError(pVM, "Error: out of memory");
418 	strncpy(type, typep, types);
419 	type[types] = '\0';
420 
421 	fp = file_findfile(name, type);
422 #else
423 	(void) ficlStackPopInteger(ficlVmGetDataStack(pVM));
424 	(void) ficlStackPopPointer(ficlVmGetDataStack(pVM));
425 	(void) ficlStackPopInteger(ficlVmGetDataStack(pVM));
426 	(void) ficlStackPopPointer(ficlVmGetDataStack(pVM));
427 
428 	fp = NULL;
429 #endif
430 	ficlStackPushPointer(ficlVmGetDataStack(pVM), fp);
431 }
432 
433 /*
434  *	isvirtualized? - Return whether the loader runs under a
435  *			hypervisor.
436  *
437  * isvirtualized? ( -- addr len flag | flag )
438  */
439 static void
440 ficlIsvirtualizedQ(ficlVm *pVM)
441 {
442 	const char *hv;
443 
444 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 0, 3);
445 
446 #ifdef _STANDALONE
447 	hv = (archsw.arch_hypervisor != NULL)
448 	    ? (*archsw.arch_hypervisor)()
449 	    : NULL;
450 #else
451 	hv = NULL;
452 #endif
453 	if (hv != NULL) {
454 		ficlStackPushPointer(ficlVmGetDataStack(pVM), (void *)hv);
455 		ficlStackPushInteger(ficlVmGetDataStack(pVM), strlen(hv));
456 		ficlStackPushInteger(ficlVmGetDataStack(pVM), FICL_TRUE);
457 	} else {
458 		ficlStackPushInteger(ficlVmGetDataStack(pVM), FICL_FALSE);
459 	}
460 }
461 
462 void
463 ficlCcall(ficlVm *pVM)
464 {
465 	int (*func)(int, ...);
466 	int result, p[10];
467 	int nparam, i;
468 
469 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 2, 0);
470 
471 	func = (int (*)(int, ...))ficlStackPopPointer(ficlVmGetDataStack(pVM));
472 	nparam = ficlStackPopInteger(ficlVmGetDataStack(pVM));
473 
474 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), nparam, 1);
475 
476 	for (i = 0; i < nparam; i++)
477 		p[i] = ficlStackPopInteger(ficlVmGetDataStack(pVM));
478 
479 	result = func(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8],
480 	    p[9]);
481 
482 	ficlStackPushInteger(ficlVmGetDataStack(pVM), result);
483 }
484 
485 void
486 ficlUuidFromString(ficlVm *pVM)
487 {
488 	char	*uuid;
489 	char	*uuid_ptr;
490 	int	uuid_size;
491 	uuid_t	*u;
492 #ifdef _STANDALONE
493 	uint32_t status;
494 #else
495 	int status;
496 #endif
497 
498 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 2, 0);
499 
500 	uuid_size = ficlStackPopInteger(ficlVmGetDataStack(pVM));
501 	uuid_ptr = ficlStackPopPointer(ficlVmGetDataStack(pVM));
502 
503 	uuid = ficlMalloc(uuid_size + 1);
504 	if (!uuid)
505 		ficlVmThrowError(pVM, "Error: out of memory");
506 	(void) memcpy(uuid, uuid_ptr, uuid_size);
507 	uuid[uuid_size] = '\0';
508 
509 	u = ficlMalloc(sizeof (*u));
510 #ifdef _STANDALONE
511 	uuid_from_string(uuid, u, &status);
512 	ficlFree(uuid);
513 	if (status != uuid_s_ok) {
514 		ficlFree(u);
515 		u = NULL;
516 	}
517 #else
518 	status = uuid_parse(uuid, *u);
519 	ficlFree(uuid);
520 	if (status != 0) {
521 		ficlFree(u);
522 		u = NULL;
523 	}
524 #endif
525 	ficlStackPushPointer(ficlVmGetDataStack(pVM), u);
526 }
527 
528 void
529 ficlUuidToString(ficlVm *pVM)
530 {
531 	char	*uuid;
532 	uuid_t	*u;
533 #ifdef _STANDALONE
534 	uint32_t status;
535 #endif
536 
537 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 1, 0);
538 
539 	u = ficlStackPopPointer(ficlVmGetDataStack(pVM));
540 #ifdef _STANDALONE
541 	uuid_to_string(u, &uuid, &status);
542 	if (status == uuid_s_ok) {
543 		ficlStackPushPointer(ficlVmGetDataStack(pVM), uuid);
544 		ficlStackPushInteger(ficlVmGetDataStack(pVM), strlen(uuid));
545 	} else
546 #else
547 	uuid = ficlMalloc(UUID_PRINTABLE_STRING_LENGTH);
548 	if (uuid != NULL) {
549 		uuid_unparse(*u, uuid);
550 		ficlStackPushPointer(ficlVmGetDataStack(pVM), uuid);
551 		ficlStackPushInteger(ficlVmGetDataStack(pVM), strlen(uuid));
552 	} else
553 #endif
554 		ficlStackPushInteger(ficlVmGetDataStack(pVM), -1);
555 }
556 
557 /*
558  * f i c l E x e c F D
559  * reads in text from file fd and passes it to ficlExec()
560  * returns FICL_VM_STATUS_OUT_OF_TEXT on success or the ficlExec() error
561  * code on failure.
562  */
563 #define	nLINEBUF	256
564 int
565 ficlExecFD(ficlVm *pVM, int fd)
566 {
567 	char cp[nLINEBUF];
568 	int nLine = 0, rval = FICL_VM_STATUS_OUT_OF_TEXT;
569 	char ch;
570 	ficlCell id;
571 	ficlString s;
572 
573 	id = pVM->sourceId;
574 	pVM->sourceId.i = fd+1; /* in loader we can get 0, there is no stdin */
575 
576 	/* feed each line to ficlExec */
577 	while (1) {
578 		int status, i;
579 
580 		i = 0;
581 		while ((status = read(fd, &ch, 1)) > 0 && ch != '\n')
582 			cp[i++] = ch;
583 		nLine++;
584 		if (!i) {
585 			if (status < 1)
586 				break;
587 			continue;
588 		}
589 		if (cp[i] == '\n')
590 			cp[i] = '\0';
591 
592 		FICL_STRING_SET_POINTER(s, cp);
593 		FICL_STRING_SET_LENGTH(s, i);
594 
595 		rval = ficlVmExecuteString(pVM, s);
596 		if (rval != FICL_VM_STATUS_QUIT &&
597 		    rval != FICL_VM_STATUS_USER_EXIT &&
598 		    rval != FICL_VM_STATUS_OUT_OF_TEXT) {
599 			pVM->sourceId = id;
600 			(void) ficlVmEvaluate(pVM, "");
601 			return (rval);
602 		}
603 	}
604 	pVM->sourceId = id;
605 
606 	/*
607 	 * Pass an empty line with SOURCE-ID == -1 to flush
608 	 * any pending REFILLs (as required by FILE wordset)
609 	 */
610 	(void) ficlVmEvaluate(pVM, "");
611 
612 	if (rval == FICL_VM_STATUS_USER_EXIT)
613 		ficlVmThrow(pVM, FICL_VM_STATUS_USER_EXIT);
614 
615 	return (rval);
616 }
617 
618 static void displayCellNoPad(ficlVm *pVM)
619 {
620 	ficlCell c;
621 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 1, 0);
622 
623 	c = ficlStackPop(ficlVmGetDataStack(pVM));
624 	(void) ficlLtoa((c).i, pVM->pad, pVM->base);
625 	ficlVmTextOut(pVM, pVM->pad);
626 }
627 
628 /*
629  * isdir? - Return whether an fd corresponds to a directory.
630  *
631  * isdir? ( fd -- bool )
632  */
633 static void
634 isdirQuestion(ficlVm *pVM)
635 {
636 	struct stat sb;
637 	ficlInteger flag;
638 	int fd;
639 
640 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 1, 1);
641 
642 	fd = ficlStackPopInteger(ficlVmGetDataStack(pVM));
643 	flag = FICL_FALSE;
644 	do {
645 		if (fd < 0)
646 			break;
647 		if (fstat(fd, &sb) < 0)
648 			break;
649 		if (!S_ISDIR(sb.st_mode))
650 			break;
651 		flag = FICL_TRUE;
652 	} while (0);
653 	ficlStackPushInteger(ficlVmGetDataStack(pVM), flag);
654 }
655 
656 /*
657  * fopen - open a file and return new fd on stack.
658  *
659  * fopen ( ptr count mode -- fd )
660  */
661 extern char *get_dev(const char *);
662 
663 static void
664 pfopen(ficlVm *pVM)
665 {
666 	int mode, fd, count;
667 	char *ptr, *name;
668 #ifndef _STANDALONE
669 	char *tmp;
670 #endif
671 
672 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 3, 1);
673 
674 	mode = ficlStackPopInteger(ficlVmGetDataStack(pVM));	/* get mode */
675 	count = ficlStackPopInteger(ficlVmGetDataStack(pVM));	/* get count */
676 	ptr = ficlStackPopPointer(ficlVmGetDataStack(pVM));	/* get ptr */
677 
678 	if ((count < 0) || (ptr == NULL)) {
679 		ficlStackPushInteger(ficlVmGetDataStack(pVM), -1);
680 		return;
681 	}
682 
683 	/* ensure that the string is null terminated */
684 	name = (char *)malloc(count+1);
685 	bcopy(ptr, name, count);
686 	name[count] = 0;
687 #ifndef _STANDALONE
688 	tmp = get_dev(name);
689 	free(name);
690 	name = tmp;
691 #endif
692 
693 	/* open the file */
694 	fd = open(name, mode);
695 	free(name);
696 	ficlStackPushInteger(ficlVmGetDataStack(pVM), fd);
697 }
698 
699 /*
700  * fclose - close a file who's fd is on stack.
701  * fclose ( fd -- )
702  */
703 static void
704 pfclose(ficlVm *pVM)
705 {
706 	int fd;
707 
708 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 1, 0);
709 
710 	fd = ficlStackPopInteger(ficlVmGetDataStack(pVM)); /* get fd */
711 	if (fd != -1)
712 		(void) close(fd);
713 }
714 
715 /*
716  * fread - read file contents
717  * fread  ( fd buf nbytes  -- nread )
718  */
719 static void
720 pfread(ficlVm *pVM)
721 {
722 	int fd, len;
723 	char *buf;
724 
725 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 3, 1);
726 
727 	len = ficlStackPopInteger(ficlVmGetDataStack(pVM));
728 	buf = ficlStackPopPointer(ficlVmGetDataStack(pVM)); /* get buffer */
729 	fd = ficlStackPopInteger(ficlVmGetDataStack(pVM)); /* get fd */
730 	if (len > 0 && buf && fd != -1)
731 		ficlStackPushInteger(ficlVmGetDataStack(pVM),
732 		    read(fd, buf, len));
733 	else
734 		ficlStackPushInteger(ficlVmGetDataStack(pVM), -1);
735 }
736 
737 /*
738  * fopendir - open directory
739  *
740  * fopendir ( addr len -- ptr TRUE | FALSE )
741  */
742 static void pfopendir(ficlVm *pVM)
743 {
744 #ifndef _STANDALONE
745 	DIR *dir;
746 	char *tmp;
747 #else
748 	struct stat sb;
749 	int fd;
750 #endif
751 	ficlInteger count;
752 	char *ptr, *name;
753 	ficlInteger flag = FICL_FALSE;
754 
755 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 2, 1);
756 
757 	count = ficlStackPopInteger(ficlVmGetDataStack(pVM));
758 	ptr = ficlStackPopPointer(ficlVmGetDataStack(pVM));	/* get ptr */
759 
760 	if ((count < 0) || (ptr == NULL)) {
761 		ficlStackPushInteger(ficlVmGetDataStack(pVM), flag);
762 		return;
763 	}
764 	/* ensure that the string is null terminated */
765 	if ((name = malloc(count + 1)) == NULL) {
766 		ficlStackPushInteger(ficlVmGetDataStack(pVM), flag);
767 		return;
768 	}
769 
770 	bcopy(ptr, name, count);
771 	name[count] = 0;
772 #ifndef _STANDALONE
773 	tmp = get_dev(name);
774 	free(name);
775 	name = tmp;
776 
777 	dir = opendir(name);
778 	if (dir == NULL) {
779 		ficlStackPushInteger(ficlVmGetDataStack(pVM), flag);
780 		return;
781 	} else
782 		flag = FICL_TRUE;
783 
784 	ficlStackPushPointer(ficlVmGetDataStack(pVM), dir);
785 	ficlStackPushInteger(ficlVmGetDataStack(pVM), flag);
786 #else
787 	fd = open(name, O_RDONLY);
788 	free(name);
789 	do {
790 		if (fd < 0)
791 			break;
792 		if (fstat(fd, &sb) < 0)
793 			break;
794 		if (!S_ISDIR(sb.st_mode))
795 			break;
796 		flag = FICL_TRUE;
797 		ficlStackPushInteger(ficlVmGetDataStack(pVM), fd);
798 		ficlStackPushInteger(ficlVmGetDataStack(pVM), flag);
799 		return;
800 	} while (0);
801 
802 	if (fd >= 0)
803 		close(fd);
804 
805 	ficlStackPushInteger(ficlVmGetDataStack(pVM), flag);
806 #endif
807 }
808 
809 /*
810  * freaddir - read directory contents
811  * freaddir ( fd -- ptr len TRUE | FALSE )
812  */
813 static void
814 pfreaddir(ficlVm *pVM)
815 {
816 #ifndef _STANDALONE
817 	static DIR *dir = NULL;
818 #else
819 	int fd;
820 #endif
821 	struct dirent *d = NULL;
822 
823 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 1, 3);
824 	/*
825 	 * libstand readdir does not always return . nor .. so filter
826 	 * them out to have consistent behaviour.
827 	 */
828 #ifndef _STANDALONE
829 	dir = ficlStackPopPointer(ficlVmGetDataStack(pVM));
830 	if (dir != NULL)
831 		do {
832 			d = readdir(dir);
833 			if (d != NULL && strcmp(d->d_name, ".") == 0)
834 				continue;
835 			if (d != NULL && strcmp(d->d_name, "..") == 0)
836 				continue;
837 			break;
838 		} while (d != NULL);
839 #else
840 	fd = ficlStackPopInteger(ficlVmGetDataStack(pVM));
841 	if (fd != -1)
842 		do {
843 			d = readdirfd(fd);
844 			if (d != NULL && strcmp(d->d_name, ".") == 0)
845 				continue;
846 			if (d != NULL && strcmp(d->d_name, "..") == 0)
847 				continue;
848 			break;
849 		} while (d != NULL);
850 #endif
851 	if (d != NULL) {
852 		ficlStackPushPointer(ficlVmGetDataStack(pVM), d->d_name);
853 		ficlStackPushInteger(ficlVmGetDataStack(pVM),
854 		    strlen(d->d_name));
855 		ficlStackPushInteger(ficlVmGetDataStack(pVM), FICL_TRUE);
856 	} else {
857 		ficlStackPushInteger(ficlVmGetDataStack(pVM), FICL_FALSE);
858 	}
859 }
860 
861 /*
862  * fclosedir - close a dir on stack.
863  *
864  * fclosedir ( fd -- )
865  */
866 static void
867 pfclosedir(ficlVm *pVM)
868 {
869 #ifndef _STANDALONE
870 	DIR *dir;
871 #else
872 	int fd;
873 #endif
874 
875 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 1, 0);
876 
877 #ifndef _STANDALONE
878 	dir = ficlStackPopPointer(ficlVmGetDataStack(pVM)); /* get dir */
879 	if (dir != NULL)
880 		(void) closedir(dir);
881 #else
882 	fd = ficlStackPopInteger(ficlVmGetDataStack(pVM)); /* get fd */
883 	if (fd != -1)
884 		(void) close(fd);
885 #endif
886 }
887 
888 /*
889  * fload - interpret file contents
890  *
891  * fload  ( fd -- )
892  */
893 static void pfload(ficlVm *pVM)
894 {
895 	int fd;
896 
897 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 1, 0);
898 
899 	fd = ficlStackPopInteger(ficlVmGetDataStack(pVM)); /* get fd */
900 	if (fd != -1)
901 		(void) ficlExecFD(pVM, fd);
902 }
903 
904 /*
905  * fwrite - write file contents
906  *
907  * fwrite  ( fd buf nbytes  -- nwritten )
908  */
909 static void
910 pfwrite(ficlVm *pVM)
911 {
912 	int fd, len;
913 	char *buf;
914 
915 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 3, 1);
916 
917 	len = ficlStackPopInteger(ficlVmGetDataStack(pVM)); /* bytes to read */
918 	buf = ficlStackPopPointer(ficlVmGetDataStack(pVM)); /* get buffer */
919 	fd = ficlStackPopInteger(ficlVmGetDataStack(pVM)); /* get fd */
920 	if (len > 0 && buf && fd != -1)
921 		ficlStackPushInteger(ficlVmGetDataStack(pVM),
922 		    write(fd, buf, len));
923 	else
924 		ficlStackPushInteger(ficlVmGetDataStack(pVM), -1);
925 }
926 
927 /*
928  * fseek - seek to a new position in a file
929  *
930  * fseek  ( fd ofs whence  -- pos )
931  */
932 static void
933 pfseek(ficlVm *pVM)
934 {
935 	int fd, pos, whence;
936 
937 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 3, 1);
938 
939 	whence = ficlStackPopInteger(ficlVmGetDataStack(pVM));
940 	pos = ficlStackPopInteger(ficlVmGetDataStack(pVM));
941 	fd = ficlStackPopInteger(ficlVmGetDataStack(pVM));
942 	ficlStackPushInteger(ficlVmGetDataStack(pVM), lseek(fd, pos, whence));
943 }
944 
945 /*
946  * key - get a character from stdin
947  *
948  * key ( -- char )
949  */
950 static void
951 key(ficlVm *pVM)
952 {
953 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 0, 1);
954 
955 	ficlStackPushInteger(ficlVmGetDataStack(pVM), getchar());
956 }
957 
958 /*
959  * key? - check for a character from stdin (FACILITY)
960  * key? ( -- flag )
961  */
962 static void
963 keyQuestion(ficlVm *pVM)
964 {
965 #ifndef _STANDALONE
966 	char ch = -1;
967 	struct termios oldt;
968 	struct termios newt;
969 #endif
970 
971 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 0, 1);
972 
973 #ifndef _STANDALONE
974 	(void) tcgetattr(STDIN_FILENO, &oldt);
975 	newt = oldt;
976 	newt.c_lflag &= ~(ICANON | ECHO);
977 	newt.c_cc[VMIN] = 0;
978 	newt.c_cc[VTIME] = 0;
979 	(void) tcsetattr(STDIN_FILENO, TCSANOW, &newt);
980 	ch = getchar();
981 	(void) tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
982 
983 	if (ch != -1)
984 		(void) ungetc(ch, stdin);
985 
986 	ficlStackPushInteger(ficlVmGetDataStack(pVM),
987 	    ch != -1? FICL_TRUE : FICL_FALSE);
988 #else
989 	ficlStackPushInteger(ficlVmGetDataStack(pVM),
990 	    ischar()? FICL_TRUE : FICL_FALSE);
991 #endif
992 }
993 
994 /*
995  * seconds - gives number of seconds since beginning of time
996  *
997  * beginning of time is defined as:
998  *
999  *	BTX	- number of seconds since midnight
1000  *	FreeBSD	- number of seconds since Jan 1 1970
1001  *
1002  * seconds ( -- u )
1003  */
1004 static void
1005 pseconds(ficlVm *pVM)
1006 {
1007 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 0, 1);
1008 
1009 	ficlStackPushUnsigned(ficlVmGetDataStack(pVM),
1010 	    (ficlUnsigned) time(NULL));
1011 }
1012 
1013 /*
1014  * ms - wait at least that many milliseconds (FACILITY)
1015  * ms ( u -- )
1016  */
1017 static void
1018 ms(ficlVm *pVM)
1019 {
1020 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 1, 0);
1021 
1022 #ifndef _STANDALONE
1023 	(void) usleep(ficlStackPopUnsigned(ficlVmGetDataStack(pVM)) * 1000);
1024 #else
1025 	delay(ficlStackPopUnsigned(ficlVmGetDataStack(pVM)) * 1000);
1026 #endif
1027 }
1028 
1029 /*
1030  * fkey - get a character from a file
1031  * fkey ( file -- char )
1032  */
1033 static void
1034 fkey(ficlVm *pVM)
1035 {
1036 	int i, fd;
1037 	char ch;
1038 
1039 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 1, 1);
1040 
1041 	fd = ficlStackPopInteger(ficlVmGetDataStack(pVM));
1042 	i = read(fd, &ch, 1);
1043 	ficlStackPushInteger(ficlVmGetDataStack(pVM), i > 0 ? ch : -1);
1044 }
1045 
1046 /*
1047  * Retrieves free space remaining on the dictionary
1048  */
1049 static void
1050 freeHeap(ficlVm *pVM)
1051 {
1052 	ficlStackPushInteger(ficlVmGetDataStack(pVM),
1053 	    ficlDictionaryCellsAvailable(ficlVmGetDictionary(pVM)));
1054 }
1055 
1056 /*
1057  * f i c l C o m p i l e P l a t f o r m
1058  * Build FreeBSD platform extensions into the system dictionary
1059  */
1060 void
1061 ficlSystemCompilePlatform(ficlSystem *pSys)
1062 {
1063 	ficlDictionary *dp = ficlSystemGetDictionary(pSys);
1064 	ficlDictionary *env = ficlSystemGetEnvironment(pSys);
1065 #ifdef _STANDALONE
1066 	ficlCompileFcn **fnpp;
1067 #endif
1068 
1069 	FICL_SYSTEM_ASSERT(pSys, dp);
1070 	FICL_SYSTEM_ASSERT(pSys, env);
1071 
1072 	(void) ficlDictionarySetPrimitive(dp, ".#", displayCellNoPad,
1073 	    FICL_WORD_DEFAULT);
1074 	(void) ficlDictionarySetPrimitive(dp, "isdir?", isdirQuestion,
1075 	    FICL_WORD_DEFAULT);
1076 	(void) ficlDictionarySetPrimitive(dp, "fopen", pfopen,
1077 	    FICL_WORD_DEFAULT);
1078 	(void) ficlDictionarySetPrimitive(dp, "fclose", pfclose,
1079 	    FICL_WORD_DEFAULT);
1080 	(void) ficlDictionarySetPrimitive(dp, "fread", pfread,
1081 	    FICL_WORD_DEFAULT);
1082 	(void) ficlDictionarySetPrimitive(dp, "fopendir", pfopendir,
1083 	    FICL_WORD_DEFAULT);
1084 	(void) ficlDictionarySetPrimitive(dp, "freaddir", pfreaddir,
1085 	    FICL_WORD_DEFAULT);
1086 	(void) ficlDictionarySetPrimitive(dp, "fclosedir", pfclosedir,
1087 	    FICL_WORD_DEFAULT);
1088 	(void) ficlDictionarySetPrimitive(dp, "fload", pfload,
1089 	    FICL_WORD_DEFAULT);
1090 	(void) ficlDictionarySetPrimitive(dp, "fkey", fkey,
1091 	    FICL_WORD_DEFAULT);
1092 	(void) ficlDictionarySetPrimitive(dp, "fseek", pfseek,
1093 	    FICL_WORD_DEFAULT);
1094 	(void) ficlDictionarySetPrimitive(dp, "fwrite", pfwrite,
1095 	    FICL_WORD_DEFAULT);
1096 	(void) ficlDictionarySetPrimitive(dp, "key", key, FICL_WORD_DEFAULT);
1097 	(void) ficlDictionarySetPrimitive(dp, "key?", keyQuestion,
1098 	    FICL_WORD_DEFAULT);
1099 	(void) ficlDictionarySetPrimitive(dp, "ms", ms, FICL_WORD_DEFAULT);
1100 	(void) ficlDictionarySetPrimitive(dp, "seconds", pseconds,
1101 	    FICL_WORD_DEFAULT);
1102 	(void) ficlDictionarySetPrimitive(dp, "heap?", freeHeap,
1103 	    FICL_WORD_DEFAULT);
1104 
1105 	(void) ficlDictionarySetPrimitive(dp, "setenv", ficlSetenv,
1106 	    FICL_WORD_DEFAULT);
1107 	(void) ficlDictionarySetPrimitive(dp, "setenv?", ficlSetenvq,
1108 	    FICL_WORD_DEFAULT);
1109 	(void) ficlDictionarySetPrimitive(dp, "getenv", ficlGetenv,
1110 	    FICL_WORD_DEFAULT);
1111 	(void) ficlDictionarySetPrimitive(dp, "unsetenv", ficlUnsetenv,
1112 	    FICL_WORD_DEFAULT);
1113 	(void) ficlDictionarySetPrimitive(dp, "copyin", ficlCopyin,
1114 	    FICL_WORD_DEFAULT);
1115 	(void) ficlDictionarySetPrimitive(dp, "copyout", ficlCopyout,
1116 	    FICL_WORD_DEFAULT);
1117 	(void) ficlDictionarySetPrimitive(dp, "findfile", ficlFindfile,
1118 	    FICL_WORD_DEFAULT);
1119 	(void) ficlDictionarySetPrimitive(dp, "isvirtualized?",
1120 	    ficlIsvirtualizedQ, FICL_WORD_DEFAULT);
1121 	(void) ficlDictionarySetPrimitive(dp, "ccall", ficlCcall,
1122 	    FICL_WORD_DEFAULT);
1123 	(void) ficlDictionarySetPrimitive(dp, "uuid-from-string",
1124 	    ficlUuidFromString, FICL_WORD_DEFAULT);
1125 	(void) ficlDictionarySetPrimitive(dp, "uuid-to-string",
1126 	    ficlUuidToString, FICL_WORD_DEFAULT);
1127 	(void) ficlDictionarySetPrimitive(dp, "fb-setpixel", ficl_fb_setpixel,
1128 	    FICL_WORD_DEFAULT);
1129 	(void) ficlDictionarySetPrimitive(dp, "fb-line", ficl_fb_line,
1130 	    FICL_WORD_DEFAULT);
1131 	(void) ficlDictionarySetPrimitive(dp, "fb-bezier", ficl_fb_bezier,
1132 	    FICL_WORD_DEFAULT);
1133 	(void) ficlDictionarySetPrimitive(dp, "fb-drawrect", ficl_fb_drawrect,
1134 	    FICL_WORD_DEFAULT);
1135 	(void) ficlDictionarySetPrimitive(dp, "fb-putimage", ficl_fb_putimage,
1136 	    FICL_WORD_DEFAULT);
1137 	(void) ficlDictionarySetPrimitive(dp, "term-drawrect",
1138 	    ficl_term_drawrect, FICL_WORD_DEFAULT);
1139 #ifdef _STANDALONE
1140 	(void) ficlDictionarySetPrimitive(dp, "term-putimage",
1141 	    ficl_term_putimage, FICL_WORD_DEFAULT);
1142 	/* Register words from linker set. */
1143 	SET_FOREACH(fnpp, Xficl_compile_set)
1144 		(*fnpp)(pSys);
1145 #endif
1146 }
1147