xref: /freebsd/stand/ficl/loader.c (revision 5e3190f700637fcfc1a52daeaa4a031fdd2557c7)
1 /*-
2  * Copyright (c) 2000 Daniel Capo Sobral
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 ** l o a d e r . c
29 ** Additional FICL words designed for FreeBSD's loader
30 **
31 *******************************************************************/
32 
33 #ifdef TESTMAIN
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <dirent.h>
37 #include <fcntl.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #else
42 #include <stand.h>
43 #endif
44 #include "bootstrap.h"
45 #include <string.h>
46 #include <uuid.h>
47 #include <gfx_fb.h>
48 #include <pnglite.h>
49 #include "ficl.h"
50 
51 /*		FreeBSD's loader interaction words and extras
52  *
53  * 		setenv      ( value n name n' -- )
54  * 		setenv?     ( value n name n' flag -- )
55  * 		getenv      ( addr n -- addr' n' | -1 )
56  * 		unsetenv    ( addr n -- )
57  * 		copyin      ( addr addr' len -- )
58  * 		copyout     ( addr addr' len -- )
59  * 		findfile    ( name len type len' -- addr )
60  * 		pnpdevices  ( -- addr )
61  * 		pnphandlers ( -- addr )
62  * 		ccall       ( [[...[p10] p9] ... p1] n addr -- result )
63  *		uuid-from-string ( addr n -- addr' )
64  *		uuid-to-string ( addr' -- addr n )
65  * 		.#	    ( value -- )
66  */
67 
68 #ifndef TESTMAIN
69 /* ( flags x1 y1 x2 y2 -- flag ) */
70 void
71 ficl_term_putimage(FICL_VM *pVM)
72 {
73         char *namep, *name;
74         int names;
75         unsigned long ret = FICL_FALSE;
76         uint32_t x1, y1, x2, y2, f;
77         png_t png;
78 	int error;
79 
80 #if FICL_ROBUST > 1
81 	vmCheckStack(pVM, 7, 1);
82 #endif
83         names = stackPopINT(pVM->pStack);
84         namep = (char *) stackPopPtr(pVM->pStack);
85         y2 = stackPopINT(pVM->pStack);
86         x2 = stackPopINT(pVM->pStack);
87         y1 = stackPopINT(pVM->pStack);
88         x1 = stackPopINT(pVM->pStack);
89         f = stackPopINT(pVM->pStack);
90 
91 	x1 = gfx_state.tg_origin.tp_col + x1 * gfx_state.tg_font.vf_width;
92 	y1 = gfx_state.tg_origin.tp_row + y1 * gfx_state.tg_font.vf_height;
93 	if (x2 != 0) {
94 		x2 = gfx_state.tg_origin.tp_col +
95 		    x2 * gfx_state.tg_font.vf_width;
96 	}
97 	if (y2 != 0) {
98 		y2 = gfx_state.tg_origin.tp_row +
99 		    y2 * gfx_state.tg_font.vf_height;
100 	}
101 
102         name = ficlMalloc(names + 1);
103         if (!name)
104 		vmThrowErr(pVM, "Error: out of memory");
105         (void) strncpy(name, namep, names);
106         name[names] = '\0';
107 
108         if ((error = png_open(&png, name)) != PNG_NO_ERROR) {
109 		if (f & FL_PUTIMAGE_DEBUG)
110 			printf("%s\n", png_error_string(error));
111 	} else {
112                 if (gfx_fb_putimage(&png, x1, y1, x2, y2, f) == 0)
113                         ret = FICL_TRUE;        /* success */
114                 (void) png_close(&png);
115 	}
116         ficlFree(name);
117 	stackPushUNS(pVM->pStack, ret);
118 }
119 
120 /* ( flags x1 y1 x2 y2 -- flag ) */
121 void
122 ficl_fb_putimage(FICL_VM *pVM)
123 {
124         char *namep, *name;
125         int names;
126         unsigned long ret = FICL_FALSE;
127         uint32_t x1, y1, x2, y2, f;
128         png_t png;
129 	int error;
130 
131 #if FICL_ROBUST > 1
132 	vmCheckStack(pVM, 7, 1);
133 #endif
134         names = stackPopINT(pVM->pStack);
135         namep = (char *) stackPopPtr(pVM->pStack);
136         y2 = stackPopINT(pVM->pStack);
137         x2 = stackPopINT(pVM->pStack);
138         y1 = stackPopINT(pVM->pStack);
139         x1 = stackPopINT(pVM->pStack);
140         f = stackPopINT(pVM->pStack);
141 
142         name = ficlMalloc(names + 1);
143         if (!name)
144 		vmThrowErr(pVM, "Error: out of memory");
145         (void) strncpy(name, namep, names);
146         name[names] = '\0';
147 
148         if ((error = png_open(&png, name)) != PNG_NO_ERROR) {
149 		if (f & FL_PUTIMAGE_DEBUG)
150 			printf("%s\n", png_error_string(error));
151 	} else {
152                 if (gfx_fb_putimage(&png, x1, y1, x2, y2, f) == 0)
153                         ret = FICL_TRUE;        /* success */
154                 (void) png_close(&png);
155 	}
156         ficlFree(name);
157 	stackPushUNS(pVM->pStack, ret);
158 }
159 
160 void
161 ficl_fb_setpixel(FICL_VM *pVM)
162 {
163         FICL_UNS x, y;
164 
165 #if FICL_ROBUST > 1
166 	vmCheckStack(pVM, 2, 0);
167 #endif
168 
169         y = stackPopUNS(pVM->pStack);
170         x = stackPopUNS(pVM->pStack);
171         gfx_fb_setpixel(x, y);
172 }
173 
174 void
175 ficl_fb_line(FICL_VM *pVM)
176 {
177 	FICL_UNS x0, y0, x1, y1, wd;
178 
179 #if FICL_ROBUST > 1
180 	vmCheckStack(pVM, 5, 0);
181 #endif
182 
183 	wd = stackPopUNS(pVM->pStack);
184 	y1 = stackPopUNS(pVM->pStack);
185 	x1 = stackPopUNS(pVM->pStack);
186 	y0 = stackPopUNS(pVM->pStack);
187 	x0 = stackPopUNS(pVM->pStack);
188 	gfx_fb_line(x0, y0, x1, y1, wd);
189 }
190 
191 void
192 ficl_fb_bezier(FICL_VM *pVM)
193 {
194 	FICL_UNS x0, y0, x1, y1, x2, y2, width;
195 
196 #if FICL_ROBUST > 1
197 	vmCheckStack(pVM, 7, 0);
198 #endif
199 
200 	width = stackPopUNS(pVM->pStack);
201 	y2 = stackPopUNS(pVM->pStack);
202 	x2 = stackPopUNS(pVM->pStack);
203 	y1 = stackPopUNS(pVM->pStack);
204 	x1 = stackPopUNS(pVM->pStack);
205 	y0 = stackPopUNS(pVM->pStack);
206 	x0 = stackPopUNS(pVM->pStack);
207 	gfx_fb_bezier(x0, y0, x1, y1, x2, y2, width);
208 }
209 
210 void
211 ficl_fb_drawrect(FICL_VM *pVM)
212 {
213 	FICL_UNS x1, x2, y1, y2, fill;
214 
215 #if FICL_ROBUST > 1
216 	vmCheckStack(pVM, 5, 0);
217 #endif
218 
219 	fill = stackPopUNS(pVM->pStack);
220 	y2 = stackPopUNS(pVM->pStack);
221 	x2 = stackPopUNS(pVM->pStack);
222 	y1 = stackPopUNS(pVM->pStack);
223 	x1 = stackPopUNS(pVM->pStack);
224 	gfx_fb_drawrect(x1, y1, x2, y2, fill);
225 }
226 
227 void
228 ficl_term_drawrect(FICL_VM *pVM)
229 {
230 	FICL_UNS x1, x2, y1, y2;
231 
232 #if FICL_ROBUST > 1
233 	vmCheckStack(pVM, 4, 0);
234 #endif
235 
236 	y2 = stackPopUNS(pVM->pStack);
237 	x2 = stackPopUNS(pVM->pStack);
238 	y1 = stackPopUNS(pVM->pStack);
239 	x1 = stackPopUNS(pVM->pStack);
240 	gfx_term_drawrect(x1, y1, x2, y2);
241 }
242 #endif	/* TESTMAIN */
243 
244 void
245 ficlSetenv(FICL_VM *pVM)
246 {
247 #ifndef TESTMAIN
248 	char	*name, *value;
249 #endif
250 	char	*namep, *valuep;
251 	int	names, values;
252 
253 #if FICL_ROBUST > 1
254 	vmCheckStack(pVM, 4, 0);
255 #endif
256 	names = stackPopINT(pVM->pStack);
257 	namep = (char*) stackPopPtr(pVM->pStack);
258 	values = stackPopINT(pVM->pStack);
259 	valuep = (char*) stackPopPtr(pVM->pStack);
260 
261 #ifndef TESTMAIN
262 	name = (char*) ficlMalloc(names+1);
263 	if (!name)
264 		vmThrowErr(pVM, "Error: out of memory");
265 	strncpy(name, namep, names);
266 	name[names] = '\0';
267 	value = (char*) ficlMalloc(values+1);
268 	if (!value)
269 		vmThrowErr(pVM, "Error: out of memory");
270 	strncpy(value, valuep, values);
271 	value[values] = '\0';
272 
273 	setenv(name, value, 1);
274 	ficlFree(name);
275 	ficlFree(value);
276 #endif
277 
278 	return;
279 }
280 
281 void
282 ficlSetenvq(FICL_VM *pVM)
283 {
284 #ifndef TESTMAIN
285 	char	*name, *value;
286 #endif
287 	char	*namep, *valuep;
288 	int	names, values, overwrite;
289 
290 #if FICL_ROBUST > 1
291 	vmCheckStack(pVM, 5, 0);
292 #endif
293 	overwrite = stackPopINT(pVM->pStack);
294 	names = stackPopINT(pVM->pStack);
295 	namep = (char*) stackPopPtr(pVM->pStack);
296 	values = stackPopINT(pVM->pStack);
297 	valuep = (char*) stackPopPtr(pVM->pStack);
298 
299 #ifndef TESTMAIN
300 	name = (char*) ficlMalloc(names+1);
301 	if (!name)
302 		vmThrowErr(pVM, "Error: out of memory");
303 	strncpy(name, namep, names);
304 	name[names] = '\0';
305 	value = (char*) ficlMalloc(values+1);
306 	if (!value)
307 		vmThrowErr(pVM, "Error: out of memory");
308 	strncpy(value, valuep, values);
309 	value[values] = '\0';
310 
311 	setenv(name, value, overwrite);
312 	ficlFree(name);
313 	ficlFree(value);
314 #endif
315 
316 	return;
317 }
318 
319 void
320 ficlGetenv(FICL_VM *pVM)
321 {
322 #ifndef TESTMAIN
323 	char	*name, *value;
324 #endif
325 	char	*namep;
326 	int	names;
327 
328 #if FICL_ROBUST > 1
329 	vmCheckStack(pVM, 2, 2);
330 #endif
331 	names = stackPopINT(pVM->pStack);
332 	namep = (char*) stackPopPtr(pVM->pStack);
333 
334 #ifndef TESTMAIN
335 	name = (char*) ficlMalloc(names+1);
336 	if (!name)
337 		vmThrowErr(pVM, "Error: out of memory");
338 	strncpy(name, namep, names);
339 	name[names] = '\0';
340 
341 	value = getenv(name);
342 	ficlFree(name);
343 
344 	if(value != NULL) {
345 		stackPushPtr(pVM->pStack, value);
346 		stackPushINT(pVM->pStack, strlen(value));
347 	} else
348 #endif
349 		stackPushINT(pVM->pStack, -1);
350 
351 	return;
352 }
353 
354 void
355 ficlUnsetenv(FICL_VM *pVM)
356 {
357 #ifndef TESTMAIN
358 	char	*name;
359 #endif
360 	char	*namep;
361 	int	names;
362 
363 #if FICL_ROBUST > 1
364 	vmCheckStack(pVM, 2, 0);
365 #endif
366 	names = stackPopINT(pVM->pStack);
367 	namep = (char*) stackPopPtr(pVM->pStack);
368 
369 #ifndef TESTMAIN
370 	name = (char*) ficlMalloc(names+1);
371 	if (!name)
372 		vmThrowErr(pVM, "Error: out of memory");
373 	strncpy(name, namep, names);
374 	name[names] = '\0';
375 
376 	unsetenv(name);
377 	ficlFree(name);
378 #endif
379 
380 	return;
381 }
382 
383 void
384 ficlCopyin(FICL_VM *pVM)
385 {
386 	void*		src;
387 	vm_offset_t	dest;
388 	size_t		len;
389 
390 #if FICL_ROBUST > 1
391 	vmCheckStack(pVM, 3, 0);
392 #endif
393 
394 	len = stackPopINT(pVM->pStack);
395 	dest = stackPopINT(pVM->pStack);
396 	src = stackPopPtr(pVM->pStack);
397 
398 #ifndef TESTMAIN
399 	archsw.arch_copyin(src, dest, len);
400 #endif
401 
402 	return;
403 }
404 
405 void
406 ficlCopyout(FICL_VM *pVM)
407 {
408 	void*		dest;
409 	vm_offset_t	src;
410 	size_t		len;
411 
412 #if FICL_ROBUST > 1
413 	vmCheckStack(pVM, 3, 0);
414 #endif
415 
416 	len = stackPopINT(pVM->pStack);
417 	dest = stackPopPtr(pVM->pStack);
418 	src = stackPopINT(pVM->pStack);
419 
420 #ifndef TESTMAIN
421 	archsw.arch_copyout(src, dest, len);
422 #endif
423 
424 	return;
425 }
426 
427 void
428 ficlFindfile(FICL_VM *pVM)
429 {
430 #ifndef TESTMAIN
431 	char	*name, *type;
432 #endif
433 	char	*namep, *typep;
434 	struct	preloaded_file* fp;
435 	int	names, types;
436 
437 #if FICL_ROBUST > 1
438 	vmCheckStack(pVM, 4, 1);
439 #endif
440 
441 	types = stackPopINT(pVM->pStack);
442 	typep = (char*) stackPopPtr(pVM->pStack);
443 	names = stackPopINT(pVM->pStack);
444 	namep = (char*) stackPopPtr(pVM->pStack);
445 #ifndef TESTMAIN
446 	name = (char*) ficlMalloc(names+1);
447 	if (!name)
448 		vmThrowErr(pVM, "Error: out of memory");
449 	strncpy(name, namep, names);
450 	name[names] = '\0';
451 	type = (char*) ficlMalloc(types+1);
452 	if (!type)
453 		vmThrowErr(pVM, "Error: out of memory");
454 	strncpy(type, typep, types);
455 	type[types] = '\0';
456 
457 	fp = file_findfile(name, type);
458 #else
459 	fp = NULL;
460 #endif
461 	stackPushPtr(pVM->pStack, fp);
462 
463 	return;
464 }
465 
466 #ifndef TESTMAIN
467 
468 /*	isvirtualized? - Return whether the loader runs under a
469  *			hypervisor.
470  *
471  * isvirtualized? ( -- flag )
472  */
473 static void
474 ficlIsvirtualizedQ(FICL_VM *pVM)
475 {
476 	FICL_INT flag;
477 	const char *hv;
478 
479 #if FICL_ROBUST > 1
480 	vmCheckStack(pVM, 0, 1);
481 #endif
482 
483 	hv = (archsw.arch_hypervisor != NULL)
484 	    ? (*archsw.arch_hypervisor)()
485 	    : NULL;
486 	flag = (hv != NULL) ? FICL_TRUE : FICL_FALSE;
487 	stackPushINT(pVM->pStack, flag);
488 }
489 
490 #endif /* ndef TESTMAIN */
491 
492 void
493 ficlCcall(FICL_VM *pVM)
494 {
495 	int (*func)(int, ...);
496 	int result, p[10];
497 	int nparam, i;
498 
499 #if FICL_ROBUST > 1
500 	vmCheckStack(pVM, 2, 0);
501 #endif
502 
503 	func = stackPopPtr(pVM->pStack);
504 	nparam = stackPopINT(pVM->pStack);
505 
506 #if FICL_ROBUST > 1
507 	vmCheckStack(pVM, nparam, 1);
508 #endif
509 
510 	for (i = 0; i < nparam; i++)
511 		p[i] = stackPopINT(pVM->pStack);
512 
513 	result = func(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8],
514 	    p[9]);
515 
516 	stackPushINT(pVM->pStack, result);
517 
518 	return;
519 }
520 
521 void
522 ficlUuidFromString(FICL_VM *pVM)
523 {
524 #ifndef	TESTMAIN
525 	char	*uuid;
526 	uint32_t status;
527 #endif
528 	char	*uuidp;
529 	int	uuids;
530 	uuid_t	*u;
531 
532 #if FICL_ROBUST > 1
533 	vmCheckStack(pVM, 2, 0);
534 #endif
535 
536 	uuids = stackPopINT(pVM->pStack);
537 	uuidp = (char *) stackPopPtr(pVM->pStack);
538 
539 #ifndef	TESTMAIN
540 	uuid = (char *)ficlMalloc(uuids + 1);
541 	if (!uuid)
542 		vmThrowErr(pVM, "Error: out of memory");
543 	strncpy(uuid, uuidp, uuids);
544 	uuid[uuids] = '\0';
545 
546 	u = (uuid_t *)ficlMalloc(sizeof (*u));
547 
548 	uuid_from_string(uuid, u, &status);
549 	ficlFree(uuid);
550 	if (status != uuid_s_ok) {
551 		ficlFree(u);
552 		u = NULL;
553 	}
554 #else
555 	u = NULL;
556 #endif
557 	stackPushPtr(pVM->pStack, u);
558 
559 
560 	return;
561 }
562 
563 void
564 ficlUuidToString(FICL_VM *pVM)
565 {
566 #ifndef	TESTMAIN
567 	char	*uuid;
568 	uint32_t status;
569 #endif
570 	uuid_t	*u;
571 
572 #if FICL_ROBUST > 1
573 	vmCheckStack(pVM, 1, 0);
574 #endif
575 
576 	u = (uuid_t *)stackPopPtr(pVM->pStack);
577 
578 #ifndef	TESTMAIN
579 	uuid_to_string(u, &uuid, &status);
580 	if (status != uuid_s_ok) {
581 		stackPushPtr(pVM->pStack, uuid);
582 		stackPushINT(pVM->pStack, strlen(uuid));
583 	} else
584 #endif
585 		stackPushINT(pVM->pStack, -1);
586 
587 	return;
588 }
589 
590 /**************************************************************************
591                         f i c l E x e c F D
592 ** reads in text from file fd and passes it to ficlExec()
593  * returns VM_OUTOFTEXT on success or the ficlExec() error code on
594  * failure.
595  */
596 #define nLINEBUF 256
597 int ficlExecFD(FICL_VM *pVM, int fd)
598 {
599     char    cp[nLINEBUF];
600     int     nLine = 0, rval = VM_OUTOFTEXT;
601     char    ch;
602     CELL    id;
603 
604     id = pVM->sourceID;
605     pVM->sourceID.i = fd;
606 
607     /* feed each line to ficlExec */
608     while (1) {
609 	int status, i;
610 
611 	i = 0;
612 	while ((status = read(fd, &ch, 1)) > 0 && ch != '\n')
613 	    cp[i++] = ch;
614         nLine++;
615 	if (!i) {
616 	    if (status < 1)
617 		break;
618 	    continue;
619 	}
620         rval = ficlExecC(pVM, cp, i);
621 	if(rval != VM_QUIT && rval != VM_USEREXIT && rval != VM_OUTOFTEXT)
622         {
623             pVM->sourceID = id;
624             return rval;
625         }
626     }
627     /*
628     ** Pass an empty line with SOURCE-ID == -1 to flush
629     ** any pending REFILLs (as required by FILE wordset)
630     */
631     pVM->sourceID.i = -1;
632     ficlExec(pVM, "");
633 
634     pVM->sourceID = id;
635     return rval;
636 }
637 
638 static void displayCellNoPad(FICL_VM *pVM)
639 {
640     CELL c;
641 #if FICL_ROBUST > 1
642     vmCheckStack(pVM, 1, 0);
643 #endif
644     c = stackPop(pVM->pStack);
645     ltoa((c).i, pVM->pad, pVM->base);
646     vmTextOut(pVM, pVM->pad, 0);
647     return;
648 }
649 
650 /*      isdir? - Return whether an fd corresponds to a directory.
651  *
652  * isdir? ( fd -- bool )
653  */
654 static void isdirQuestion(FICL_VM *pVM)
655 {
656     struct stat sb;
657     FICL_INT flag;
658     int fd;
659 
660 #if FICL_ROBUST > 1
661     vmCheckStack(pVM, 1, 1);
662 #endif
663 
664     fd = stackPopINT(pVM->pStack);
665     flag = FICL_FALSE;
666     do {
667         if (fd < 0)
668             break;
669         if (fstat(fd, &sb) < 0)
670             break;
671         if (!S_ISDIR(sb.st_mode))
672             break;
673         flag = FICL_TRUE;
674     } while (0);
675     stackPushINT(pVM->pStack, flag);
676 }
677 
678 /*          fopen - open a file and return new fd on stack.
679  *
680  * fopen ( ptr count mode -- fd )
681  */
682 static void pfopen(FICL_VM *pVM)
683 {
684     int     mode, fd, count;
685     char    *ptr, *name;
686 
687 #if FICL_ROBUST > 1
688     vmCheckStack(pVM, 3, 1);
689 #endif
690 
691     mode = stackPopINT(pVM->pStack);    /* get mode */
692     count = stackPopINT(pVM->pStack);   /* get count */
693     ptr = stackPopPtr(pVM->pStack);     /* get ptr */
694 
695     if ((count < 0) || (ptr == NULL)) {
696         stackPushINT(pVM->pStack, -1);
697         return;
698     }
699 
700     /* ensure that the string is null terminated */
701     name = (char *)malloc(count+1);
702     bcopy(ptr,name,count);
703     name[count] = 0;
704 
705     /* open the file */
706     fd = open(name, mode);
707 #ifdef LOADER_VERIEXEC
708     if (fd >= 0) {
709 	if (verify_file(fd, name, 0, VE_GUESS, __func__) < 0) {
710 	    /* not verified writing ok but reading is not */
711 	    if ((mode & O_ACCMODE) != O_WRONLY) {
712 		close(fd);
713 		fd = -1;
714 	    }
715 	} else {
716 	    /* verified reading ok but writing is not */
717 	    if ((mode & O_ACCMODE) != O_RDONLY) {
718 		close(fd);
719 		fd = -1;
720 	    }
721 	}
722     }
723 #endif
724     free(name);
725     stackPushINT(pVM->pStack, fd);
726     return;
727 }
728 
729 /*          fclose - close a file who's fd is on stack.
730  *
731  * fclose ( fd -- )
732  */
733 static void pfclose(FICL_VM *pVM)
734 {
735     int fd;
736 
737 #if FICL_ROBUST > 1
738     vmCheckStack(pVM, 1, 0);
739 #endif
740     fd = stackPopINT(pVM->pStack); /* get fd */
741     if (fd != -1)
742 	close(fd);
743     return;
744 }
745 
746 /*          fread - read file contents
747  *
748  * fread  ( fd buf nbytes  -- nread )
749  */
750 static void pfread(FICL_VM *pVM)
751 {
752     int     fd, len;
753     char *buf;
754 
755 #if FICL_ROBUST > 1
756     vmCheckStack(pVM, 3, 1);
757 #endif
758     len = stackPopINT(pVM->pStack); /* get number of bytes to read */
759     buf = stackPopPtr(pVM->pStack); /* get buffer */
760     fd = stackPopINT(pVM->pStack); /* get fd */
761     if (len > 0 && buf && fd != -1)
762 	stackPushINT(pVM->pStack, read(fd, buf, len));
763     else
764 	stackPushINT(pVM->pStack, -1);
765     return;
766 }
767 
768 /*      freaddir - read directory contents
769  *
770  * freaddir ( fd -- ptr len TRUE | FALSE )
771  */
772 static void pfreaddir(FICL_VM *pVM)
773 {
774 #ifdef TESTMAIN
775     static struct dirent dirent;
776     struct stat sb;
777     char *buf;
778     off_t off, ptr;
779     u_int blksz;
780     int bufsz;
781 #endif
782     struct dirent *d;
783     int fd;
784 
785 #if FICL_ROBUST > 1
786     vmCheckStack(pVM, 1, 3);
787 #endif
788 
789     fd = stackPopINT(pVM->pStack);
790 #if TESTMAIN
791     /*
792      * The readdirfd() function is specific to the loader environment.
793      * We do the best we can to make freaddir work, but it's not at
794      * all guaranteed.
795      */
796     d = NULL;
797     buf = NULL;
798     do {
799 	if (fd == -1)
800 	    break;
801 	if (fstat(fd, &sb) == -1)
802 	    break;
803 	blksz = (sb.st_blksize) ? sb.st_blksize : getpagesize();
804 	if ((blksz & (blksz - 1)) != 0)
805 	    break;
806 	buf = malloc(blksz);
807 	if (buf == NULL)
808 	    break;
809 	off = lseek(fd, 0LL, SEEK_CUR);
810 	if (off == -1)
811 	    break;
812 	ptr = off;
813 	if (lseek(fd, 0, SEEK_SET) == -1)
814 	    break;
815 	bufsz = getdents(fd, buf, blksz);
816 	while (bufsz > 0 && bufsz <= ptr) {
817 	    ptr -= bufsz;
818 	    bufsz = getdents(fd, buf, blksz);
819 	}
820 	if (bufsz <= 0)
821 	    break;
822 	d = (void *)(buf + ptr);
823 	dirent = *d;
824 	off += d->d_reclen;
825 	d = (lseek(fd, off, SEEK_SET) != off) ? NULL : &dirent;
826     } while (0);
827     if (buf != NULL)
828 	free(buf);
829 #else
830     d = readdirfd(fd);
831 #endif
832     if (d != NULL) {
833         stackPushPtr(pVM->pStack, d->d_name);
834         stackPushINT(pVM->pStack, strlen(d->d_name));
835         stackPushINT(pVM->pStack, FICL_TRUE);
836     } else {
837         stackPushINT(pVM->pStack, FICL_FALSE);
838     }
839 }
840 
841 /*          fload - interpret file contents
842  *
843  * fload  ( fd -- )
844  */
845 static void pfload(FICL_VM *pVM)
846 {
847     int     fd;
848 
849 #if FICL_ROBUST > 1
850     vmCheckStack(pVM, 1, 0);
851 #endif
852     fd = stackPopINT(pVM->pStack); /* get fd */
853     if (fd != -1)
854 	ficlExecFD(pVM, fd);
855     return;
856 }
857 
858 /*          fwrite - write file contents
859  *
860  * fwrite  ( fd buf nbytes  -- nwritten )
861  */
862 static void pfwrite(FICL_VM *pVM)
863 {
864     int     fd, len;
865     char *buf;
866 
867 #if FICL_ROBUST > 1
868     vmCheckStack(pVM, 3, 1);
869 #endif
870     len = stackPopINT(pVM->pStack); /* get number of bytes to read */
871     buf = stackPopPtr(pVM->pStack); /* get buffer */
872     fd = stackPopINT(pVM->pStack); /* get fd */
873     if (len > 0 && buf && fd != -1)
874 	stackPushINT(pVM->pStack, write(fd, buf, len));
875     else
876 	stackPushINT(pVM->pStack, -1);
877     return;
878 }
879 
880 /*          fseek - seek to a new position in a file
881  *
882  * fseek  ( fd ofs whence  -- pos )
883  */
884 static void pfseek(FICL_VM *pVM)
885 {
886     int     fd, pos, whence;
887 
888 #if FICL_ROBUST > 1
889     vmCheckStack(pVM, 3, 1);
890 #endif
891     whence = stackPopINT(pVM->pStack);
892     pos = stackPopINT(pVM->pStack);
893     fd = stackPopINT(pVM->pStack);
894     stackPushINT(pVM->pStack, lseek(fd, pos, whence));
895     return;
896 }
897 
898 /*           key - get a character from stdin
899  *
900  * key ( -- char )
901  */
902 static void key(FICL_VM *pVM)
903 {
904 #if FICL_ROBUST > 1
905     vmCheckStack(pVM, 0, 1);
906 #endif
907     stackPushINT(pVM->pStack, getchar());
908     return;
909 }
910 
911 /*           key? - check for a character from stdin (FACILITY)
912  *
913  * key? ( -- flag )
914  */
915 static void keyQuestion(FICL_VM *pVM)
916 {
917 #if FICL_ROBUST > 1
918     vmCheckStack(pVM, 0, 1);
919 #endif
920 #ifdef TESTMAIN
921     /* XXX Since we don't fiddle with termios, let it always succeed... */
922     stackPushINT(pVM->pStack, FICL_TRUE);
923 #else
924     /* But here do the right thing. */
925     stackPushINT(pVM->pStack, ischar()? FICL_TRUE : FICL_FALSE);
926 #endif
927     return;
928 }
929 
930 /* seconds - gives number of seconds since beginning of time
931  *
932  * beginning of time is defined as:
933  *
934  *	BTX	- number of seconds since midnight
935  *	FreeBSD	- number of seconds since Jan 1 1970
936  *
937  * seconds ( -- u )
938  */
939 static void pseconds(FICL_VM *pVM)
940 {
941 #if FICL_ROBUST > 1
942     vmCheckStack(pVM,0,1);
943 #endif
944     stackPushUNS(pVM->pStack, (FICL_UNS) time(NULL));
945     return;
946 }
947 
948 /* ms - wait at least that many milliseconds (FACILITY)
949  *
950  * ms ( u -- )
951  *
952  */
953 static void ms(FICL_VM *pVM)
954 {
955 #if FICL_ROBUST > 1
956     vmCheckStack(pVM,1,0);
957 #endif
958 #ifdef TESTMAIN
959     usleep(stackPopUNS(pVM->pStack)*1000);
960 #else
961     delay(stackPopUNS(pVM->pStack)*1000);
962 #endif
963     return;
964 }
965 
966 /*           fkey - get a character from a file
967  *
968  * fkey ( file -- char )
969  */
970 static void fkey(FICL_VM *pVM)
971 {
972     int i, fd;
973     char ch;
974 
975 #if FICL_ROBUST > 1
976     vmCheckStack(pVM, 1, 1);
977 #endif
978     fd = stackPopINT(pVM->pStack);
979     i = read(fd, &ch, 1);
980     stackPushINT(pVM->pStack, i > 0 ? ch : -1);
981     return;
982 }
983 
984 
985 /*
986 ** Retrieves free space remaining on the dictionary
987 */
988 
989 static void freeHeap(FICL_VM *pVM)
990 {
991     stackPushINT(pVM->pStack, dictCellsAvail(ficlGetDict(pVM->pSys)));
992 }
993 
994 
995 /******************* Increase dictionary size on-demand ******************/
996 
997 static void ficlDictThreshold(FICL_VM *pVM)
998 {
999     stackPushPtr(pVM->pStack, &dictThreshold);
1000 }
1001 
1002 static void ficlDictIncrease(FICL_VM *pVM)
1003 {
1004     stackPushPtr(pVM->pStack, &dictIncrease);
1005 }
1006 
1007 /**************************************************************************
1008                         f i c l C o m p i l e P l a t f o r m
1009 ** Build FreeBSD platform extensions into the system dictionary
1010 **************************************************************************/
1011 void ficlCompilePlatform(FICL_SYSTEM *pSys)
1012 {
1013     ficlCompileFcn **fnpp;
1014     FICL_DICT *dp = pSys->dp;
1015     assert (dp);
1016 
1017     dictAppendWord(dp, ".#",        displayCellNoPad,    FW_DEFAULT);
1018     dictAppendWord(dp, "isdir?",    isdirQuestion,  FW_DEFAULT);
1019     dictAppendWord(dp, "fopen",	    pfopen,	    FW_DEFAULT);
1020     dictAppendWord(dp, "fclose",    pfclose,	    FW_DEFAULT);
1021     dictAppendWord(dp, "fread",	    pfread,	    FW_DEFAULT);
1022     dictAppendWord(dp, "freaddir",  pfreaddir,	    FW_DEFAULT);
1023     dictAppendWord(dp, "fload",	    pfload,	    FW_DEFAULT);
1024     dictAppendWord(dp, "fkey",	    fkey,	    FW_DEFAULT);
1025     dictAppendWord(dp, "fseek",     pfseek,	    FW_DEFAULT);
1026     dictAppendWord(dp, "fwrite",    pfwrite,	    FW_DEFAULT);
1027     dictAppendWord(dp, "key",	    key,	    FW_DEFAULT);
1028     dictAppendWord(dp, "key?",	    keyQuestion,    FW_DEFAULT);
1029     dictAppendWord(dp, "ms",        ms,             FW_DEFAULT);
1030     dictAppendWord(dp, "seconds",   pseconds,       FW_DEFAULT);
1031     dictAppendWord(dp, "heap?",     freeHeap,       FW_DEFAULT);
1032     dictAppendWord(dp, "dictthreshold", ficlDictThreshold, FW_DEFAULT);
1033     dictAppendWord(dp, "dictincrease", ficlDictIncrease, FW_DEFAULT);
1034 
1035     dictAppendWord(dp, "setenv",    ficlSetenv,	    FW_DEFAULT);
1036     dictAppendWord(dp, "setenv?",   ficlSetenvq,    FW_DEFAULT);
1037     dictAppendWord(dp, "getenv",    ficlGetenv,	    FW_DEFAULT);
1038     dictAppendWord(dp, "unsetenv",  ficlUnsetenv,   FW_DEFAULT);
1039     dictAppendWord(dp, "copyin",    ficlCopyin,	    FW_DEFAULT);
1040     dictAppendWord(dp, "copyout",   ficlCopyout,    FW_DEFAULT);
1041     dictAppendWord(dp, "findfile",  ficlFindfile,   FW_DEFAULT);
1042     dictAppendWord(dp, "ccall",	    ficlCcall,	    FW_DEFAULT);
1043     dictAppendWord(dp, "uuid-from-string", ficlUuidFromString, FW_DEFAULT);
1044     dictAppendWord(dp, "uuid-to-string", ficlUuidToString, FW_DEFAULT);
1045 #ifndef TESTMAIN
1046     dictAppendWord(dp, "fb-setpixel", ficl_fb_setpixel, FW_DEFAULT);
1047     dictAppendWord(dp, "fb-line", ficl_fb_line, FW_DEFAULT);
1048     dictAppendWord(dp, "fb-bezier", ficl_fb_bezier, FW_DEFAULT);
1049     dictAppendWord(dp, "fb-drawrect", ficl_fb_drawrect, FW_DEFAULT);
1050     dictAppendWord(dp, "fb-putimage", ficl_fb_putimage, FW_DEFAULT);
1051     dictAppendWord(dp, "term-drawrect", ficl_term_drawrect, FW_DEFAULT);
1052     dictAppendWord(dp, "term-putimage", ficl_term_putimage, FW_DEFAULT);
1053     dictAppendWord(dp, "isvirtualized?",ficlIsvirtualizedQ, FW_DEFAULT);
1054 #endif
1055 
1056     SET_FOREACH(fnpp, Xficl_compile_set)
1057 	(*fnpp)(pSys);
1058 
1059 #if defined(__i386__)
1060     ficlSetEnv(pSys, "arch-i386",         FICL_TRUE);
1061     ficlSetEnv(pSys, "arch-powerpc",      FICL_FALSE);
1062 #elif defined(__powerpc__)
1063     ficlSetEnv(pSys, "arch-i386",         FICL_FALSE);
1064     ficlSetEnv(pSys, "arch-powerpc",      FICL_TRUE);
1065 #endif
1066 
1067     return;
1068 }
1069