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/character_map.h"
25 #include "glk/tads/tads2/command_line.h"
26 #include "glk/tads/tads2/error.h"
27 #include "glk/tads/tads2/file_io.h"
28 #include "glk/tads/tads2/memory_cache_heap.h"
29 #include "glk/tads/tads2/os.h"
30 #include "glk/tads/tads2/play.h"
31 #include "glk/tads/tads2/post_compilation.h"
32 #include "glk/tads/tads2/run.h"
33 #include "glk/tads/tads2/runtime_app.h"
34 #include "glk/tads/tads2/tokenizer.h"
35 #include "glk/tads/tads2/tads2.h"
36 #include "glk/tads/tads2/vocabulary.h"
37 #include "glk/tads/os_frob_tads.h"
38 
39 namespace Glk {
40 namespace TADS {
41 namespace TADS2 {
42 
43 /* dummy setup function */
supgnam(char * buf,tokthdef * tab,objnum sc)44 void supgnam(char *buf, tokthdef *tab, objnum sc)
45 {
46     strcpy(buf, "???");
47 }
48 
49 /* dummy file read functions */
tok_read_defines(tokcxdef * tctx,osfildef * fp,errcxdef * ec)50 void tok_read_defines(tokcxdef *tctx, osfildef *fp, errcxdef *ec)
51 {
52     errsig(ec, ERR_UNKRSC);
53 }
54 
55 /* dummy debugger functions */
56 
dbgbpset(dbgcxdef * ctx,char * addr,int * bpnum)57 int dbgbpset(dbgcxdef *ctx, char *addr, int *bpnum) { return 0; }
58 
dbgbpat(dbgcxdef * ctx,objnum objn,objnum self,uint ofs,int * bpnum,char * bpname,int toggle,char * condition,int * did_set)59 int dbgbpat(dbgcxdef *ctx, objnum objn, objnum self,
60 	uint ofs, int *bpnum, char *bpname, int toggle,
61 	char *condition, int *did_set) { return 0; }
62 
dbgbpatid(dbgcxdef * ctx,int bpnum,objnum target,objnum self,uint ofs,char * bpname,int toggle,char * cond,int * did_set)63 int dbgbpatid(dbgcxdef *ctx, int bpnum, objnum target, objnum self,
64 	uint ofs, char *bpname, int toggle, char *cond,
65 	int *did_set) { return 0; }
66 
dbgisbp(dbgcxdef * ctx,objnum target,objnum self,uint ofs,int * bpnum)67 int dbgisbp(dbgcxdef *ctx, objnum target, objnum self, uint ofs, int *bpnum) { return 0; }
68 
dbgisbpena(dbgcxdef * ctx,int bpnum)69 int dbgisbpena(dbgcxdef *ctx, int bpnum) { return 0; }
70 
dbgbpdel(dbgcxdef * ctx,int bpnum)71 int dbgbpdel(dbgcxdef *ctx, int bpnum) { return 0; }
72 
dbgbpdis(dbgcxdef * ctx,int bpnum,int disable)73 int dbgbpdis(dbgcxdef *ctx, int bpnum, int disable) { return 0; }
74 
dbgbpsetcond(dbgcxdef * ctx,int bpnum,char * cond)75 int dbgbpsetcond(dbgcxdef *ctx, int bpnum, char *cond) { return 0; }
76 
dbgbplist(dbgcxdef * ctx,void (* dispfn)(void * ctx,const char * str,int len),void * dispctx)77 void dbgbplist(dbgcxdef *ctx, void(*dispfn)(void *ctx, const char *str, int len), void *dispctx) {}
78 
dbgbpenum(dbgcxdef * ctx,void (* cbfunc)(void * cbctx,int bpnum,const char * desc,const char * cond,int disabled),void * cbctx)79 void dbgbpenum(dbgcxdef *ctx, void(*cbfunc)(void *cbctx, int bpnum, const char *desc,
80 		const char *cond, int disabled), void *cbctx) {}
81 
dbgbpeach(dbgcxdef * ctx,void (* fn)(void *,int,uchar *,uint),void * fnctx)82 void dbgbpeach(dbgcxdef *ctx, void(*fn)(void *, int, uchar *, uint), void *fnctx) {}
83 
dbgbpgetinfo(dbgcxdef * ctx,int bpnum,char * descbuf,size_t descbuflen,char * condbuf,size_t condbuflen)84 int dbgbpgetinfo(dbgcxdef *ctx, int bpnum, char *descbuf, size_t descbuflen,
85 	char *condbuf, size_t condbuflen) { return 0; }
86 
dbgeval(dbgcxdef * ctx,char * expr,void (* dispfn)(void * dispctx,const char * str,int strl),void * dispctx,int level,int showtype)87 int dbgeval(dbgcxdef *ctx, char *expr,
88 	void(*dispfn)(void *dispctx, const char *str, int strl),
89 	void *dispctx, int level, int showtype) { return 0; }
90 
dbgevalext(dbgcxdef * ctx,char * expr,void (* dispfn)(void * dispctx,const char * str,int strl),void * dispctx,int level,int showtype,dattyp * dat,void (* aggcb)(void * aggctx,const char * subname,int subnamelen,const char * relationship),void * aggctx,int speculative)91 int dbgevalext(dbgcxdef *ctx, char *expr,
92 	void(*dispfn)(void *dispctx, const char *str, int strl),
93 	void *dispctx, int level, int showtype, dattyp *dat,
94 	void(*aggcb)(void *aggctx, const char *subname,
95 		int subnamelen, const char *relationship),
96 	void *aggctx, int speculative) { return 0 ;}
97 
dbgenumlcl(dbgcxdef * ctx,int level,void (* func)(void * ctx,const char * lclnam,size_t lclnamlen),void * cbctx)98 void dbgenumlcl(dbgcxdef *ctx, int level,
99 	void(*func)(void *ctx, const char *lclnam, size_t lclnamlen),
100 	void *cbctx) {}
101 
dbgcompile(dbgcxdef * ctx,char * expr,dbgfdef * fr,objnum * objn,int speculative)102 int dbgcompile(dbgcxdef *ctx, char *expr, dbgfdef *fr, objnum *objn,
103 	int speculative) { return 0; }
104 
dbgwhere(dbgcxdef * ctx,char * buf)105 void dbgwhere(dbgcxdef *ctx, char *buf) {}
106 
dbgwxset(dbgcxdef * ctx,char * expr,int * wxnum,int level)107 int dbgwxset(dbgcxdef *ctx, char *expr, int *wxnum, int level) { return 0; }
108 
dbgwxdel(dbgcxdef * ctx,int wxnum)109 int dbgwxdel(dbgcxdef *ctx, int wxnum) { return 0; }
110 
dbgwxupd(dbgcxdef * ctx,void (* dispfn)(void * dispctx,const char * txt,int len),void * dispctx)111 void dbgwxupd(dbgcxdef *ctx,
112 	void(*dispfn)(void *dispctx, const char *txt, int len),
113 	void *dispctx) {}
114 
dbgswitch(struct lindef ** linp,struct lindef * newlin)115 void dbgswitch(struct lindef **linp, struct lindef *newlin) {}
116 
dbguini(dbgcxdef * ctx,const char * game_filename)117 void dbguini(dbgcxdef *ctx, const char *game_filename) {}
118 
dbguini2(dbgcxdef * ctx)119 void dbguini2(dbgcxdef *ctx) {}
120 
dbgu_err_resume(dbgcxdef * ctx)121 int dbgu_err_resume(dbgcxdef *ctx) { return 0; }
122 
dbgu_find_src(const char * origname,int origlen,char * fullname,size_t full_len,int must_find_file)123 int dbgu_find_src(const char *origname, int origlen,
124 	char *fullname, size_t full_len, int must_find_file) { return 0; }
125 
dbgucmd(dbgcxdef * ctx,int bphit,int err,unsigned int * exec_ofs)126 void dbgucmd(dbgcxdef *ctx, int bphit, int err, unsigned int *exec_ofs) {}
127 
dbguquitting(dbgcxdef * ctx)128 void dbguquitting(dbgcxdef *ctx) {}
129 
dbguterm(dbgcxdef * ctx)130 void dbguterm(dbgcxdef *ctx) {}
131 
dbguerr(dbgcxdef * ctx,int errnum,char * msg)132 void dbguerr(dbgcxdef *ctx, int errnum, char *msg) {}
133 
trchid(void)134 void trchid(void) {}
trcsho(void)135 void trcsho(void) {}
136 
dbgfrfind(dbgcxdef * ctx,objnum frobj,uint frofs)137 struct runsdef *dbgfrfind(dbgcxdef *ctx, objnum frobj, uint frofs)
138 {
139     VARUSED(frobj);
140     VARUSED(frofs);
141     errsig(ctx->dbgcxerr, ERR_INACTFR);
142     return 0;
143 }
144 
dbgss(struct dbgcxdef * ctx,uint ofs,int instr,int err,uchar * noreg * p)145 void dbgss(struct dbgcxdef *ctx, uint ofs, int instr, int err,
146            uchar *noreg *p)
147 {
148     VARUSED(ctx);
149     VARUSED(ofs);
150     VARUSED(instr);
151     VARUSED(err);
152     VARUSED(p);
153 }
154 
dbgstart(struct dbgcxdef * ctx)155 int dbgstart(struct dbgcxdef *ctx)
156 {
157     VARUSED(ctx);
158     return TRUE;
159 }
160 
161 /* printf-style formatting */
trdptf(const char * fmt,...)162 static void trdptf(const char *fmt, ...)
163 {
164     char buf[256];
165     va_list va;
166 
167     /* format the string */
168     va_start(va, fmt);
169     vsprintf(buf, fmt, va);
170     va_end(va);
171 
172     /* print the formatted buffer */
173     os_printz(buf);
174 }
175 
176 
177 /*
178  *   display a range of usage messages
179  */
trdusage_show_range(errcxdef * ec,int msg_first,int msg_last)180 static void trdusage_show_range(errcxdef *ec, int msg_first, int msg_last)
181 {
182     int  i;
183     char buf[128];
184 
185     for (i = msg_first ; i <= msg_last ; ++i)
186     {
187         errmsg(ec, buf, (uint)sizeof(buf), i);
188         trdptf("%s\n", buf);
189     }
190 }
191 
192 
193 /*
194  *   display a range of usage messages, then throw the usage error
195  */
trdusage_range(errcxdef * ec,int msg_first,int msg_last)196 static void trdusage_range(errcxdef *ec, int msg_first, int msg_last)
197 {
198     /* show the message range */
199     trdusage_show_range(ec, msg_first, msg_last);
200 
201     /* signal the usage error */
202     errsig(ec, ERR_USAGE);
203 }
204 
205 /*
206  *   display general run-time usage information
207  */
trdusage(errcxdef * ec)208 static void trdusage(errcxdef *ec)
209 {
210     int first;
211 
212     /*
213      *   if we have an app display name, display it instead of the
214      *   hard-coded text in the message identifying the app
215      */
216     first = ERR_TRUS1;
217     if (ec->errcxappctx != 0 && ec->errcxappctx->usage_app_name != 0)
218     {
219         char buf[128];
220         char buf2[128];
221         erradef argv[1];
222 
223         /* get the parameterized usage message */
224         errmsg(ec, buf, (uint)sizeof(buf), ERR_TRUSPARM);
225 
226         /* format in the application name */
227         argv[0].errastr = ec->errcxappctx->usage_app_name;
228         errfmt(buf2, (int)sizeof(buf2), buf, 1, argv);
229 
230         /* display it */
231         trdptf("%s\n", buf2);
232 
233         /* start at the next message */
234         ++first;
235     }
236 
237     /* display the main option list messages */
238     trdusage_show_range(ec, first, ERR_TRUSL);
239 
240     /* display the OS-specific option messages, if any */
241     trdusage_show_range(ec, ERR_TRUS_OS_FIRST, ERR_TRUS_OS_LAST);
242 
243     /* display the usage footer messages */
244     trdusage_range(ec, ERR_TRUSFT1, ERR_TRUSFTL);
245 }
246 
247 /*
248  *   display -s suboptions
249  */
trdusage_s(errcxdef * ec)250 static void trdusage_s(errcxdef *ec)
251 {
252     trdusage_range(ec, ERR_TRSUS1, ERR_TRSUSL);
253 }
254 
255 
trdmain1(errcxdef * ec,int argc,char * argv[],appctxdef * appctx,const char * save_ext)256 static void trdmain1(errcxdef *ec, int argc, char *argv[],
257                      appctxdef *appctx, const char *save_ext)
258 {
259     osfildef  *swapfp = (osfildef *)0;
260     runcxdef   runctx;
261     bifcxdef   bifctx;
262     voccxdef   vocctx;
263     void     (*bif[100])(struct bifcxdef *, int);
264     mcmcxdef  *mctx = 0;
265     mcmcx1def *globalctx = 0;
266     dbgcxdef   dbg;
267     supcxdef   supctx;
268     char      *swapname = 0;
269     char       swapbuf[OSFNMAX];
270     char     **argp;
271     char      *arg;
272     char      *infile;
273     char       infile_abs[OSFNMAX];      /* fully-qualified input file name */
274     char       infile_path[OSFNMAX];         /* absolute path to input file */
275     const char *exefile;           /* try with executable file if no infile */
276     ulong      swapsize = 0xffffffffL;        /* allow unlimited swap space */
277     int        swapena = OS_DEFAULT_SWAP_ENABLED;      /* swapping enabled? */
278     int        i;
279     int        pause = FALSE;                 /* pause after finishing game */
280     fiolcxdef  fiolctx;
281     noreg int  loadopen = FALSE;
282     char       inbuf[OSFNMAX];
283     ulong      cachelimit = 0xffffffff;
284     ushort     undosiz = TRD_UNDOSIZ;      /* default undo context size 16k */
285     objucxdef *undoptr = 0;
286     uint       flags;         /* flags used to write the file we're reading */
287     objnum     preinit;         /* preinit object, if we need to execute it */
288     uint       heapsiz = TRD_HEAPSIZ;
289     uint       stksiz = TRD_STKSIZ;
290     runsdef   *mystack;
291     uchar     *myheap;
292     extern osfildef *cmdfile;     /* hacky v1 qa interface - command log fp */
293     extern osfildef *logfp;        /* hacky v1 qa interface - output log fp */
294     int        preload = FALSE;              /* TRUE => preload all objects */
295     ulong      totsize;
296     extern voccxdef *main_voc_ctx;
297     int        safety_read, safety_write;          /* file I/O safety level */
298     char      *restore_file = 0;                    /* .SAV file to restore */
299     char      *charmap = 0;                           /* character map file */
300     int        charmap_none;       /* explicitly do not use a character set */
301     int        doublespace = TRUE;        /* formatter double-space setting */
302 
303     NOREG((&loadopen))
304 
305     /* initialize the output formatter */
306     out_init();
307 
308     /* set safety level to 2 by default - read any/write current dir only */
309     safety_read = safety_write = 2;
310 
311     /* no -ctab- yet */
312     charmap_none = FALSE;
313 
314     /* parse arguments */
315     for (i = 1, argp = argv + 1 ; i < argc ; ++argp, ++i)
316     {
317         arg = *argp;
318         if (*arg == '-')
319         {
320             switch(*(arg+1))
321             {
322             case 'c':
323                 if (!strcmp(arg+1, "ctab"))
324                 {
325                     /* get the character mapping table */
326                     charmap = cmdarg(ec, &argp, &i, argc, 4, trdusage);
327                 }
328                 else if (!strcmp(arg+1, "ctab-"))
329                 {
330                     /* use the default mapping */
331                     charmap_none = TRUE;
332                 }
333                 else
334                     trdusage(ec);
335                 break;
336 
337             case 'r':
338                 /* restore a game */
339                 restore_file = cmdarg(ec, &argp, &i, argc, 1, trdusage);
340                 break;
341 
342             case 'i':
343                 qasopn(cmdarg(ec, &argp, &i, argc, 1, trdusage), TRUE);
344                 break;
345 
346             case 'o':
347                 cmdfile = osfopwt(cmdarg(ec, &argp, &i, argc, 1, trdusage),
348                                   OSFTCMD);
349                 break;
350 
351             case 'l':
352                 logfp = osfopwt(cmdarg(ec, &argp, &i, argc, 1, trdusage),
353                                 OSFTCMD);
354                 break;
355 
356             case 'p':
357                 if (!scumm_stricmp(arg, "-plain"))
358                 {
359                     os_plain();
360                     break;
361                 }
362                 pause = cmdtog(ec, pause, arg, 1, trdusage);
363                 break;
364 
365             case 'd':
366                 if (!scumm_strnicmp(arg, "-double", 7))
367                 {
368                     /* get the argument value */
369                     doublespace = cmdtog(ec, doublespace, arg, 6, trdusage);
370 
371                     /* set the double-space mode in the formatter */
372                     out_set_doublespace(doublespace);
373                     break;
374                 }
375                 break;
376 
377             case 's':
378                 {
379                     char *p;
380 
381                     /* get the option */
382                     p = cmdarg(ec, &argp, &i, argc, 1, trdusage);
383 
384                     /* if they're asking for help, display detailed usage */
385                     if (*p == '?')
386                         trdusage_s(ec);
387 
388                     /* get the safety level from the argument */
389                     safety_read = *p - '0';
390                     safety_write = (*(p+1) != '\0' ? *(p+1) - '0' :
391                                     safety_read);
392 
393                     /* range-check the values */
394                     if (safety_read < 0 || safety_read > 4
395                         || safety_write < 0 || safety_write > 4)
396                         trdusage_s(ec);
397 
398                     /* tell the host system about the setting */
399                     if (appctx != 0 && appctx->set_io_safety_level != 0)
400                         (*appctx->set_io_safety_level)
401                             (appctx->io_safety_level_ctx,
402                              safety_read, safety_write);
403                 }
404                 break;
405 
406             case 'm':
407                 switch(*(arg + 2))
408                 {
409                 case 's':
410                     stksiz = atoi(cmdarg(ec, &argp, &i, argc, 2, trdusage));
411                     break;
412 
413                 case 'h':
414                     heapsiz = atoi(cmdarg(ec, &argp, &i, argc, 2, trdusage));
415                     break;
416 
417                 default:
418                     cachelimit = atol(cmdarg(ec, &argp, &i, argc, 1,
419                                              trdusage));
420                     break;
421                 }
422                 break;
423 
424             case 't':
425                 /* swap file options:  -tf file, -ts size, -t- (no swap) */
426                 switch(*(arg+2))
427                 {
428                 case 'f':
429                     swapname = cmdarg(ec, &argp, &i, argc, 2, trdusage);
430                     break;
431 
432                 case 's':
433                     swapsize = atol(cmdarg(ec, &argp, &i, argc, 2, trdusage));
434                     break;
435 
436                 case 'p':
437                     preload = cmdtog(ec, preload, arg, 2, trdusage);
438                     break;
439 
440                 default:
441                     swapena = cmdtog(ec, swapena, arg, 1, trdusage);
442                     break;
443                 }
444                 break;
445 
446             case 'u':
447                 undosiz = atoi(cmdarg(ec, &argp, &i, argc, 1, trdusage));
448                 break;
449 
450             default:
451                 trdusage(ec);
452             }
453         }
454         else break;
455     }
456 
457     /* presume we won't take the .gam from the application executable */
458     exefile = 0;
459 
460     /* get input name argument, and make sure it's the last argument */
461     if (i == argc)
462     {
463         osfildef *fp;
464         ulong     curpos;
465         ulong     endpos;
466         int       use_exe;
467 
468         /*
469          *   There's no input name argument, so we need to find the game
470          *   to play some other way.  First, check to see if we have a
471          *   game to restore, and if so whether it has the .GAM name
472          *   encoded into it.  Next, look to see if there's a game
473          *   attached to the executable file; if so, use it.  If not, see
474          *   if the host system wants to provide a name through its
475          *   callback.
476          */
477 
478         /* presume we won't find a game attached to the executable file */
479         infile = 0;
480         use_exe = FALSE;
481 
482         /*
483          *   see if we have a saved game to restore, and it specifies the
484          *   GAM file that saved it
485          */
486         if (restore_file != 0)
487         {
488             /* try getting the game name from the restore file */
489             if (fiorso_getgame(restore_file, inbuf, sizeof(inbuf)))
490             {
491                 /* got it - use this file */
492                 infile = inbuf;
493             }
494         }
495 
496         /*
497          *   it that didn't work, try to read from os-dependent part of
498          *   program being executed
499          */
500         if (infile == 0)
501         {
502             /* try opening the executable file */
503             exefile = (argv && argv[0] ? argv[0] : "TRX");
504             fp = os_exeseek(exefile, "TGAM");
505             if (fp != 0)
506             {
507                 /* see if there's a game file attached to the executable */
508                 curpos = osfpos(fp);
509                 osfseek(fp, 0L, OSFSK_END);
510                 endpos = osfpos(fp);
511                 osfcls(fp);
512 
513                 /* if we found it, use it */
514                 if (endpos != curpos)
515                     use_exe = TRUE;
516             }
517         }
518 
519         /*
520          *   if we didn't find a game in the executable, try the host
521          *   system callback
522          */
523         if (infile == 0 && !use_exe)
524         {
525             /*
526              *   ask the host system callback what to do - if we don't
527              *   have a host system callback, or the callback
528              */
529             if (appctx != 0 && appctx->get_game_name != 0)
530             {
531                 /* call the host system callback */
532                 if ((*appctx->get_game_name)(appctx->get_game_name_ctx,
533                                              inbuf, sizeof(inbuf)))
534                 {
535                     /* the host system provided a name - use it */
536                     infile = inbuf;
537                 }
538                 else
539                 {
540                     /*
541                      *   the host didn't provide a name - simply display a
542                      *   message indicating that no game file has been
543                      *   chosen, and return
544                      */
545                     trdptf("\n");
546                     trdptf("(No game has been selected.)\n");
547                     return;
548                 }
549             }
550             else
551             {
552                 /*
553                  *   we've run out of ways to get a filename - give the
554                  *   user the usage message and quit
555                  */
556                 trdusage(ec);
557             }
558         }
559     }
560     else
561     {
562         infile = *argp;
563         if (i + 1 != argc)
564             trdusage(ec);
565 
566 #ifndef OS_HATES_EXTENSIONS
567         /*
568          *   If original name exists, use it; otherwise, try adding .GAM.
569          *   Note that this code is ifdef'd so that platforms that don't
570          *   use filename extensions in the manner conventional for DOS
571          *   and Unix won't use this code.
572          */
573         if (osfacc(infile))
574         {
575             strcpy(inbuf, infile);
576             os_defext(inbuf, "gam");
577             infile = inbuf;
578         }
579 #endif /* !defined(OS_HATES_EXTENSIONS) */
580     }
581 
582     /* open up the swap file */
583     if (swapena && swapsize)
584     {
585         swapfp = os_create_tempfile(swapname, swapbuf);
586         if (swapname == 0) swapname = swapbuf;
587         if (swapfp == 0) errsig(ec, ERR_OPSWAP);
588     }
589 
590     /* load the character map */
591     if (charmap_none)
592         cmap_override();
593     else if (cmap_load(charmap))
594         errsig(ec, ERR_INVCMAP);
595 
596     ERRBEGIN(ec)
597 
598     /* initialize cache manager context */
599     globalctx = mcmini(cachelimit, 128, swapsize, swapfp, swapname, ec);
600     mctx = mcmcini(globalctx, 128, fioldobj, &fiolctx,
601                    objrevert, (void *)0);
602     mctx->mcmcxrvc = mctx;
603 
604     /* set up an undo context */
605     if (undosiz)
606         undoptr = objuini(mctx, undosiz, vocdundo, vocdusz, &vocctx);
607     else
608         undoptr = (objucxdef *)0;
609 
610     /* set up vocabulary context */
611     vocini(&vocctx, ec, mctx, &runctx, undoptr, 100, 100, 200);
612 
613     /*
614      *   save a pointer to the voc context globally, so that certain
615      *   external routines (such as Unix-style signal handlers) can reach
616      *   it
617      */
618     main_voc_ctx = &vocctx;
619 
620     /* allocate stack and heap */
621     totsize = (ulong)stksiz * (ulong)sizeof(runsdef);
622     if (totsize != (size_t)totsize)
623         errsig1(ec, ERR_STKSIZE, ERRTINT, (uint)(65535/sizeof(runsdef)));
624     mystack = (runsdef *)mchalo(ec, (size_t)totsize, "runtime stack");
625     myheap = mchalo(ec, heapsiz, "runtime heap");
626 
627     /* get the absolute path for the input file */
628     if (infile != 0)
629         os_get_abs_filename(infile_abs, sizeof(infile_abs), infile);
630     else if (exefile != 0)
631         os_get_abs_filename(infile_abs, sizeof(infile_abs), exefile);
632     else
633         infile_abs[0] = '\0';
634     os_get_path_name(infile_path, sizeof(infile_path), infile_abs);
635 
636     /* set up execution context */
637     runctx.runcxerr = ec;
638     runctx.runcxmem = mctx;
639     runctx.runcxstk = mystack;
640     runctx.runcxstop = &mystack[stksiz];
641     runctx.runcxsp = mystack;
642     runctx.runcxbp = mystack;
643     runctx.runcxheap = myheap;
644     runctx.runcxhp = myheap;
645     runctx.runcxhtop = &myheap[heapsiz];
646     runctx.runcxundo = undoptr;
647     runctx.runcxbcx = &bifctx;
648     runctx.runcxbi = bif;
649     runctx.runcxtio = (tiocxdef *)0;
650     runctx.runcxdbg = &dbg;
651     runctx.runcxvoc = &vocctx;
652     runctx.runcxdmd = supcont;
653     runctx.runcxdmc = &supctx;
654     runctx.runcxext = 0;
655     runctx.runcxgamename = infile;
656     runctx.runcxgamepath = infile_path;
657 
658     /* set up setup context */
659     supctx.supcxerr = ec;
660     supctx.supcxmem = mctx;
661     supctx.supcxtab = (tokthdef *)0;
662     supctx.supcxbuf = (uchar *)0;
663     supctx.supcxlen = 0;
664     supctx.supcxvoc = &vocctx;
665     supctx.supcxrun = &runctx;
666 
667     /* set up debug context */
668     dbg.dbgcxtio = (tiocxdef *)0;
669     dbg.dbgcxmem = mctx;
670     dbg.dbgcxerr = ec;
671     dbg.dbgcxtab = (tokthdef *)0;
672     dbg.dbgcxfcn = 0;
673     dbg.dbgcxdep = 0;
674     dbg.dbgcxflg = 0;
675     dbg.dbgcxlin = (lindef *)0;                      /* no line sources yet */
676 
677     /* set up built-in function context */
678     bifctx.bifcxerr = ec;
679     bifctx.bifcxrun = &runctx;
680     bifctx.bifcxtio = (tiocxdef *)0;
681     bifctx.bifcxrnd = 0;
682     bifctx.bifcxrndset = FALSE;
683     bifctx.bifcxappctx = appctx;
684     bifctx.bifcxsafetyr = safety_read;
685     bifctx.bifcxsafetyw = safety_write;
686     bifctx.bifcxsavext = save_ext;
687 
688     /* initialize the regular expression parser context */
689     re_init(&bifctx.bifcxregex, ec);
690 
691     /* add the built-in functions, keywords, etc */
692     supbif(&supctx, bif, (int)(sizeof(bif)/sizeof(bif[0])));
693 
694     /* set up status line hack */
695     runistat(&vocctx, &runctx, (tiocxdef *)0);
696 
697     /* turn on the "busy" cursor before loading */
698     os_csr_busy(TRUE);
699 
700     /* read the game from the binary file */
701     fiord(mctx, &vocctx, (struct tokcxdef *)0,
702           infile, exefile, &fiolctx, &preinit, &flags,
703           (struct tokpdef *)0, (uchar **)0, (uint *)0, (uint *)0,
704           (preload ? 2 : 0), appctx, argv[0]);
705     loadopen = TRUE;
706 
707     /* turn off the "busy" cursor */
708     os_csr_busy(FALSE);
709 
710     /* play the game */
711     plygo(&runctx, &vocctx, (tiocxdef *)0, preinit, restore_file);
712 
713     /* close load file */
714     fiorcls(&fiolctx);
715 
716     if (pause)
717     {
718         trdptf("[press a key to exit]");
719         os_waitc();
720         trdptf("\n");
721     }
722 
723     /* close and delete swapfile, if one was opened */
724     trd_close_swapfile(&runctx);
725 
726     /* make sure the script file is closed, if we have one */
727     qasclose();
728 
729     ERRCLEAN(ec)
730         /* close and delete swapfile, if one was opened */
731         trd_close_swapfile(&runctx);
732 
733         /* close the load file if one was opened */
734         if (loadopen)
735             fiorcls(&fiolctx);
736 
737         /* vocctx is going out of scope - forget the global reference to it */
738         main_voc_ctx = 0;
739 
740         /* delete the voc context */
741         vocterm(&vocctx);
742 
743         /* delete the undo context */
744         if (undoptr != 0)
745             objuterm(undoptr);
746 
747         /* release the object cache structures */
748         if (mctx != 0)
749             mcmcterm(mctx);
750         if (globalctx != 0)
751             mcmterm(globalctx);
752     ERRENDCLN(ec)
753 
754     /* vocctx is going out of scope - forget the global reference to it */
755     main_voc_ctx = 0;
756 
757     /* delete the voc contxt */
758     vocterm(&vocctx);
759 
760     /* delete the undo context */
761     if (undoptr != 0)
762         objuterm(undoptr);
763 
764     /* release the object cache structures */
765     mcmcterm(mctx);
766     mcmterm(globalctx);
767 }
768 
769 /*
770  *   If the OS configuration so desires, use a less technical format for
771  *   run-time error messages by leaving out the numeric error code.  Note
772  *   that we'll only do this if the error messages are linked directly
773  *   into the run-time, since we need the numeric code as a last resort
774  *   when the error message may not be present.
775  */
776 #ifdef OS_SKIP_ERROR_CODES
777 # ifdef ERR_LINK_MESSAGES
778 #  define TRDLOGERR_PREFIX "\n[An error has occurred within TADS: "
779 # endif
780 #endif
781 
782 /*
783  *   If we didn't define a different error prefix format, use the default
784  *   format with the numeric error code.
785  */
786 #ifndef TRDLOGERR_PREFIX
787 # define TRDLOGERR_PREFIX "\n[%s-%d: "
788 #endif
789 
790 /* log an error */
trdlogerr(void * ctx0,const char * fac,int err,int argc,erradef * argv)791 static void trdlogerr(void *ctx0, const char *fac, int err, int argc, erradef *argv) {
792     errcxdef *ctx = (errcxdef *)ctx0;
793     char      buf[256];
794     char      msg[256];
795 
796     /* display the prefix message to the console and log file */
797     sprintf(buf, TRDLOGERR_PREFIX, fac, err);
798     trdptf("%s", buf);
799     out_logfile_print(buf, FALSE);
800 
801     /* display the error message text to the console and log file */
802     errmsg(ctx, msg, (uint)sizeof(msg), err);
803     errfmt(buf, (int)sizeof(buf), msg, argc, argv);
804     trdptf("%s]\n", buf);
805     out_logfile_print(buf, FALSE);
806     out_logfile_print("]", TRUE);
807 }
808 
809 
810 /*
811  *   close and delete the swap file
812  */
trd_close_swapfile(runcxdef * runctx)813 void trd_close_swapfile(runcxdef *runctx)
814 {
815     extern voccxdef *main_voc_ctx;
816     mcmcxdef        *mctx;
817     mcmcx1def       *globalctx;
818     mcscxdef        *mcsctx;
819 
820     /* if no run context was supplied, find it from the main voc context */
821     if (runctx == 0)
822     {
823         /* if there is no main voc context, we're out of luck */
824         if (main_voc_ctx == 0)
825             return;
826 
827         /* get the run context */
828         runctx = main_voc_ctx->voccxrun;
829     }
830 
831     /* get the other relevant contexts */
832     mctx = runctx->runcxmem;
833     globalctx = mctx->mcmcxgl;
834     mcsctx = &globalctx->mcmcxswc;
835 
836     /* if we have a swap file open, close it */
837     if (mcsctx->mcscxfp != 0)
838     {
839         /* close the file */
840         osfcls(mcsctx->mcscxfp);
841 
842         /* forget about the file, so we don't try to close it again */
843         mcsctx->mcscxfp = (osfildef *)0;
844     }
845 
846     /* if we have a filename, delete the file */
847     if (mcsctx->mcscxfname != 0)
848     {
849         /* delete the file */
850         osfdel_temp(mcsctx->mcscxfname);
851 
852         /* forget the filename, so we don't try to delete the file again */
853         mchfre(mcsctx->mcscxfname);
854         mcsctx->mcscxfname = 0;
855     }
856 }
857 
858 /* main - called by os main after setting up arguments */
trdmain(int argc,char * argv[],appctxdef * appctx,const char * save_ext)859 int trdmain(int argc, char *argv[], appctxdef *appctx, const char *save_ext)
860 {
861     errcxdef  errctx;
862     int       err;
863     osfildef *fp;
864 
865     errctx.errcxlog = trdlogerr;
866     errctx.errcxlgc = &errctx;
867     errctx.errcxfp  = (osfildef *)0;
868     errctx.errcxofs = 0;
869     errctx.errcxappctx = appctx;
870     fp = oserrop(argv[0]);
871     errini(&errctx, fp);
872 
873     /* copyright-date-string */
874 #ifndef NO_T2_COPYRIGHT_NOTICE
875     trdptf("%s - A %s TADS %s Interpreter.\n",
876            G_tads_oem_app_name, G_tads_oem_display_mode,
877            TADS_RUNTIME_VERSION);
878     trdptf("%sopyright (c) 1993, 2012 by Michael J. Roberts.\n",
879            G_tads_oem_copyright_prefix ? "TADS c" : "C");
880     trdptf("%s\n", G_tads_oem_author);
881 #endif
882 
883     ERRBEGIN(&errctx)
884         trdmain1(&errctx, argc, argv, appctx, save_ext);
885     ERRCATCH(&errctx, err)
886         /*
887          *   log the error, unless it's usage (in which case we logged it
888          *   already) or we're simply quitting the game
889          */
890         if (err != ERR_USAGE && err != ERR_RUNQUIT)
891             errclog(&errctx);
892 
893         /* close the error file */
894         if (errctx.errcxfp != 0)
895             osfcls(errctx.errcxfp);
896 
897         /* pause before exiting if the OS desires it */
898         os_expause();
899 
900         /* return failure unless we're simply quitting the game */
901         return (err == ERR_RUNQUIT ? OSEXSUCC : OSEXFAIL);
902     ERREND(&errctx)
903 
904     /* close the error file if we opened it */
905     if (errctx.errcxfp != 0)
906         osfcls(errctx.errcxfp);
907 
908     /* successful completion */
909     return(OSEXSUCC);
910 }
911 
912 } // End of namespace TADS2
913 } // End of namespace TADS
914 } // End of namespace Glk
915