/* * Main program entrypoint */ int main(int argc, char **argv) { int stat; MyClientIfc clientifc; CVmHostIfc *hostifc = new CVmHostIfcStdio(argv[0]); /* initialize for testing */ test_init(); /* * Initialize the OS layer. Since this is a command-line-only * implementation, there's no need to ask the OS layer to try to get * us a filename to run, so pass in null for the prompt and filename * buffer. */ os_init(&argc, argv, 0, 0, 0); /* run the image */ stat = vm_run_image_main(&clientifc, "test_exec", argc, argv, TRUE, TRUE, hostifc); /* uninitialize the OS layer */ os_uninit(); /* done with the host interface */ delete hostifc; /* show any unfreed memory */ t3_list_memory_blocks(0); /* done */ return stat; }
/* * Invoke the T3 VM with the given command-line arguments */ static int main_t3(int argc, char **argv) { CVmMainClientConsole clientifc; int stat; CVmHostIfc *hostifc = new CVmHostIfcStdio(argv[0]); #ifdef GARGLK garglk_set_program_name("TADS " T3VM_VSN_STRING); char *s; s = strrchr(argv[1], '/'); if (!s) s = strrchr(argv[1], '\\'); garglk_set_story_name(s ? s + 1 : argv[1]); #endif /* * Initialize the OS layer. Since this is a command-line-only * implementation, there's no need to ask the OS layer to try to get * us a filename to run, so pass in null for the prompt and filename * buffer. */ os_init(&argc, argv, 0, 0, 0); /* invoke the basic entrypoint */ stat = vm_run_image_main(&clientifc, "t3run", argc, argv, TRUE, FALSE, hostifc); /* uninitialize the OS layer */ os_uninit(); /* done with the host interface */ delete hostifc; /* show any unfreed memory */ t3_list_memory_blocks(0); /* exit with status code */ os_term(stat); /* we shouldn't get here, but in case os_term doesn't really exit... */ return stat; }
/* * Invoke the T3 VM with the given command-line arguments */ static int main_t3(int argc, char **argv) { CVmMainClientConsole clientifc; int stat; CVmHostIfc *hostifc = new CVmHostIfcStdio(argv[0]); /* check for a "-plain" option */ check_plain_option(argc, argv); /* * Initialize the OS layer. Since this is a command-line-only * implementation, there's no need to ask the OS layer to try to get * us a filename to run, so pass in null for the prompt and filename * buffer. */ os_init(&argc, argv, 0, 0, 0); /* invoke the basic entrypoint */ stat = vm_run_image_main(&clientifc, "t3run", argc, argv, TRUE, FALSE, hostifc); /* uninitialize the OS layer */ os_uninit(); /* done with the host interface */ delete hostifc; /* show any unfreed memory */ t3_list_memory_blocks(0); /* exit with status code */ os_term(stat); /* we shouldn't get here, but in case os_term doesn't really exit... */ return stat; }
int main(int argc, char **argv) { CResLoader *res_loader; CTcHostIfc *hostifc; int curarg; int fatal_error_count = 0; osfildef *fpout = 0; int next_local = 0; CVmFile *imgfile = 0; CVmFile *objfile = 0; const char *imgfname; int success; char pathbuf[OSFNMAX]; static const char tool_data[4] = { 't', 's', 't', 'L' }; /* initialize for testing */ test_init(); /* create the host interface object */ hostifc = new CTcHostIfcStdio(); /* create a resource loader */ os_get_special_path(pathbuf, sizeof(pathbuf), argv[0], OS_GSP_T3_RES); res_loader = new CResLoader(pathbuf); /* initialize the compiler */ CTcMain::init(hostifc, res_loader, 0); err_try { /* scan arguments */ for (curarg = 1 ; curarg < argc ; ++curarg) { char *p; /* get the argument string for easy reference */ p = argv[curarg]; /* if it's not an option, we're done */ if (*p != '-') break; if (*(p + 1) == 'v') { /* set verbose mode */ G_tcmain->set_verbosity(TRUE); } else { /* * invalid usage - consume all the arguments and fall * through to the usage checker */ curarg = argc; break; } } /* check arguments */ if (curarg + 2 > argc) { /* terminate the compiler */ CTcMain::terminate(); /* delete our objects */ delete res_loader; /* exit with an error */ errexit("usage: test_link [options] obj-file [obj-file [...]] " "image-file\n" "options:\n" " -v - verbose error messages"); } /* set up an output file */ imgfname = argv[argc - 1]; fpout = osfopwb(imgfname, OSFTT3IMG); if (fpout == 0) errexit("unable to open image file"); imgfile = new CVmFile(); imgfile->set_file(fpout, 0); /* read the object files */ for ( ; curarg < argc - 1 ; ++curarg) { osfildef *fpobj; /* open this object file */ fpobj = osfoprb(argv[curarg], OSFTT3OBJ); if (fpobj == 0) { printf("unable to open object file \"%s\"\n", argv[curarg]); goto done; } /* note the loading */ printf("loading %s\n", argv[curarg]); /* set up the CVmFile object for it */ objfile = new CVmFile(); objfile->set_file(fpobj, 0); /* read the object file */ G_cg->load_object_file(objfile, argv[curarg]); /* done with the object file */ delete objfile; objfile = 0; } /* check for unresolved externals */ if (G_prs->check_unresolved_externs()) goto done; /* write the image file */ G_cg->write_to_image(imgfile, 0, tool_data); done: ; } err_catch(exc) { /* * if it's not a general internal or fatal error, log it; don't * log general errors, since these will have been logged as * specific internal errors before being thrown */ if (exc->get_error_code() != TCERR_INTERNAL_ERROR && exc->get_error_code() != TCERR_FATAL_ERROR) G_tok->log_error(TC_SEV_FATAL, exc->get_error_code()); /* count the fatal error */ ++fatal_error_count; } err_end; /* report errors */ fprintf(stderr, "Warnings: %d\n" "Errors: %d\n" "Longest string: %d, longest list: %d\n", G_tcmain->get_warning_count(), G_tcmain->get_error_count() + fatal_error_count, G_cg->get_max_str_len(), G_cg->get_max_list_cnt()); /* * note whether or not the compilation was successful - it succeeded * if we had no errors or fatal errors */ success = (G_tcmain->get_error_count() + fatal_error_count == 0); /* delete the object file object (this closes the file) */ delete imgfile; /* if we have an open object file, close it */ if (objfile != 0) delete objfile; /* * if any errors occurred, delete the object file in the external * file system - this prevents us from leaving around an incomplete * or corrupted image file when compilation fails, and helps * 'make'-type tools realize that they must generate the image file * target again on the next build, even if source files didn't * change */ if (!success) osfdel(imgfname); /* shut down the compiler */ CTcMain::terminate(); /* done with the res loader */ delete res_loader; /* delete the host interface */ delete hostifc; /* show any unfreed memory */ t3_list_memory_blocks(0); /* * terminate - exit with a success indication if we had no errors * (other than warnings); exit with an error indication otherwise */ return (success ? OSEXSUCC : OSEXFAIL); }
int main(int argc, char **argv) { CResLoader *res_loader; CTcHostIfc *hostifc; int curarg; CTcTokFileDesc *desc; long linenum; int pp_mode = FALSE; char pathbuf[OSFNMAX]; /* initialize for testing */ test_init(); /* create the host interface object */ hostifc = new CTcHostIfcStdio(); /* create a resource loader */ os_get_special_path(pathbuf, sizeof(pathbuf), argv[0], OS_GSP_T3_RES); res_loader = new CResLoader(pathbuf); /* initialize the compiler */ CTcMain::init(hostifc, res_loader, "us-ascii"); /* set test reporting mode */ G_tok->set_test_report_mode(TRUE); G_tcmain->set_test_report_mode(TRUE); err_try { /* add some pre-defined symbols for testing */ G_tok->add_define("_MSC_VER", "1100"); G_tok->add_define("_WIN32", "1"); G_tok->add_define("_M_IX86", "500"); G_tok->add_define("__STDC__", "0"); G_tok->add_define("_INTEGRAL_MAX_BITS", "64"); G_tok->add_define("__cplusplus", "1"); /* scan -I arguments */ for (curarg = 1 ; curarg < argc ; ++curarg) { char *p; /* get the argument string for easy reference */ p = argv[curarg]; /* if it's not an option, we're done */ if (*p != '-') break; /* if it's a -I argument, use it */ if (*(p + 1) == 'I') { char *path; /* * if it's with this argument, read it, otherwise move * on to the next argument */ if (*(p + 2) == '\0') path = argv[++curarg]; else path = p + 2; /* add the directory to the include path list */ G_tok->add_inc_path(path); } else if (*(p + 1) == 'P') { /* set preprocess-only mode */ G_tok->set_mode_pp_only(TRUE); pp_mode = TRUE; } else if (*(p + 1) == 'v') { /* set verbose mode */ G_tcmain->set_verbosity(TRUE); } else { /* * invalid usage - consume all the arguments and fall * through to the usage checker */ curarg = argc; break; } } /* check arguments */ if (curarg + 1 != argc) { /* terminate the compiler */ CTcMain::terminate(); /* delete our objects */ delete res_loader; /* exit with an error */ errexit("usage: test_tok [options] <source-file>\n" "options:\n" " -Idir - add dir to include path\n" " -P - preprocess to standard output\n" " -v - verbose error messages"); } /* set up the tokenizer with the main input file */ if (G_tok->set_source(argv[curarg], argv[curarg])) errexit("unable to open source file"); /* start out with no stream */ desc = 0; linenum = 0; /* read lines of input */ for (;;) { /* read the next line, and stop if we've reached end of file */ if (G_tok->read_line_pp()) break; /* * If we're in a different stream than for the last line, or * the new line number is more than the last line number plus * 1, add a #line directive to the output stream. * * In order to make test log output independent of local path * naming conventions and the local directory structure, use * only the root filename in the #line directive. */ if (pp_mode && (G_tok->get_last_desc() != desc || G_tok->get_last_linenum() != linenum + 1)) printf("#line %ld %s\n", G_tok->get_last_linenum(), G_tok->get_last_desc()->get_dquoted_rootname()); /* remember the last line we read */ desc = G_tok->get_last_desc(); linenum = G_tok->get_last_linenum(); /* show this line */ printf("%s\n", G_tok->get_cur_line()); } /* dump the hash table, to see what it looks like */ G_tok->get_defines_table()->debug_dump(); } err_catch(exc) { /* * if it's not the general internal error, log it; don't log * general internal errors, since these will have been logged as * specific internal errors before being thrown */ if (exc->get_error_code() != TCERR_INTERNAL_ERROR) printf("exception caught: %d\n", exc->get_error_code()); } err_end; /* shut down the compiler */ CTcMain::terminate(); /* done with the res loader */ delete res_loader; /* delete host interface */ delete hostifc; /* show any unfreed memory */ t3_list_memory_blocks(0); /* success */ return 0; }
/* * Main entrypoint */ int main(int argc, char **argv) { int stat; int i; CVmHostIfc *hostifc; CVmMainClientConsole clientifc; /* * Check for a "-plain" option; if it's there, set the terminal to * plain text mode. We must make this check before doing anything * else, because os_plain() must be called prior to os_init() if * it's going to be called at all. */ for (i = 1 ; i < argc ; ++i) { if (strcmp(argv[i], "-plain") == 0) { /* set plain text mode in the OS layer */ os_plain(); /* we've found what we're looking for - no need to look further */ break; } } /* * Initialize the OS layer. Since this is a command-line-only * implementation, there's no need to ask the OS layer to try to get us * a filename to run, so pass in null for the prompt and filename * buffer. */ os_init(&argc, argv, 0, 0, 0); /* install the OS break handler while we're running */ os_instbrk(1); /* create the host interface */ hostifc = new CVmHostIfcStdio(argv[0]); /* invoke the basic entrypoint */ stat = vm_run_image_main(&clientifc, "t3run", argc, argv, TRUE, FALSE, hostifc); #ifdef TADSNET /* * Disconnect the Web UI, if applicable. Leave any final UI window * state displayed until the user manually closes it, so that the user * can read any final messages displayed when the game program * terminated. */ osnet_disconnect_webui(FALSE); /* shut down the network layer, if applicable */ os_net_cleanup(); #endif /* remove the OS break handler */ os_instbrk(0); /* uninitialize the OS layer */ os_uninit(); /* delete the host interface object */ delete hostifc; /* show any unfreed memory */ t3_list_memory_blocks(0); /* exit with status code */ os_term(stat); /* we shouldn't get here, but in case os_term doesn't really exit... */ AFTER_OS_TERM(return stat;) }
int main(int argc, char **argv) { CResLoader *res_loader; CTcHostIfc *hostifc; int curarg; int fatal_error_count = 0; osfildef *fpout = 0; CVmFile *imgfile = 0; CTcUnasSrcCodeStr *unas_in; CTcUnasOutStdio unas_out; CTPNStmProg *node; const char *image_fname; int success; int unasm = FALSE; uchar xor_mask = 0; char pathbuf[OSFNMAX]; /* initialize for testing */ test_init(); /* create the host interface object */ hostifc = new CTcHostIfcStdio(); /* create a resource loader */ os_get_special_path(pathbuf, sizeof(pathbuf), argv[0], OS_GSP_T3_RES); res_loader = new CResLoader(pathbuf); /* initialize the compiler */ CTcMain::init(hostifc, res_loader, "us-ascii"); /* use test reporting mode */ G_tok->set_test_report_mode(TRUE); G_tcmain->set_test_report_mode(TRUE); /* create the disassembler input stream */ unas_in = new CTcUnasSrcCodeStr(G_cs); err_try { static const char tool_data[4] = { 't', 's', 't', 'P' }; /* scan options */ for (curarg = 1 ; curarg < argc ; ++curarg) { char *p; /* get the argument string for easy reference */ p = argv[curarg]; /* if it's not an option, we're done */ if (*p != '-') break; /* if it's a -I argument, use it */ if (*(p + 1) == 'I') { char *path; /* * if it's with this argument, read it, otherwise move * on to the next argument */ if (*(p + 2) == '\0') path = argv[++curarg]; else path = p + 2; /* add the directory to the include path list */ G_tok->add_inc_path(path); } else if (*(p + 1) == 'v') { /* set verbose mode */ G_tcmain->set_verbosity(TRUE); } else if (*(p + 1) == 'u') { /* note unassembly mode */ unasm = TRUE; } else { /* * invalid usage - consume all the arguments and fall * through to the usage checker */ curarg = argc; break; } } /* check arguments */ if (curarg + 2 != argc) { /* terminate the compiler */ CTcMain::terminate(); /* delete our objects */ delete res_loader; /* exit with an error */ errexit("usage: test_prs [options] <source-file> <image-file>\n" "options:\n" " -Idir - add dir to include path\n" " -v - verbose error messages"); } /* add the default system include directory to the include path */ os_get_special_path(pathbuf, sizeof(pathbuf), argv[0], OS_GSP_T3_INC); G_tok->add_inc_path(pathbuf); /* set up the tokenizer with the main input file */ if (G_tok->set_source(argv[curarg], argv[curarg])) errexit("unable to open source file"); /* set up an output file */ image_fname = argv[curarg+1]; fpout = osfopwb(image_fname, OSFTT3IMG); if (fpout == 0) errexit("unable to open image file"); imgfile = new CVmFile(); imgfile->set_file(fpout, 0); /* read the first token */ G_tok->next(); /* parse at the top level */ node = G_prs->parse_top(); /* if errors occurred during parsing, stop here */ if (G_tcmain->get_error_count() != 0 || node == 0) goto done; /* fold symbolic constants for all nodes */ node->fold_constants(G_prs->get_global_symtab()); /* if errors occurred during constant folding, stop now */ if (G_tcmain->get_error_count() != 0) goto done; /* generate code and write the image file */ node->build_image(imgfile, xor_mask, tool_data); /* if errors occurred during code generation, stop now */ if (G_tcmain->get_error_count() != 0) goto done; /* disassemble the result if desired */ if (unasm) CTcT3Unasm::disasm(unas_in, &unas_out); done: ; } err_catch(exc) { /* * if it's not a general internal or fatal error, log it; don't * log general errors, since these will have been logged as * specific internal errors before being thrown */ if (exc->get_error_code() != TCERR_INTERNAL_ERROR && exc->get_error_code() != TCERR_FATAL_ERROR) G_tok->log_error(TC_SEV_FATAL, exc->get_error_code()); /* count the fatal error */ ++fatal_error_count; } err_end; /* report errors */ fprintf(stderr, "Warnings: %d\n" "Errors: %d\n" "Longest string: %d, longest list: %d\n", G_tcmain->get_warning_count(), G_tcmain->get_error_count() + fatal_error_count, G_cg->get_max_str_len(), G_cg->get_max_list_cnt()); /* * note whether or not the compilation was successful - it succeeded * if we had no errors or fatal errors */ success = (G_tcmain->get_error_count() + fatal_error_count == 0); /* delete the image file object (this closes the file) */ delete imgfile; /* * if any errors occurred, delete the image file in the external * file system - this prevents us from leaving around an incomplete * or corrupted image file when compilation fails, and helps * 'make'-type tools realize that they must generate the image file * target again on the next build, even if source files didn't * change */ if (!success) osfdel(image_fname); /* delete the disassembler input object */ delete unas_in; /* shut down the compiler */ CTcMain::terminate(); /* done with the res loader */ delete res_loader; /* delete the host interface */ delete hostifc; /* show any unfreed memory */ t3_list_memory_blocks(0); /* * terminate - exit with a success indication if we had no errors * (other than warnings); exit with an error indication otherwise */ return (success ? OSEXSUCC : OSEXFAIL); }
int main(int argc, char **argv) { CResLoader *res_loader; CTcHostIfc *hostifc; int curarg; int fatal_error_count = 0; node_entry *node_head = 0; node_entry *node_tail = 0; osfildef *fpout = 0; CVmFile *imgfile = 0; ulong next_obj_id = 1; uint next_prop_id = 1; int next_local = 0; CTcTokFileDesc *desc; long linenum; CTcUnasSrcCodeStr *unas_in; CTcUnasOutStdio unas_out; char pathbuf[OSFNMAX]; /* initialize for testing */ test_init(); /* create the host interface object */ hostifc = new CTcHostIfcStdio(); /* create a resource loader */ os_get_special_path(pathbuf, sizeof(pathbuf), argv[0], OS_GSP_T3_RES); res_loader = new CResLoader(pathbuf); /* initialize the compiler */ CTcMain::init(hostifc, res_loader, 0); /* create the disassembler input stream */ unas_in = new CTcUnasSrcCodeStr(G_cs); err_try { /* scan -I arguments */ for (curarg = 1 ; curarg < argc ; ++curarg) { char *p; /* get the argument string for easy reference */ p = argv[curarg]; /* if it's not an option, we're done */ if (*p != '-') break; /* if it's a -I argument, use it */ if (*(p + 1) == 'I') { char *path; /* * if it's with this argument, read it, otherwise move * on to the next argument */ if (*(p + 2) == '\0') path = argv[++curarg]; else path = p + 2; /* add the directory to the include path list */ G_tok->add_inc_path(path); } else if (*(p + 1) == 'v') { /* set verbose mode */ G_tcmain->set_verbosity(TRUE); } else { /* * invalid usage - consume all the arguments and fall * through to the usage checker */ curarg = argc; break; } } /* check arguments */ if (curarg + 2 != argc) { /* terminate the compiler */ CTcMain::terminate(); /* delete our objects */ delete res_loader; /* exit with an error */ errexit("usage: test_prs [options] <source-file> <image-file>\n" "options:\n" " -Idir - add dir to include path\n" " -v - verbose error messages"); } /* set up the tokenizer with the main input file */ if (G_tok->set_source(argv[curarg], argv[curarg])) errexit("unable to open source file"); /* set up an output file */ fpout = osfopwb(argv[curarg+1], OSFTT3IMG); if (fpout == 0) errexit("unable to open image file"); imgfile = new CVmFile(); imgfile->set_file(fpout, 0); /* read the first token */ G_tok->next(); /* parse expressions */ for (;;) { CTcPrsNode *result; CTcSymbol *entry; /* if we're at end of file, we're done */ if (G_tok->getcur()->gettyp() == TOKT_EOF) break; /* check for our fake declarations */ switch(G_tok->getcur()->gettyp()) { case TOKT_OBJECT: /* add an object symbol */ G_tok->next(); entry = new CTcSymObj(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len(), FALSE, next_obj_id++, FALSE, TC_META_TADSOBJ, 0); G_prs->get_global_symtab()->add_entry(entry); /* skip the object name */ G_tok->next(); break; case TOKT_FUNCTION: /* add a function symbol */ G_tok->next(); entry = new CTcSymFunc(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len(), FALSE, 0, 0, FALSE, TRUE, FALSE, FALSE, FALSE); G_prs->get_global_symtab()->add_entry(entry); /* skip the function name */ G_tok->next(); break; case TOKT_LOCAL: /* add a local variable symbol */ G_tok->next(); entry = new CTcSymLocal(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len(), FALSE, FALSE, next_local++); G_prs->get_global_symtab()->add_entry(entry); /* skip the function name */ G_tok->next(); break; default: /* note the starting line */ desc = G_tok->get_last_desc(); linenum = G_tok->get_last_linenum(); /* parse an expression */ result = G_prs->parse_expr(); /* add it to our list */ if (result != 0) { node_entry *cur; /* create a new list entry */ cur = new node_entry(result, desc, linenum); /* link it at the end of our list */ cur->nxt = 0; if (node_tail != 0) node_tail->nxt = cur; else node_head = cur; node_tail = cur; } } /* parse a semicolon */ if (G_prs->parse_req_sem()) break; } /* * if there were no parse errors, run through our node list and * generate code */ if (G_tcmain->get_error_count() == 0) { /* * loop through our node list; generate code and then delete * each list entry */ while (node_head != 0) { node_entry *nxt; /* remember the next entry */ nxt = node_head->nxt; /* * set this line's descriptor as current, for error * reporting purposes */ G_tok->set_line_info(node_head->desc, node_head->linenum); /* fold symbolic constants */ node_head->node = node_head->node ->fold_constants(G_prs->get_global_symtab()); /* if it's a constant value, display it */ show_const(0, node_head->node); /* * generate code; for testing purposes, don't discard * anything, to ensure we perform all generation */ node_head->node->gen_code(FALSE, FALSE); /* disassemble this much */ unas_out.print("// line %lu\n", node_head->linenum); CTcT3Unasm::disasm(unas_in, &unas_out); /* delete this entry */ delete node_head; /* move on to the next entry */ node_head = nxt; } } } err_catch(exc) { /* * if it's not a general internal or fatal error, log it; don't * log general errors, since these will have been logged as * specific internal errors before being thrown */ if (exc->get_error_code() != TCERR_INTERNAL_ERROR && exc->get_error_code() != TCERR_FATAL_ERROR) G_tok->log_error(TC_SEV_FATAL, exc->get_error_code()); /* count the fatal error */ ++fatal_error_count; } err_end; /* report errors */ fprintf(stderr, "Warnings: %d\n" "Errors: %d\n" "Longest string: %d, longest list: %d\n", G_tcmain->get_warning_count(), G_tcmain->get_error_count() + fatal_error_count, G_cg->get_max_str_len(), G_cg->get_max_list_cnt()); /* delete the disassembler input object */ delete unas_in; /* shut down the compiler */ CTcMain::terminate(); /* done with the res loader */ delete res_loader; /* delete the image file */ delete imgfile; /* delete the host interface */ delete hostifc; /* show any unfreed memory */ t3_list_memory_blocks(0); /* success */ return 0; }
int main(int argc, char **argv) { osfildef *fpin; osfildef *fpout; CVmFile *file_in; CVmFile *file_out; int status; CVmHostIfc *hostifc; CVmMainClientConsole clientifc; int curarg; /* initialize for testing */ test_init(); /* initialize the error stack */ err_init(1024); /* start at the first argument */ curarg = 1; /* check usage */ if (curarg + 2 > argc) { printf("usage: t3pre <original-image> <new-image> [program-args]\n" "\n" "Runs preinitialization on the image file, writing a new " "image file\n" "with the state of the program after preinitialization.\n"); return OSEXFAIL; } /* open the files */ if ((fpin = osfoprb(argv[curarg], OSFTT3IMG)) == 0) { printf("Error opening original image file \"%s\"\n", argv[1]); return OSEXFAIL; } if ((fpout = osfopwb(argv[curarg + 1], OSFTT3IMG)) == 0) { printf("Error opening new image file \"%s\"\n", argv[2]); return OSEXFAIL; } /* create the CVmFile objects */ file_in = new CVmFile(); file_in->set_file(fpin, 0); file_out = new CVmFile(); file_out->set_file(fpout, 0); /* create our host interface */ hostifc = new CVmHostIfcStdio(argv[0]); /* run preinit and write the new image file */ err_try { /* load, run, and write */ vm_run_preinit(file_in, argv[1], file_out, hostifc, &clientifc, argv + curarg + 1, argc - curarg - 1, 0); /* note the success status */ status = OSEXSUCC; } err_catch(exc) { const char *msg; char buf[128]; /* look up the message */ msg = err_get_msg(vm_messages, vm_message_count, exc->get_error_code(), FALSE); /* if that failed, just show the error number */ if (msg == 0) { sprintf(buf, "[no message: code %d]", exc->get_error_code()); msg = buf; } /* show the message */ printf("VM Error: %s\n", msg); /* note the failure status */ status = OSEXFAIL; } err_end; /* delete the file objects, which will close the files */ delete file_in; delete file_out; /* terminate the error mechanism */ err_terminate(); /* delete our host interface object */ delete hostifc; /* log any memory leaks */ t3_list_memory_blocks(0); /* exit with appropriate results */ return status; }
/* * Main program entrypoint. This is meant to be replaced by the actual * host application. * * Don't worry if you're using an OS that uses something other than the * standard Unix-style "main()" as its entrypoint - you're going to remove * this entire file anyway and replace it with your own, so you can use * whatever style of OS entrypoint is appropriate. The only reason this * is here is to serve as an example of how you invoke the T3 VM to get * your compiled T3 program running in the first place. * * Note also that you don't have to call the VM directly from your * entrypoint function. We call the VM from main() only because we have * nothing else useful to do. You can do as much as you want to set up * your program or even run interactively for a while before calling the * VM to run the T3 program. */ int main(int argc, char **argv) { int stat; int load_from_exe; const char *image_file_name; /* * For our purposes, we will assume that our argument vector contains * only one argument, which is the name of the program to run. If the * arguments don't look right, terminate with an error. */ if (argc != 2) { printf("usage: t3core <program-name>\n"); exit(1); } /* * The image (.t3) file's name is given by the first argument. * * Some applications might want to bind the .t3 file directly into the * application's executable file (the .exe file on Windows, for * example) rather than loading a separate .t3 file. Fortunately, * this is easy. Two steps are required. * * 1. After building the application executable and compiling the T3 * program to yield a .t3 file, use the appropriate OS-specific TADS * tool to bind two together into a single executable file. On * DOS/Windows, this tool is 'maketrx32' - simply specify the name of * your application executable, the name of your .t3 file, and the * name of a new executable, and the tool will generate a new * executable that has both files bound together. * * 2. In our call to vm_run_image(), rather than passing the name of * a .t3 file to laod, we'd pass the name of the application * executable file itself (this is simply argv[0] with our unix-style * main(), but on other systems we might have to obtain this * information some other way), and we'd pass TRUE for the * 'load_from_exe' argument to vm_run_image(). This will make the VM * look for the .t3 file bound into the application executable using * an appropriate OS-specific mechanism. */ image_file_name = argv[1]; load_from_exe = FALSE; /* * Create the "host interface." For our purposes, we use the simple * "stdio" host interface implementation, which the T3 source code * provides for convenience. The host interface lets the main * application (the "host") communicate certain information about the * execution environment to the VM, and carries out certain tasks on * behalf of the VM; the purpose of this is to allow the VM adapt more * easily to different application environments by deferring certain * operations to the host application, so that the VM doesn't have to * make assumptions about the host environment that might not always * be true. * * Most real applications will not want to use the simple "stdio" host * interface implementation, because this standard implementation does * make lots of assumptions about the host environment that might not * always be true. That's why we have to create the object here - the * VM can't create it, because it can't know what kind of object we'd * want to use. * * If you do want to customize the host interface, you'll need to * implement a C++ class as a subclass of CVmHostIfc - see vmhost.h. * You'll need to provide an implementation for each method defined in * CVmHostIfc. * * Note that we use "new" to allocate this object, rather than * allocating it on the stack, because of a logistical detail: * CVmHostIfcStdio actually allocates some memory upon creating an * instance, which it frees when the CVmHostIfcStdio instance itself * is destroyed. If we allocated this instance on the stack, the * instance wouldn't be destroyed until this function returns. * However, we want to run a memory leak test (by calling * t3_list_memory_blocks(), below) before we return from the function. * If we allocated this object on the stack, it wouldn't be deleted * until after we return, and so the memory it allocates won't be * deleted until after we return, so our memory test would show those * blocks still allocated and warn of a memory leak. We deal with * this by explicitly allocating it with 'new' here so that we can * explicitly destroy it with 'delete' before running the memory * check. */ CVmHostIfc *hostifc = new CVmHostIfcStdio(argv[0]); /* * Create the "client interface" object. This is similar in purpose * to the host interface; this is defined as a separate interface * because it provides functionality that is somewhat orthogonal to * the host interface, so in some cases we might want to mix and match * different pairs of client and host interface implementations. For * example, HTML TADS uses its own custom host interface, and most * character-mode TADS interpreters use the "stdio" host interface, * but both types of interpreters use the same "console" client * interface implementation. * * There's only one standard system client interface implementation, * which is based on the system "console." The console is the TADS * output formatter subsystem. We do NOT use this standard * implementation, because we don't want to depend on the console * layer: the whole point of this core VM configuration is to provide * a version of the VM without any UI dependencies, and including the * console formatter introduces all kinds of dependencies. * * So, we define our own custom implementation of the client * interface. See the definition earlier in the file. */ MyClientIfc clientifc; /* * Load and run the image file. This is how we run the T3 program: * this loads the program, sets everything up in the VM, and executes * the program. This call doesn't return until the program terminates. * * If your application runs on an event-oriented operating system, * such as Windows or Macintosh, you might be wondering at this point * exactly where you're supposed to put your message loop if the T3 * program is going to hog the CPU from now until the program * terminates. There are several approaches you can use: * * 1. You can use a separate thread to run the T3 program, and run * the UI event loop in the main thread. To do this, rather than * calling the VM directly here, you'd intead spawn another thread, * and let that thread call the VM. So, the VM would run in that new * thread, leaving the main thread free to proces UI events. This * would require proper synchronization any time one of your intrinsic * functions needs to access UI features, but otherwise it would be * pretty simple, because the VM is otherwise fairly self-contained. * * 2. You can write the event loop in the T3 program itself (i.e., in * the interpreted code running under the VM). You would have to * write an intrinsic function to retrieve and dispatch events. This * approach would probably be a lot of work, because it would mean * that you'd have to provide access to a fair chunk of your OS's GUI * API in your intrinsic function set or sets. In all likelihood, * you've already implemented most or all of your UI in the C++ part * of your application, and you won't have any desire to provide broad * access to the low-level OS GUI API directly to the T3 program, so * this option is probably not suitable in most cases. * * 3. You can do what HTML TADS does, which is run everything in one * thread, and run *recursive* event loops in the intrinsic functions. * In HTML TADS, the T3 program runs happily along, oblivious to the * UI, until it wants to read some text from the keyboard or display * some text on the console, at which point it has to call an * intrinsic function. The intrinsic makes the appropriate OS-level * API calls to display the text or whatever. If the intrinsic's * purpose is to read some input from the user, HTML TADS runs a * recursive event loop to allow the user to interact with the * program. The recursive event loop monitors a flag, controlled by * the intrinsic, that indicates when the recursive loop is done. For * example, if the intrinsic's purpose is to wait for a keystroke from * the user, the intrinsic tells the main window that it's waiting for * a key, then calls the recursive event loop; the event loop simply * reads and dispatches events as long as the "done" flag is false; * and the main window, when it receives a keystroke event, notices * that it's waiting for a key, so it stashes the keystroke for * retrieval by the intrinsic and sets the "done" flag to true; the * recursive event loop notices this and returns to the intrinsic, * which finds the keystroke it wanted stashed away, and returns. */ stat = vm_run_image(&clientifc, image_file_name, hostifc, 0, 0, 0, FALSE, 0, 0, load_from_exe, FALSE, 0, 0, 0, 0); /* we're done with the host interface object, so delete it */ delete hostifc; /* * Show any unfreed memory. This is purely for debugging purposes * during development of T3 itself; most real applications that link * in T3 won't want to bother with this, unless they're suspicious * that T3 is leaking memory on them and want to check it. */ t3_list_memory_blocks(0); /* terminate with the status code from the VM */ exit(stat); }
/* * Main program entrypoint */ int main(int argc, char **argv) { int curarg; int create; int recurse = TRUE; const char *image_fname; CRcResList *res_list = 0; rcmain_res_op_mode_t op_mode; int exit_stat; int err; MyHostIfc hostifc; char image_buf[OSFNMAX]; /* assume we will operate on an existing file */ create = FALSE; /* start out in add-recursive mode */ op_mode = RCMAIN_RES_OP_MODE_ADD; /* scan options */ for (curarg = 1 ; curarg < argc && argv[curarg][0] == '-' ; ++curarg) { switch(argv[curarg][1]) { case 'c': if (strcmp(argv[curarg], "-create") == 0) create = TRUE; else goto bad_option; break; default: bad_option: /* invalid option - skip all arguments so we go to usage */ curarg = argc; break; } } /* if there's nothing left, show usage */ if (curarg >= argc) { show_usage: /* display usage */ printf("usage: t3res [options] <image-file> [operations]\n" "Options:\n" " -create - create a new resource file\n" "Operations:\n" " -add - add the following resource files (default)\n" " -recurse - recursive - include files in " "subdirectories (default)\n" " -norecurse - do not include files in subdirectories\n" " <file> - add the file\n" " <dir> - add files in the directory\n" " <file>=<res> - add file, using <res> as resource name\n" "\n" "-add is assumed if no conflicting option is specified.\n" "If no resource name is explicitly provided for a file, " "the resource is named\n" "by converting the filename to a URL-style resource name.\n"); /* give up */ exit_stat = OSEXFAIL; goto done; } /* get the image filename */ image_fname = argv[curarg]; /* create our resource list */ res_list = new CRcResList(); /* parse the operations list */ for (++curarg ; curarg < argc ; ++curarg) { /* check for an option */ if (argv[curarg][0] == '-') { /* see what we have */ if (strcmp(argv[curarg], "-add")) { /* set 'add' mode */ op_mode = RCMAIN_RES_OP_MODE_ADD; } else if (strcmp(argv[curarg], "-recurse")) { /* set recursive mode */ recurse = TRUE; } else if (strcmp(argv[curarg], "-norecurse")) { /* set non-recursive mode */ recurse = FALSE; } else { /* invalid option */ goto show_usage; } } else { char *p; char *alias; /* check for an alias */ for (p = argv[curarg] ; *p != '\0' && *p != '=' ; ++p) ; if (*p == '=') { /* * overwrite the '=' with a null byte so that the * filename ends here */ *p = '\0'; /* the alias starts after the '=' */ alias = p + 1; } else { /* there's no alias */ alias = 0; } /* it's a file - add the file to the operations list */ res_list->add_file(argv[curarg], alias, recurse); } } /* * if we're not creating, and the image doesn't exist, try adding * the default image file extension */ if (!create && osfacc(image_fname)) { strcpy(image_buf, image_fname); os_defext(image_buf, "t3"); /* formerly "t3x" */ image_fname = image_buf; } /* we've parsed the arguments - go apply the operations list */ err = CResCompMain::add_resources(image_fname, res_list, &hostifc, create, OSFTT3IMG, FALSE); /* set the appropriate exit status */ exit_stat = (err ? OSEXFAIL : OSEXSUCC); done: /* delete the resource list if we created one */ if (res_list != 0) delete res_list; /* show any unfreed memory (if we're in a debug build) */ t3_list_memory_blocks(0); /* exit with current status */ return exit_stat; }