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 *	$FreeBSD$
27 */
28
29/*******************************************************************
30** l o a d e r . c
31** Additional FICL words designed for FreeBSD's loader
32**
33*******************************************************************/
34
35#ifdef TESTMAIN
36#include <stdlib.h>
37#else
38#include <stand.h>
39#endif
40#include "bootstrap.h"
41#include <string.h>
42#include "ficl.h"
43
44/*		FreeBSD's loader interaction words and extras
45 *
46 * 		setenv      ( value n name n' -- )
47 * 		setenv?     ( value n name n' flag -- )
48 * 		getenv      ( addr n -- addr' n' | -1 )
49 * 		unsetenv    ( addr n -- )
50 * 		copyin      ( addr addr' len -- )
51 * 		copyout     ( addr addr' len -- )
52 * 		findfile    ( name len type len' -- addr )
53 * 		pnpdevices  ( -- addr )
54 * 		pnphandlers ( -- addr )
55 * 		ccall       ( [[...[p10] p9] ... p1] n addr -- result )
56 * 		.#	    ( value -- )
57 */
58
59void
60ficlSetenv(FICL_VM *pVM)
61{
62#ifndef TESTMAIN
63	char	*name, *value;
64#endif
65	char	*namep, *valuep;
66	int	names, values;
67
68#if FICL_ROBUST > 1
69	vmCheckStack(pVM, 4, 0);
70#endif
71	names = stackPopINT(pVM->pStack);
72	namep = (char*) stackPopPtr(pVM->pStack);
73	values = stackPopINT(pVM->pStack);
74	valuep = (char*) stackPopPtr(pVM->pStack);
75
76#ifndef TESTMAIN
77	name = (char*) ficlMalloc(names+1);
78	if (!name)
79		vmThrowErr(pVM, "Error: out of memory");
80	strncpy(name, namep, names);
81	name[names] = '\0';
82	value = (char*) ficlMalloc(values+1);
83	if (!value)
84		vmThrowErr(pVM, "Error: out of memory");
85	strncpy(value, valuep, values);
86	value[values] = '\0';
87
88	setenv(name, value, 1);
89	ficlFree(name);
90	ficlFree(value);
91#endif
92
93	return;
94}
95
96void
97ficlSetenvq(FICL_VM *pVM)
98{
99#ifndef TESTMAIN
100	char	*name, *value;
101#endif
102	char	*namep, *valuep;
103	int	names, values, overwrite;
104
105#if FICL_ROBUST > 1
106	vmCheckStack(pVM, 5, 0);
107#endif
108	overwrite = stackPopINT(pVM->pStack);
109	names = stackPopINT(pVM->pStack);
110	namep = (char*) stackPopPtr(pVM->pStack);
111	values = stackPopINT(pVM->pStack);
112	valuep = (char*) stackPopPtr(pVM->pStack);
113
114#ifndef TESTMAIN
115	name = (char*) ficlMalloc(names+1);
116	if (!name)
117		vmThrowErr(pVM, "Error: out of memory");
118	strncpy(name, namep, names);
119	name[names] = '\0';
120	value = (char*) ficlMalloc(values+1);
121	if (!value)
122		vmThrowErr(pVM, "Error: out of memory");
123	strncpy(value, valuep, values);
124	value[values] = '\0';
125
126	setenv(name, value, overwrite);
127	ficlFree(name);
128	ficlFree(value);
129#endif
130
131	return;
132}
133
134void
135ficlGetenv(FICL_VM *pVM)
136{
137#ifndef TESTMAIN
138	char	*name;
139#endif
140	char	*namep, *value;
141	int	names;
142
143#if FICL_ROBUST > 1
144	vmCheckStack(pVM, 2, 2);
145#endif
146	names = stackPopINT(pVM->pStack);
147	namep = (char*) stackPopPtr(pVM->pStack);
148
149#ifndef TESTMAIN
150	name = (char*) ficlMalloc(names+1);
151	if (!name)
152		vmThrowErr(pVM, "Error: out of memory");
153	strncpy(name, namep, names);
154	name[names] = '\0';
155
156	value = getenv(name);
157	ficlFree(name);
158
159	if(value != NULL) {
160		stackPushPtr(pVM->pStack, value);
161		stackPushINT(pVM->pStack, strlen(value));
162	} else
163#endif
164		stackPushINT(pVM->pStack, -1);
165
166	return;
167}
168
169void
170ficlUnsetenv(FICL_VM *pVM)
171{
172#ifndef TESTMAIN
173	char	*name;
174#endif
175	char	*namep;
176	int	names;
177
178#if FICL_ROBUST > 1
179	vmCheckStack(pVM, 2, 0);
180#endif
181	names = stackPopINT(pVM->pStack);
182	namep = (char*) stackPopPtr(pVM->pStack);
183
184#ifndef TESTMAIN
185	name = (char*) ficlMalloc(names+1);
186	if (!name)
187		vmThrowErr(pVM, "Error: out of memory");
188	strncpy(name, namep, names);
189	name[names] = '\0';
190
191	unsetenv(name);
192	ficlFree(name);
193#endif
194
195	return;
196}
197
198void
199ficlCopyin(FICL_VM *pVM)
200{
201	void*		src;
202	vm_offset_t	dest;
203	size_t		len;
204
205#if FICL_ROBUST > 1
206	vmCheckStack(pVM, 3, 0);
207#endif
208
209	len = stackPopINT(pVM->pStack);
210	dest = stackPopINT(pVM->pStack);
211	src = stackPopPtr(pVM->pStack);
212
213#ifndef TESTMAIN
214	archsw.arch_copyin(src, dest, len);
215#endif
216
217	return;
218}
219
220void
221ficlCopyout(FICL_VM *pVM)
222{
223	void*		dest;
224	vm_offset_t	src;
225	size_t		len;
226
227#if FICL_ROBUST > 1
228	vmCheckStack(pVM, 3, 0);
229#endif
230
231	len = stackPopINT(pVM->pStack);
232	dest = stackPopPtr(pVM->pStack);
233	src = stackPopINT(pVM->pStack);
234
235#ifndef TESTMAIN
236	archsw.arch_copyout(src, dest, len);
237#endif
238
239	return;
240}
241
242void
243ficlFindfile(FICL_VM *pVM)
244{
245#ifndef TESTMAIN
246	char	*name;
247#endif
248	char	*type, *namep, *typep;
249	struct	preloaded_file* fp;
250	int	names, types;
251
252#if FICL_ROBUST > 1
253	vmCheckStack(pVM, 4, 1);
254#endif
255
256	types = stackPopINT(pVM->pStack);
257	typep = (char*) stackPopPtr(pVM->pStack);
258	names = stackPopINT(pVM->pStack);
259	namep = (char*) stackPopPtr(pVM->pStack);
260#ifndef TESTMAIN
261	name = (char*) ficlMalloc(names+1);
262	if (!name)
263		vmThrowErr(pVM, "Error: out of memory");
264	strncpy(name, namep, names);
265	name[names] = '\0';
266	type = (char*) ficlMalloc(types+1);
267	if (!type)
268		vmThrowErr(pVM, "Error: out of memory");
269	strncpy(type, typep, types);
270	type[types] = '\0';
271
272	fp = file_findfile(name, type);
273#else
274	fp = NULL;
275#endif
276	stackPushPtr(pVM->pStack, fp);
277
278	return;
279}
280
281#ifndef TESTMAIN
282#ifdef HAVE_PNP
283
284void
285ficlPnpdevices(FICL_VM *pVM)
286{
287	static int pnp_devices_initted = 0;
288#if FICL_ROBUST > 1
289	vmCheckStack(pVM, 0, 1);
290#endif
291
292	if(!pnp_devices_initted) {
293		STAILQ_INIT(&pnp_devices);
294		pnp_devices_initted = 1;
295	}
296
297	stackPushPtr(pVM->pStack, &pnp_devices);
298
299	return;
300}
301
302void
303ficlPnphandlers(FICL_VM *pVM)
304{
305#if FICL_ROBUST > 1
306	vmCheckStack(pVM, 0, 1);
307#endif
308
309	stackPushPtr(pVM->pStack, pnphandlers);
310
311	return;
312}
313
314#endif
315
316#endif /* ndef TESTMAIN */
317
318void
319ficlCcall(FICL_VM *pVM)
320{
321	int (*func)(int, ...);
322	int result, p[10];
323	int nparam, i;
324
325#if FICL_ROBUST > 1
326	vmCheckStack(pVM, 2, 0);
327#endif
328
329	func = stackPopPtr(pVM->pStack);
330	nparam = stackPopINT(pVM->pStack);
331
332#if FICL_ROBUST > 1
333	vmCheckStack(pVM, nparam, 1);
334#endif
335
336	for (i = 0; i < nparam; i++)
337		p[i] = stackPopINT(pVM->pStack);
338
339	result = func(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8],
340	    p[9]);
341
342	stackPushINT(pVM->pStack, result);
343
344	return;
345}
346
347/**************************************************************************
348                        f i c l E x e c F D
349** reads in text from file fd and passes it to ficlExec()
350 * returns VM_OUTOFTEXT on success or the ficlExec() error code on
351 * failure.
352 */
353#define nLINEBUF 256
354int ficlExecFD(FICL_VM *pVM, int fd)
355{
356    char    cp[nLINEBUF];
357    int     nLine = 0, rval = VM_OUTOFTEXT;
358    char    ch;
359    CELL    id;
360
361    id = pVM->sourceID;
362    pVM->sourceID.i = fd;
363
364    /* feed each line to ficlExec */
365    while (1) {
366	int status, i;
367
368	i = 0;
369	while ((status = read(fd, &ch, 1)) > 0 && ch != '\n')
370	    cp[i++] = ch;
371        nLine++;
372	if (!i) {
373	    if (status < 1)
374		break;
375	    continue;
376	}
377        rval = ficlExecC(pVM, cp, i);
378	if(rval != VM_QUIT && rval != VM_USEREXIT && rval != VM_OUTOFTEXT)
379        {
380            pVM->sourceID = id;
381            return rval;
382        }
383    }
384    /*
385    ** Pass an empty line with SOURCE-ID == -1 to flush
386    ** any pending REFILLs (as required by FILE wordset)
387    */
388    pVM->sourceID.i = -1;
389    ficlExec(pVM, "");
390
391    pVM->sourceID = id;
392    return rval;
393}
394
395static void displayCellNoPad(FICL_VM *pVM)
396{
397    CELL c;
398#if FICL_ROBUST > 1
399    vmCheckStack(pVM, 1, 0);
400#endif
401    c = stackPop(pVM->pStack);
402    ltoa((c).i, pVM->pad, pVM->base);
403    vmTextOut(pVM, pVM->pad, 0);
404    return;
405}
406
407/*          fopen - open a file and return new fd on stack.
408 *
409 * fopen ( ptr count mode -- fd )
410 */
411static void pfopen(FICL_VM *pVM)
412{
413    int     mode, fd, count;
414    char    *ptr, *name;
415
416#if FICL_ROBUST > 1
417    vmCheckStack(pVM, 3, 1);
418#endif
419
420    mode = stackPopINT(pVM->pStack);    /* get mode */
421    count = stackPopINT(pVM->pStack);   /* get count */
422    ptr = stackPopPtr(pVM->pStack);     /* get ptr */
423
424    if ((count < 0) || (ptr == NULL)) {
425        stackPushINT(pVM->pStack, -1);
426        return;
427    }
428
429    /* ensure that the string is null terminated */
430    name = (char *)malloc(count+1);
431    bcopy(ptr,name,count);
432    name[count] = 0;
433
434    /* open the file */
435    fd = open(name, mode);
436    free(name);
437    stackPushINT(pVM->pStack, fd);
438    return;
439}
440
441/*          fclose - close a file who's fd is on stack.
442 *
443 * fclose ( fd -- )
444 */
445static void pfclose(FICL_VM *pVM)
446{
447    int fd;
448
449#if FICL_ROBUST > 1
450    vmCheckStack(pVM, 1, 0);
451#endif
452    fd = stackPopINT(pVM->pStack); /* get fd */
453    if (fd != -1)
454	close(fd);
455    return;
456}
457
458/*          fread - read file contents
459 *
460 * fread  ( fd buf nbytes  -- nread )
461 */
462static void pfread(FICL_VM *pVM)
463{
464    int     fd, len;
465    char *buf;
466
467#if FICL_ROBUST > 1
468    vmCheckStack(pVM, 3, 1);
469#endif
470    len = stackPopINT(pVM->pStack); /* get number of bytes to read */
471    buf = stackPopPtr(pVM->pStack); /* get buffer */
472    fd = stackPopINT(pVM->pStack); /* get fd */
473    if (len > 0 && buf && fd != -1)
474	stackPushINT(pVM->pStack, read(fd, buf, len));
475    else
476	stackPushINT(pVM->pStack, -1);
477    return;
478}
479
480/*          fload - interpret file contents
481 *
482 * fload  ( fd -- )
483 */
484static void pfload(FICL_VM *pVM)
485{
486    int     fd;
487
488#if FICL_ROBUST > 1
489    vmCheckStack(pVM, 1, 0);
490#endif
491    fd = stackPopINT(pVM->pStack); /* get fd */
492    if (fd != -1)
493	ficlExecFD(pVM, fd);
494    return;
495}
496
497/*          fwrite - write file contents
498 *
499 * fwrite  ( fd buf nbytes  -- nwritten )
500 */
501static void pfwrite(FICL_VM *pVM)
502{
503    int     fd, len;
504    char *buf;
505
506#if FICL_ROBUST > 1
507    vmCheckStack(pVM, 3, 1);
508#endif
509    len = stackPopINT(pVM->pStack); /* get number of bytes to read */
510    buf = stackPopPtr(pVM->pStack); /* get buffer */
511    fd = stackPopINT(pVM->pStack); /* get fd */
512    if (len > 0 && buf && fd != -1)
513	stackPushINT(pVM->pStack, write(fd, buf, len));
514    else
515	stackPushINT(pVM->pStack, -1);
516    return;
517}
518
519/*          fseek - seek to a new position in a file
520 *
521 * fseek  ( fd ofs whence  -- pos )
522 */
523static void pfseek(FICL_VM *pVM)
524{
525    int     fd, pos, whence;
526
527#if FICL_ROBUST > 1
528    vmCheckStack(pVM, 3, 1);
529#endif
530    whence = stackPopINT(pVM->pStack);
531    pos = stackPopINT(pVM->pStack);
532    fd = stackPopINT(pVM->pStack);
533    stackPushINT(pVM->pStack, lseek(fd, pos, whence));
534    return;
535}
536
537/*           key - get a character from stdin
538 *
539 * key ( -- char )
540 */
541static void key(FICL_VM *pVM)
542{
543#if FICL_ROBUST > 1
544    vmCheckStack(pVM, 0, 1);
545#endif
546    stackPushINT(pVM->pStack, getchar());
547    return;
548}
549
550/*           key? - check for a character from stdin (FACILITY)
551 *
552 * key? ( -- flag )
553 */
554static void keyQuestion(FICL_VM *pVM)
555{
556#if FICL_ROBUST > 1
557    vmCheckStack(pVM, 0, 1);
558#endif
559#ifdef TESTMAIN
560    /* XXX Since we don't fiddle with termios, let it always succeed... */
561    stackPushINT(pVM->pStack, FICL_TRUE);
562#else
563    /* But here do the right thing. */
564    stackPushINT(pVM->pStack, ischar()? FICL_TRUE : FICL_FALSE);
565#endif
566    return;
567}
568
569/* seconds - gives number of seconds since beginning of time
570 *
571 * beginning of time is defined as:
572 *
573 *	BTX	- number of seconds since midnight
574 *	FreeBSD	- number of seconds since Jan 1 1970
575 *
576 * seconds ( -- u )
577 */
578static void pseconds(FICL_VM *pVM)
579{
580#if FICL_ROBUST > 1
581    vmCheckStack(pVM,0,1);
582#endif
583    stackPushUNS(pVM->pStack, (FICL_UNS) time(NULL));
584    return;
585}
586
587/* ms - wait at least that many milliseconds (FACILITY)
588 *
589 * ms ( u -- )
590 *
591 */
592static void ms(FICL_VM *pVM)
593{
594#if FICL_ROBUST > 1
595    vmCheckStack(pVM,1,0);
596#endif
597#ifdef TESTMAIN
598    usleep(stackPopUNS(pVM->pStack)*1000);
599#else
600    delay(stackPopUNS(pVM->pStack)*1000);
601#endif
602    return;
603}
604
605/*           fkey - get a character from a file
606 *
607 * fkey ( file -- char )
608 */
609static void fkey(FICL_VM *pVM)
610{
611    int i, fd;
612    char ch;
613
614#if FICL_ROBUST > 1
615    vmCheckStack(pVM, 1, 1);
616#endif
617    fd = stackPopINT(pVM->pStack);
618    i = read(fd, &ch, 1);
619    stackPushINT(pVM->pStack, i > 0 ? ch : -1);
620    return;
621}
622
623/*
624** Retrieves free space remaining on the dictionary
625*/
626
627static void freeHeap(FICL_VM *pVM)
628{
629    stackPushINT(pVM->pStack, dictCellsAvail(ficlGetDict(pVM->pSys)));
630}
631
632
633/******************* Increase dictionary size on-demand ******************/
634
635static void ficlDictThreshold(FICL_VM *pVM)
636{
637    stackPushPtr(pVM->pStack, &dictThreshold);
638}
639
640static void ficlDictIncrease(FICL_VM *pVM)
641{
642    stackPushPtr(pVM->pStack, &dictIncrease);
643}
644
645
646/**************************************************************************
647                        f i c l C o m p i l e P l a t f o r m
648** Build FreeBSD platform extensions into the system dictionary
649**************************************************************************/
650void ficlCompilePlatform(FICL_SYSTEM *pSys)
651{
652    FICL_DICT *dp = pSys->dp;
653    assert (dp);
654
655    dictAppendWord(dp, ".#",        displayCellNoPad,    FW_DEFAULT);
656    dictAppendWord(dp, "fopen",	    pfopen,	    FW_DEFAULT);
657    dictAppendWord(dp, "fclose",    pfclose,	    FW_DEFAULT);
658    dictAppendWord(dp, "fread",	    pfread,	    FW_DEFAULT);
659    dictAppendWord(dp, "fload",	    pfload,	    FW_DEFAULT);
660    dictAppendWord(dp, "fkey",	    fkey,	    FW_DEFAULT);
661    dictAppendWord(dp, "fseek",     pfseek,	    FW_DEFAULT);
662    dictAppendWord(dp, "fwrite",    pfwrite,	    FW_DEFAULT);
663    dictAppendWord(dp, "key",	    key,	    FW_DEFAULT);
664    dictAppendWord(dp, "key?",	    keyQuestion,    FW_DEFAULT);
665    dictAppendWord(dp, "ms",        ms,             FW_DEFAULT);
666    dictAppendWord(dp, "seconds",   pseconds,       FW_DEFAULT);
667    dictAppendWord(dp, "heap?",     freeHeap,       FW_DEFAULT);
668    dictAppendWord(dp, "dictthreshold", ficlDictThreshold, FW_DEFAULT);
669    dictAppendWord(dp, "dictincrease", ficlDictIncrease, FW_DEFAULT);
670
671    dictAppendWord(dp, "setenv",    ficlSetenv,	    FW_DEFAULT);
672    dictAppendWord(dp, "setenv?",   ficlSetenvq,    FW_DEFAULT);
673    dictAppendWord(dp, "getenv",    ficlGetenv,	    FW_DEFAULT);
674    dictAppendWord(dp, "unsetenv",  ficlUnsetenv,   FW_DEFAULT);
675    dictAppendWord(dp, "copyin",    ficlCopyin,	    FW_DEFAULT);
676    dictAppendWord(dp, "copyout",   ficlCopyout,    FW_DEFAULT);
677    dictAppendWord(dp, "findfile",  ficlFindfile,   FW_DEFAULT);
678    dictAppendWord(dp, "ccall",	    ficlCcall,	    FW_DEFAULT);
679#ifndef TESTMAIN
680#ifdef __i386__
681    dictAppendWord(dp, "outb",      ficlOutb,       FW_DEFAULT);
682    dictAppendWord(dp, "inb",       ficlInb,        FW_DEFAULT);
683#endif
684#ifdef HAVE_PNP
685    dictAppendWord(dp, "pnpdevices",ficlPnpdevices, FW_DEFAULT);
686    dictAppendWord(dp, "pnphandlers",ficlPnphandlers, FW_DEFAULT);
687#endif
688#endif
689
690#if defined(PC98)
691    ficlSetEnv(pSys, "arch-pc98",         FICL_TRUE);
692#elif defined(__i386__)
693    ficlSetEnv(pSys, "arch-i386",         FICL_TRUE);
694    ficlSetEnv(pSys, "arch-ia64",         FICL_FALSE);
695    ficlSetEnv(pSys, "arch-powerpc",      FICL_FALSE);
696#elif defined(__ia64__)
697    ficlSetEnv(pSys, "arch-i386",         FICL_FALSE);
698    ficlSetEnv(pSys, "arch-ia64",         FICL_TRUE);
699    ficlSetEnv(pSys, "arch-powerpc",      FICL_FALSE);
700#elif defined(__powerpc__)
701    ficlSetEnv(pSys, "arch-i386",         FICL_FALSE);
702    ficlSetEnv(pSys, "arch-ia64",         FICL_FALSE);
703    ficlSetEnv(pSys, "arch-powerpc",      FICL_TRUE);
704#endif
705
706    return;
707}
708
709