1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "glk/tads/tads2/built_in.h"
24 #include "glk/tads/tads2/appctx.h"
25 #include "glk/tads/tads2/character_map.h"
26 #include "glk/tads/tads2/error.h"
27 #include "glk/tads/tads2/file_io.h"
28 #include "glk/tads/tads2/list.h"
29 #include "glk/tads/tads2/os.h"
30 #include "glk/tads/tads2/run.h"
31 #include "glk/tads/tads2/string_resources.h"
32 #include "glk/tads/tads2/vocabulary.h"
33 #include "glk/tads/os_glk.h"
34 
35 namespace Glk {
36 namespace TADS {
37 namespace TADS2 {
38 
39 
40 /* yorn - yes or no */
bifyon(bifcxdef * ctx,int argc)41 void bifyon(bifcxdef *ctx, int argc)
42 {
43     char     rsp[128];
44     char    *p;
45     runsdef  val;
46     char     yesbuf[64];
47     char     nobuf[64];
48     re_context  rectx;
49     int      match_yes;
50     int      match_no;
51 
52     bifcntargs(ctx, 0, argc);            /* check for proper argument count */
53 
54     /* load the "yes" and "no" reply patterns */
55     if (os_get_str_rsc(RESID_YORN_YES, yesbuf, sizeof(yesbuf)))
56         strcpy(yesbuf, "[Yy].*");
57     if (os_get_str_rsc(RESID_YORN_NO, nobuf, sizeof(nobuf)))
58         strcpy(nobuf, "[Nn].*");
59 
60     /* if we're in HTML mode, switch to input font */
61     if (tio_is_html_mode())
62         tioputs(ctx->bifcxtio, "<font face='TADS-Input'>");
63 
64     /* ensure the prompt is displayed */
65     tioflushn(ctx->bifcxtio, 0);
66 
67     /* reset count of lines since the last keyboard input */
68     tioreset(ctx->bifcxtio);
69 
70     /* read a line of text */
71     if (tiogets(ctx->bifcxtio, (char *)0, rsp, (int)sizeof(rsp)))
72         runsig(ctx->bifcxrun, ERR_RUNQUIT);
73 
74     /* if we're in HTML mode, close the input font tag */
75     if (tio_is_html_mode())
76         tioputs(ctx->bifcxtio, "</font>");
77 
78     /* scan off leading spaces */
79     for (p = rsp ; t_isspace(*p) ; ++p) ;
80 
81     /* set up our regex context */
82     re_init(&rectx, ctx->bifcxerr);
83 
84     /* check for a "yes" response */
85     match_yes = re_compile_and_match(&rectx, yesbuf, strlen(yesbuf),
86                                      p, strlen(p));
87 
88     /* check for a "no" response */
89     match_no = re_compile_and_match(&rectx, nobuf, strlen(nobuf),
90                                     p, strlen(p));
91 
92     /* check the result */
93     if (match_yes == (int)strlen(p))
94         val.runsv.runsvnum = 1;
95     else if (match_no == (int)strlen(p))
96         val.runsv.runsvnum = 0;
97     else
98         val.runsv.runsvnum = -1;
99 
100     /* delete our regex context */
101     re_delete(&rectx);
102 
103     /* push the result */
104     runpush(ctx->bifcxrun, DAT_NUMBER, &val);
105 }
106 
107 /* setfuse */
bifsfs(bifcxdef * ctx,int argc)108 void bifsfs(bifcxdef *ctx, int argc)
109 {
110     objnum    func;
111     uint      tm;
112     runsdef   val;
113     voccxdef *voc = ctx->bifcxrun->runcxvoc;
114 
115     bifcntargs(ctx, 3, argc);            /* check for proper argument count */
116     func = runpopfn(ctx->bifcxrun);
117     tm = runpopnum(ctx->bifcxrun);
118     runpop(ctx->bifcxrun, &val);
119 
120     /* limitation:  don't allow string or list for value */
121     if (val.runstyp == DAT_LIST || val.runstyp == DAT_SSTRING)
122         runsig(ctx->bifcxrun, ERR_FUSEVAL);
123 
124     vocsetfd(voc, voc->voccxfus, func, (prpnum)0,
125              tm, &val, ERR_MANYFUS);
126 }
127 
128 /* remfuse */
bifrfs(bifcxdef * ctx,int argc)129 void bifrfs(bifcxdef *ctx, int argc)
130 {
131     objnum    func;
132     runsdef   val;
133     voccxdef *voc = ctx->bifcxrun->runcxvoc;
134 
135     bifcntargs(ctx, 2, argc);
136     func = runpopfn(ctx->bifcxrun);
137     runpop(ctx->bifcxrun, &val);
138     vocremfd(voc, voc->voccxfus, func, (prpnum)0,
139              &val, ERR_NOFUSE);
140 }
141 
142 /* setdaemon */
bifsdm(bifcxdef * ctx,int argc)143 void bifsdm(bifcxdef *ctx, int argc)
144 {
145     objnum    func;
146     runsdef   val;
147     voccxdef *voc = ctx->bifcxrun->runcxvoc;
148 
149     bifcntargs(ctx, 2, argc);            /* check for proper argument count */
150     func = runpopfn(ctx->bifcxrun);
151     runpop(ctx->bifcxrun, &val);
152 
153     /* limitation:  don't allow string or list for value */
154     if (val.runstyp == DAT_LIST || val.runstyp == DAT_SSTRING)
155         runsig(ctx->bifcxrun, ERR_FUSEVAL);
156 
157     vocsetfd(voc, voc->voccxdmn, func, (prpnum)0, 0,
158              &val, ERR_MANYDMN);
159 }
160 
161 /* remdaemon */
bifrdm(bifcxdef * ctx,int argc)162 void bifrdm(bifcxdef *ctx, int argc)
163 {
164     objnum    func;
165     runsdef   val;
166     voccxdef *voc = ctx->bifcxrun->runcxvoc;
167 
168     bifcntargs(ctx, 2, argc);
169     func = runpopfn(ctx->bifcxrun);
170     runpop(ctx->bifcxrun, &val);
171     vocremfd(voc, voc->voccxdmn, func, (prpnum)0,
172              &val, ERR_NODMN);
173 }
174 
175 /* incturn */
bifinc(bifcxdef * ctx,int argc)176 void bifinc(bifcxdef *ctx, int argc)
177 {
178     int turncnt;
179 
180     if (argc == 1)
181     {
182         /* get the number of turns to skip */
183         turncnt = runpopnum(ctx->bifcxrun);
184         if (turncnt < 1)
185             runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "incturn");
186     }
187     else
188     {
189         /* no arguments -> increment by one turn */
190         bifcntargs(ctx, 0, argc);
191         turncnt = 1;
192     }
193 
194     /* skip the given number of turns */
195     vocturn(ctx->bifcxrun->runcxvoc, turncnt, TRUE);
196 }
197 
198 /* skipturn */
bifskt(bifcxdef * ctx,int argc)199 void bifskt(bifcxdef *ctx, int argc)
200 {
201     int turncnt;
202 
203     bifcntargs(ctx, 1, argc);
204     turncnt = runpopnum(ctx->bifcxrun);
205     if (turncnt < 1)
206         runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "skipturn");
207     vocturn(ctx->bifcxrun->runcxvoc, turncnt, FALSE);
208 }
209 
210 /* quit */
bifqui(bifcxdef * ctx,int argc)211 void bifqui(bifcxdef *ctx, int argc)
212 {
213     /* check for proper argument count */
214     bifcntargs(ctx, 0, argc);
215 
216     /* flush output buffer, and signal the end of the game */
217     tioflush(ctx->bifcxtio);
218     errsig(ctx->bifcxerr, ERR_RUNQUIT);
219 }
220 
221 /* internal function to convert a TADS string into a C-string */
bifcstr(bifcxdef * ctx,char * buf,size_t bufsiz,uchar * str)222 static void bifcstr(bifcxdef *ctx, char *buf, size_t bufsiz, uchar *str)
223 {
224     size_t  srcrem;
225     size_t  dstrem;
226     uchar  *src;
227     char   *dst;
228 
229     /* get the length and text portion of the string */
230     srcrem = osrp2(str) - 2;
231     str += 2;
232 
233     /* scan the string, and convert escapes */
234     for (src = str, dst = buf, dstrem = bufsiz ;
235          srcrem != 0 && dstrem != 0 ; ++src, --srcrem)
236     {
237         /* if we have an escape sequence, convert it */
238         if (*src == '\\')
239         {
240             /* skip the backslash in the input */
241             ++src;
242             --srcrem;
243 
244             /* if there's nothing left, store the backslash */
245             if (srcrem == 0)
246             {
247                 /* store the backslash */
248                 *dst++ = '\\';
249                 --dstrem;
250 
251                 /* there's nothing left to scan */
252                 break;
253             }
254 
255             /* see what the second half of the escape sequence is */
256             switch(*src)
257             {
258             case 'n':
259                 /* store a C-style newline character */
260                 *dst++ = '\n';
261                 --dstrem;
262                 break;
263 
264             case 't':
265                 /* store a C-style tab */
266                 *dst++ = '\t';
267                 --dstrem;
268                 break;
269 
270             case '(':
271             case ')':
272                 /* entirely omit the highlighting sequences */
273                 break;
274 
275             default:
276                 /* store everything else unchanged */
277                 *dst++ = *src;
278                 --dstrem;
279                 break;
280             }
281         }
282         else
283         {
284             /* copy this character unchanged */
285             *dst++ = *src;
286             --dstrem;
287         }
288     }
289 
290     /* if the buffer wasn't big enough, signal an error */
291     if (dstrem == 0)
292         runsig(ctx->bifcxrun, ERR_BIFCSTR);
293 
294     /* null-terminate the result string */
295     *dst = '\0';
296 }
297 
298 /* save */
bifsav(bifcxdef * ctx,int argc)299 void bifsav(bifcxdef *ctx, int argc)
300 {
301     uchar   *fn;
302     char     buf[OSFNMAX];
303     int      err;
304     runsdef  val;
305 
306     bifcntargs(ctx, 1, argc);
307     fn = runpopstr(ctx->bifcxrun);
308     bifcstr(ctx, buf, (size_t)sizeof(buf), fn);
309     os_defext(buf, ctx->bifcxsavext != 0 ? ctx->bifcxsavext : "sav");
310     err = fiosav(ctx->bifcxrun->runcxvoc, buf, ctx->bifcxrun->runcxgamename);
311     runpush(ctx->bifcxrun, runclog(err), &val);
312 }
313 
314 /* restore */
bifrso(bifcxdef * ctx,int argc)315 void bifrso(bifcxdef *ctx, int argc)
316 {
317     uchar    *fn;
318     char      buf[OSFNMAX];
319     int       err;
320     voccxdef *vctx = ctx->bifcxrun->runcxvoc;
321 
322     bifcntargs(ctx, 1, argc);
323 
324     /* check for special restore(nil) - restore game given as parameter */
325     if (runtostyp(ctx->bifcxrun) == DAT_NIL)
326     {
327         /* get filename from startup parameter, if any */
328         if (!os_paramfile(buf))
329         {
330             /* no startup parameter */
331             runpnum(ctx->bifcxrun, FIORSO_NO_PARAM_FILE);
332             return;
333         }
334     }
335     else
336     {
337         /* get string parameter - it's the filename */
338         fn = runpopstr(ctx->bifcxrun);
339         bifcstr(ctx, buf, (size_t)sizeof(buf), fn);
340         os_defext(buf, ctx->bifcxsavext != 0 ? ctx->bifcxsavext : "sav");
341     }
342 
343     /* try restoring the file */
344     err = fiorso(vctx, buf);
345 
346     /* blow away all undo records */
347     objulose(vctx->voccxundo);
348 
349     /* return the result code from fiorso */
350     runpnum(ctx->bifcxrun, err);
351 
352     /* note that the rest of the command line is to be ignored */
353     vctx->voccxflg |= VOCCXFCLEAR;
354 }
355 
356 /* logging */
biflog(bifcxdef * ctx,int argc)357 void biflog(bifcxdef *ctx, int argc)
358 {
359     char   buf[OSFNMAX];
360     uchar *str;
361 
362     bifcntargs(ctx, 1, argc);
363     if (runtostyp(ctx->bifcxrun) == DAT_NIL)
364     {
365         rundisc(ctx->bifcxrun);
366         tiologcls(ctx->bifcxtio);
367     }
368     else
369     {
370         str = runpopstr(ctx->bifcxrun);
371         bifcstr(ctx, buf, (size_t)sizeof(buf), str);
372         tiologopn(ctx->bifcxtio, buf);
373     }
374 }
375 
376 /* restart */
bifres(bifcxdef * ctx,int argc)377 void bifres(bifcxdef *ctx, int argc)
378 {
379     voccxdef *vctx = ctx->bifcxrun->runcxvoc;
380     objnum    fn;
381 
382     if (argc == 2)
383         fn = runpopfn(ctx->bifcxrun);            /* get function if present */
384     else
385     {
386         bifcntargs(ctx, 0, argc);        /* check for proper argument count */
387         fn = MCMONINV;                         /* no function was specified */
388     }
389 
390     objulose(vctx->voccxundo);                /* blow away all undo records */
391     vocrevert(vctx);                /* revert all objects to original state */
392     vocdmnclr(vctx);                   /* clear out fuses/deamons/notifiers */
393 
394     /* restore the original "Me" object */
395     vctx->voccxme = vctx->voccxme_init;
396 
397     /* call preinit if necessary (call it before invoking the user callback) */
398     if (vctx->voccxpreinit != MCMONINV)
399         runfn(ctx->bifcxrun, vctx->voccxpreinit, 0);
400 
401     /*
402      *   If a restart function was provided, call it.  Note that we left
403      *   the argument for the function on the stack, so there's no need to
404      *   re-push it!
405      */
406     if (fn != MCMONINV) runfn(ctx->bifcxrun, fn, 1);
407 
408     /* restart the game */
409     errsig(ctx->bifcxerr, ERR_RUNRESTART);
410 }
411 
412 /* input - get a line of input from the keyboard */
bifinp(bifcxdef * ctx,int argc)413 void bifinp(bifcxdef *ctx, int argc)
414 {
415     char inbuf[128];
416 
417     /* check for proper argument count */
418     bifcntargs(ctx, 0, argc);
419 
420     /* make sure the prompt is displayed */
421     tioflushn(ctx->bifcxtio, 0);
422 
423      /* reset count of lines since the last keyboard input */
424     tioreset(ctx->bifcxtio);
425 
426     /* read a line of text */
427     if (tiogets(ctx->bifcxtio, (char *)0, inbuf, (int)sizeof(inbuf)))
428         runsig(ctx->bifcxrun, ERR_RUNQUIT);
429 
430     /* push the string, converting escapes */
431     runpushcstr(ctx->bifcxrun, inbuf, strlen(inbuf), 0);
432 }
433 
434 /* notify */
bifnfy(bifcxdef * ctx,int argc)435 void bifnfy(bifcxdef *ctx, int argc)
436 {
437     objnum    objn;
438     prpnum    prp;
439     uint      tm;
440     voccxdef *voc = ctx->bifcxrun->runcxvoc;
441 
442     bifcntargs(ctx, 3, argc);            /* check for proper argument count */
443     objn = runpopobj(ctx->bifcxrun);
444     prp = runpopprp(ctx->bifcxrun);
445     tm = runpopnum(ctx->bifcxrun);
446 
447     /* a time of zero means every turn */
448     if (tm == 0)
449         tm = VOCDTIM_EACH_TURN;
450 
451     vocsetfd(voc, voc->voccxalm, objn, prp, tm,
452              (runsdef *)0, ERR_MANYNFY);
453 }
454 
455 
456 /* unnotify */
bifunn(bifcxdef * ctx,int argc)457 void bifunn(bifcxdef *ctx, int argc)
458 {
459     objnum    objn;
460     prpnum    prop;
461     voccxdef *voc = ctx->bifcxrun->runcxvoc;
462 
463     bifcntargs(ctx, 2, argc);
464     objn = runpopobj(ctx->bifcxrun);
465     prop = runpopprp(ctx->bifcxrun);
466     vocremfd(voc, voc->voccxalm, objn, prop,
467              (runsdef *)0, ERR_NONFY);
468 }
469 
470 /* trace on/off */
biftrc(bifcxdef * ctx,int argc)471 void biftrc(bifcxdef *ctx, int argc)
472 {
473     runsdef val;
474     int     n;
475     int     flag;
476 
477     if (argc == 2)
478     {
479         /* get the type indicator and the on/off status */
480         n = runpopnum(ctx->bifcxrun);
481         flag = runpoplog(ctx->bifcxrun);
482 
483         /* see what type of debugging they want to turn on or off */
484         switch(n)
485         {
486         case 1:
487             /* turn on parser tracing */
488             if (flag)
489                 ctx->bifcxrun->runcxvoc->voccxflg |= VOCCXFDBG;
490             else
491                 ctx->bifcxrun->runcxvoc->voccxflg &= ~VOCCXFDBG;
492             break;
493 
494         default:
495             /* ignore other requests */
496             runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "debugTrace");
497         }
498     }
499     else
500     {
501         /* break into debugger; return whether debugger is present */
502         bifcntargs(ctx, 0, argc);
503         runpush(ctx->bifcxrun, runclog(dbgstart(ctx->bifcxrun->runcxdbg)),
504                 &val);
505     }
506 }
507 
508 /* say */
bifsay(bifcxdef * ctx,int argc)509 void bifsay(bifcxdef *ctx, int argc)
510 {
511     uchar *str;
512     long   num;
513     char   numbuf[30];
514 
515     if (argc != 2) bifcntargs(ctx, 1, argc);
516 
517     switch(runtostyp(ctx->bifcxrun))
518     {
519     case DAT_NUMBER:
520         num = runpopnum(ctx->bifcxrun);
521         sprintf(numbuf, "%ld", num);
522         tioputs(ctx->bifcxtio, numbuf);
523         break;
524 
525     case DAT_SSTRING:
526         str = runpopstr(ctx->bifcxrun);
527         outfmt(ctx->bifcxtio, str);
528         break;
529 
530     case DAT_NIL:
531         (void)runpoplog(ctx->bifcxrun);
532         break;
533 
534     default:
535         runsig1(ctx->bifcxrun, ERR_INVTBIF, ERRTSTR, "say");
536     }
537 }
538 
539 /* car */
bifcar(bifcxdef * ctx,int argc)540 void bifcar(bifcxdef *ctx, int argc)
541 {
542     uchar   *lstp;
543     uint     lstsiz;
544     runsdef  val;
545 
546     bifcntargs(ctx, 1, argc);
547     bifchkarg(ctx, DAT_LIST);
548 
549     lstp = runpoplst(ctx->bifcxrun);
550 
551     /* get list's size, and point to its data string */
552     lstsiz = osrp2(lstp) - 2;
553     lstp += 2;
554 
555     /* push first element if one is present, otherwise push nil */
556     if (lstsiz)
557         runpbuf(ctx->bifcxrun, *lstp, lstp+1);
558     else
559         runpush(ctx->bifcxrun, DAT_NIL, &val);
560 }
561 
562 /* cdr */
bifcdr(bifcxdef * ctx,int argc)563 void bifcdr(bifcxdef *ctx, int argc)
564 {
565     uchar   *lstp;
566     uint     siz;
567     uint     lstsiz;
568     runsdef  val;
569     runsdef  stkval;
570 
571     bifcntargs(ctx, 1, argc);
572     bifchkarg(ctx, DAT_LIST);
573 
574     lstp = runpoplst(ctx->bifcxrun);
575     stkval.runstyp = DAT_LIST;
576     stkval.runsv.runsvstr = lstp;
577 
578     /* get list's size, and point to its data string */
579     lstsiz = osrp2(lstp) - 2;
580     lstp += 2;
581 
582     if (lstsiz != 0)
583     {
584         /* deduct size of first element from size of list */
585         siz = datsiz(*lstp, lstp+1) + 1;
586         lstsiz -= siz;
587 
588         /* add in the size prefix for our new list size */
589         lstsiz += 2;
590 
591         /* allocate space for new list containing rest of list */
592         runhres1(ctx->bifcxrun, lstsiz, 1, &stkval);
593         lstp = stkval.runsv.runsvstr + siz + 2;
594 
595         /* write out size followed by list value string */
596         oswp2(ctx->bifcxrun->runcxhp, lstsiz);
597         memcpy(ctx->bifcxrun->runcxhp+2, lstp, (size_t)(lstsiz-2));
598 
599         val.runsv.runsvstr = ctx->bifcxrun->runcxhp;
600         val.runstyp = DAT_LIST;
601         ctx->bifcxrun->runcxhp += lstsiz;
602         runrepush(ctx->bifcxrun, &val);
603     }
604     else
605         runpush(ctx->bifcxrun, DAT_NIL, &val);   /* empty list - cdr is nil */
606 }
607 
608 /* caps */
bifcap(bifcxdef * ctx,int argc)609 void bifcap(bifcxdef *ctx, int argc)
610 {
611     bifcntargs(ctx, 0, argc);
612     tiocaps(ctx->bifxtio);  /* set output driver next-char-capitalized flag */
613 }
614 
615 /* nocaps */
bifnoc(bifcxdef * ctx,int argc)616 void bifnoc(bifcxdef *ctx, int argc)
617 {
618     bifcntargs(ctx, 0, argc);
619     tionocaps(ctx->bifxtio);               /* set next-not-capitalized flag */
620 }
621 
622 /* length */
biflen(bifcxdef * ctx,int argc)623 void biflen(bifcxdef *ctx, int argc)
624 {
625     uchar   *p;
626     runsdef  val;
627     long     len = 0;
628     int      l;
629 
630     bifcntargs(ctx, 1, argc);
631     switch(runtostyp(ctx->bifcxrun))
632     {
633     case DAT_SSTRING:
634         p = (uchar *)runpopstr(ctx->bifcxrun);
635         len = osrp2(p) - 2;
636         break;
637 
638     case DAT_LIST:
639         p = runpoplst(ctx->bifcxrun);
640         l = osrp2(p) - 2;
641         p += 2;
642 
643         /* count all elements in list */
644         for (len = 0 ; l ; ++len)
645         {
646             int cursiz;
647 
648             /* get size of this element, and move past it */
649             cursiz = datsiz(*p, p+1) + 1;
650             l -= cursiz;
651             p += cursiz;
652         }
653         break;
654 
655     default:
656         runsig1(ctx->bifcxrun, ERR_INVTBIF, ERRTSTR, "length");
657     }
658 
659     val.runsv.runsvnum = len;
660     runpush(ctx->bifcxrun, DAT_NUMBER, &val);
661 }
662 
663 /* find */
biffnd(bifcxdef * ctx,int argc)664 void biffnd(bifcxdef *ctx, int argc)
665 {
666     uchar   *p1, *p2;
667     int      len1, len2;
668 	int      outv = 0;
669     runsdef  val;
670     int      typ = 0;
671     int      siz;
672 
673     bifcntargs(ctx, 2, argc);
674     switch(runtostyp(ctx->bifcxrun))
675     {
676     case DAT_SSTRING:
677         p1 = runpopstr(ctx->bifcxrun);
678         len1 = osrp2(p1) - 2;
679         p1 += 2;
680 
681         p2 = runpopstr(ctx->bifcxrun);
682         len2 = osrp2(p2) - 2;
683         p2 += 2;
684 
685         /* look for p2 within p1 */
686         for (typ = DAT_NIL, outv = 1 ; len1 >= len2 ; ++p1, --len1, ++outv)
687         {
688             if (!memcmp(p1, p2, (size_t)len2))
689             {
690                 typ = DAT_NUMBER;           /* use number in outv after all */
691                 break;                        /* that's it - we've found it */
692             }
693         }
694         break;
695 
696     case DAT_LIST:
697         p1 = runpoplst(ctx->bifcxrun);
698         len1 = osrp2(p1) - 2;
699         p1 += 2;
700 
701         /* get second item:  any old datatype */
702         runpop(ctx->bifcxrun, &val);
703 
704         for (typ = DAT_NIL, outv = 1 ; len1 ; ++outv, p1 += siz, len1 -= siz)
705         {
706             siz = datsiz(*p1, p1 + 1) + 1;      /* get size of this element */
707             if (val.runstyp != *p1) continue;          /* types don't match */
708 
709             switch(val.runstyp)
710             {
711             case DAT_NUMBER:
712                 if (val.runsv.runsvnum != osrp4s(p1 + 1)) continue;
713                 break;
714 
715             case DAT_SSTRING:
716             case DAT_LIST:
717                 if (osrp2(p1 + 1) != osrp2(val.runsv.runsvstr) ||
718                     memcmp(p1 + 3, val.runsv.runsvstr + 2,
719                            (size_t)(osrp2(p1 + 1) - 2)))
720                     continue;
721                 break;
722 
723             case DAT_PROPNUM:
724                 if (osrp2(p1 + 1) != val.runsv.runsvprp) continue;
725                 break;
726 
727             case DAT_OBJECT:
728             case DAT_FNADDR:
729                 if (osrp2(p1 + 1) != val.runsv.runsvobj) continue;
730                 break;
731 
732             default:
733                 break;
734             }
735 
736             /* if we got here, it means we found a match */
737             typ = DAT_NUMBER;                      /* use the value in outv */
738             break;                            /* that's it - we've found it */
739         }
740         break;
741 
742     default:
743         runsig1(ctx->bifcxrun, ERR_INVTBIF, ERRTSTR, "find");
744     }
745 
746     /* push the value given by typ and outv */
747     val.runsv.runsvnum = outv;
748     runpush(ctx->bifcxrun, typ, &val);
749 }
750 
751 /* setit - set current 'it' */
bifsit(bifcxdef * ctx,int argc)752 void bifsit(bifcxdef *ctx, int argc)
753 {
754     objnum    obj;
755     int       typ;
756     voccxdef *vcx = ctx->bifcxrun->runcxvoc;
757 
758     /* check for extended version that allows setting him/her */
759     if (argc == 2)
760     {
761         if (runtostyp(ctx->bifcxrun) == DAT_NIL)
762         {
763             rundisc(ctx->bifcxrun);                      /* discard the nil */
764             obj = MCMONINV;                           /* use invalid object */
765         }
766         else
767             obj = runpopobj(ctx->bifcxrun);               /* get the object */
768 
769         typ = runpopnum(ctx->bifcxrun);                     /* get the code */
770         vcx->voccxthc = 0;                         /* clear the 'them' list */
771 
772         switch(typ)
773         {
774         case 0:                                                 /* set "it" */
775             vcx->voccxit = obj;
776             break;
777 
778         case 1:                                                /* set "him" */
779             vcx->voccxhim = obj;
780             break;
781 
782         case 2:                                                /* set "her" */
783             vcx->voccxher = obj;
784             break;
785         }
786         return;
787     }
788 
789     /* "setit classic" has one argument only */
790     bifcntargs(ctx, 1, argc);
791 
792     /* check to see if we're setting 'it' or 'them' */
793     if (runtostyp(ctx->bifcxrun) == DAT_LIST)
794     {
795         uchar *lst;
796         uint   siz;
797         int    cnt;
798 
799         lst = runpoplst(ctx->bifcxrun);
800         siz = osrp2(lst);
801         lst += 2;
802         siz -= 2;
803 
804         for (cnt = 0 ; siz ; )
805         {
806             /* if this is an object, add to 'them' list (otherwise ignore) */
807             if (*lst == DAT_OBJECT)
808                 vcx->voccxthm[cnt++] = osrp2(lst+1);
809 
810             lstadv(&lst, &siz);
811         }
812         vcx->voccxthc = cnt;
813         vcx->voccxit = MCMONINV;
814     }
815     else
816     {
817         /* set 'it', and delete 'them' list */
818         if (runtostyp(ctx->bifcxrun) == DAT_NIL)
819         {
820             vcx->voccxit = MCMONINV;
821             rundisc(ctx->bifcxrun);
822         }
823         else
824             vcx->voccxit = runpopobj(ctx->bifcxrun);
825         vcx->voccxthc = 0;
826     }
827 }
828 
829 /* randomize - seed random number generator */
bifsrn(bifcxdef * ctx,int argc)830 void bifsrn(bifcxdef *ctx, int argc)
831 {
832     bifcntargs(ctx, 0, argc);
833     os_rand(&ctx->bifcxrnd);
834     ctx->bifcxrndset = TRUE;
835 }
836 
837 /* rand - get a random number */
bifrnd(bifcxdef * ctx,int argc)838 void bifrnd(bifcxdef *ctx, int argc)
839 {
840     unsigned long result, max, randseed;
841     int      tmp;
842     runsdef  val;
843 
844     /* get argument - number giving upper bound of generated number */
845     bifcntargs(ctx, 1, argc);
846     bifchkarg(ctx, DAT_NUMBER);
847     max = runpopnum(ctx->bifcxrun);
848 
849     /* if the max is zero, just return zero */
850     if (max == 0)
851     {
852         runpnum(ctx->bifcxrun, 0);
853         return;
854     }
855 
856     /*
857      *   If the random number generator has been seeded by a call to
858      *   randomize(), use the new, improved random number generator.  If
859      *   not, use the old random number generator to ensure that the same
860      *   sequence of numbers is generated as always (to prevent breaking
861      *   existing test scripts based on the old sequence).
862      */
863     if (!ctx->bifcxrndset)
864     {
865         /* compute the next number in sequence, using old cheesy generator */
866         randseed = ctx->bifcxrnd;
867         randseed *= 1033;
868         randseed += 5;
869         tmp = randseed / 16384;
870         randseed %= 16384;
871         result = tmp / 7;
872 
873         /* adjust the result to be in the requested range */
874         result = ( randseed % max ) + 1;
875 
876         /* save the new seed value, and return the value */
877         ctx->bifcxrnd = randseed;
878         val.runsv.runsvnum = result;
879         runpush(ctx->bifcxrun, DAT_NUMBER, &val);
880     }
881     else
882     {
883 #define BIF_RAND_M  ((ulong)2147483647)
884 #define BIF_RAND_Q  ((ulong)127773)
885 #define BIF_RAND_A  ((ulong)16807)
886 #define BIF_RAND_R  ((ulong)2836)
887 
888         long lo, hi, test;
889 
890         lo = ctx->bifcxrnd / BIF_RAND_Q;
891         hi = ctx->bifcxrnd % BIF_RAND_Q;
892         test = BIF_RAND_A*lo - BIF_RAND_R*hi;
893         ctx->bifcxrnd = test;
894         if (test > 0)
895             ctx->bifcxrnd = test;
896         else
897             ctx->bifcxrnd = test + BIF_RAND_M;
898         runpnum(ctx->bifcxrun, (((ulong)ctx->bifcxrnd) % max) + 1);
899     }
900 }
901 
902 /*
903  *   case-insensitive substring matching
904  */
bif_stristr(const char * s1,const char * s2)905 static const char *bif_stristr(const char *s1, const char *s2)
906 {
907     size_t s1len;
908     size_t s2len;
909 
910     /* scan for a match */
911     for (s1len = strlen(s1), s2len = strlen(s2) ; s1len >= s2len ;
912          ++s1, --s1len)
913     {
914         /* if this is a match, return this substring */
915         if (memicmp(s1, s2, s2len) == 0)
916             return (const char *)s1;
917     }
918 
919     return 0;
920 }
921 
922 /*
923  *   askfile flags
924  */
925 #define BIF_ASKF_EXT_RET  1                        /* extended return codes */
926 
927 /*
928  *   askfile
929  */
bifask(bifcxdef * ctx,int argc)930 void bifask(bifcxdef *ctx, int argc)
931 {
932     uchar *prompt;
933     char   buf[OSFNMAX + 2];
934     char   pbuf[128];
935     int    err;
936     int    prompt_type;
937     int    file_type;
938     ulong  flags;
939 
940     /* make sure we have an acceptable number of arguments */
941     if (argc != 1 && argc != 3 && argc != 4)
942         runsig(ctx->bifcxrun, ERR_BIFARGC);
943 
944     /* get the first argument - the prompt string */
945     prompt = runpopstr(ctx->bifcxrun);
946     bifcstr(ctx, pbuf, (size_t)sizeof(pbuf), prompt);
947 
948     /* presume we will have no flags */
949     flags = 0;
950 
951     /* if we have the prompt type and file type parameters, get them */
952     if (argc >= 3)
953     {
954         /* get the prompt-type and the file-type arguments */
955         prompt_type = (int)runpopnum(ctx->bifcxrun);
956         file_type = (int)runpopnum(ctx->bifcxrun);
957 
958         /* if we have a fourth argument, it's the flags */
959         if (argc == 4)
960             flags = runpopnum(ctx->bifcxrun);
961     }
962     else
963     {
964         static const char *save_strs[] =
965         {
966             "save",
967             "write",
968             0
969         };
970         static const char *game_strs[] =
971         {
972             "restore",
973             "game",
974             0
975         };
976         const char **sp;
977 
978         /*
979          *   No prompt type or file type were specified.  Try to infer the
980          *   dialog type and file type from the text of the prompt.  (This
981          *   is mostly to support older games, in particular those based
982          *   on older versions of adv.t, since newer games should always
983          *   provide explicit values for the file type and dialog type.
984          *   We are thus inferring the types based on the prompt strings
985          *   that older adv.t's used when calling askfile.)
986          *
987          *   If the prompt contains any substring such as "save" or
988          *   "write", specify that we're saving; otherwise, assume that
989          *   we're opening an existing file for reading.
990          *
991          *   If the prompt contains the substrings "restore" AND "game",
992          *   assume that we're opening a game file; otherwise, don't make
993          *   any assumptions, and use the "unknown" file type.
994          */
995 
996         /* presume we're going to open a saved-game file */
997         prompt_type = OS_AFP_OPEN;
998         file_type = OSFTSAVE;
999 
1000         /* look for any one of the "save" substrings */
1001         for (sp = save_strs ; *sp != 0 ; ++sp)
1002         {
1003             /* check to see if this substring matches */
1004             if (bif_stristr(pbuf, *sp))
1005             {
1006                 /* found it - use the "save" prompt */
1007                 prompt_type = OS_AFP_SAVE;
1008 
1009                 /* no need to look any further */
1010                 break;
1011             }
1012         }
1013 
1014         /*
1015          *   look for *all* of the "restore game" strings - if we fail to
1016          *   find any of them, be conservative and make no assumptions
1017          *   about the file type
1018          */
1019         for (sp = game_strs ; *sp != 0 ; ++sp)
1020         {
1021             if (bif_stristr(pbuf, *sp) == 0)
1022             {
1023                 /*
1024                  *   this one doesn't match - don't make assumptions about
1025                  *   the file type
1026                  */
1027                 file_type = OSFTUNK;
1028 
1029                 /* no need to look any further */
1030                 break;
1031             }
1032         }
1033 
1034         /* check for a transcript */
1035         if (file_type == OSFTUNK
1036             && prompt_type == OS_AFP_SAVE
1037             && bif_stristr(pbuf, "script") != 0)
1038         {
1039             /* looks like a log file */
1040             file_type = OSFTLOG;
1041         }
1042     }
1043 
1044     /* ask for a file */
1045     err = tio_askfile(pbuf, buf, (int)sizeof(buf), prompt_type, (os_filetype_t)file_type);
1046 
1047     /*
1048      *   if the caller requested extended return codes, return a list
1049      *   containing the return code as the first element and, if
1050      *   successful, the string as the second element
1051      */
1052     if ((flags & BIF_ASKF_EXT_RET) != 0)
1053     {
1054         ushort len;
1055         runsdef val;
1056         uchar *p;
1057 
1058         /*
1059          *   Allocate space for the starter list - if we have a string to
1060          *   return, just allocate space for the number element for now;
1061          *   otherwise, allocate space for the number plus a nil second
1062          *   element (one byte).
1063          */
1064         len = 2 + (1 + 4);
1065         if (err != OS_AFE_SUCCESS)
1066             ++len;
1067 
1068         /* allocate the space */
1069         runhres(ctx->bifcxrun, len, 0);
1070 
1071         /* set up our list pointer */
1072         val.runstyp = DAT_LIST;
1073         val.runsv.runsvstr = p = ctx->bifcxrun->runcxhp;
1074 
1075         /* write the length prefix */
1076         oswp2(p, len);
1077         p += 2;
1078 
1079         /* write the return code as the first element */
1080         *p++ = DAT_NUMBER;
1081         oswp4s(p, err);
1082         p += 4;
1083 
1084         /* write the 'nil' second element if there's an error */
1085         if (err != OS_AFE_SUCCESS)
1086             *p++ = DAT_NIL;
1087 
1088         /* commit the list's memory */
1089         ctx->bifcxrun->runcxhp = p;
1090 
1091         /* push the list */
1092         runrepush(ctx->bifcxrun, &val);
1093 
1094         /* if we were successful, add the string to the list */
1095         if (err == OS_AFE_SUCCESS)
1096         {
1097             runsdef val2;
1098 
1099             /* push the string value, converting to our string format */
1100             runpushcstr(ctx->bifcxrun, buf, strlen(buf), 1);
1101 
1102             /* add it to the list already on the stack */
1103             runpop(ctx->bifcxrun, &val2);
1104             runpop(ctx->bifcxrun, &val);
1105             runadd(ctx->bifcxrun, &val, &val2, 2);
1106 
1107             /* re-push the result */
1108             runrepush(ctx->bifcxrun, &val);
1109         }
1110     }
1111     else
1112     {
1113         /*
1114          *   use the traditional return codes - if askfile failed, return
1115          *   nil; otherwise, return the filename
1116          */
1117         if (err)
1118             runpnil(ctx->bifcxrun);
1119         else
1120             runpushcstr(ctx->bifcxrun, buf, strlen(buf), 0);
1121     }
1122 }
1123 
1124 /* setscore */
bifssc(bifcxdef * ctx,int argc)1125 void bifssc(bifcxdef *ctx, int argc)
1126 {
1127     int s1, s2;
1128 
1129     /* optional new way - string argument */
1130     if (argc == 1 && runtostyp(ctx->bifcxrun) == DAT_SSTRING)
1131     {
1132         char   buf[80];
1133         uchar *p;
1134 
1135         p = runpopstr(ctx->bifcxrun);
1136         bifcstr(ctx, buf, (size_t)sizeof(buf), p);
1137         tiostrsc(ctx->bifcxtio, buf);
1138     }
1139     else
1140     {
1141         /* old way - two numeric arguments (displays: x/y) */
1142         bifcntargs(ctx, 2, argc);
1143         s1 = runpopnum(ctx->bifcxrun);
1144         s2 = runpopnum(ctx->bifcxrun);
1145         tioscore(ctx->bifcxtio, s1, s2);
1146     }
1147 }
1148 
1149 /* substr */
bifsub(bifcxdef * ctx,int argc)1150 void bifsub(bifcxdef *ctx, int argc)
1151 {
1152     uchar   *p;
1153     int      ofs;
1154     int      asklen;
1155     int      outlen;
1156     int      len;
1157 
1158     bifcntargs(ctx, 3, argc);
1159 
1160     /* get the string argument */
1161     bifchkarg(ctx, DAT_SSTRING);
1162     p = runpopstr(ctx->bifcxrun);
1163     len = osrp2(p) - 2;
1164     p += 2;
1165 
1166     /* get the offset argument */
1167     bifchkarg(ctx, DAT_NUMBER);
1168     ofs = runpopnum(ctx->bifcxrun);
1169     if (ofs < 1) runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "substr");
1170 
1171     /* get the length argument */
1172     bifchkarg(ctx, DAT_NUMBER);
1173     asklen = runpopnum(ctx->bifcxrun);
1174     if (asklen < 0) runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "substr");
1175 
1176     --ofs;          /* convert offset to a zero bias (user provided 1-bias) */
1177     p += ofs;                           /* advance string pointer by offset */
1178 
1179     if (ofs >= len)
1180         outlen = 0;                         /* offset is past end of string */
1181     else if (asklen > len - ofs)
1182         outlen = len - ofs;                      /* just use rest of string */
1183     else
1184         outlen = asklen;                /* requested length can be provided */
1185 
1186     runpstr(ctx->bifcxrun, (char *)p, outlen, 3);
1187 }
1188 
1189 /* cvtstr - convert value to a string */
bifcvs(bifcxdef * ctx,int argc)1190 void bifcvs(bifcxdef *ctx, int argc)
1191 {
1192     const char *p = nullptr;
1193     int   len = 0;
1194     char  buf[30];
1195 
1196     bifcntargs(ctx, 1, argc);
1197     switch(runtostyp(ctx->bifcxrun))
1198     {
1199     case DAT_NIL:
1200         p = "nil";
1201         len = 3;
1202         (void)runpoplog(ctx->bifcxrun);
1203         break;
1204 
1205     case DAT_TRUE:
1206         p = "true";
1207         len = 4;
1208         (void)runpoplog(ctx->bifcxrun);
1209         break;
1210 
1211     case DAT_NUMBER:
1212         sprintf(buf, "%ld", runpopnum(ctx->bifcxrun));
1213         p = buf;
1214         len = strlen(buf);
1215         break;
1216 
1217     case DAT_SSTRING:
1218         /* leave the string value on the stack unchanged */
1219         return;
1220 
1221     default:
1222         /* throw the RUNEXITOBJ error */
1223         runsig1(ctx->bifcxrun, ERR_INVTBIF, ERRTSTR, "cvtstr");
1224     }
1225 
1226     runpstr(ctx->bifcxrun, p, len, 0);
1227 }
1228 
1229 /* cvtnum  - convert a value to a number */
bifcvn(bifcxdef * ctx,int argc)1230 void bifcvn(bifcxdef *ctx, int argc)
1231 {
1232     runsdef  val;
1233     uchar   *p;
1234     int      len;
1235     int      typ;
1236     long     acc;
1237     int      neg;
1238 
1239     bifcntargs(ctx, 1, argc);
1240     p = runpopstr(ctx->bifcxrun);
1241     len = osrp2(p) - 2;
1242     p += 2;
1243 
1244     if (len == 3 && !memcmp(p, "nil", (size_t)3))
1245         typ = DAT_NIL;
1246     else if (len == 4 && !memcmp(p, "true", (size_t)4))
1247         typ = DAT_TRUE;
1248     else
1249     {
1250         typ = DAT_NUMBER;
1251         for ( ; len != 0 && t_isspace(*p) ; ++p, --len) ;
1252         if (len != 0 && *p == '-')
1253         {
1254             neg = TRUE;
1255             for (++p, --len ; len != 0 && t_isspace(*p) ; ++p, --len) ;
1256         }
1257         else neg = FALSE;
1258 
1259         /* accumulate the number digit by digit */
1260         for (acc = 0 ; len != 0 && Common::isDigit(*p) ; ++p, --len)
1261             acc = (acc << 3) + (acc << 1) + ((*p) - '0');
1262 
1263         if (neg) acc = -acc;
1264         val.runsv.runsvnum = acc;
1265     }
1266 
1267     runpush(ctx->bifcxrun, typ, &val);
1268 }
1269 
1270 /* general string conversion function */
bifcvtstr(bifcxdef * ctx,void (* cvtfn)(uchar *,int),int argc)1271 static void bifcvtstr(bifcxdef *ctx, void (*cvtfn)(uchar *, int), int argc)
1272 {
1273     uchar   *p;
1274     int      len;
1275     runsdef  val;
1276     runsdef  stkval;
1277 
1278     bifcntargs(ctx, 1, argc);
1279     bifchkarg(ctx, DAT_SSTRING);
1280 
1281     p = runpopstr(ctx->bifcxrun);
1282     stkval.runstyp = DAT_SSTRING;
1283     stkval.runsv.runsvstr = p;
1284     len = osrp2(p);
1285 
1286     /* allocate space in heap for the string and convert */
1287     runhres1(ctx->bifcxrun, len, 1, &stkval);
1288     p = stkval.runsv.runsvstr;
1289     memcpy(ctx->bifcxrun->runcxhp, p, (size_t)len);
1290     (*cvtfn)(ctx->bifcxrun->runcxhp + 2, len - 2);
1291 
1292     val.runsv.runsvstr = ctx->bifcxrun->runcxhp;
1293     val.runstyp = DAT_SSTRING;
1294     ctx->bifcxrun->runcxhp += len;
1295     runrepush(ctx->bifcxrun, &val);
1296 }
1297 
1298 /* routine to convert a counted-length string to uppercase */
bifstrupr(uchar * str,int len)1299 static void bifstrupr(uchar *str, int len)
1300 {
1301     for ( ; len ; --len, ++str)
1302     {
1303         if (*str == '\\' && len > 1)
1304             --len, ++str;
1305         else if (Common::isLower(*str))
1306             *str = toupper(*str);
1307     }
1308 }
1309 
1310 /* upper */
bifupr(bifcxdef * ctx,int argc)1311 void bifupr(bifcxdef *ctx, int argc)
1312 {
1313     bifcvtstr(ctx, bifstrupr, argc);
1314 }
1315 
1316 /* convert a counted-length string to lowercase */
bifstrlwr(uchar * str,int len)1317 static void bifstrlwr(uchar *str, int len)
1318 {
1319     for ( ; len ; --len, ++str)
1320     {
1321         if (*str == '\\' && len > 1)
1322             --len, ++str;
1323         else if (Common::isUpper(*str))
1324             *str = tolower(*str);
1325     }
1326 }
1327 
1328 /* lower */
biflwr(bifcxdef * ctx,int argc)1329 void biflwr(bifcxdef *ctx, int argc)
1330 {
1331     bifcvtstr(ctx, bifstrlwr, argc);
1332 }
1333 
1334 /* internal check to determine if object is of a class */
bifinh(voccxdef * voc,vocidef * v,objnum cls)1335 int bifinh(voccxdef *voc, vocidef *v, objnum cls)
1336 {
1337     int     i;
1338     objnum *sc;
1339 
1340     if (!v) return(FALSE);
1341     for (i = v->vocinsc, sc = v->vocisc ; i ; ++sc, --i)
1342     {
1343         if (*sc == cls
1344             || bifinh(voc, vocinh(voc, *sc), cls))
1345             return(TRUE);
1346     }
1347     return(FALSE);
1348 }
1349 
1350 /* isclass(obj, cls) */
bifisc(bifcxdef * ctx,int argc)1351 void bifisc(bifcxdef *ctx, int argc)
1352 {
1353     objnum    obj;
1354     objnum    cls;
1355     runsdef   val;
1356     voccxdef *voc = ctx->bifcxrun->runcxvoc;
1357 
1358     bifcntargs(ctx, 2, argc);
1359 
1360     /* if checking for nil, return nil */
1361     if (runtostyp(ctx->bifcxrun) == DAT_NIL)
1362     {
1363         rundisc(ctx->bifcxrun);
1364         rundisc(ctx->bifcxrun);
1365         runpnil(ctx->bifcxrun);
1366         return;
1367     }
1368 
1369     /* get the arguments:  object, class */
1370     obj = runpopobj(ctx->bifcxrun);
1371     cls = runpopobj(ctx->bifcxrun);
1372 
1373     /* return the result from bifinh() */
1374     runpush(ctx->bifcxrun, runclog(bifinh(voc, vocinh(voc, obj), cls)), &val);
1375 }
1376 
1377 /* firstsc(obj) - get the first superclass of an object */
bif1sc(bifcxdef * ctx,int argc)1378 void bif1sc(bifcxdef *ctx, int argc)
1379 {
1380     objnum obj;
1381     objnum sc;
1382 
1383     bifcntargs(ctx, 1, argc);
1384     obj = runpopobj(ctx->bifcxrun);
1385     sc = objget1sc(ctx->bifcxrun->runcxmem, obj);
1386     runpobj(ctx->bifcxrun, sc);
1387 }
1388 
1389 /* firstobj */
biffob(bifcxdef * ctx,int argc)1390 void biffob(bifcxdef *ctx, int argc)
1391 {
1392     vocidef ***vpg;
1393     vocidef  **v;
1394     objnum     obj;
1395     int        i;
1396     int        j;
1397     objnum     cls = 0;
1398     voccxdef  *voc = ctx->bifcxrun->runcxvoc;
1399 
1400     /* get class to search for, if one is specified */
1401     if (argc == 0)
1402         cls = MCMONINV;
1403     else if (argc == 1)
1404         cls = runpopobj(ctx->bifcxrun);
1405     else
1406         runsig(ctx->bifcxrun, ERR_BIFARGC);
1407 
1408     for (vpg = voc->voccxinh, i = 0 ; i < VOCINHMAX ; ++vpg, ++i)
1409     {
1410         if (!*vpg) continue;
1411         for (v = *vpg, obj = (i << 8), j = 0 ; j < 256 ; ++v, ++obj, ++j)
1412         {
1413             if (!*v || ((*v)->vociflg & VOCIFCLASS)
1414                 || (cls != MCMONINV && !bifinh(voc, *v, cls)))
1415                 continue;
1416 
1417             /* this is an object we can use - push it */
1418             runpobj(ctx->bifcxrun, obj);
1419             return;
1420         }
1421     }
1422 
1423     /* no objects found at all - return nil */
1424     runpnil(ctx->bifcxrun);
1425 }
1426 
1427 /* nextobj */
bifnob(bifcxdef * ctx,int argc)1428 void bifnob(bifcxdef *ctx, int argc)
1429 {
1430     objnum     prv;
1431     vocidef ***vpg;
1432     vocidef  **v;
1433     objnum     obj;
1434     int        i;
1435     int        j;
1436     objnum     cls = 0;
1437     voccxdef  *voc = ctx->bifcxrun->runcxvoc;
1438 
1439     /* get last position in search */
1440     prv = runpopobj(ctx->bifcxrun);
1441 
1442     /* get class to search for, if one is specified */
1443     if (argc == 1)
1444         cls = MCMONINV;
1445     else if (argc == 2)
1446         cls = runpopobj(ctx->bifcxrun);
1447     else
1448         runsig(ctx->bifcxrun, ERR_BIFARGC);
1449 
1450     /* start at previous object plus 1 */
1451     i = (prv >> 8);
1452     vpg = voc->voccxinh + i;
1453     j = (prv & 255);
1454     obj = prv;
1455     v = (*vpg) + j;
1456 
1457     for (;;)
1458     {
1459         ++j;
1460         ++obj;
1461         ++v;
1462         if (j == 256)
1463         {
1464             j = 0;
1465             ++i;
1466             ++vpg;
1467             if (!*vpg)
1468             {
1469                 obj += 255;
1470                 j += 255;
1471                 continue;
1472             }
1473             v = (*vpg);
1474         }
1475         if (i >= VOCINHMAX)
1476         {
1477             runpnil(ctx->bifcxrun);
1478             return;
1479         }
1480 
1481         if (!*v || ((*v)->vociflg & VOCIFCLASS)
1482             || (cls != MCMONINV && !bifinh(voc, *v, cls)))
1483             continue;
1484 
1485         /* this is an object we can use - push it */
1486         runpobj(ctx->bifcxrun, obj);
1487         return;
1488     }
1489 }
1490 
1491 /* setversion */
bifsvn(bifcxdef * ctx,int argc)1492 void bifsvn(bifcxdef *ctx, int argc)
1493 {
1494     bifcntargs(ctx, 1, argc);
1495     (void)runpopstr(ctx->bifcxrun);
1496     /* note - setversion doesn't do anything in v2; uses timestamp instead */
1497 }
1498 
1499 /* getarg */
bifarg(bifcxdef * ctx,int argc)1500 void bifarg(bifcxdef *ctx, int argc)
1501 {
1502     int argnum;
1503 
1504     bifcntargs(ctx, 1, argc);
1505     bifchkarg(ctx, DAT_NUMBER);
1506 
1507     /* get and verify argument number */
1508     argnum = runpopnum(ctx->bifcxrun);
1509     if (argnum < 1) runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "getarg");
1510 
1511     runrepush(ctx->bifcxrun, ctx->bifcxrun->runcxbp - argnum - 1);
1512 }
1513 
1514 /* datatype */
biftyp(bifcxdef * ctx,int argc)1515 void biftyp(bifcxdef *ctx, int argc)
1516 {
1517     runsdef val;
1518 
1519     bifcntargs(ctx, 1, argc);
1520 
1521     /* get whatever it is, and push the type */
1522     runpop(ctx->bifcxrun, &val);
1523     val.runsv.runsvnum = val.runstyp;          /* new value is the datatype */
1524     runpush(ctx->bifcxrun, DAT_NUMBER, &val);
1525 }
1526 
1527 /* undo */
bifund(bifcxdef * ctx,int argc)1528 void bifund(bifcxdef *ctx, int argc)
1529 {
1530     objucxdef *ucx = ctx->bifcxrun->runcxvoc->voccxundo;
1531     mcmcxdef  *mcx = ctx->bifcxrun->runcxmem;
1532     errcxdef  *ec  = ctx->bifcxerr;
1533     int        err;
1534     int        undone;
1535     runsdef    val;
1536 
1537     bifcntargs(ctx, 0, argc);                               /* no arguments */
1538 
1539     ERRBEGIN(ec)
1540         if (ucx)
1541         {
1542             objundo(mcx, ucx);         /* try to undo to previous savepoint */
1543             undone = TRUE;                       /* looks like we succeeded */
1544         }
1545         else
1546             undone = FALSE;                  /* no undo context; can't undo */
1547     ERRCATCH(ec, err)
1548         if (err == ERR_NOUNDO || err == ERR_ICUNDO)
1549             undone = FALSE;
1550         else
1551             errrse(ec);            /* don't know how to handle other errors */
1552     ERREND(ec)
1553 
1554     /* return a value indicating whether the undo operation succeeded */
1555     runpush(ctx->bifcxrun, runclog(undone), &val);
1556 
1557     /* note that the rest of the command line is to be ignored */
1558     ctx->bifcxrun->runcxvoc->voccxflg |= VOCCXFCLEAR;
1559 }
1560 
1561 /* flags for defined() function */
1562 #define BIFDEF_DEFINED_ANY           1
1563 #define BIFDEF_DEFINED_DIRECTLY      2
1564 #define BIFDEF_DEFINED_INHERITS      3
1565 #define BIFDEF_DEFINED_GET_CLASS     4
1566 
1567 /* defined */
bifdef(bifcxdef * ctx,int argc)1568 void bifdef(bifcxdef *ctx, int argc)
1569 {
1570     prpnum  prpn;
1571     objnum  objn;
1572     uint    ofs;
1573     runsdef val;
1574     objnum  def_objn;
1575     int     flag;
1576 
1577     /* get object and property arguments */
1578     objn = runpopobj(ctx->bifcxrun);
1579     prpn = runpopprp(ctx->bifcxrun);
1580 
1581     /* if there's a flag argument, get it as well */
1582     if (argc == 3)
1583     {
1584         /* get the flag */
1585         flag = (int)runpopnum(ctx->bifcxrun);
1586     }
1587     else
1588     {
1589         /* check the argument count */
1590         bifcntargs(ctx, 2, argc);
1591 
1592         /* use the default flag value (DEFINES_OR_INHERITS) */
1593         flag = BIFDEF_DEFINED_ANY;
1594     }
1595 
1596     /* get the offset of the property and the defining object */
1597     ofs = objgetap(ctx->bifcxrun->runcxmem, objn, prpn, &def_objn, FALSE);
1598 
1599     /* determine the type of information they want */
1600     switch(flag)
1601     {
1602     case BIFDEF_DEFINED_ANY:
1603         /* if the property is defined, return true, else return nil */
1604         runpush(ctx->bifcxrun, runclog(ofs != 0), &val);
1605         break;
1606 
1607     case BIFDEF_DEFINED_DIRECTLY:
1608         /* if the property is defined directly by the object, return true */
1609         runpush(ctx->bifcxrun, runclog(ofs != 0 && def_objn == objn), &val);
1610         break;
1611 
1612     case BIFDEF_DEFINED_INHERITS:
1613         /* if the property is inherited, return true */
1614         runpush(ctx->bifcxrun, runclog(ofs != 0 && def_objn != objn), &val);
1615         break;
1616 
1617     case BIFDEF_DEFINED_GET_CLASS:
1618         /* if it's defined, return the defining object, otherwise nil */
1619         if (ofs == 0)
1620             runpnil(ctx->bifcxrun);
1621         else
1622             runpobj(ctx->bifcxrun, def_objn);
1623         break;
1624 
1625     default:
1626         /* invalid flag value */
1627         runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "defined");
1628     }
1629 }
1630 
1631 /* proptype */
bifpty(bifcxdef * ctx,int argc)1632 void bifpty(bifcxdef *ctx, int argc)
1633 {
1634     prpnum   prpn;
1635     objnum   objn;
1636     uint     ofs;
1637     runsdef  val;
1638     objnum   orn;
1639     objdef  *objptr;
1640     prpdef  *propptr;
1641 
1642     bifcntargs(ctx, 2, argc);
1643 
1644     /* get offset of obj.prop */
1645     objn = runpopobj(ctx->bifcxrun);
1646     prpn = runpopprp(ctx->bifcxrun);
1647     ofs = objgetap(ctx->bifcxrun->runcxmem, objn, prpn, &orn, FALSE);
1648 
1649     if (ofs)
1650     {
1651         /* lock the object, read the prpdef, and unlock it */
1652         objptr = (objdef *)mcmlck(ctx->bifcxrun->runcxmem, (mcmon)orn);
1653         propptr = objofsp(objptr, ofs);
1654         val.runsv.runsvnum = prptype(propptr);
1655         mcmunlck(ctx->bifcxrun->runcxmem, (mcmon)orn);
1656     }
1657     else
1658     {
1659         /* property is not defined by object - indicate that type is nil */
1660         val.runsv.runsvnum = DAT_NIL;
1661     }
1662 
1663     /* special case:  DAT_DEMAND -> DAT_LIST (for contents properties) */
1664     if (val.runsv.runsvnum == DAT_DEMAND)
1665         val.runsv.runsvnum = DAT_LIST;
1666 
1667     /* return the property type as a number */
1668     runpush(ctx->bifcxrun, DAT_NUMBER, &val);
1669 }
1670 
1671 /* outhide */
bifoph(bifcxdef * ctx,int argc)1672 void bifoph(bifcxdef *ctx, int argc)
1673 {
1674     runsdef val;
1675     int     hidden, output_occurred;
1676 
1677     bifcntargs(ctx, 1, argc);
1678     outstat(&hidden, &output_occurred);
1679     if (runtostyp(ctx->bifcxrun) == DAT_TRUE)
1680     {
1681         /* throw away the flag */
1682         rundisc(ctx->bifcxrun);
1683 
1684         /* figure out appropriate return value */
1685         if (!hidden)
1686             val.runsv.runsvnum = 0;
1687         else if (!output_occurred)
1688             val.runsv.runsvnum = 1;
1689         else
1690             val.runsv.runsvnum = 2;
1691         runpush(ctx->bifcxrun, DAT_NUMBER, &val);
1692 
1693         /* actually hide the output, resetting count flag */
1694         outhide();
1695     }
1696     else if (runtostyp(ctx->bifcxrun) == DAT_NIL)
1697     {
1698         /* throw away the flag */
1699         rundisc(ctx->bifcxrun);
1700 
1701         /* show output, returning status */
1702         runpush(ctx->bifcxrun, runclog(outshow()), &val);
1703     }
1704     else if (runtostyp(ctx->bifcxrun) == DAT_NUMBER)
1705     {
1706         int n = runpopnum(ctx->bifcxrun);
1707 
1708         if (n == 0)
1709         {
1710             /* output was not hidden - show output and return status */
1711             runpush(ctx->bifcxrun, runclog(outshow()), &val);
1712         }
1713         else if (n == 1)
1714         {
1715             /*
1716              *   Output was hidden, but no output had occurred yet.
1717              *   Leave output hidden and return whether any output has
1718              *   occurred.
1719              */
1720             runpush(ctx->bifcxrun, runclog(output_occurred), &val);
1721         }
1722         else if (n == 2)
1723         {
1724             /*
1725              *   Output was hidden, and output had already occurred.  If
1726              *   more output has occurred, return true, else return nil.
1727              *   In either case, set the output_occurred flag back to
1728              *   true, since it was true before the outhide(true).
1729              */
1730             runpush(ctx->bifcxrun, runclog(output_occurred), &val);
1731             outsethidden();
1732         }
1733         else
1734             errsig1(ctx->bifcxerr, ERR_INVVBIF, ERRTSTR, "outhide");
1735     }
1736     else
1737         errsig(ctx->bifcxerr, ERR_REQNUM);
1738 }
1739 
1740 /* put a numeric value in a list */
bifputnum(uchar * lstp,uint val)1741 static uchar *bifputnum(uchar *lstp, uint val)
1742 {
1743     *lstp++ = DAT_NUMBER;
1744     oswp4s(lstp, (long)val);
1745     return(lstp + 4);
1746 }
1747 
1748 /* gettime */
biftim(bifcxdef * ctx,int argc)1749 void biftim(bifcxdef *ctx, int argc) {
1750 	TimeDate tm;
1751 	uint timer;
1752     uchar      ret[80];
1753     uchar     *p;
1754     runsdef    val;
1755     int        typ;
1756 	int tm_yday;
1757 	const int MONTH_DAYS[11] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30 };
1758 
1759     if (argc == 1)
1760     {
1761         /* get the time type */
1762         typ = (int)runpopnum(ctx->bifcxrun);
1763     }
1764     else
1765     {
1766         /* make sure no arguments are specified */
1767         bifcntargs(ctx, 0, argc);
1768 
1769         /* use the default time type */
1770         typ = 1;
1771     }
1772 
1773     switch(typ)
1774     {
1775     case 1:
1776         /*
1777          *   default information format - list format with current system
1778          *   time and date
1779          */
1780 
1781         /* make sure the time zone is set up properly */
1782         os_tzset();
1783 
1784         /* get the local time information */
1785 		g_system->getTimeAndDate(tm);
1786 
1787         /* adjust values for return format */
1788         tm.tm_wday++;
1789 
1790 		// Get number of days since start of year
1791 		tm_yday = tm.tm_mday;
1792 		for (int monthNum = 1; monthNum < tm.tm_mon; ++monthNum) {
1793 			int daysInMonth = MONTH_DAYS[monthNum - 1];
1794 			if (monthNum == 2)
1795 				daysInMonth = (tm.tm_year % 4) == 0 && (((tm.tm_year % 100) != 0) || ((tm.tm_year % 400) == 0)) ? 29 : 28;
1796 
1797 			tm_yday += daysInMonth;
1798 		}
1799 
1800 		// TODO: Convert dae/tme to Unix style local time
1801 		timer = 0;
1802 
1803         /* build return list value */
1804         oswp2(ret, 47);
1805         p = ret + 2;
1806         p = bifputnum(p, tm.tm_year);
1807         p = bifputnum(p, tm.tm_mon);
1808         p = bifputnum(p, tm.tm_mday);
1809         p = bifputnum(p, tm.tm_wday);
1810         p = bifputnum(p, tm_yday);
1811         p = bifputnum(p, tm.tm_hour);
1812         p = bifputnum(p, tm.tm_min);
1813         p = bifputnum(p, tm.tm_sec);
1814         *p++ = DAT_NUMBER;
1815         oswp4s(p, (long)timer);
1816 
1817         val.runstyp = DAT_LIST;
1818         val.runsv.runsvstr = ret;
1819         runpush(ctx->bifcxrun, DAT_LIST, &val);
1820         break;
1821 
1822     case 2:
1823         /*
1824          *   High-precision system timer value - returns the system time
1825          *   in milliseconds, relative to an arbitrary zero point
1826          */
1827         runpnum(ctx->bifcxrun, os_get_sys_clock_ms());
1828         break;
1829 
1830     default:
1831         /* other types are invalid */
1832         runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "gettime");
1833         break;
1834     }
1835 }
1836 
1837 /* getfuse */
bifgfu(bifcxdef * ctx,int argc)1838 void bifgfu(bifcxdef *ctx, int argc)
1839 {
1840     vocddef  *daem;
1841     objnum    func;
1842     runsdef   val;
1843     runcxdef *rcx = ctx->bifcxrun;
1844     int       slots;
1845     prpnum    prop;
1846     voccxdef *vcx = ctx->bifcxrun->runcxvoc;
1847 
1848     bifcntargs(ctx, 2, argc);
1849 
1850     if (runtostyp(rcx) == DAT_FNADDR)
1851     {
1852         /* check on a setfuse()-style fuse: get fnaddr, parm */
1853         func = runpopfn(rcx);
1854         runpop(rcx, &val);
1855 
1856         for (slots = vcx->voccxfuc, daem = vcx->voccxfus ;
1857              slots ; ++daem, --slots)
1858         {
1859             if (daem->vocdfn == func
1860                 && daem->vocdarg.runstyp == val.runstyp
1861                 && !memcmp(&val.runsv, &daem->vocdarg.runsv,
1862                            (size_t)datsiz(val.runstyp, &val.runsv)))
1863                 goto ret_num;
1864         }
1865     }
1866     else
1867     {
1868         /* check on a notify()-style fuse: get object, &message */
1869         func = runpopobj(rcx);
1870         prop = runpopprp(rcx);
1871 
1872         for (slots = vcx->voccxalc, daem = vcx->voccxalm ;
1873              slots ; ++daem, --slots)
1874         {
1875             if (daem->vocdfn == func && daem->vocdprp == prop)
1876                 goto ret_num;
1877         }
1878     }
1879 
1880     /* didn't find anything - return nil */
1881     runpush(rcx, DAT_NIL, &val);
1882     return;
1883 
1884 ret_num:
1885     /* return current daem->vocdtim */
1886     runpnum(rcx, (long)daem->vocdtim);
1887     return;
1888 }
1889 
1890 /* runfuses */
bifruf(bifcxdef * ctx,int argc)1891 void bifruf(bifcxdef *ctx, int argc)
1892 {
1893     int     ret;
1894     runsdef val;
1895 
1896     bifcntargs(ctx, 0, argc);
1897     ret = exefuse(ctx->bifcxrun->runcxvoc, TRUE);
1898     runpush(ctx->bifcxrun, runclog(ret), &val);
1899 }
1900 
1901 /* rundaemons */
bifrud(bifcxdef * ctx,int argc)1902 void bifrud(bifcxdef *ctx, int argc)
1903 {
1904     bifcntargs(ctx, 0, argc);
1905     exedaem(ctx->bifcxrun->runcxvoc);
1906 }
1907 
1908 /* intersect */
bifsct(bifcxdef * bifctx,int argc)1909 void bifsct(bifcxdef *bifctx, int argc)
1910 {
1911     runcxdef *ctx = bifctx->bifcxrun;
1912     uchar    *l1;
1913     uchar    *l2;
1914     uchar    *l3;
1915     uint      siz1;
1916     uint      siz2;
1917     uint      siz3;
1918     uchar    *p;
1919     uint      l;
1920     uint      dsz1;
1921     uint      dsz2;
1922     runsdef   val;
1923     runsdef   stk1, stk2;
1924 
1925     bifcntargs(bifctx, 2, argc);
1926     l1 = runpoplst(ctx);
1927     siz1 = osrp2(l1);
1928     l2 = runpoplst(ctx);
1929     siz2 = osrp2(l2);
1930 
1931     /* make sure the first list is smaller - if not, switch them */
1932     if (siz1 > siz2)
1933         l3 = l1, l1 = l2, l2 = l3, siz3 = siz1, siz1 = siz2, siz2 = siz3;
1934 
1935     /* size of result is at most size of smaller list (which is now siz1) */
1936     stk1.runstyp = stk2.runstyp = DAT_LIST;
1937     stk1.runsv.runsvstr = l1;
1938     stk2.runsv.runsvstr = l2;
1939     runhres2(ctx, siz1, 2, &stk1, &stk2);
1940     l1 = stk1.runsv.runsvstr;
1941     l2 = stk2.runsv.runsvstr;
1942     l3 = ctx->runcxhp + 2;
1943 
1944     /* go through list1, and copy each element that is found in list2 */
1945     for (l1 += 2, l2 += 2, siz1 -= 2, siz2 -= 2 ; siz1 ; lstadv(&l1, &siz1))
1946     {
1947         dsz1 = datsiz(*l1, l1 + 1) + 1;
1948         for (l = siz2, p = l2 ; l ; lstadv(&p, &l))
1949         {
1950             dsz2 = datsiz(*p, p + 1) + 1;
1951 #ifndef AMIGA
1952             if (dsz1 == dsz2 && !memcmp(l1, p, (size_t)dsz1))
1953 #else /* AMIGA */
1954             if (!memcmp(l1, p, (size_t)dsz1) && (dsz1 == dsz2) )
1955 #endif /* AMIGA */
1956             {
1957                 memcpy(l3, p, (size_t)dsz1);
1958                 l3 += dsz1;
1959                 break;
1960             }
1961         }
1962     }
1963 
1964     /* set up return value, take it out of the heap, and push value */
1965     val.runsv.runsvstr = ctx->runcxhp;
1966     val.runstyp = DAT_LIST;
1967     oswp2(ctx->runcxhp, (uint)(l3 - ctx->runcxhp));
1968     ctx->runcxhp = l3;
1969     runrepush(ctx, &val);
1970 }
1971 
1972 /*
1973  *   Portable keystroke mappings.  We map the extended key codes to these
1974  *   strings, so that the TADS code can access arrow keys and the like.
1975  */
1976 static const char *ext_key_names[] =
1977 {
1978     "[up]",                                                   /* CMD_UP - 1 */
1979     "[down]",                                               /* CMD_DOWN - 2 */
1980     "[right]",                                             /* CMD_RIGHT - 3 */
1981     "[left]",                                               /* CMD_LEFT - 4 */
1982     "[end]",                                                 /* CMD_END - 5 */
1983     "[home]",                                               /* CMD_HOME - 6 */
1984     "[del-eol]",                                            /* CMD_DEOL - 7 */
1985     "[del-line]",                                           /* CMD_KILL - 8 */
1986     "[del]",                                                 /* CMD_DEL - 9 */
1987     "[scroll]",                                             /* CMD_SCR - 10 */
1988     "[page up]",                                           /* CMD_PGUP - 11 */
1989     "[page down]",                                         /* CMD_PGDN - 12 */
1990     "[top]",                                                /* CMD_TOP - 13 */
1991     "[bottom]",                                             /* CMD_BOT - 14 */
1992     "[f1]",                                                  /* CMD_F1 - 15 */
1993     "[f2]",                                                  /* CMD_F2 - 16 */
1994     "[f3]",                                                  /* CMD_F3 - 17 */
1995     "[f4]",                                                  /* CMD_F4 - 18 */
1996     "[f5]",                                                  /* CMD_F5 - 19 */
1997     "[f6]",                                                  /* CMD_F6 - 20 */
1998     "[f7]",                                                  /* CMD_F7 - 21 */
1999     "[f8]",                                                  /* CMD_F8 - 22 */
2000     "[f9]",                                                  /* CMD_F9 - 23 */
2001     "[f10]",                                                /* CMD_F10 - 24 */
2002     "[?]",                                  /* invalid key - CMD_CHOME - 25 */
2003     "[tab]",                                                /* CMD_TAB - 26 */
2004     "[?]",                                   /* invalid key - shift-F2 - 27 */
2005     "[?]",                                      /* not used (obsolete) - 28 */
2006     "[word-left]",                                    /* CMD_WORD_LEFT - 29 */
2007     "[word-right]",                                  /* CMD_WORD_RIGHT - 30 */
2008     "[del-word]",                                      /* CMD_WORDKILL - 31 */
2009     "[eof]",                                                /* CMD_EOF - 32 */
2010     "[break]"                                             /* CMD_BREAK - 33 */
2011 };
2012 
2013 /*
2014  *   Get the name of a keystroke.  Pass in the one or two characters
2015  *   returned by os_getc(), and we'll fill in the buffer with the
2016  *   inputkey() name of the keystroke.  Returns true if the key was valid,
2017  *   false if not.  'c' is the first character returned by os_getc() for
2018  *   the keystroke; if 'c' is zero, then 'extc' is the character returned
2019  *   by the second call to os_getc() to get the CMD_xxx code for the
2020  *   keystroke.
2021  *
2022  *   The name buffer should be 20 characters long - this will ensure that
2023  *   any name will fit.
2024  *
2025  *   For ordinary, printable characters, we'll simply return the
2026  *   character; the letter 'a', for example, is returned as the string "a".
2027  *
2028  *   For extended keys, we'll look up the CMD_xxx code and return the name
2029  *   of the command, enclosed in square brackets; see the ext_key_names
2030  *   table for the mappings.  The left-arrow cursor key, for example,
2031  *   returns "[left]".
2032  *
2033  *   For control characters, we'll generate a name like "[ctrl-a]", except
2034  *   for the following characters:
2035  *
2036  *.  ascii 10 returns "\n"
2037  *.  ascii 13 returns "\n"
2038  *.  ascii 9 returns "\t"
2039  *.  ascii 8 returns "[bksp]"
2040  */
get_ext_key_name(char * namebuf,int c,int extc)2041 static int get_ext_key_name(char *namebuf, int c, int extc)
2042 {
2043     /* if it's a control character, translate it */
2044     if (c >= 1 && c <= 27)
2045     {
2046         switch(c)
2047         {
2048         case 10:
2049         case 13:
2050             /* return '\n' for LF and CR characters */
2051             strcpy(namebuf, "\\n");
2052             return TRUE;
2053 
2054         case 9:
2055             /* return '\t' for TAB characters */
2056             strcpy(namebuf, "\\t");
2057             return TRUE;
2058 
2059         case 8:
2060             /* return '[bksp]' for backspace characters */
2061             strcpy(namebuf, "[bksp]");
2062             return TRUE;
2063 
2064         case 27:
2065             /* return '[esc]' for the escape key */
2066             strcpy(namebuf, "[esc]");
2067             return TRUE;
2068 
2069         default:
2070             /* return '[ctrl-X]' for other control characters */
2071             strcpy(namebuf, "[ctrl-X]");
2072             namebuf[6] = (char)(c + 'a' - 1);
2073             return TRUE;
2074         }
2075     }
2076 
2077     /* if it's any other non-extended key, return it as-is */
2078     if (c != 0)
2079     {
2080         namebuf[0] = c;
2081         namebuf[1] = '\0';
2082         return TRUE;
2083     }
2084 
2085     /* if it's in the key name array, use the array entry */
2086     if (extc >= 1
2087         && extc <= (int)(sizeof(ext_key_names)/sizeof(ext_key_names[0])))
2088     {
2089         /* use the array name */
2090         strcpy(namebuf, ext_key_names[extc - 1]);
2091         return TRUE;
2092     }
2093 
2094     /* if it's in the ALT key range, generate an ALT key name */
2095     if (extc >= CMD_ALT && extc <= CMD_ALT + 25)
2096     {
2097         /* generate an ALT key name */
2098         strcpy(namebuf, "[alt-X]");
2099         namebuf[5] = (char)(extc - CMD_ALT + 'a');
2100         return TRUE;
2101     }
2102 
2103     /* it's not a valid key - use '[?]' as the name */
2104     strcpy(namebuf, "[?]");
2105     return FALSE;
2106 }
2107 
2108 
2109 /* inputkey */
bifink(bifcxdef * ctx,int argc)2110 void bifink(bifcxdef *ctx, int argc)
2111 {
2112     int    c;
2113     int    extc;
2114     char   str[20];
2115     size_t len;
2116 
2117     bifcntargs(ctx, 0, argc);
2118     tioflushn(ctx->bifcxtio, 0);
2119 
2120     /* get a key */
2121     c = os_getc_raw();
2122 
2123     /* if it's extended, get the second part of the extended sequence */
2124     extc = (c == 0 ? os_getc_raw() : 0);
2125 
2126     /* map the extended key name */
2127     get_ext_key_name(str, c, extc);
2128 
2129     /* get the length of the name */
2130     len = strlen(str);
2131 
2132     /* reset the [more] counter */
2133     outreset();
2134 
2135     /* return the string, translating escapes */
2136     runpstr(ctx->bifcxrun, str, len, 0);
2137 }
2138 
2139 /* get direct/indirect object word list */
bifwrd(bifcxdef * ctx,int argc)2140 void bifwrd(bifcxdef *ctx, int argc)
2141 {
2142     int       ob;
2143     vocoldef *v;
2144     uchar     buf[128];
2145     uchar *dst;
2146     const uchar *src;
2147     uint      len;
2148     runsdef   val;
2149 
2150     bifcntargs(ctx, 1, argc);
2151 
2152     /* figure out what word list to get */
2153     ob = runpopnum(ctx->bifcxrun);
2154     switch(ob)
2155     {
2156     case 1:
2157         v = ctx->bifcxrun->runcxvoc->voccxdobj;
2158         break;
2159 
2160     case 2:
2161         v = ctx->bifcxrun->runcxvoc->voccxiobj;
2162         break;
2163 
2164     default:
2165         runpnil(ctx->bifcxrun);
2166         return;
2167     }
2168 
2169     /* now build a list of strings from the words, if there are any */
2170     if (v != 0 && voclistlen(v) != 0 && v->vocolfst != 0 && v->vocollst != 0)
2171     {
2172         for (dst = buf + 2, src = (const uchar *)v->vocolfst ;
2173              src <= (const uchar *)v->vocollst ; src += len + 1)
2174         {
2175             *dst++ = DAT_SSTRING;
2176             len = strlen((const char *)src);
2177             oswp2(dst, len + 2);
2178             strcpy((char *)dst + 2, (const char *)src);
2179             dst += len + 2;
2180         }
2181     }
2182     else
2183         dst = buf + 2;
2184 
2185     /* finish setting up the list length and return it */
2186     len = dst - buf;
2187     oswp2(buf, len);
2188     val.runsv.runsvstr = buf;
2189     val.runstyp = DAT_LIST;
2190     runpush(ctx->bifcxrun, DAT_LIST, &val);
2191 }
2192 
2193 /* add a vocabulary word to an object */
bifadw(bifcxdef * ctx,int argc)2194 void bifadw(bifcxdef *ctx, int argc)
2195 {
2196     uchar    *wrd;
2197     objnum    objn;
2198     prpnum    prpn;
2199     vocidef  *voci;
2200     int       classflg;
2201     voccxdef *voc = ctx->bifcxrun->runcxvoc;
2202 
2203     bifcntargs(ctx, 3, argc);
2204 
2205     /* get the arguments */
2206     objn = runpopobj(ctx->bifcxrun);
2207     prpn = runpopprp(ctx->bifcxrun);
2208     wrd = runpopstr(ctx->bifcxrun);
2209 
2210     /* make sure the property is a valid part of speech property */
2211     if (!prpisvoc(prpn))
2212         runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "addword");
2213 
2214     /* get the vocidef for the object, and see if it's a class object */
2215     voci = vocinh(voc, objn);
2216 
2217     classflg = VOCFNEW;
2218     if (voci->vociflg & VOCIFCLASS) classflg |= VOCFCLASS;
2219 
2220     /* add the word */
2221     vocadd(voc, prpn, objn, classflg, (char *)wrd);
2222 
2223     /* generate undo for the operation */
2224     vocdusave_addwrd(voc, objn, prpn, classflg, (char *)wrd);
2225 }
2226 
2227 /* delete a vocabulary word from an object */
bifdlw(bifcxdef * ctx,int argc)2228 void bifdlw(bifcxdef *ctx, int argc)
2229 {
2230     uchar    *wrd;
2231     objnum    objn;
2232     prpnum    prpn;
2233     voccxdef *voc = ctx->bifcxrun->runcxvoc;
2234 
2235     bifcntargs(ctx, 3, argc);
2236 
2237     /* get the arguments */
2238     objn = runpopobj(ctx->bifcxrun);
2239     prpn = runpopprp(ctx->bifcxrun);
2240     wrd = runpopstr(ctx->bifcxrun);
2241 
2242     /* make sure the property is a valid part of speech property */
2243     if (!prpisvoc(prpn))
2244         runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "delword");
2245 
2246     /* delete the word */
2247     vocdel1(voc, objn, (char *)wrd, prpn, FALSE, FALSE, TRUE);
2248 }
2249 
2250 /* callback context for word list builder */
2251 struct bifgtw_cb_ctx
2252 {
2253     uchar *p;
2254     int    typ;
2255 };
2256 
2257 /* callback for word list builder */
bifgtw_cb(void * ctx0,vocdef * voc,vocwdef * vocw)2258 static void bifgtw_cb(void *ctx0, vocdef *voc, vocwdef *vocw)
2259 {
2260     struct bifgtw_cb_ctx *ctx = (struct bifgtw_cb_ctx *)ctx0;
2261 
2262     /* ignore deleted objects */
2263     if (vocw->vocwflg & VOCFDEL)
2264         return;
2265 
2266     /* ignore objects of the inappropriate type */
2267     if (vocw->vocwtyp != ctx->typ)
2268         return;
2269 
2270     /* the datatype is string */
2271     *ctx->p = DAT_SSTRING;
2272 
2273     /* copy the first word */
2274     memcpy(ctx->p + 3, voc->voctxt, (size_t)voc->voclen);
2275 
2276     /* if there are two words, add a space and the second word */
2277     if (voc->vocln2)
2278     {
2279         *(ctx->p + 3 + voc->voclen) = ' ';
2280         memcpy(ctx->p + 4 + voc->voclen, voc->voctxt + voc->voclen,
2281                (size_t)voc->vocln2);
2282         oswp2(ctx->p + 1, voc->voclen + voc->vocln2 + 3);
2283         ctx->p += voc->voclen + voc->vocln2 + 4;
2284     }
2285     else
2286     {
2287         oswp2(ctx->p + 1, voc->voclen+2);
2288         ctx->p += voc->voclen + 3;
2289     }
2290 }
2291 
2292 /* get the list of words for an object for a particular part of speech */
bifgtw(bifcxdef * ctx,int argc)2293 void bifgtw(bifcxdef *ctx, int argc)
2294 {
2295     objnum    objn;
2296     prpnum    prpn;
2297     voccxdef *voc = ctx->bifcxrun->runcxvoc;
2298     int       cnt;
2299     int       siz;
2300     runsdef   val;
2301     struct bifgtw_cb_ctx fnctx;
2302 
2303     bifcntargs(ctx, 2, argc);
2304 
2305     /* get the arguments */
2306     objn = runpopobj(ctx->bifcxrun);
2307     prpn = runpopprp(ctx->bifcxrun);
2308 
2309     /* make sure the property is a valid part of speech property */
2310     if (!prpisvoc(prpn))
2311         runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "delword");
2312 
2313     /* get the size of the list we'll need to build */
2314     voc_count(voc, objn, prpn, &cnt, &siz);
2315 
2316     /*
2317      *   calculate how much space it will take to make a list out of all
2318      *   these words: 2 bytes for the list length header; plus, for each
2319      *   entry, 1 byte for the type header, 2 bytes for the string size
2320      *   header, and possibly one extra byte for the two-word separator --
2321      *   a total of 4 bytes extra per word.
2322      */
2323     siz += 2 + 4*cnt;
2324 
2325     /* reserve the space */
2326     runhres(ctx->bifcxrun, siz, 0);
2327 
2328     /* set up our callback context, and build the list */
2329     fnctx.p = ctx->bifcxrun->runcxhp + 2;
2330     fnctx.typ = prpn;
2331     voc_iterate(voc, objn, bifgtw_cb, &fnctx);
2332 
2333     /* set up the return value */
2334     val.runstyp = DAT_LIST;
2335     val.runsv.runsvstr = ctx->bifcxrun->runcxhp;
2336 
2337     /* write the list length, and advance past the space we used */
2338     oswp2(ctx->bifcxrun->runcxhp, fnctx.p - ctx->bifcxrun->runcxhp);
2339     ctx->bifcxrun->runcxhp = fnctx.p;
2340 
2341     /* return the list */
2342     runrepush(ctx->bifcxrun, &val);
2343 }
2344 
2345 /* verbinfo service routine - add an object to the output list */
bifvin_putprpn(uchar * p,prpnum prpn)2346 static uchar *bifvin_putprpn(uchar *p, prpnum prpn)
2347 {
2348     *p++ = DAT_PROPNUM;
2349     oswp2(p, prpn);
2350     return p + 2;
2351 }
2352 
2353 /* verbinfo */
bifvin(bifcxdef * ctx,int argc)2354 void bifvin(bifcxdef *ctx, int argc)
2355 {
2356     objnum  verb;
2357     objnum  prep;
2358     uchar   tplbuf[VOCTPL2SIZ];
2359     int     newstyle;
2360 
2361     /* get the verb */
2362     verb = runpopobj(ctx->bifcxrun);
2363 
2364     /* check for the presence of a preposition */
2365     if (argc == 1)
2366     {
2367         /* no preposition */
2368         prep = MCMONINV;
2369     }
2370     else
2371     {
2372         /* the second argument is the preposition */
2373         bifcntargs(ctx, 2, argc);
2374         prep = runpopobj(ctx->bifcxrun);
2375     }
2376 
2377     /* look up the template */
2378     if (voctplfnd(ctx->bifcxrun->runcxvoc, verb, prep, tplbuf, &newstyle))
2379     {
2380         prpnum   prp_do, prp_verdo, prp_io, prp_verio;
2381         int      flg_dis_do;
2382         ushort   siz;
2383         uchar   *p;
2384         runsdef  val;
2385 
2386         /* get the information from the template */
2387         prp_do     = voctpldo(tplbuf);
2388         prp_verdo  = voctplvd(tplbuf);
2389         prp_io     = voctplio(tplbuf);
2390         prp_verio  = voctplvi(tplbuf);
2391         flg_dis_do = (voctplflg(tplbuf) & VOCTPLFLG_DOBJ_FIRST) != 0;
2392 
2393         /*
2394          *   figure space for the return value: if there's a prep, three
2395          *   property pointers plus a boolean, otherwise just two property
2396          *   pointers
2397          */
2398         siz = 2 + 2*(2+1);
2399         if (prep != MCMONINV)
2400             siz += (2+1) + 1;
2401 
2402         /* reserve the space */
2403         runhres(ctx->bifcxrun, siz, 0);
2404 
2405         /* build the output list */
2406         p = ctx->bifcxrun->runcxhp;
2407         oswp2(p, siz);
2408         p += 2;
2409 
2410         p = bifvin_putprpn(p, prp_verdo);
2411         if (prep == MCMONINV)
2412         {
2413             p = bifvin_putprpn(p, prp_do);
2414         }
2415         else
2416         {
2417             p = bifvin_putprpn(p, prp_verio);
2418             p = bifvin_putprpn(p, prp_io);
2419             *p++ = runclog(flg_dis_do);
2420         }
2421 
2422         /* build the return value */
2423         val.runstyp = DAT_LIST;
2424         val.runsv.runsvstr = ctx->bifcxrun->runcxhp;
2425 
2426         /* consume the space */
2427         ctx->bifcxrun->runcxhp += siz;
2428 
2429         /* return the list */
2430         runrepush(ctx->bifcxrun, &val);
2431     }
2432     else
2433     {
2434         /* no template for this verb - return nil */
2435         runpnil(ctx->bifcxrun);
2436     }
2437 }
2438 
2439 
2440 /* clearscreen */
bifcls(bifcxdef * ctx,int argc)2441 void bifcls(bifcxdef *ctx, int argc)
2442 {
2443     /* this takes no arguments */
2444     bifcntargs(ctx, 0, argc);
2445 
2446     /* flush any pending output */
2447     tioflushn(ctx->bifcxtio, 0);
2448 
2449     /* clear the screen */
2450     oscls();
2451 }
2452 
2453 /*
2454  *   File operations
2455  */
2456 
2457 /*
2458  *   fopen(file, mode).
2459  *
2460  *   Operations are allowed only if they conform to the current I/O safety
2461  *   level.  The safety level can be set by the user on the command line
2462  *   when running the game, and some implementations may allow the setting
2463  *   to be saved as a preference.  The possible levels are:
2464  *
2465  *.  0 - minimum safety - read and write in any directory
2466  *.  1 - read in any directory, write in current directory
2467  *.  2 - read/write access in current directory only
2468  *.  3 - read-only access in current directory only
2469  *.  4 - maximum safety - no file I/O allowed
2470  *
2471  *   When operations are allowed only in the current directory, the
2472  *   operations will fail if the filename contains any sort of path
2473  *   specifier (for example, on Unix, any file that contains a '/' is
2474  *   considered to have a path specifier, and will always fail if
2475  *   operations are only allowed in the current directory).
2476  */
biffopen(bifcxdef * ctx,int argc)2477 void biffopen(bifcxdef *ctx, int argc)
2478 {
2479     char      fname[OSFNMAX];
2480     uchar    *p;
2481     uchar    *mode;
2482     int       modelen;
2483     int       fnum;
2484     osfildef *fp;
2485     int       bin_mode = TRUE;   /* flag: mode is binary (rather than text) */
2486     int       rw_mode = FALSE;     /* flag: both read and write are allowed */
2487     char      main_mode;                     /* 'r' for read, 'w' for write */
2488     int       in_same_dir;            /* flag: file is in current directory */
2489     appctxdef *appctx;
2490 
2491     bifcntargs(ctx, 2, argc);
2492 
2493     /* get the filename */
2494     p = runpopstr(ctx->bifcxrun);
2495     bifcstr(ctx, fname, (size_t)sizeof(fname), p);
2496 
2497     /*
2498      *   If it's a relative path, combine it with the game file path to form
2499      *   the absolute path.  This ensures that relative paths are always
2500      *   relative to the original working directory if the OS-level working
2501      *   directory has changed.
2502      */
2503     if (!os_is_file_absolute(fname))
2504     {
2505         /* combine the game file path with the relative filename */
2506         char newname[OSFNMAX];
2507         os_build_full_path(newname, sizeof(newname),
2508                            ctx->bifcxrun->runcxgamepath, fname);
2509 
2510         /* replace the original filename with the full path */
2511         strcpy(fname, newname);
2512     }
2513 
2514     /* get the mode string */
2515     mode = runpopstr(ctx->bifcxrun);
2516     modelen = osrp2(mode) - 2;
2517     mode += 2;
2518     if (modelen < 1)
2519         goto bad_mode;
2520 
2521     /* allocate a filenum for the file */
2522     for (fnum = 0 ; fnum < BIFFILMAX ; ++fnum)
2523     {
2524         if (ctx->bifcxfile[fnum].fp == 0)
2525             break;
2526     }
2527     if (fnum == BIFFILMAX)
2528     {
2529         /* return nil to indicate failure */
2530         runpnil(ctx->bifcxrun);
2531         return;
2532     }
2533 
2534     /* parse the main mode */
2535     switch(*mode)
2536     {
2537     case 'w':
2538     case 'W':
2539         main_mode = 'w';
2540         break;
2541 
2542     case 'r':
2543     case 'R':
2544         main_mode = 'r';
2545         break;
2546 
2547     default:
2548         goto bad_mode;
2549     }
2550 
2551     /* skip the main mode, and check for a '+' flag */
2552     ++mode;
2553     --modelen;
2554     if (modelen > 0 && *mode == '+')
2555     {
2556         /* note the read/write mode */
2557         rw_mode = TRUE;
2558 
2559         /* skip the speciifer */
2560         ++mode;
2561         --modelen;
2562     }
2563 
2564     /* check for a binary/text specifier */
2565     if (modelen > 0)
2566     {
2567         switch(*mode)
2568         {
2569         case 'b':
2570         case 'B':
2571             bin_mode = TRUE;
2572             break;
2573 
2574         case 't':
2575         case 'T':
2576             bin_mode = FALSE;
2577             break;
2578 
2579         default:
2580             goto bad_mode;
2581         }
2582 
2583         /* skip the binary/text specifier */
2584         ++mode;
2585         --modelen;
2586     }
2587 
2588     /* it's an error if there's anything left unparsed */
2589     if (modelen > 0)
2590         goto bad_mode;
2591 
2592     /*
2593      *   If we have a host application context, and it provides a file
2594      *   safety level callback function, ask the host system for its
2595      *   current file safety level, which overrides our current setting.
2596      */
2597     appctx = ctx->bifcxappctx;
2598     if (appctx != 0 && appctx->get_io_safety_level != 0)
2599     {
2600         /*
2601          *   ask the host system for the current level, and override any
2602          *   setting we previously had
2603          */
2604         (*appctx->get_io_safety_level)(
2605             appctx->io_safety_level_ctx,
2606             &ctx->bifcxsafetyr, &ctx->bifcxsafetyw);
2607     }
2608 
2609     /*
2610      *   Check to see if the file is in the current working directory - if
2611      *   not, we may have to disallow the operation based on safety level
2612      *   settings.
2613      */
2614     in_same_dir = os_is_file_in_dir(
2615         fname, ctx->bifcxrun->runcxgamepath, TRUE, FALSE);
2616 
2617     /* check file safety settings */
2618     switch(main_mode)
2619     {
2620     case 'w':
2621         /*
2622          *   writing - we must be at a safety level no higher than 2
2623          *   (read/write current directory) to write at all, and we must be
2624          *   level 0 to write a file that's not in the current directory
2625          */
2626         if (ctx->bifcxsafetyw > 2
2627             || (!in_same_dir && ctx->bifcxsafetyw > 0))
2628         {
2629             /* this operation is not allowed - return failure */
2630             runpnil(ctx->bifcxrun);
2631             return;
2632         }
2633         break;
2634 
2635     case 'r':
2636         /*
2637          *   reading - we must be at a safety level no higher than 3 (read
2638          *   current directory) to read at all, and we must be at safety
2639          *   level 1 (read any directory) or lower to read a file that's not
2640          *   in the current directory
2641          */
2642         if (ctx->bifcxsafetyr > 3
2643             || (!in_same_dir && ctx->bifcxsafetyr > 1))
2644         {
2645             /* this operation is not allowed - return failure */
2646             runpnil(ctx->bifcxrun);
2647             return;
2648         }
2649         break;
2650 
2651     default:
2652         /*
2653          *   fail the operation, as a code maintenance measure to make
2654          *   sure that we add appropriate cases to this switch (even if
2655          *   merely to allow the operation unconditionally) in the event
2656          *   that more modes are added in the future
2657          */
2658         goto bad_mode;
2659     }
2660 
2661     /* try opening the file */
2662     switch(main_mode)
2663     {
2664     case 'w':
2665         /* check for binary vs text mode */
2666         if (bin_mode)
2667         {
2668             /*
2669              *   binary mode -- allow read/write or just writing, but in
2670              *   either case truncate the file if it already exists, and
2671              *   create a new file if it doesn't exist
2672              */
2673             if (rw_mode)
2674                 fp = osfoprwtb(fname, OSFTDATA);
2675             else
2676                 fp = osfopwb(fname, OSFTDATA);
2677         }
2678         else
2679         {
2680             /* text mode - don't allow read/write on a text file */
2681             if (rw_mode)
2682                 goto bad_mode;
2683 
2684             /* open the file */
2685             fp = osfopwt(fname, OSFTTEXT);
2686         }
2687         break;
2688 
2689     case 'r':
2690         /* check for binary vs text mode */
2691         if (bin_mode)
2692         {
2693             /*
2694              *   Binary mode -- allow read/write or just reading; leave
2695              *   any existing file intact.
2696              */
2697             if (rw_mode)
2698             {
2699                 /* open for reading and writing, keeping existing data */
2700                 fp = osfoprwb(fname, OSFTDATA);
2701             }
2702             else
2703             {
2704                 /* open for read-only */
2705                 fp = osfoprb(fname, OSFTDATA);
2706             }
2707         }
2708         else
2709         {
2710             /* text mode -- only allow reading */
2711             if (rw_mode)
2712                 goto bad_mode;
2713 
2714             /* open the file */
2715             fp = osfoprt(fname, OSFTTEXT);
2716         }
2717         break;
2718 
2719     default:
2720         goto bad_mode;
2721     }
2722 
2723     /* if we couldn't open it, return nil */
2724     if (fp == 0)
2725     {
2726         runpnil(ctx->bifcxrun);
2727         return;
2728     }
2729 
2730     /* store the flags */
2731     ctx->bifcxfile[fnum].flags = 0;
2732     if (bin_mode)
2733         ctx->bifcxfile[fnum].flags |= BIFFIL_F_BINARY;
2734 
2735     /* remember the file handle */
2736     ctx->bifcxfile[fnum].fp = fp;
2737 
2738     /* return the file number (i.e., the slot number) */
2739     runpnum(ctx->bifcxrun, (long)fnum);
2740     return;
2741 
2742 
2743     /* come here on a mode error */
2744 bad_mode:
2745     runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "fopen");
2746 }
2747 
2748 /* service routine for file routines - get and validate a file number */
bif_get_file(bifcxdef * ctx,int * fnump,int * bin_modep)2749 static osfildef *bif_get_file(bifcxdef *ctx, int *fnump, int *bin_modep)
2750 {
2751     long fnum;
2752 
2753     /* get the file number and make sure it's valid */
2754     fnum = runpopnum(ctx->bifcxrun);
2755     if (fnum < 0 || fnum >= BIFFILMAX || ctx->bifcxfile[fnum].fp == 0)
2756         runsig(ctx->bifcxrun, ERR_BADFILE);
2757 
2758     /* put the validated file number, if the caller wants it */
2759     if (fnump != 0)
2760         *fnump = (int)fnum;
2761 
2762     /* set the binary-mode flag, if the caller wants it */
2763     if (bin_modep != 0)
2764         *bin_modep = ((ctx->bifcxfile[fnum].flags & BIFFIL_F_BINARY) != 0);
2765 
2766     /* return the file array pointer */
2767     return ctx->bifcxfile[fnum].fp;
2768 }
2769 
biffclose(bifcxdef * ctx,int argc)2770 void biffclose(bifcxdef *ctx, int argc)
2771 {
2772     int       fnum;
2773     osfildef *fp;
2774 
2775     /* get the file number */
2776     bifcntargs(ctx, 1, argc);
2777     fp = bif_get_file(ctx, &fnum, 0);
2778 
2779     /* close the file and release the slot */
2780     osfcls(fp);
2781     ctx->bifcxfile[fnum].fp = 0;
2782 }
2783 
bifftell(bifcxdef * ctx,int argc)2784 void bifftell(bifcxdef *ctx, int argc)
2785 {
2786     osfildef *fp;
2787 
2788     /* get the file number */
2789     bifcntargs(ctx, 1, argc);
2790     fp = bif_get_file(ctx, (int *)0, 0);
2791 
2792     /* return the seek position */
2793     runpnum(ctx->bifcxrun, osfpos(fp));
2794 }
2795 
biffseek(bifcxdef * ctx,int argc)2796 void biffseek(bifcxdef *ctx, int argc)
2797 {
2798     osfildef *fp;
2799     long      pos;
2800 
2801     /* get the file pointer */
2802     bifcntargs(ctx, 2, argc);
2803     fp = bif_get_file(ctx, (int *)0, 0);
2804 
2805     /* get the seek position, and seek there */
2806     pos = runpopnum(ctx->bifcxrun);
2807     osfseek(fp, pos, OSFSK_SET);
2808 }
2809 
biffseekeof(bifcxdef * ctx,int argc)2810 void biffseekeof(bifcxdef *ctx, int argc)
2811 {
2812     osfildef *fp;
2813 
2814     /* get the file pointer */
2815     bifcntargs(ctx, 1, argc);
2816     fp = bif_get_file(ctx, (int *)0, 0);
2817 
2818     /* seek to the end */
2819     osfseek(fp, 0L, OSFSK_END);
2820 }
2821 
biffwrite(bifcxdef * ctx,int argc)2822 void biffwrite(bifcxdef *ctx, int argc)
2823 {
2824     osfildef *fp;
2825     char      typ;
2826     char      buf[32];
2827     runsdef   val;
2828     int       bin_mode;
2829 
2830     /* get the file */
2831     bifcntargs(ctx, 2, argc);
2832     fp = bif_get_file(ctx, (int *)0, &bin_mode);
2833 
2834     /* get the value to write */
2835     runpop(ctx->bifcxrun, &val);
2836     typ = val.runstyp;
2837 
2838     if (bin_mode)
2839     {
2840         /* put a byte indicating the type */
2841         if (osfwb(fp, &typ, 1))
2842             goto ret_error;
2843 
2844         /* see what type of data we want to put */
2845         switch(typ)
2846         {
2847         case DAT_NUMBER:
2848             oswp4s(buf, val.runsv.runsvnum);
2849             if (osfwb(fp, buf, 4))
2850                 goto ret_error;
2851             break;
2852 
2853         case DAT_SSTRING:
2854             /* write the string, including the length prefix */
2855             if (osfwb(fp, val.runsv.runsvstr, osrp2(val.runsv.runsvstr)))
2856                 goto ret_error;
2857             break;
2858 
2859         case DAT_TRUE:
2860             /* all we need for this is the type prefix */
2861             break;
2862 
2863         default:
2864             /* other types are not acceptable */
2865             runsig1(ctx->bifcxrun, ERR_INVTBIF, ERRTSTR, "fwrite");
2866         }
2867     }
2868     else
2869     {
2870         uint rem;
2871         uchar *p;
2872 
2873         switch(typ)
2874         {
2875         case DAT_SSTRING:
2876             /*
2877              *   Copy and translate the string to our buffer, in pieces if
2878              *   the size of the string exceeds that of our buffer.  If we
2879              *   encounter any escape codes, translate them.
2880              */
2881             rem = osrp2(val.runsv.runsvstr) - 2;
2882             p = val.runsv.runsvstr + 2;
2883             while (rem > 0)
2884             {
2885                 uchar *dst;
2886                 uchar dbuf[256];
2887 
2888                 /* fill up the buffer */
2889                 for (dst = dbuf ;
2890                      rem != 0 && (size_t)(dst - dbuf) < sizeof(dbuf) - 1 ;
2891                      ++p, --rem)
2892                 {
2893                     /* if we have an escape character, translate it */
2894                     if (*p == '\\' && rem > 1)
2895                     {
2896                         /* skip the opening slash */
2897                         ++p;
2898                         --rem;
2899 
2900                         /* translate it */
2901                         switch(*p)
2902                         {
2903                         case 'n':
2904                             *dst++ = '\n';
2905                             break;
2906 
2907                         case 't':
2908                             *dst++ = '\t';
2909                             break;
2910 
2911                         default:
2912                             *dst++ = *p;
2913                             break;
2914                         }
2915                     }
2916                     else
2917                     {
2918                         /* copy this character directly */
2919                         *dst++ = *p;
2920                     }
2921                 }
2922 
2923                 /* null-terminate the buffer */
2924                 *dst = '\0';
2925 
2926                 /* write it out */
2927                 if (osfputs((char *)dbuf, fp) == EOF)
2928                     goto ret_error;
2929             }
2930 
2931             /* done */
2932             break;
2933 
2934         default:
2935             /* other types are not allowed */
2936             runsig1(ctx->bifcxrun, ERR_INVTBIF, ERRTSTR, "fwrite");
2937         }
2938     }
2939 
2940     /* success */
2941     runpnil(ctx->bifcxrun);
2942     return;
2943 
2944 ret_error:
2945     val.runstyp = DAT_TRUE;
2946     runpush(ctx->bifcxrun, DAT_TRUE, &val);
2947 }
2948 
biffread(bifcxdef * ctx,int argc)2949 void biffread(bifcxdef *ctx, int argc)
2950 {
2951     osfildef *fp;
2952     char      typ;
2953     char      buf[32];
2954     runsdef   val;
2955     ushort    len;
2956     int       bin_mode;
2957 
2958     /* get the file pointer */
2959     bifcntargs(ctx, 1, argc);
2960     fp = bif_get_file(ctx, (int *)0, &bin_mode);
2961 
2962     if (bin_mode)
2963     {
2964         /* binary file - read the type byte */
2965         if (osfrb(fp, &typ, 1))
2966             goto ret_error;
2967 
2968         /* read the data according to the type */
2969         switch(typ)
2970         {
2971         case DAT_NUMBER:
2972             if (osfrb(fp, buf, 4))
2973                 goto ret_error;
2974             runpnum(ctx->bifcxrun, osrp4s(buf));
2975             break;
2976 
2977         case DAT_SSTRING:
2978             /* get the size */
2979             if (osfrb(fp, buf, 2))
2980                 goto ret_error;
2981             len = osrp2(buf);
2982 
2983             /* reserve space */
2984             runhres(ctx->bifcxrun, len, 0);
2985 
2986             /* read the string into the reserved space */
2987             if (osfrb(fp, ctx->bifcxrun->runcxhp + 2, len - 2))
2988                 goto ret_error;
2989 
2990             /* set up the string */
2991             oswp2(ctx->bifcxrun->runcxhp, len);
2992             val.runstyp = DAT_SSTRING;
2993             val.runsv.runsvstr = ctx->bifcxrun->runcxhp;
2994 
2995             /* consume the space */
2996             ctx->bifcxrun->runcxhp += len;
2997 
2998             /* push the value */
2999             runrepush(ctx->bifcxrun, &val);
3000             break;
3001 
3002         case DAT_TRUE:
3003             val.runstyp = DAT_TRUE;
3004             runpush(ctx->bifcxrun, DAT_TRUE, &val);
3005             break;
3006 
3007         default:
3008             goto ret_error;
3009         }
3010     }
3011     else
3012     {
3013         uchar  dbuf[257];
3014         uchar *dst;
3015         uchar *src;
3016         uint   dlen;
3017         uint   res_total;
3018         int    found_nl;
3019 
3020         /*
3021          *   reserve some space in the heap - we'll initially reserve
3022          *   space for twice our buffer, in case every single character
3023          *   needs to be expanded into an escape sequence
3024          */
3025         res_total = sizeof(dbuf) * 2;
3026         runhres(ctx->bifcxrun, res_total, 0);
3027 
3028         /* set up our output value */
3029         val.runstyp = DAT_SSTRING;
3030         val.runsv.runsvstr = ctx->bifcxrun->runcxhp;
3031         dst = ctx->bifcxrun->runcxhp + 2;
3032 
3033         /* keep going until we find a newline or run out of data */
3034         for (found_nl = FALSE ; !found_nl ; )
3035         {
3036             /* text-mode - read the result into our buffer */
3037             if (!osfgets((char *)dbuf, sizeof(dbuf) - 1, fp))
3038             {
3039                 /*
3040                  *   if we found a newline, return what we have;
3041                  *   otherwise, return an error
3042                  */
3043                 if (found_nl)
3044                     break;
3045                 else
3046                     goto ret_error;
3047             }
3048 
3049             /*
3050              *   make sure it's null-terminated, in case the buffer was
3051              *   full
3052              */
3053             dbuf[256] = '\0';
3054 
3055             /* translate into the heap area we've reserved */
3056             for (src = dbuf ; *src != '\0' ; ++src, ++dst)
3057             {
3058                 /* determine if we need translations */
3059                 switch(*src)
3060                 {
3061                 case '\n':
3062                 case '\r':
3063                     /* translate to a newline sequence */
3064                     *dst++ = '\\';
3065                     *dst = 'n';
3066 
3067                     /* note that we've found our newline */
3068                     found_nl = TRUE;
3069                     break;
3070 
3071                 case '\t':
3072                     /* translate to a tab sequence */
3073                     *dst++ = '\\';
3074                     *dst = 't';
3075                     break;
3076 
3077                 case '\\':
3078                     /* expand to a double-backslash sequence */
3079                     *dst++ = '\\';
3080                     *dst = '\\';
3081                     break;
3082 
3083                 default:
3084                     /* leave other characters intact */
3085                     *dst = *src;
3086                     break;
3087                 }
3088             }
3089 
3090             /*
3091              *   If we didn't find the newline, we'll need more space.
3092              *   This is a bit tricky, because the space we've already set
3093              *   up may move if we compact the heap while asking for more
3094              *   space.  So, remember our current length, reserve another
3095              *   buffer-full of space, and set everything up at the new
3096              *   output location if necessary.
3097              */
3098             if (!found_nl)
3099             {
3100                 /* reserve another buffer-full (double for expansion) */
3101                 res_total += sizeof(dbuf) * 2;
3102 
3103                 /* note our current offset */
3104                 dlen = dst - val.runsv.runsvstr;
3105                 oswp2(val.runsv.runsvstr, dlen);
3106 
3107                 /* ask for the space */
3108                 runhres(ctx->bifcxrun, res_total, 0);
3109 
3110                 /*
3111                  *   Since we were at the top of the heap before, we
3112                  *   should still be at the top of the heap.  If not,
3113                  *   we'll have to copy from our old location to the new
3114                  *   top of the heap.
3115                  */
3116                 if (val.runsv.runsvstr != ctx->bifcxrun->runcxhp)
3117                 {
3118                     /* copy our existing text to our new location */
3119                     memmove(ctx->bifcxrun->runcxhp, val.runsv.runsvstr, dlen);
3120 
3121                     /* fix up our pointer */
3122                     val.runsv.runsvstr = ctx->bifcxrun->runcxhp;
3123                 }
3124 
3125                 /* re-establish our output pointer at our new location */
3126                 dst = val.runsv.runsvstr + dlen;
3127             }
3128         }
3129 
3130         /* finish setting up the string */
3131         dlen = dst - val.runsv.runsvstr;
3132         oswp2(val.runsv.runsvstr, dlen);
3133 
3134         /* consume the space */
3135         ctx->bifcxrun->runcxhp += dlen;
3136 
3137         /* push the value */
3138         runrepush(ctx->bifcxrun, &val);
3139     }
3140 
3141     /* success - we've already pushed the return value */
3142     return;
3143 
3144 ret_error:
3145     runpnil(ctx->bifcxrun);
3146 }
3147 
bifcapture(bifcxdef * ctx,int argc)3148 void bifcapture(bifcxdef *ctx, int argc)
3149 {
3150     mcmcxdef *mcx = ctx->bifcxrun->runcxmem;
3151     mcmon     obj;
3152     uint      siz;
3153     uint      ofs;
3154     uchar    *p;
3155 
3156     /* get the capture on/off flag */
3157     bifcntargs(ctx, 1, argc);
3158     switch(runtostyp(ctx->bifcxrun))
3159     {
3160     case DAT_TRUE:
3161         /* turn on capturing */
3162         tiocapture(ctx->bifcxtio, mcx, TRUE);
3163 
3164         /*
3165          *   The return value is a status code used to restore the
3166          *   original status on the bracketing call to turn off output.
3167          *   The only status necessary is the current output size.
3168          */
3169         siz = tiocapturesize(ctx->bifcxtio);
3170         runpnum(ctx->bifcxrun, (long)siz);
3171         break;
3172 
3173     case DAT_NUMBER:
3174         /* get the original offset */
3175         ofs = runpopnum(ctx->bifcxrun);
3176 
3177         /* get the capture object and size */
3178         obj = tiogetcapture(ctx->bifcxtio);
3179         siz = tiocapturesize(ctx->bifcxtio);
3180         if (obj == MCMONINV)
3181         {
3182             runpnil(ctx->bifcxrun);
3183             return;
3184         }
3185 
3186         /* turn off capturing and reset the buffer on the outermost call */
3187         if (ofs == 0)
3188         {
3189             tiocapture(ctx->bifcxtio, mcx, FALSE);
3190             tioclrcapture(ctx->bifcxtio);
3191         }
3192 
3193         /* lock the object */
3194         p = mcmlck(mcx, obj);
3195 
3196         /* include only the part that happened after the matching call */
3197         p += ofs;
3198         siz = (ofs > siz) ? 0 : siz - ofs;
3199 
3200         ERRBEGIN(ctx->bifcxerr)
3201 
3202         /* push the string onto the stack */
3203         runpstr(ctx->bifcxrun, (char *)p, siz, 0);
3204 
3205         ERRCLEAN(ctx->bifcxerr)
3206             /* done with the object - unlock it */
3207             mcmunlck(mcx, obj);
3208         ERRENDCLN(ctx->bifcxerr)
3209 
3210         /* done with the object - unlock it */
3211         mcmunlck(mcx, obj);
3212         break;
3213 
3214     default:
3215         runsig1(ctx->bifcxrun, ERR_INVTBIF, ERRTSTR, "outcapture");
3216     }
3217 }
3218 
3219 /*
3220  *   systemInfo
3221  */
bifsysinfo(bifcxdef * ctx,int argc)3222 void bifsysinfo(bifcxdef *ctx, int argc)
3223 {
3224     runsdef val;
3225     int id;
3226     long result;
3227 
3228     /* see what we have */
3229     switch(id = (int)runpopnum(ctx->bifcxrun))
3230     {
3231     case SYSINFO_SYSINFO:
3232         /* systemInfo call is supported in this version - return true */
3233         bifcntargs(ctx, 1, argc);
3234         val.runstyp = DAT_TRUE;
3235         runpush(ctx->bifcxrun, DAT_TRUE, &val);
3236         break;
3237 
3238     case SYSINFO_VERSION:
3239         /* get the run-time version string */
3240         bifcntargs(ctx, 1, argc);
3241         runpushcstr(ctx->bifcxrun, TADS_RUNTIME_VERSION,
3242                     strlen(TADS_RUNTIME_VERSION), 0);
3243         break;
3244 
3245     case SYSINFO_OS_NAME:
3246         /* get the operating system name */
3247         bifcntargs(ctx, 1, argc);
3248         runpushcstr(ctx->bifcxrun, OS_SYSTEM_NAME, strlen(OS_SYSTEM_NAME), 0);
3249         break;
3250 
3251     case SYSINFO_HTML:
3252     case SYSINFO_JPEG:
3253     case SYSINFO_PNG:
3254     case SYSINFO_WAV:
3255     case SYSINFO_MIDI:
3256     case SYSINFO_WAV_MIDI_OVL:
3257     case SYSINFO_WAV_OVL:
3258     case SYSINFO_PREF_IMAGES:
3259     case SYSINFO_PREF_SOUNDS:
3260     case SYSINFO_PREF_MUSIC:
3261     case SYSINFO_PREF_LINKS:
3262     case SYSINFO_MPEG:
3263     case SYSINFO_MPEG1:
3264     case SYSINFO_MPEG2:
3265     case SYSINFO_MPEG3:
3266     case SYSINFO_LINKS_HTTP:
3267     case SYSINFO_LINKS_FTP:
3268     case SYSINFO_LINKS_NEWS:
3269     case SYSINFO_LINKS_MAILTO:
3270     case SYSINFO_LINKS_TELNET:
3271     case SYSINFO_PNG_TRANS:
3272     case SYSINFO_PNG_ALPHA:
3273     case SYSINFO_OGG:
3274     case SYSINFO_MNG:
3275     case SYSINFO_MNG_TRANS:
3276     case SYSINFO_MNG_ALPHA:
3277     case SYSINFO_TEXT_HILITE:
3278     case SYSINFO_INTERP_CLASS:
3279         /*
3280          *   these information types are all handled by the OS layer, and
3281          *   take no additional arguments
3282          */
3283         bifcntargs(ctx, 1, argc);
3284         if (os_get_sysinfo(id, 0, &result))
3285         {
3286             /* we got a valid result - return it */
3287             runpnum(ctx->bifcxrun, result);
3288         }
3289         else
3290         {
3291             /* the code was unknown - return nil */
3292             runpnil(ctx->bifcxrun);
3293         }
3294         break;
3295 
3296     case SYSINFO_HTML_MODE:
3297         /* ask the output formatter for its current HTML setting */
3298         bifcntargs(ctx, 1, argc);
3299         val.runstyp = runclog(tio_is_html_mode());
3300         runpush(ctx->bifcxrun, val.runstyp, &val);
3301         break;
3302 
3303     case SYSINFO_TEXT_COLORS:
3304         /*
3305          *   Text colors are only supported in full HTML interpreters.  If
3306          *   this is an HTML interpreter, ask the underlying OS layer about
3307          *   color support; otherwise, colors are not available, since we
3308          *   don't handle colors in our text-only HTML subset.
3309          *
3310          *   Colors are NOT supported in the HTML mini-parser in text-only
3311          *   interpreters in TADS 2.  So, even if we're running in HTML
3312          *   mode, if this is a text-only interpreter, we can't display text
3313          *   colors.
3314          */
3315         bifcntargs(ctx, 1, argc);
3316         if (os_get_sysinfo(SYSINFO_HTML, 0, &result) && result != 0)
3317         {
3318             /*
3319              *   we're in HTML mode, so ask the underlying HTML OS
3320              *   implementation for its level of text color support
3321              */
3322             if (os_get_sysinfo(id, 0, &result))
3323             {
3324                 /* push the OS-level result */
3325                 runpnum(ctx->bifcxrun, result);
3326             }
3327             else
3328             {
3329                 /* the OS code doesn't recognize it; assume no support */
3330                 runpnum(ctx->bifcxrun, SYSINFO_TXC_NONE);
3331             }
3332         }
3333         else
3334         {
3335             /* we're a text-only interpreter - no color support */
3336             runpnum(ctx->bifcxrun, SYSINFO_TXC_NONE);
3337         }
3338         break;
3339 
3340     case SYSINFO_BANNERS:
3341         /* TADS 2 does not offer banner support */
3342         bifcntargs(ctx, 1, argc);
3343         runpnum(ctx->bifcxrun, 0);
3344         break;
3345 
3346     default:
3347         /*
3348          *   Other codes fail harmlessly with a nil return value.  Pop all
3349          *   remaining arguments and return nil.
3350          */
3351         for ( ; argc > 1 ; --argc)
3352             rundisc(ctx->bifcxrun);
3353         runpnil(ctx->bifcxrun);
3354         break;
3355     }
3356 }
3357 
3358 /*
3359  *   morePrompt - display the more prompt and wait for the user to respond
3360  */
bifmore(bifcxdef * ctx,int argc)3361 void bifmore(bifcxdef *ctx, int argc)
3362 {
3363     /* this function takes no arguments */
3364     bifcntargs(ctx, 0, argc);
3365 
3366     /* display the MORE prompt */
3367     tioflushn(ctx->bifcxtio, 1);
3368     out_more_prompt();
3369 }
3370 
3371 /*
3372  *   parserSetMe
3373  */
bifsetme(bifcxdef * ctx,int argc)3374 void bifsetme(bifcxdef *ctx, int argc)
3375 {
3376     objnum new_me;
3377 
3378     /* this function takes one argument */
3379     bifcntargs(ctx, 1, argc);
3380 
3381     /* get the new "Me" object */
3382     new_me = runpopobj(ctx->bifcxrun);
3383 
3384     /* "Me" cannot be nil */
3385     if (new_me == MCMONINV)
3386         runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "parserSetMe");
3387 
3388     /* set the current "Me" object in the parser */
3389     voc_set_me(ctx->bifcxrun->runcxvoc, new_me);
3390 }
3391 
3392 /*
3393  *   parserGetMe
3394  */
bifgetme(bifcxdef * ctx,int argc)3395 void bifgetme(bifcxdef *ctx, int argc)
3396 {
3397     /* this function takes no arguments */
3398     bifcntargs(ctx, 0, argc);
3399 
3400     /* return the current Me object */
3401     runpobj(ctx->bifcxrun, ctx->bifcxrun->runcxvoc->voccxme);
3402 }
3403 
3404 /*
3405  *   reSearch
3406  */
bifresearch(bifcxdef * ctx,int argc)3407 void bifresearch(bifcxdef *ctx, int argc)
3408 {
3409     uchar  *patstr;
3410     size_t  patlen;
3411     uchar  *searchstr;
3412     size_t  searchlen;
3413     int     result_len;
3414     int     match_ofs;
3415 
3416     /* this function takes two parameters: pattern, string */
3417     bifcntargs(ctx, 2, argc);
3418 
3419     /* get the pattern string */
3420     patstr = runpopstr(ctx->bifcxrun);
3421     patlen = osrp2(patstr) - 2;
3422     patstr += 2;
3423 
3424     /* get the search string */
3425     searchstr = runpopstr(ctx->bifcxrun);
3426     searchlen = osrp2(searchstr) - 2;
3427     searchstr += 2;
3428 
3429     /* search for the pattern in the string */
3430     match_ofs = re_compile_and_search(&ctx->bifcxregex,
3431                                       (char *)patstr, patlen,
3432                                       (char *)searchstr, searchlen,
3433                                       &result_len);
3434 
3435     /*
3436      *   if we didn't match, return nil; otherwise, return a list with the
3437      *   match offset and length
3438      */
3439     if (match_ofs < 0)
3440     {
3441         /* no match - return nil */
3442         runpnil(ctx->bifcxrun);
3443     }
3444     else
3445     {
3446         ushort listsiz;
3447         runsdef val;
3448         uchar *p;
3449 
3450         /*
3451          *   build a list consisting of two numbers and a string: two
3452          *   bytes for the list header, then two elements at (one byte for
3453          *   the datatype header, four bytes for the number), then the
3454          *   string element with (one byte for the datatype, two bytes for
3455          *   the string length prefix, and the bytes of the string)
3456          */
3457         listsiz = 2 + (1+4)*2 + (1 + 2 + (ushort)(result_len));
3458 
3459         /* allocate the space */
3460         runhres(ctx->bifcxrun, listsiz, 0);
3461 
3462         /* set up the list stack item */
3463         val.runstyp = DAT_LIST;
3464         p = val.runsv.runsvstr = ctx->bifcxrun->runcxhp;
3465 
3466         /* set the list's length */
3467         oswp2(p, listsiz);
3468         p += 2;
3469 
3470         /*
3471          *   Add the offset element.  For consistency with TADS
3472          *   conventions, use 1 as the offset of the first character in
3473          *   the string - this makes it easy to use the offset value with
3474          *   substr().
3475          */
3476         *p++ = DAT_NUMBER;
3477         oswp4s(p, match_ofs + 1);
3478         p += 4;
3479 
3480         /* add the length element */
3481         *p++ = DAT_NUMBER;
3482         oswp4s(p, result_len);
3483         p += 4;
3484 
3485         /* add the result string */
3486         *p++ = DAT_SSTRING;
3487         oswp2(p, result_len + 2);
3488         p += 2;
3489         memcpy(p, ctx->bifcxregex.strbuf + match_ofs, result_len);
3490 
3491         /* reserve the space in the heap */
3492         ctx->bifcxrun->runcxhp += listsiz;
3493 
3494         /* return the list */
3495         runrepush(ctx->bifcxrun, &val);
3496     }
3497 }
3498 
3499 /* reGetGroup */
bifregroup(bifcxdef * ctx,int argc)3500 void bifregroup(bifcxdef *ctx, int argc)
3501 {
3502     int grp;
3503     size_t len;
3504     re_group_register *reg;
3505     ushort hplen;
3506     runsdef val;
3507     uchar *p;
3508     long numval;
3509 
3510     /* this function takes one parameter: the group number to retrieve */
3511     bifcntargs(ctx, 1, argc);
3512 
3513     /* get the group number */
3514     grp = (int)runpopnum(ctx->bifcxrun);
3515 
3516     /* make sure it's within range */
3517     if (grp < 1 || grp > RE_GROUP_REG_CNT)
3518         runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "reGetGroup");
3519 
3520     /* adjust from a 1-bias to an array index */
3521     --grp;
3522 
3523     /* if the group was never set, return nil */
3524     if (grp >= ctx->bifcxregex.cur_group)
3525     {
3526         runpnil(ctx->bifcxrun);
3527         return;
3528     }
3529 
3530     /* get the register */
3531     reg = &ctx->bifcxregex.regs[grp];
3532 
3533     /* if the group wasn't set, return nil */
3534     if (reg->start_ofs == 0 || reg->end_ofs == 0)
3535     {
3536         runpnil(ctx->bifcxrun);
3537         return;
3538     }
3539 
3540     /* calculate the length of the string in this group */
3541     len = reg->end_ofs - reg->start_ofs;
3542 
3543     /*
3544      *   reserve the necessary heap space: two bytes for the list length
3545      *   prefix, two number elements (one byte each for the type, four
3546      *   bytes each for the value), and the string element (one byte for
3547      *   the type, two bytes for the length prefix, plus the string
3548      *   itself).
3549      */
3550     hplen = (ushort)(2 + 2*(1+4) + (1 + 2 + len));
3551     runhres(ctx->bifcxrun, hplen, 0);
3552 
3553     /* set up the stack value */
3554     val.runstyp = DAT_LIST;
3555     p = val.runsv.runsvstr = ctx->bifcxrun->runcxhp;
3556 
3557     /* put in the list length prefix */
3558     oswp2(p, hplen);
3559     p += 2;
3560 
3561     /* add the starting character position of the group - adjust to 1-bias */
3562     *p++ = DAT_NUMBER;
3563     numval = (long)(reg->start_ofs - ctx->bifcxregex.strbuf) + 1;
3564     oswp4s(p, numval);
3565     p += 4;
3566 
3567     /* add the length of the group */
3568     *p++ = DAT_NUMBER;
3569     numval = (long)(reg->end_ofs - reg->start_ofs);
3570     oswp4s(p, numval);
3571     p += 4;
3572 
3573     /* set up the string */
3574     *p++ = DAT_SSTRING;
3575     oswp2(p, len+2);
3576     p += 2;
3577     memcpy(p, reg->start_ofs, len);
3578 
3579     /* consume the heap space */
3580     ctx->bifcxrun->runcxhp += hplen;
3581 
3582     /* push the result */
3583     runrepush(ctx->bifcxrun, &val);
3584 }
3585 
3586 
3587 /*
3588  *   inputevent
3589  */
bifinpevt(bifcxdef * ctx,int argc)3590 void bifinpevt(bifcxdef *ctx, int argc)
3591 {
3592 	unsigned long timeout = 0;
3593     int use_timeout = FALSE;
3594     os_event_info_t info;
3595     int evt;
3596     uchar *p;
3597     ushort lstsiz;
3598     runsdef val;
3599     size_t paramlen = 0;
3600     char keyname[20];
3601 
3602     /* check for a timeout value */
3603     if (argc == 0)
3604     {
3605         /* there's no timeout */
3606         use_timeout = FALSE;
3607         timeout = 0;
3608     }
3609     else if (argc >= 1)
3610     {
3611         /* get the timeout value */
3612         use_timeout = TRUE;
3613         timeout = (unsigned long)runpopnum(ctx->bifcxrun);
3614     }
3615 
3616     /* ensure we don't have too many arguments */
3617     if (argc > 1)
3618         runsig(ctx->bifcxrun, ERR_BIFARGC);
3619 
3620     /* flush any pending output */
3621     tioflushn(ctx->bifcxtio, 0);
3622 
3623     /* reset count of lines since keyboard input */
3624     tioreset(ctx->bifcxtio);
3625 
3626     /* ask the OS code for an event */
3627     evt = os_get_event(timeout, use_timeout, &info);
3628 
3629     /*
3630      *   the list always minimally needs two bytes of length prefix plus a
3631      *   number with the event code (one byte for the type, four bytes for
3632      *   the value)
3633      */
3634     lstsiz = 2 + (1 + 4);
3635 
3636     /* figure out how much space we'll need based on the event type */
3637     switch(evt)
3638     {
3639     case OS_EVT_KEY:
3640         /*
3641          *   we need space for a string with one or two bytes (depending
3642          *   on whether or not we have an extended key code) - 1 byte for
3643          *   type code, 2 for length prefix, and 1 or 2 for the string's
3644          *   contents
3645          */
3646         paramlen = (info.key[0] == 0 ? 2 : 1);
3647 
3648         /* map the extended key */
3649         get_ext_key_name(keyname, info.key[0], info.key[1]);
3650 
3651         /* determine the length we need for the string */
3652         paramlen = strlen(keyname);
3653 
3654         /* add it into the list */
3655         lstsiz += 1 + 2 + paramlen;
3656         break;
3657 
3658     case OS_EVT_HREF:
3659         /*
3660          *   we need space for the href string - 1 byte for type code, 2
3661          *   for length prefix, plus the string's contents
3662          */
3663         paramlen = strlen(info.href);
3664         lstsiz += 1 + 2 + (ushort)paramlen;
3665         break;
3666 
3667     default:
3668         /* other event types have no extra data */
3669         break;
3670     }
3671 
3672     /* allocate space for the list */
3673     runhres(ctx->bifcxrun, lstsiz, 0);
3674 
3675     /* set up the stack value */
3676     val.runstyp = DAT_LIST;
3677     p = val.runsv.runsvstr = ctx->bifcxrun->runcxhp;
3678 
3679     /* set up the list length prefix */
3680     oswp2(p, lstsiz);
3681     p += 2;
3682 
3683     /* set up the event type element */
3684     *p++ = DAT_NUMBER;
3685     oswp4s(p, evt);
3686     p += 4;
3687 
3688     /* add the event parameters, if any */
3689     switch(evt)
3690     {
3691     case OS_EVT_KEY:
3692         /* set up the string for the key */
3693         *p++ = DAT_SSTRING;
3694         oswp2(p, paramlen + 2);
3695         p += 2;
3696 
3697         /* add the characters to the string */
3698         memcpy(p, keyname, paramlen);
3699         p += paramlen;
3700         break;
3701 
3702     case OS_EVT_HREF:
3703         /* add the string for the href */
3704         *p++ = DAT_SSTRING;
3705         oswp2(p, paramlen + 2);
3706         memcpy(p + 2, info.href, paramlen);
3707         break;
3708     }
3709 
3710     /* consume the heap space */
3711     ctx->bifcxrun->runcxhp += lstsiz;
3712 
3713     /* push the result */
3714     runrepush(ctx->bifcxrun, &val);
3715 }
3716 
3717 /* timeDelay */
bifdelay(bifcxdef * ctx,int argc)3718 void bifdelay(bifcxdef *ctx, int argc)
3719 {
3720     long delay;
3721 
3722     /* ensure we have the right number of arguments */
3723     bifcntargs(ctx, 1, argc);
3724 
3725     /* flush any pending output */
3726     tioflushn(ctx->bifcxtio, 0);
3727 
3728     /* get the delay time */
3729     delay = runpopnum(ctx->bifcxrun);
3730 
3731     /* let the system perform the delay */
3732     os_sleep_ms(delay);
3733 }
3734 
3735 /* setOutputFilter */
bifsetoutfilter(bifcxdef * ctx,int argc)3736 void bifsetoutfilter(bifcxdef *ctx, int argc)
3737 {
3738     /* ensure we have the right number of arguments */
3739     bifcntargs(ctx, 1, argc);
3740 
3741     /* see what we have */
3742     switch(runtostyp(ctx->bifcxrun))
3743     {
3744     case DAT_NIL:
3745         /* remove the current filter */
3746         out_set_filter(MCMONINV);
3747 
3748         /* discard the argument */
3749         rundisc(ctx->bifcxrun);
3750         break;
3751 
3752     case DAT_FNADDR:
3753         /* set the filter to the given function */
3754         out_set_filter(runpopfn(ctx->bifcxrun));
3755         break;
3756 
3757     default:
3758         /* anything else is invalid */
3759         runsig1(ctx->bifcxrun, ERR_INVTBIF, ERRTSTR, "setOutputFilter");
3760     }
3761 }
3762 
3763 /*
3764  *   Get an optional object argument.  If the next argument is not an
3765  *   object value, or we're out of arguments, we'll return MCMONINV.
3766  *   Otherwise, we'll pop the object value and return it, decrementing the
3767  *   remaining argument counter provided.
3768  */
bif_get_optional_obj_arg(bifcxdef * ctx,int * rem_argc)3769 static objnum bif_get_optional_obj_arg(bifcxdef *ctx, int *rem_argc)
3770 {
3771     /* if we're out of arguments, there's no object value */
3772     if (*rem_argc == 0)
3773         return MCMONINV;
3774 
3775     /*
3776      *   if the next argument is not an object or nil, we're out of object
3777      *   arguments
3778      */
3779     if (runtostyp(ctx->bifcxrun) != DAT_OBJECT
3780         && runtostyp(ctx->bifcxrun) != DAT_NIL)
3781         return MCMONINV;
3782 
3783     /* we have an object - remove it from the remaining argument count */
3784     --(*rem_argc);
3785 
3786     /* pop and return the object value */
3787     return runpopobjnil(ctx->bifcxrun);
3788 }
3789 
3790 /*
3791  *   execCommand flag values
3792  */
3793 #define EC_HIDE_SUCCESS     0x00000001
3794 #define EC_HIDE_ERROR       0x00000002
3795 #define EC_SKIP_VALIDDO     0x00000004
3796 #define EC_SKIP_VALIDIO     0x00000008
3797 
3798 /*
3799  *   execCommand - execute a recursive command
3800  */
bifexec(bifcxdef * ctx,int argc)3801 void bifexec(bifcxdef *ctx, int argc)
3802 {
3803     objnum actor;
3804     objnum verb;
3805     objnum dobj;
3806     objnum prep;
3807     objnum iobj;
3808     int    err;
3809 	uint   capture_start = 0;
3810     uint   capture_end;
3811     objnum capture_obj;
3812     ulong  flags;
3813     int    hide_any;
3814     int    rem_argc;
3815 
3816     /*
3817      *   Check for the correct argument count.  The first two arguments
3818      *   are required; additional arguments are optional.
3819      */
3820     if (argc < 2 || argc > 6)
3821         runsig(ctx->bifcxrun, ERR_BIFARGC);
3822 
3823     /* pop the arguments - actor, verb, dobj, prep, iobj */
3824     actor = runpopobjnil(ctx->bifcxrun);
3825     verb = runpopobjnil(ctx->bifcxrun);
3826 
3827     /*
3828      *   The other object arguments are optional.  If we run into a
3829      *   numeric argument, it's the flags value, in which case we're out
3830      *   of objects.
3831      */
3832     rem_argc = argc - 2;
3833     dobj = bif_get_optional_obj_arg(ctx, &rem_argc);
3834     prep = bif_get_optional_obj_arg(ctx, &rem_argc);
3835     iobj = bif_get_optional_obj_arg(ctx, &rem_argc);
3836 
3837     /* if we have a flags argument, pop it */
3838     if (rem_argc > 0)
3839     {
3840         /* the last argument is the flags - pop the numeric value */
3841         flags = runpopnum(ctx->bifcxrun);
3842 
3843         /* remove it from the remaining argument counter */
3844         --rem_argc;
3845     }
3846     else
3847     {
3848         /* no flags specified - use zero by default */
3849         flags = 0;
3850     }
3851 
3852     /*
3853      *   make sure we don't have any arguments left - if we do, then it
3854      *   means that we got an incorrect type and skipped an argument when
3855      *   we were trying to sense the meanings of the arguments from their
3856      *   types
3857      */
3858     if (rem_argc != 0)
3859         runsig1(ctx->bifcxrun, ERR_INVTBIF, ERRTSTR, "execCommand");
3860 
3861     /* if we're hiding any output, start output capture */
3862     hide_any = ((flags & (EC_HIDE_SUCCESS | EC_HIDE_ERROR)) != 0);
3863     if (hide_any)
3864     {
3865         /* start capturing */
3866         tiocapture(ctx->bifcxtio, ctx->bifcxrun->runcxmem, TRUE);
3867 
3868         /* note the current output position */
3869         capture_start = tiocapturesize(ctx->bifcxtio);
3870     }
3871 
3872     /* execute the command */
3873     err = execmd_recurs(ctx->bifcxrun->runcxvoc,
3874                         actor, verb, dobj, prep, iobj,
3875                         (flags & EC_SKIP_VALIDDO) == 0,
3876                         (flags & EC_SKIP_VALIDIO) == 0);
3877 
3878     /* if we're hiding any output, end hiding */
3879     if (hide_any)
3880     {
3881         uchar *p;
3882         int hide;
3883 
3884         /* get the capture buffer size */
3885         capture_end = tiocapturesize(ctx->bifcxtio);
3886 
3887         /* turn off capture if it wasn't already on when we started */
3888         if (capture_start == 0)
3889             tiocapture(ctx->bifcxtio, ctx->bifcxrun->runcxmem, FALSE);
3890 
3891         /* determine whether we're hiding or showing the result */
3892         if (err == 0)
3893             hide = ((flags & EC_HIDE_SUCCESS) != 0);
3894         else
3895             hide = ((flags & EC_HIDE_ERROR) != 0);
3896 
3897         /* show or hide the result, as appropriate */
3898         if (hide)
3899         {
3900             /*
3901              *   We're hiding this result, so do not display the string.
3902              *   If there's an enclosing capture, remove the string from
3903              *   the enclosing capture.
3904              */
3905             if (capture_start != 0)
3906                 tiopopcapture(ctx->bifcxtio, capture_start);
3907         }
3908         else
3909         {
3910             /*
3911              *   We're showing the text.  If we're in an enclosing
3912              *   capture, do nothing - simply leave the string in the
3913              *   enclosing capture buffer; otherwise, actually display it
3914              */
3915             if (capture_start == 0)
3916             {
3917                 /* lock the capture object */
3918                 capture_obj = tiogetcapture(ctx->bifcxtio);
3919                 p = mcmlck(ctx->bifcxrun->runcxmem, capture_obj);
3920 
3921                 ERRBEGIN(ctx->bifcxerr)
3922                 {
3923                     /* display the string */
3924                     outformatlen((char *)p + capture_start,
3925                                  capture_end - capture_start);
3926                 }
3927                 ERRCLEAN(ctx->bifcxerr)
3928                 {
3929                     /* unlock the capture object before signalling out */
3930                     mcmunlck(ctx->bifcxrun->runcxmem, capture_obj);
3931                 }
3932                 ERRENDCLN(ctx->bifcxerr);
3933 
3934                 /* unlock the capture object */
3935                 mcmunlck(ctx->bifcxrun->runcxmem, capture_obj);
3936             }
3937         }
3938 
3939         /* clear the capture buffer if it wasn't on when we started */
3940         if (capture_start == 0)
3941             tioclrcapture(ctx->bifcxtio);
3942     }
3943 
3944     /* push the result code */
3945     runpnum(ctx->bifcxrun, err);
3946 }
3947 
3948 /*
3949  *   parserGetObj - get one of the objects associated with the command
3950  */
bifgetobj(bifcxdef * ctx,int argc)3951 void bifgetobj(bifcxdef *ctx, int argc)
3952 {
3953     int id;
3954 	objnum obj = 0;
3955     voccxdef *voc = ctx->bifcxrun->runcxvoc;
3956 
3957     /* check the argument count */
3958     bifcntargs(ctx, 1, argc);
3959 
3960     /* get the argument */
3961     id = (int)runpopnum(ctx->bifcxrun);
3962 
3963     /* get the appropriate object */
3964     switch(id)
3965     {
3966     case 1:
3967         /* get the current actor */
3968         obj = voc->voccxactor;
3969 
3970         /* if there's no current actor, use the current 'me' by default */
3971         if (obj == MCMONINV)
3972             obj = voc->voccxme;
3973 
3974         /* done */
3975         break;
3976 
3977     case 2:
3978         /* verb */
3979         obj = voc->voccxverb;
3980         break;
3981 
3982     case 3:
3983         /* direct object */
3984         obj = (voc->voccxdobj == 0 ? MCMONINV : voc->voccxdobj->vocolobj);
3985         break;
3986 
3987     case 4:
3988         /* preposition */
3989         obj = voc->voccxprep;
3990         break;
3991 
3992     case 5:
3993         /* indirect object */
3994         obj = (voc->voccxiobj == 0 ? MCMONINV : voc->voccxiobj->vocolobj);
3995         break;
3996 
3997     case 6:
3998         /* "it" */
3999         obj = voc->voccxit;
4000         break;
4001 
4002     case 7:
4003         /* "him" */
4004         obj = voc->voccxhim;
4005         break;
4006 
4007     case 8:
4008         /* "her" */
4009         obj = voc->voccxher;
4010         break;
4011 
4012     case 9:
4013         /* them */
4014         voc_push_objlist(voc, voc->voccxthm, voc->voccxthc);
4015 
4016         /*
4017          *   return directly, since we've already pushed the result (it's
4018          *   a list, not an object)
4019          */
4020         return;
4021 
4022     default:
4023         /* invalid argument */
4024         runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "parserGetObj");
4025         break;
4026     }
4027 
4028     /* return the object */
4029     runpobj(ctx->bifcxrun, obj);
4030 }
4031 
4032 /*
4033  *   parseNounList - parse a noun list.  Call like this:
4034  *
4035  *   parserParseNounList(wordlist, typelist, starting_index, complain,
4036  *   multi, check_actor);
4037  */
bifparsenl(bifcxdef * ctx,int argc)4038 void bifparsenl(bifcxdef *ctx, int argc)
4039 {
4040     /* check the argument count */
4041     bifcntargs(ctx, 6, argc);
4042 
4043     /* call the parser */
4044     voc_parse_np(ctx->bifcxrun->runcxvoc);
4045 }
4046 
4047 /*
4048  *   parserTokenize - given a string, produce a list of tokens.  Returns
4049  *   nil on error, or a list of token strings.
4050  *
4051  *   parserTokenize(commandString);
4052  */
bifprstok(bifcxdef * ctx,int argc)4053 void bifprstok(bifcxdef *ctx, int argc)
4054 {
4055     /* check arguments */
4056     bifcntargs(ctx, 1, argc);
4057 
4058     /* call the parser */
4059     voc_parse_tok(ctx->bifcxrun->runcxvoc);
4060 }
4061 
4062 /*
4063  *   parserGetTokTypes - given a list of tokens (represented as strings),
4064  *   get a corresponding list of token types.
4065  *
4066  *   parserGetTokTypes(tokenList);
4067  */
bifprstoktyp(bifcxdef * ctx,int argc)4068 void bifprstoktyp(bifcxdef *ctx, int argc)
4069 {
4070     /* check arguments */
4071     bifcntargs(ctx, 1, argc);
4072 
4073     /* call the parser */
4074     voc_parse_types(ctx->bifcxrun->runcxvoc);
4075 }
4076 
4077 /*
4078  *   parserDictLookup - given a list of tokens and their types, produce a
4079  *   list of all of the objects that match all of the words.
4080  *
4081  *   parserDictLookup(tokenList, typeList);
4082  */
bifprsdict(bifcxdef * ctx,int argc)4083 void bifprsdict(bifcxdef *ctx, int argc)
4084 {
4085     /* check arguments */
4086     bifcntargs(ctx, 2, argc);
4087 
4088     /* call the parser */
4089     voc_parse_dict_lookup(ctx->bifcxrun->runcxvoc);
4090 }
4091 
4092 /*
4093  *   parserResolveObjects - resolve an object list of the sort returned by
4094  *   parseNounList.  Validates and disambiguates the objects.
4095  *
4096  *   parserResolveObjects(actor, verb, prep, otherobj, usageType,
4097  *   verprop, tokenList, objList, silent)
4098  */
bifprsrslv(bifcxdef * ctx,int argc)4099 void bifprsrslv(bifcxdef *ctx, int argc)
4100 {
4101     /* check arguments */
4102     bifcntargs(ctx, 9, argc);
4103 
4104     /* call the parser */
4105     voc_parse_disambig(ctx->bifcxrun->runcxvoc);
4106 }
4107 
4108 /*
4109  *   parserReplaceCommand - replace the current command line with a new
4110  *   string.  Aborts the current command.
4111  */
bifprsrplcmd(bifcxdef * ctx,int argc)4112 void bifprsrplcmd(bifcxdef *ctx, int argc)
4113 {
4114     /* check arguments */
4115     bifcntargs(ctx, 1, argc);
4116 
4117     /* call the parser */
4118     voc_parse_replace_cmd(ctx->bifcxrun->runcxvoc);
4119 }
4120 
4121 /*
4122  *   exitobj - throw a RUNEXITOBJ error
4123  */
bifexitobj(bifcxdef * ctx,int argc)4124 void bifexitobj(bifcxdef *ctx, int argc)
4125 {
4126     /* no arguments are allowed */
4127     bifcntargs(ctx, 0, argc);
4128 
4129     /* throw the RUNEXITOBJ error */
4130     errsig(ctx->bifcxerr, ERR_RUNEXITOBJ);
4131 }
4132 
4133 /*
4134  *   Standard system button labels for bifinpdlg()
4135  */
4136 #define BIFINPDLG_LBL_OK      1
4137 #define BIFINPDLG_LBL_CANCEL  2
4138 #define BIFINPDLG_LBL_YES     3
4139 #define BIFINPDLG_LBL_NO      4
4140 
4141 /*
4142  *   inputdialog
4143  */
bifinpdlg(bifcxdef * ctx,int argc)4144 void bifinpdlg(bifcxdef *ctx, int argc)
4145 {
4146     uchar *p;
4147     char prompt[256];
4148     char lblbuf[256];
4149     const char *labels[10];
4150     char *dst;
4151     char *xp;
4152     uint len;
4153     size_t bcnt;
4154     int default_resp, cancel_resp;
4155     int resp;
4156     int std_btns;
4157     int icon_id;
4158 
4159     /* check for proper arguments */
4160     bifcntargs(ctx, 5, argc);
4161 
4162     /* get the icon number */
4163     icon_id = runpopnum(ctx->bifcxrun);
4164 
4165     /* get the prompt string */
4166     p = runpopstr(ctx->bifcxrun);
4167     bifcstr(ctx, prompt, (size_t)sizeof(prompt), p);
4168 
4169     /* translate from internal to local characters */
4170     for (xp = prompt ; *xp != '\0' ; xp++)
4171         *xp = (char)cmap_i2n(*xp);
4172 
4173     /* check for a standard button set selection */
4174     if (runtostyp(ctx->bifcxrun) == DAT_NUMBER)
4175     {
4176         /* get the standard button set ID */
4177         std_btns = runpopnum(ctx->bifcxrun);
4178 
4179         /* there are no actual buttons */
4180         bcnt = 0;
4181     }
4182     else
4183     {
4184         /* we're not using standard buttons */
4185         std_btns = 0;
4186 
4187         /* get the response string list */
4188         p = runpoplst(ctx->bifcxrun);
4189         len = osrp2(p);
4190         p += 2;
4191 
4192         /* build our internal button list */
4193         for (bcnt = 0, dst = lblbuf ; len != 0 ; lstadv(&p, &len))
4194         {
4195             /* see what we have */
4196             if (*p == DAT_SSTRING)
4197             {
4198                 /* it's a label string - convert to a C string */
4199                 bifcstr(ctx, dst, sizeof(lblbuf) - (dst - lblbuf), p + 1);
4200 
4201                 /* translate from internal to local characters */
4202                 for (xp = dst ; *xp != '\0' ; xp++)
4203                     *xp = (char)cmap_i2n(*xp);
4204 
4205                 /* set this button to point to the converted text */
4206                 labels[bcnt++] = dst;
4207 
4208                 /* move past this label in the button buffer */
4209                 dst += strlen(dst) + 1;
4210             }
4211             else if (*p == DAT_NUMBER)
4212             {
4213                 int id;
4214                 int resid;
4215 
4216                 /* it's a standard system label ID - get the ID */
4217                 id = (int)osrp4s(p + 1);
4218 
4219                 /* translate it to the appropriate string resource */
4220                 switch(id)
4221                 {
4222                 case BIFINPDLG_LBL_OK:
4223                     resid = RESID_BTN_OK;
4224                     break;
4225 
4226                 case BIFINPDLG_LBL_CANCEL:
4227                     resid = RESID_BTN_CANCEL;
4228                     break;
4229 
4230                 case BIFINPDLG_LBL_YES:
4231                     resid = RESID_BTN_YES;
4232                     break;
4233 
4234                 case BIFINPDLG_LBL_NO:
4235                     resid = RESID_BTN_NO;
4236                     break;
4237 
4238                 default:
4239                     resid = 0;
4240                     break;
4241                 }
4242 
4243                 /*
4244                  *   if we got a valid resource ID, load the resource;
4245                  *   otherwise, skip this button
4246                  */
4247                 if (resid != 0
4248                     && !os_get_str_rsc(resid, dst,
4249                                        sizeof(lblbuf) - (dst - lblbuf)))
4250                 {
4251                     /* set this button to point to the converted text */
4252                     labels[bcnt++] = dst;
4253 
4254                     /* move past this label in the button buffer */
4255                     dst += strlen(dst) + 1;
4256                 }
4257             }
4258 
4259             /* if we have exhausted our label array, stop now */
4260             if (bcnt >= sizeof(labels)/sizeof(labels[0])
4261                 || dst >= lblbuf + sizeof(lblbuf))
4262                 break;
4263         }
4264     }
4265 
4266     /* get the default response */
4267     if (runtostyp(ctx->bifcxrun) == DAT_NIL)
4268     {
4269         rundisc(ctx->bifcxrun);
4270         default_resp = 0;
4271     }
4272     else
4273         default_resp = runpopnum(ctx->bifcxrun);
4274 
4275     /* get the cancel response */
4276     if (runtostyp(ctx->bifcxrun) == DAT_NIL)
4277     {
4278         rundisc(ctx->bifcxrun);
4279         cancel_resp = 0;
4280     }
4281     else
4282         cancel_resp = runpopnum(ctx->bifcxrun);
4283 
4284     /* flush output before showing the dialog */
4285     tioflushn(ctx->bifcxtio, 0);
4286 
4287     /* show the dialog */
4288     resp = tio_input_dialog(icon_id, prompt, std_btns,
4289                             (const char **)labels, bcnt,
4290                             default_resp, cancel_resp);
4291 
4292     /* return the result */
4293     runpnum(ctx->bifcxrun, resp);
4294 }
4295 
4296 /*
4297  *   Determine if a resource exists
4298  */
bifresexists(bifcxdef * ctx,int argc)4299 void bifresexists(bifcxdef *ctx, int argc)
4300 {
4301     uchar *p;
4302     char resname[OSFNMAX];
4303     appctxdef *appctx;
4304     int found;
4305     runsdef val;
4306 
4307     /* check for proper arguments */
4308     bifcntargs(ctx, 1, argc);
4309 
4310     /* get the resource name string */
4311     p = runpopstr(ctx->bifcxrun);
4312     bifcstr(ctx, resname, (size_t)sizeof(resname), p);
4313 
4314     /*
4315      *   if we have a host application context, and it provides a resource
4316      *   finder function, ask the resource finder if the resource is
4317      *   available; otherwise, report that the resource is not loadable,
4318      *   since we must not be running a version of the interpreter that
4319      *   supports external resource loading
4320      */
4321     appctx = ctx->bifcxappctx;
4322     found = (appctx != 0
4323              && appctx->resfile_exists != 0
4324              && (*appctx->resfile_exists)(appctx->resfile_exists_ctx,
4325                                           resname, strlen(resname)));
4326 
4327     /* push the result */
4328     runpush(ctx->bifcxrun, runclog(found), &val);
4329 }
4330 
4331 } // End of namespace TADS2
4332 } // End of namespace TADS
4333 } // End of namespace Glk
4334