/* * Try loading a resource from the executable file */ osfildef *CResLoader::open_exe_res(const char *respath, const char *restype) { osfildef *exe_fp; /* * if we don't have an executable filename stored, or we don't have an * executable resource type ID, we can't load the resource */ if (exe_filename_ == 0 || restype == 0) return 0; /* find the executable file's resources */ exe_fp = os_exeseek(exe_filename_, restype); /* if we found something, try loading from that file */ if (exe_fp != 0) { CVmImageLoaderMres_resload res_ifc(respath); /* try loading the resources */ CVmImageLoader:: load_resources_from_fp(exe_fp, exe_filename_, &res_ifc); /* check to see if we found it */ if (res_ifc.found_resource()) { /* check the type */ if (res_ifc.get_link_fname() != 0) { /* * it's a linked local file - close the exe file and open * the local file instead */ osfcls(exe_fp); exe_fp = osfoprb(res_ifc.get_link_fname(), OSFOPRB); } else { /* we got an exe resource - seek to the starting byte */ osfseek(exe_fp, res_ifc.get_resource_seek(), OSFSK_SET); } } else { /* didn't find it - close and forget the executable file */ osfcls(exe_fp); exe_fp = 0; } } /* return the executable file pointer, if we found the resource */ return exe_fp; }
/* * Try loading a resource from a resource library */ osfildef *CResLoader::open_lib_res(const char *libfile, const char *respath) { osfildef *fp; /* try opening the file */ fp = osfoprb(libfile, OSFTT3IMG); /* if we couldn't open the file, we can't load the resource */ if (fp == 0) return 0; /* set up a resource finder for our resource */ CVmImageLoaderMres_resload res_ifc(respath); /* load the file, so that we can try finding the resource */ CVmImageLoader::load_resources_from_fp(fp, libfile, &res_ifc); /* check to see if we found it */ if (res_ifc.found_resource()) { /* we got it - check the type */ if (res_ifc.get_link_fname() != 0) { /* * linked local file - close the library file and open the * local file instead */ osfcls(fp); fp = osfoprb(res_ifc.get_link_fname(), OSFOPRB); } else { /* embedded resource - seek to the first byte */ osfseek(fp, res_ifc.get_resource_seek(), OSFSK_SET); } /* return the library file handle */ return fp; } else { /* didn't find the resource - close the library */ osfcls(fp); /* tell the caller we didn't find the resource */ return 0; } }
int CVmNetFile::can_write(VMG_ const char *fname, int sfid) { /* note whether the file already exists */ int existed = !osfacc(fname); /* try opening it (keeping existing contents) or creating it */ osfildef *fp = osfoprwb(fname, OSFTBIN); if (fp != 0) { /* successfully opened it - close it */ osfcls(fp); /* if it didn't already exist, delete it */ if (!existed) osfdel(fname); /* success */ return TRUE; } else { /* couldn't open the file */ return FALSE; } }
/* * Deletion */ CTcSrcFile::~CTcSrcFile() { /* close my source file */ if (fp_ != 0) osfcls(fp_); /* release my character mapper */ if (mapper_ != 0) mapper_->release_ref(); }
/* * Load a Unicode mapping file */ static void load_unicode_file(char *filename, unicode_map_t map[256], char *infile, int linenum) { osfildef *fp; int linenum_u; /* open the unicode file */ fp = osfoprs(filename, OSFTTEXT); if (fp == 0) { printf("%s: line %d: unable to open unicode mapping file \"%s\"\n", infile, linenum, filename); return; } /* read it */ for (linenum_u = 1 ;; ++linenum_u) { char buf[256]; char *p; unsigned int n1; unsigned int n2; /* read the next line */ if (osfgets(buf, sizeof(buf), fp) == 0) break; /* skip leading spaces */ for (p = buf ; isspace(*p) ; ++p) ; /* if it's blank or starts with a comment, ignore it */ if (*p == 0x1a || *p == '#' || *p == '\n' || *p == '\r' || *p == '\0') continue; /* read the first number - this is the native character value */ if (read_number(&n1, &p, filename, linenum_u, TRUE)) break; /* read the second number */ if (read_number(&n2, &p, filename, linenum_u, FALSE)) break; /* set the association */ map[n1].unicode_val = n2; } /* done with the file */ osfcls(fp); }
static void copybytes(osfildef *fpin, osfildef *fpout, ulong siz) { uint cursiz; /* copy bytes until we run out */ while (siz != 0) { /* we can copy up to one full buffer at a time */ cursiz = (siz > sizeof(copybuf) ? sizeof(copybuf) : siz); /* deduct the amount we're copying from the total */ siz -= cursiz; /* read from input, copy to output */ if (osfrb(fpin, copybuf, cursiz) || osfwb(fpout, copybuf, cursiz)) { /* error - close files and display an error */ osfcls(fpin); osfcls(fpout); errexit("error copying resource", 1); } } }
/* * Given the name of a file, determine the engine (TADS 2 or TADS 3) for * which the file was compiled, based on the file's signature. Returns * one of the VM_GGT_xxx codes. */ static int vm_get_game_type_for_file(const char *filename) { osfildef *fp; char buf[16]; int ret; /* try opening the filename exactly as given */ fp = osfoprb(filename, OSFTBIN); /* if the file doesn't exist, tell the caller */ if (fp == 0) return VM_GGT_NOT_FOUND; /* read the first few bytes of the file, where the signature resides */ if (osfrb(fp, buf, 16)) { /* * error reading the file - any valid game file is going to be at * least long enough to hold the number of bytes we asked for, so * it must not be a valid file */ ret = VM_GGT_INVALID; } else { /* check the signature we read against the known signatures */ if (memcmp(buf, "TADS2 bin\012\015\032", 12) == 0) ret = VM_GGT_TADS2; else if (memcmp(buf, "T3-image\015\012\032", 11) == 0) ret = VM_GGT_TADS3; else ret = VM_GGT_INVALID; } /* close the file */ osfcls(fp); /* return the version identifier */ return ret; }
/* write game to binary file */ void fiowrt(mcmcxdef *mctx, voccxdef *vctx, tokcxdef *tokctx, tokthdef *tab, uchar *fmts, uint fmtl, char *fname, uint flags, objnum preinit, int extc, uint prpcnt, char *filever) { osfildef *fp; /* open the file */ if (!(fp = osfoprwtb(fname, OSFTGAME))) errsig(vctx->voccxerr, ERR_OPWGAM); ERRBEGIN(vctx->voccxerr) /* write the file */ fiowrt1(mctx, vctx, tokctx, tab, fmts, fmtl, fp, flags, preinit, extc, prpcnt, filever); os_settype(fname, OSFTGAME); ERRCLEAN(vctx->voccxerr) /* clean up by closing the file */ osfcls(fp); ERRENDCLN(vctx->voccxerr) }
/* * Initialize the error subsystem for the compiler */ void CTcMain::tc_err_init(size_t param_stack_size, CResLoader *res_loader) { /* initialize the error stack */ err_init(1024); /* if this is the first initializer, set things up */ if (err_refs_ == 0) { /* if we haven't loaded external compiler messages, load them */ if (!err_no_extern_messages_ && tc_messages == &tc_messages_english[0]) { osfildef *fp; /* try finding a message file */ fp = res_loader->open_res_file("t3make.msg", 0, "XMSG"); if (fp != 0) { /* try loading it */ err_load_message_file(fp, &tc_messages, &tc_message_count, &tc_messages_english[0], tc_message_count_english); /* done with the file */ osfcls(fp); } else { /* note the failure, so we don't try again */ err_no_extern_messages_ = FALSE; } } } /* count the reference depth */ ++err_refs_; }
/* * Find a resource in a file, given the filename. * * Fills in the resource information structure with the seek offset and * size of the resource in the file and returns true if the resource is * found; returns false if a resource with the given name doesn't exist in * the file. */ int tads_find_resource(const char *fname, const char *resname, tads_resinfo *info) { osfildef *fp; int found; /* open the file */ if ((fp = osfoprb(fname, OSFTGAME)) == 0 && (fp = osfoprb(fname, OSFTT3IMG)) == 0) { /* we couldn't open the file, so there's no resource to be found */ return FALSE; } /* find the resource in the file */ found = tads_find_resource_fp(fp, resname, info); /* we're done with the file - close it */ osfcls(fp); /* return our found or not-found indication */ return found; }
/* * Parse a game file and retrieve game information */ int CTadsGameInfo::read_from_file(const char *fname) { /* open the file */ osfildef *fp; if ((fp = osfoprb(fname, OSFTGAME)) == 0 && (fp = osfoprb(fname, OSFTT3IMG)) == 0) { /* * we can't open the file, so we obviously can't parse it to find * game information */ return 0; } /* parse the file and find the game information */ int ret = read_from_fp(fp); /* we're done with the file - close it */ osfcls(fp); /* return the results from the parser */ return ret; }
/* * Main Entrypoint for command-line invocations. For simplicity, a * normal C main() or equivalent entrypoint can invoke this routine * directly, using the usual argc/argv conventions. * * Returns a status code suitable for use with exit(): OSEXSUCC if we * successfully loaded and ran an executable, OSEXFAIL on failure. If * an error occurs, we'll fill in 'errbuf' with a message describing the * problem. */ int vm_run_image_main(CVmMainClientIfc *clientifc, const char *executable_name, int argc, char **argv, int defext, int test_mode, CVmHostIfc *hostifc) { int curarg; char image_file_name[OSFNMAX]; int stat; const char *script_file; int script_quiet = TRUE; const char *log_file; const char *cmd_log_file; const char *res_dir; int load_from_exe; int show_banner; int found_image; int hide_usage; int usage_err; const char *charset; const char *log_charset; char *saved_state; /* we haven't found an image file yet */ found_image = FALSE; /* presume we'll show usage on error */ hide_usage = FALSE; /* presume there will be no usage error */ usage_err = FALSE; /* presume we won't have any console input/output files */ script_file = 0; log_file = 0; cmd_log_file = 0; /* presume we'll use the default OS character sets */ charset = 0; log_charset = 0; /* presume we won't show the banner */ show_banner = FALSE; /* presume we won't load from the .exe file */ load_from_exe = FALSE; /* check to see if we can load from the .exe file */ { osfildef *fp; /* look for an image file attached to the executable */ fp = os_exeseek(argv[0], "TGAM"); if (fp != 0) { /* close the file */ osfcls(fp); /* note that we want to load from the executable */ load_from_exe = TRUE; } } /* presume we won't restore a saved state file */ saved_state = 0; /* presume we won't have a resource directory specified */ res_dir = 0; /* scan options */ for (curarg = 1 ; curarg < argc && argv[curarg][0] == '-' ; ++curarg) { /* * if the argument is just '-', it means we're explicitly leaving * the image filename blank and skipping to the arguments to the VM * program itself */ if (argv[curarg][1] == '\0') break; /* check the argument */ switch(argv[curarg][1]) { case 'b': if (strcmp(argv[curarg], "-banner") == 0) { /* make a note to show the banner */ show_banner = TRUE; } else goto opt_error; break; case 'c': if (strcmp(argv[curarg], "-cs") == 0) { ++curarg; if (curarg < argc) charset = argv[curarg]; else goto opt_error; } else if (strcmp(argv[curarg], "-csl") == 0) { ++curarg; if (curarg < argc) log_charset = argv[curarg]; else goto opt_error; } else goto opt_error; break; case 'n': if (strcmp(argv[curarg], "-nobanner") == 0) { /* make a note not to show the banner */ show_banner = FALSE; } else goto opt_error; break; case 's': /* file safety level - check the range */ if (argv[curarg][2] < '0' || argv[curarg][2] > '4' || argv[curarg][3] != '\0') { /* invalid level */ goto opt_error; } else { /* set the level in the host application */ hostifc->set_io_safety(argv[curarg][2] - '0'); } break; case 'i': case 'I': /* * read from a script file (little 'i' reads silently, big 'I' * echoes the log as it goes) - the next argument, or the * remainder of this argument, is the filename */ script_quiet = (argv[curarg][1] == 'i'); script_file = get_opt_arg(argc, argv, &curarg, 2); if (script_file == 0) goto opt_error; break; case 'l': /* log output to file */ log_file = get_opt_arg(argc, argv, &curarg, 2); if (log_file == 0) goto opt_error; break; case 'o': /* log commands to file */ cmd_log_file = get_opt_arg(argc, argv, &curarg, 2); if (cmd_log_file == 0) goto opt_error; break; case 'p': /* check what follows */ if (strcmp(argv[curarg], "-plain") == 0) { /* tell the client to set plain ASCII mode */ clientifc->set_plain_mode(); break; } else goto opt_error; break; case 'r': /* get the name of the saved state file to restore */ saved_state = get_opt_arg(argc, argv, &curarg, 2); if (saved_state == 0) goto opt_error; break; case 'R': /* note the resource root directory */ res_dir = get_opt_arg(argc, argv, &curarg, 2); if (res_dir == 0) goto opt_error; break; default: opt_error: /* discard remaining arguments */ curarg = argc; /* note the error */ usage_err = TRUE; break; } } /* * If there was no usage error so far, but we don't have an image * filename argument, try to find the image file some other way. If we * found an image file embedded in the executable, don't even bother * looking for an image-file argument - we can only run the embedded * image in this case. */ if (usage_err) { /* there was a usage error - don't bother looking for an image file */ } else if (!load_from_exe && curarg + 1 <= argc && strcmp(argv[curarg], "-") != 0) { /* the last argument is the image file name */ strcpy(image_file_name, argv[curarg]); found_image = TRUE; /* * If the given filename exists, use it as-is; otherwise, if * we're allowed to add an extension, try applying a default * extension of "t3" (formerly "t3x") to the given name. */ if (defext && osfacc(image_file_name)) { /* the given name doesn't exist - try a default extension */ os_defext(image_file_name, "t3"); /* formerly "t3x" */ } } else { /* * if we're loading from the executable, try using the executable * filename as the image file */ if (load_from_exe && os_get_exe_filename(image_file_name, sizeof(image_file_name), argv[0])) found_image = TRUE; /* * If we still haven't found an image file, try to get the image * file from the saved state file, if one was specified. Don't * attempt this if we're loading the image from the executable, as * we don't want to allow running a different image file in that * case. */ if (!load_from_exe && !found_image && saved_state != 0) { osfildef *save_fp; /* open the saved state file */ save_fp = osfoprb(saved_state, OSFTT3SAV); if (save_fp != 0) { /* get the name of the image file */ if (CVmSaveFile::restore_get_image( save_fp, image_file_name, sizeof(image_file_name)) == 0) { /* we successfully obtained the filename */ found_image = TRUE; } /* close the file */ osfcls(save_fp); } } /* * if we haven't found the image, and the host system provides a * way of asking the user for a filename, try that */ if (!load_from_exe && !found_image) { /* ask the host system for a game name */ switch (hostifc->get_image_name(image_file_name, sizeof(image_file_name))) { case VMHOST_GIN_IGNORED: /* no effect - we have no new information */ break; case VMHOST_GIN_CANCEL: /* * the user cancelled the dialog - we don't have a * filename, but we also don't want to show usage, since * the user chose not to proceed */ hide_usage = TRUE; break; case VMHOST_GIN_ERROR: /* * an error occurred showing the dialog - there's not * much we can do except show the usage message */ break; case VMHOST_GIN_SUCCESS: /* that was successful - we have an image file now */ found_image = TRUE; break; } } } /* * if we don't have an image file name by this point, we can't * proceed - show the usage message and terminate */ if (usage_err || !found_image) { char buf[OSFNMAX + 1024]; /* show the usage message if allowed */ if (load_from_exe && !usage_err) { sprintf(buf, "An error occurred loading the T3 VM program from " "the embedded data file. This application executable " "file might be corrupted.\n"); /* display the message */ clientifc->display_error(0, buf, FALSE); } else if (!hide_usage) { /* build the usage message */ sprintf(buf, "%s\n" "usage: %s [options] %sarguments]\n" "options:\n" " -banner - show the version/copyright banner\n" " -cs xxx - use character set 'xxx' for keyboard " "and display\n" " -csl xxx - use character set 'xxx' for log files\n" " -i file - read command input from file (quiet mode)\n" " -I file - read command input from file (echo mode)\n" " -l file - log all console input/output to file\n" " -o file - log console input to file\n" " -plain - run in plain mode (no cursor positioning, " "colors, etc.)\n" " -r file - restore saved state from file\n" " -R dir - set directory for external resources\n" " -s# - set I/O safety level (# in range 0 to 4 - 0 " "is the least\n" " restrictive, 4 allows no file I/O at all)\n" "\n" "If provided, the optional extra arguments are passed " "to the program's\n" "main entrypoint.\n", T3VM_BANNER_STRING, executable_name, load_from_exe ? "[- " : "<image-file-name> ["); /* display the message */ clientifc->display_error(0, buf, FALSE); } /* return failure */ return OSEXFAIL; } /* * if we're in test mode, replace the first argument to the program * with its root name, so that we don't include any path information * in the argument list */ if (test_mode && curarg <= argc && argv[curarg] != 0) argv[curarg] = os_get_root_name(argv[curarg]); /* run the program */ stat = vm_run_image(clientifc, image_file_name, hostifc, argv + curarg, argc - curarg, script_file, script_quiet, log_file, cmd_log_file, load_from_exe, show_banner, charset, log_charset, saved_state, res_dir); /* return the status code */ return stat; }
/* process an operation */ static void procop(osfildef *fpout, opdef *op, ulong *first_xfcn) { osfildef *fpin; char buf[128]; uint fsiz; ulong sizpos; ulong endpos; /* remember location of first resource if necessary */ if (fpout && *first_xfcn == 0) *first_xfcn = osfpos(fpout); fpin = osfoprb(op->opfile, OSFTGAME); if (fpin == 0) { rscptf("%s: ", op->opfile); errexit("unable to open file", 1); } /* get file size */ osfseek(fpin, 0L, OSFSK_END); fsiz = (uint)osfpos(fpin); osfseek(fpin, 0L, OSFSK_SET); /* set up the resource type part of the header */ switch(op->oprestype) { case RESTYPE_XFCN: buf[0] = 4; memcpy(buf + 1, "XFCN", 4); break; case RESTYPE_HTML: buf[0] = 4; memcpy(buf + 1, "HTML", 3); break; } /* set up the rest of the header */ if (osfwb(fpout, buf, buf[0] + 1)) errexit("error writing resource", 1); sizpos = osfpos(fpout); /* remember where size field goes */ oswp4(buf, 0); if (osfwb(fpout, buf, 4)) errexit("error writing resource", 1); /* set up the header */ oswp2(buf, fsiz); buf[2] = strlen(op->opres); strcpy(buf + 3, op->opres); if (osfwb(fpout, buf, (uint)(buf[2] + 3))) errexit("error writing resource", 1); /* copy the resource to the output */ copybytes(fpin, fpout, fsiz); /* write end position in the resource header */ endpos = osfpos(fpout); oswp4(buf, endpos); osfseek(fpout, sizpos, OSFSK_SET); if (osfwb(fpout, buf, 4)) errexit("error writing resource", 1); /* seek back to the end of the resource in the output file */ osfseek(fpout, endpos, OSFSK_SET); /* done with the input file */ osfcls(fpin); }
/* * Main entrypoint */ int main(int argc, char **argv) { int curarg; unsigned char input_map[256]; unsigned char output_map[256]; unsigned char input_map_set[256]; unsigned char output_map_set[256]; unsigned char *p; int i; osfildef *fp; char *infile; char *outfile; int linenum; static char sig[] = CMAP_SIG_S100; int strict_mode = FALSE; char id[5]; char ldesc[CMAP_LDESC_MAX_LEN + 1]; size_t len; unsigned char lenbuf[2]; char *sys_info; entity_map_t *entity_first; entity_map_t *entity_last; /* no parameters have been specified yet */ memset(id, 0, sizeof(id)); ldesc[0] = '\0'; sys_info = 0; /* we have no entities in our entity mapping list yet */ entity_first = entity_last = 0; /* scan options */ for (curarg = 1 ; curarg < argc && argv[curarg][0] == '-' ; ++curarg) { if (!stricmp(argv[curarg], "-strict")) { /* they want extra warnings */ strict_mode = TRUE; } else { /* consume all remaining options so we get a usage message */ curarg = argc; break; } } /* check for required arguments */ if (curarg + 1 >= argc) { printf("usage: mkchrtab [options] <source> <dest>\n" " <source> is the input file\n" " <dest> is the output file\n" "Options:\n" " -strict warn if any codes 128-255 are unassigned\n"); #if 0 /* * The information about what goes in the file made the message way too * long, so this has been removed. Users will want to the documentation * instead of the usage message for information this detailed, so it * didn't seem useful to keep it in here. */ printf("\n" "The source file contains one entry per line, as follows:\n" "\n" "Set the internal character set identifier, which can be up " "to four letters long\n" "(note that the mapping file MUST contain an ID entry):\n" " ID = id\n" "\n"); printf("Set the internal character set's full display name:\n" " LDESC = full name of character set\n" "\n" "Set system-dependent extra information (the meaning varies " "by system):\n" " EXTRA_SYSTEM_INFO = info-string\n" "\n" "Set the native default character:\n" " NATIVE_DEFAULT = charval\n" "Set the internal default character:\n" " INTERNAL_DEFAULT = charval\n"); printf("Load Unicode mapping files:\n" " UNICODE NATIVE=native-mapping INTERNAL=internal-mapping\n" "\n" "Reversibly map a native character code to an internal code:\n" " native <-> internal\n" "\n" "Map a native code to an internal code, and map the internal " "code back\nto a different native code:\n" " native -> internal -> native\n" "\n" "Map a native code to an internal code, where the internal " "code is already\nmapped to a native code by a previous line:\n" " native -> internal\n" "\n"); printf("Map an internal code to a native code, where the native " "code is already\nmapped to an internal code by a previous " "line:\n" " native <- internal\n" "\n" "Map an HTML entity name to a native code or string:\n" " &entity = internal-code [internal-code ...]\n" "\n" "Numbers can be specified in decimal (default), octal (by " "prefixing the number\nwith a zero, as in '037'), or hex (by " "prefixing the number with '0x', as in\n'0xb2'). A number " "can also be entered as a character by enclosing the\n" "character in single quotes.\n" "\n" "Blank lines and lines starting with a pound sign ('#') are " "ignored.\n"); #endif /* 0 */ os_term(OSEXFAIL); } /* get the input and output filenames */ infile = argv[curarg]; outfile = argv[curarg + 1]; /* * initialize the tables - by default, a character code in one * character set maps to the same code in the other character set */ for (p = input_map, i = 0 ; i < sizeof(input_map)/sizeof(input_map[0]) ; ++i, ++p) *p = (unsigned char)i; for (p = output_map, i = 0 ; i < sizeof(output_map)/sizeof(output_map[0]) ; ++i, ++p) *p = (unsigned char)i; /* * initialize the "set" flags all to false, since we haven't set any * of the values yet -- we'll use these flags to detect when the * user attempts to set the same value more than once, so that we * can issue a warning (multiple mappings are almost certainly in * error) */ for (i = 0 ; i < sizeof(input_map_set)/sizeof(input_map_set[0]) ; ++i) input_map_set[i] = output_map_set[i] = FALSE; /* open the input file */ fp = osfoprs(infile, OSFTTEXT); if (fp == 0) { printf("error: unable to open input file \"%s\"\n", infile); os_term(OSEXFAIL); } /* parse the input file */ for (linenum = 1 ; ; ++linenum) { char buf[256]; char *p; unsigned int n1, n2, n3; int set_input; int set_output; /* presume we're going to set both values */ set_input = set_output = TRUE; /* read the next line */ if (osfgets(buf, sizeof(buf), fp) == 0) break; /* scan off leading spaces */ for (p = buf ; isspace(*p) ; ++p) ; /* if this line is blank, or starts with a '#', ignore it */ if (*p == '\0' || *p == '\n' || *p == '\r' || *p == '#') continue; /* check for special directives */ if (isalpha(*p) || *p == '_') { char *sp; char *val; size_t vallen; size_t idlen; /* find the end of the directive name */ for (sp = p ; isalpha(*sp) || *sp == '_' ; ++sp) ; idlen = sp - p; /* find the equals sign, if present */ for (val = sp ; isspace(*val) ; ++val) ; if (*val == '=') { /* skip the '=' and any spaces that follow */ for (++val ; isspace(*val) ; ++val) ; /* find the end of the value */ for (sp = val ; *sp != '\n' && *sp != '\r' && *sp != '\0' ; ++sp) ; /* note its length */ vallen = sp - val; } else { /* there's no value */ val = 0; } /* see what we have */ if (id_matches(p, idlen, "id")) { /* this directive requires a value */ if (val == 0) goto val_required; /* ID's can never be more than four characters long */ if (vallen > 4) { printf("%s: line %d: ID too long - no more than four " "characters are allowed\n", infile, linenum); } else { /* remember the ID */ memcpy(id, val, vallen); id[vallen] = '\0'; } } else if (id_matches(p, idlen, "ldesc")) { /* this directive requires a value */ if (val == 0) goto val_required; /* make sure it fits */ if (vallen > sizeof(ldesc) - 1) { printf("%s: line %d: LDESC too long - no more than %u " "characters are allowed\n", infile, linenum, sizeof(ldesc) - 1); } else { /* remember the ldesc */ memcpy(ldesc, val, vallen); ldesc[vallen] = '\0'; } } else if (id_matches(p, idlen, "extra_system_info")) { /* this directive requires a value */ if (val == 0) goto val_required; /* allocate space for it */ sys_info = (char *)malloc(vallen + 1); memcpy(sys_info, val, vallen); sys_info[vallen] = '\0'; } else if (id_matches(p, idlen, "native_default")) { unsigned int nval; int i; /* this directive requires a value */ if (val == 0) goto val_required; /* parse the character value */ if (read_number(&nval, &val, infile, linenum, TRUE)) continue; /* apply the default */ for (i = 128 ; i < 256 ; ++i) { /* set the default only if we haven't mapped this one */ if (!output_map_set[i]) output_map[i] = nval; } } else if (id_matches(p, idlen, "internal_default")) { unsigned int nval; int i; /* this directive requires a value */ if (val == 0) goto val_required; /* parse the character value */ if (read_number(&nval, &val, infile, linenum, TRUE)) continue; /* apply the default */ for (i = 128 ; i < 256 ; ++i) { /* apply the default only if we haven't set this one */ if (!input_map_set[i]) input_map[i] = nval; } } else if (id_matches(p, idlen, "unicode")) { /* skip the 'unicode' string and any intervening spaces */ for (p += idlen ; isspace(*p) ; ++p) ; /* parse the unicode files */ parse_unicode_files(p, strlen(p), infile, linenum, input_map, input_map_set, output_map, output_map_set, &entity_first, &entity_last); } else { /* unknown directive */ printf("%s: line %d: invalid directive '%.*s'\n", infile, linenum, idlen, p); } /* done processing this line */ continue; /* come here if the directive needs a value and there isn't one */ val_required: printf("%s: line %d: '=' required with directive '%.*s'\n", infile, linenum, idlen, p); continue; } /* check for an entity name */ if (*p == '&') { entity_map_t *mapp; /* skip the '&' */ ++p; /* * parse the entity - if it succeeds, link the resulting * mapping entry into our list */ mapp = parse_entity(p, infile, linenum); if (mapp != 0) { if (entity_last == 0) entity_first = mapp; else entity_last->nxt = mapp; entity_last = mapp; } /* done */ continue; } /* read the first number */ if (read_number(&n1, &p, infile, linenum, TRUE)) continue; /* determine which operator we have */ if (*p == '<') { /* make sure it's "<->" or "<-" */ if (*(p+1) == '-' && *(p+2) != '>') { /* skip the operator */ p += 2; /* * This is a "from" translation - it only affects the * output mapping from the internal character set to the * native character set. Read the second number. There * is no third number, since we don't want to change the * input mapping. */ if (read_number(&n2, &p, infile, linenum, TRUE)) continue; /* * The forward translation is not affected; set only the * output translation. Note that the first number was * the output (native) value for the internal index in * the second number, so move the first value to n3. */ n3 = n1; set_input = FALSE; } else if (*(p+1) == '-' && *(p+2) == '>') { /* skip it */ p += 3; /* * this is a reversible translation, so we only need one * more number - the third number is implicitly the same * as the first */ n3 = n1; if (read_number(&n2, &p, infile, linenum, TRUE)) continue; } else { printf("%s: line %d: invalid operator - expected <->\n", infile, linenum); continue; } } else if (*p == '-') { /* make sure it's "->" */ if (*(p+1) != '>') { printf("%s: line %d: invalid operator - expected ->\n", infile, linenum); continue; } /* skip it */ p += 2; /* get the next number */ if (read_number(&n2, &p, infile, linenum, TRUE)) continue; /* * we may or may not have a third number - if we have * another -> operator, read the third number; if we don't, * the reverse translation is not affected by this entry */ if (*p == '-') { /* make sure it's "->" */ if (*(p+1) != '>') { printf("%s: line %d: invalid operator - expected ->\n", infile, linenum); continue; } /* skip it */ p += 2; /* read the third number */ if (read_number(&n3, &p, infile, linenum, TRUE)) continue; } else { /* * There's no third number - the reverse translation is * not affected by this line. */ set_output = FALSE; } } else { printf("%s: line %d: invalid operator - expected " "-> or <-> or <-\n", infile, linenum); continue; } /* make sure we're at the end of the line, and warn if not */ if (*p != '\0' && *p != '\n' && *p != '\r' && *p != '#') printf("%s: line %d: extra characters at end of line ignored\n", infile, linenum); /* set the input mapping, if necessary */ if (set_input) { /* warn the user if this value has already been set before */ if (input_map_set[n1]) printf("%s: line %d: warning - native character %u has " "already been\n mapped to internal value %u\n", infile, linenum, n1, input_map[n1]); /* set it */ input_map[n1] = n2; /* note that it's been set */ input_map_set[n1] = TRUE; } /* set the output mapping, if necessary */ if (set_output) { /* warn the user if this value has already been set before */ if (output_map_set[n2]) printf("%s: line %d: warning - internal character %u has " "already been\n mapped to native value %u\n", infile, linenum, n2, input_map[n2]); /* set it */ output_map[n2] = n3; /* note that it's been set */ output_map_set[n2] = TRUE; } } /* we're done with the input file */ osfcls(fp); /* * It's an error if we didn't get an ID or LDESC */ if (id[0] == '\0') { printf("Error: No ID was specified. An ID is required.\n"); os_term(OSEXFAIL); } else if (ldesc[0] == '\0') { printf("Error: No LDESC was specified. An LDESC is required.\n"); os_term(OSEXFAIL); } /* open the output file */ fp = osfopwb(outfile, OSFTCMAP); if (fp == 0) { printf("error: unable to open output file \"%s\"\n", outfile); os_term(OSEXFAIL); } /* write our signature */ if (osfwb(fp, sig, sizeof(sig))) printf("error writing signature to output file\n"); /* write the ID and LDESC */ len = strlen(ldesc) + 1; oswp2(lenbuf, len); if (osfwb(fp, id, 4) || osfwb(fp, lenbuf, 2) || osfwb(fp, ldesc, len)) printf("error writing ID information to output file\n"); /* write the mapping tables */ if (osfwb(fp, input_map, sizeof(input_map)) || osfwb(fp, output_map, sizeof(output_map))) printf("error writing character maps to output file\n"); /* write the extra system information if present */ if (sys_info != 0) { /* write it out, with the "SYSI" flag so we know it's there */ len = strlen(sys_info) + 1; oswp2(lenbuf, len); if (osfwb(fp, "SYSI", 4) || osfwb(fp, lenbuf, 2) || osfwb(fp, sys_info, len)) printf("error writing EXTRA_SYSTEM_INFO to output file\n"); /* we're done with the allocated buffer now */ free(sys_info); } /* * Write the entity mapping list, if we have any entities */ if (entity_first != 0) { entity_map_t *entp; entity_map_t *next_entity; char lenbuf[2]; char cvalbuf[2]; /* write out the entity list header */ if (osfwb(fp, "ENTY", 4)) printf("error writing entity marker to output file\n"); /* run through the list, writing out each entry */ for (entp = entity_first ; entp != 0 ; entp = next_entity) { /* write out this entity */ oswp2(lenbuf, entp->exp_len); oswp2(cvalbuf, entp->html_char); if (osfwb(fp, lenbuf, 2) || osfwb(fp, cvalbuf, 2) || osfwb(fp, entp->expansion, entp->exp_len)) { printf("error writing entity mapping to output file\n"); break; } /* remember the next entity before we delete this one */ next_entity = entp->nxt; /* we're done with this entity, so we can delete it now */ free(entp); } /* * write out the end marker, which is just a length marker and * character marker of zero */ oswp2(lenbuf, 0); oswp2(cvalbuf, 0); if (osfwb(fp, lenbuf, 2) || osfwb(fp, cvalbuf, 2)) printf("error writing entity list end marker to output file\n"); } /* write the end-of-file marker */ if (osfwb(fp, "$EOF", 4)) printf("error writing end-of-file marker to output file\n"); /* done with the output file */ osfcls(fp); /* if we're in strict mode, check for unassigned mappings */ if (strict_mode) { int in_cnt, out_cnt; int cnt; /* count unassigned characters */ for (i = 128, in_cnt = out_cnt = 0 ; i < 256 ; ++i) { if (!input_map_set[i]) ++in_cnt; if (!output_map_set[i]) ++out_cnt; } /* if we have any unassigned native characters, list them */ if (in_cnt != 0) { printf("\nUnassigned native -> internal mappings:\n "); for (i = 128, cnt = 0 ; i < 256 ; ++i) { if (!input_map_set[i]) { /* go to a new line if necessary */ if (cnt >= 16) { printf("\n "); cnt = 0; } /* display this item */ printf("%3d ", i); ++cnt; } } printf("\n"); } /* list unassigned internal characters */ if (out_cnt != 0) { printf("\nUnassigned internal -> native mappings:\n "); for (i = 128, cnt = 0 ; i < 256 ; ++i) { if (!output_map_set[i]) { /* go to a new line if necessary */ if (cnt >= 16) { printf("\n "); cnt = 0; } /* display this item */ printf("%3d ", i); ++cnt; } } printf("\n"); } } /* success */ os_term(OSEXSUCC); return OSEXSUCC; }
/* * Process an HTML resource list. If 'old_htmlres' is true, it * indicates that the input file is pointing to an old resource map; * otherwise, we need to construct a brand new one. */ static opdef *prochtmlres(osfildef *fp, osfildef *fpout, opdef *oplist, int *copyrsc, int *showed_heading, int old_htmlres) { opdef *op; opdef *add_list = 0; opdef *prev_op; opdef *next_op; int found; char buf[512]; ulong in_entry_cnt; ulong in_table_siz; ulong out_hdr_siz; ulong out_hdr_pos; ulong i; ulong out_res_cnt; ulong out_total_name_len; ulong rem; struct idx_t **in_list = 0, *out_list = 0, **p, *cur; ulong in_res_base, out_res_base; ulong out_endpos; /* * Scan the oplist for an HTML resource. If there aren't any, we * don't need to modify the HTMLRES list, so tell the caller to copy * this resource unchanged. */ for (op = oplist, found = FALSE ; op != 0 ; op = op->opnxt) { /* if this is an HTML resource, note it and stop looking */ if (op->oprestype == RESTYPE_HTML) { found = TRUE; break; } } /* * If we didn't find any operations on this resource, and we're not * simply listing resources or we don't have an old resource to * list, tell the caller to copy it unchanged. */ if (!found && (fpout != 0 || !old_htmlres)) { *copyrsc = TRUE; return oplist; } /* we'll be handling the resource - tell the caller not to copy it */ *copyrsc = FALSE; /* if there's an old HTMLRES resource, read it */ if (old_htmlres) { /* read the index entry count and size */ if (osfrb(fp, buf, 8)) listexit(fp, "unable to read HTMLRES header"); in_entry_cnt = osrp4(buf); in_table_siz = osrp4(buf + 4); /* allocate space for pointers to all of the entries */ if (in_entry_cnt != 0) { in_list = (struct idx_t **) malloc(in_entry_cnt * sizeof(struct idx_t *)); if (in_list == 0) listexit(fp, "unable to allocate space for HTMLRES entries"); } /* read the index table entries */ for (i = 0, p = in_list ; i < in_entry_cnt ; ++i, ++p) { ushort name_siz; ulong res_siz; ulong res_ofs; /* read the entry information */ if (osfrb(fp, buf, 10)) listexit(fp, "unable to read HTMLRES index table entry (prefix)"); /* get the resource size */ res_ofs = osrp4(buf); res_siz = osrp4(buf + 4); /* read the name */ name_siz = osrp2(buf + 8); if (name_siz > sizeof(buf)) listexit(fp, "name too large in HTMLRES index table entry"); if (osfrb(fp, buf, name_siz)) listexit(fp, "unable to read HTMLRES index table entry (name)"); /* build this entry */ *p = alloc_idx_entry(res_ofs, res_siz, buf, name_siz, 0, 0); } /* if we don't have an output file, list the HTMLRES contents */ if (fpout == 0) { /* display all of the entries */ for (i = 0, p = in_list ; i < in_entry_cnt ; ++i, ++p) show_list_item(showed_heading, "HTML", (*p)->siz, (*p)->nam, (*p)->namlen); /* there's no more processing to do */ goto done; } /* * The resources start at the end of the index table - note the * location of the end of the input table, since it's the base * address relative to which the resource offsets are stated. */ in_res_base = osfpos(fp); /* * Go through the resource table in the input file. Find each * one in the op list. If it's not in the op list, we'll copy * it to the output file. */ for (i = 0, p = in_list ; i < in_entry_cnt ; ++i, ++p) { int remove_res = FALSE; int add_res = FALSE; /* see if we can find this entry in the op list */ for (prev_op = 0, op = oplist ; op != 0 ; prev_op = op, op = op->opnxt) { /* if this one matches, note it */ if (op->oprestype == RESTYPE_HTML && strlen(op->opres) == (*p)->namlen && !memicmp(op->opres, (*p)->nam, (*p)->namlen)) { /* * if we're adding this resource (not replacing it), * warn that it's already in the file, and ignore * this op; if we're removing it or replacing it, * simply delete this entry from the input list so * it doesn't get copied to the output. */ if (!(op->opflag & OPFDEL)) { /* warn that the old one will stay */ rscptf("warning: HTML resource \"%s\" already " "present\n" " -- old version will be kept (use -replace " "to replace it)\n", op->opres); /* remove it from the processing list */ remove_res = TRUE; } else { /* we are deleting it; see if we're also adding it */ if (op->opflag & OPFADD) { /* * we're replacing this resource - take this * op out of the main list and put it into * the add list */ remove_res = TRUE; add_res = TRUE; /* note the addition */ show_op("replacing", op->opres, strlen(op->opres), op->oprestype); } else { /* note the deletion */ show_op("deleting", op->opres, strlen(op->opres), op->oprestype); /* just remove it */ remove_res = TRUE; } /* get rid of this item from the input list */ free(*p); *p = 0; } /* no need to look further in the operations list */ break; } } /* * If desired, remove this resource from the main list, and * add it into the list of resources to add. */ if (remove_res) { /* unlink it from the main list */ if (prev_op == 0) oplist = op->opnxt; else prev_op->opnxt = op->opnxt; /* if desired, add it to the additions list */ if (add_res) { /* we're adding it - put it in the additions list */ op->opnxt = add_list; add_list = op; } else { /* this item has been processed - delete it */ free(op); } } } } else { /* there are no entries in the input file */ in_entry_cnt = 0; in_table_siz = 0; } /* * Move all of the HTML resources marked as additions in the main * operations list into the additions list. */ for (prev_op = 0, op = oplist ; op != 0 ; op = next_op) { /* note the next op, in case we move this one to the other list */ next_op = op->opnxt; /* * if it's an HTML resource to be added, move it to the * additions list */ if (op->oprestype == RESTYPE_HTML && (op->opflag & OPFADD) != 0) { /* show what we're doing */ show_op("adding", op->opres, strlen(op->opres), op->oprestype); /* unlink it from the main list */ if (prev_op == 0) oplist = op->opnxt; else prev_op->opnxt = op->opnxt; /* add it to the additions list */ op->opnxt = add_list; add_list = op; /* * note that we don't want to advance the 'prev_op' pointer, * since we just removed this item - the previous item is * still the same as it was on the last iteration */ } else { /* * we're leaving this op in the original list - it's now the * previous op in the main list */ prev_op = op; } } /* * Figure out what we'll be putting in the HTMLRES list: we'll add * each surviving entry from the input file, plus all of the items * in the add list, plus all of the HTML items in the main list that * are marked for addition. */ out_res_cnt = 0; out_total_name_len = 0; /* count input file entries that we're going to copy */ for (i = 0, p = in_list ; i < in_entry_cnt ; ++i, ++p) { if (*p != 0) add_idx_entry(&out_list, &out_res_cnt, &out_total_name_len, 0, 0, (*p)->nam, (*p)->namlen, 0, *p); } /* * Count items in the additions list. Note that every HTML resource * marked for addition is in the additions list, since we moved all * such resources out of the main list and into the additions list * earlier. */ for (op = add_list ; op != 0 ; op = op->opnxt) add_idx_entry(&out_list, &out_res_cnt, &out_total_name_len, 0, 0, op->opres, (ushort)strlen(op->opres), op, 0); /* write the resource header */ if (osfwb(fpout, "\007HTMLRES\0\0\0\0", 12)) listexit(fp, "unable to write HTMLRES type header"); out_hdr_pos = osfpos(fpout); /* * Reserve space in the output file for the index table. We need * eight bytes for the index table prefix, then ten bytes per entry * plus the name sizes. */ out_hdr_siz = 8 + (10 * out_res_cnt) + out_total_name_len; /* write the index table prefix */ oswp4(buf, out_res_cnt); oswp4(buf + 4, out_hdr_siz); if (osfwb(fpout, buf, 8)) listexit(fp, "unable to write HTMLRES prefix"); /* * Reserve space for the headers. Don't actually write them yet, * since we don't know the actual locations and sizes of the * entries; for now, simply reserve the space, so that we can come * back here later and write the actual headers. Note that we * deduct the eight bytes we've already written from the amount of * filler to put in. */ for (rem = out_hdr_siz - 8 ; rem != 0 ; ) { ulong amt; /* write out a buffer full */ amt = (rem > sizeof(buf) ? sizeof(buf) : rem); if (osfwb(fpout, buf, amt)) listexit(fp, "unable to write HTMLRES header"); /* deduct the amount we wrote from the remainder */ rem -= amt; } /* * note the current position in the output file - this is the base * address of the resources */ out_res_base = osfpos(fpout); /* * Write the resources. */ for (cur = out_list ; cur != 0 ; cur = cur->nxt) { /* * note the current file position as an offset from the resource * base in the output file - this is the offset that we need to * store in the index entry for this object */ cur->ofs = osfpos(fpout) - out_res_base; /* * Copy the resource to the output. If it comes from the input * file, copy from there, otherwise go out and find the external * file and copy its contents. */ if (cur->src_op != 0) { osfildef *fpext; ulong fsiz; /* it comes from an external file - open the file */ fpext = osfoprb(cur->src_op->opfile, OSFTGAME); if (fpext == 0) { rscptf("%s: ", cur->src_op->opfile); errexit("unable to open file", 1); } /* figure the size of the file */ osfseek(fpext, 0L, OSFSK_END); fsiz = osfpos(fpext); osfseek(fpext, 0L, OSFSK_SET); /* copy the contents of the external file to the output */ copybytes(fpext, fpout, fsiz); /* the size is the same as the external file's size */ cur->siz = fsiz; /* done with the file */ osfcls(fpext); } else { /* * it comes from the input resource file - seek to the start * of the resource in the input file, and copy the data to * the output file */ osfseek(fp, in_res_base + cur->src_idx->ofs, OSFSK_SET); copybytes(fp, fpout, cur->src_idx->siz); /* the size is the same as in the input file */ cur->siz = cur->src_idx->siz; } } /* note the current output position - this is the end of the resource */ out_endpos = osfpos(fpout); /* * Now that we've written all of the resources and know their actual * layout in the file, we can go back and write the index table. */ osfseek(fpout, out_hdr_pos + 8, OSFSK_SET); for (cur = out_list ; cur != 0 ; cur = cur->nxt) { /* build this object's index table entry */ oswp4(buf, cur->ofs); oswp4(buf + 4, cur->siz); oswp2(buf + 8, cur->namlen); /* write the entry */ if (osfwb(fpout, buf, 10) || osfwb(fpout, cur->nam, cur->namlen)) listexit(fp, "unable to write HTML index table entry"); } /* * We're done building the resource; now all we need to do is go * back and write the ending position of the resource in the * resource header. */ osfseek(fpout, out_hdr_pos - 4, OSFSK_SET); oswp4(buf, out_endpos); if (osfwb(fpout, buf, 4)) errexit("error writing resource", 1); /* seek back to the end of the resource in the output file */ osfseek(fpout, out_endpos, OSFSK_SET); done: /* if we have an input list, free it */ if (in_list != 0) { /* delete all of the entries in the input table */ for (i = 0, p = in_list ; i < in_entry_cnt ; ++i, ++p) { /* delete this entry if we haven't already done so */ if (*p != 0) free(*p); } /* delete the input pointer list itself */ free(in_list); } /* * delete everything in the additions list, since we're done with * them now */ for (op = add_list ; op != 0 ; op = next_op) { /* note the next entry in the list */ next_op = op->opnxt; /* delete this entry */ free(op); } /* return the op list in its current form */ return oplist; }
/* * Open a source file */ CTcSrcFile *CTcSrcFile::open_source(const char *filename, class CResLoader *res_loader, const char *default_charset, int *charset_error, int *default_charset_error) { char buf[275]; size_t siz; osfildef *fp; long startofs; CCharmapToUni *mapper; /* presume we won't find an invalid #charset directive */ *charset_error = FALSE; /* presume we'll have no problem with the default character set */ *default_charset_error = FALSE; /* * open the file in binary mode, so that we can scan the first few * bytes to see if we can detect the character set from information * at the beginning of the file */ fp = osfoprb(filename, OSFTTEXT); /* if we couldn't open the file, return failure */ if (fp == 0) return 0; /* note the starting offset in the file */ startofs = osfpos(fp); /* read the first few bytes of the file */ siz = osfrbc(fp, buf, sizeof(buf)); /* check for a 3-byte UTF-8 byte-order marker */ if (siz >= 3 && (uchar)buf[0] == 0xEF && (uchar)buf[1] == 0xBB && (uchar)buf[2] == 0xBF) { char *p; size_t rem; uint skip; /* skip at least the three-byte marker sequence */ skip = 3; /* * check for a #charset marker for utf-8 - this would be redundant, * but we'll allow it */ p = buf + 3; rem = siz - 3; if (rem > 9 && memcmp(p, "#charset ", 9) == 0) { /* skip spaces */ for (p += 9, rem -= 9 ; rem != 0 && (*p == ' ' || *p == '\t') ; ++p, --rem); /* check for valid character set markers */ if (rem >= 7 && memicmp(p, "\"utf-8\"", 7) == 0) { /* skip the whole sequence */ skip = (p + 7) - buf; } else if (rem >= 6 && memicmp(p, "\"utf8\"", 6) == 0) { /* skip the whole sequence */ skip = (p + 6) - buf; } } /* seek past the character set markers */ osfseek(fp, startofs + skip, OSFSK_SET); /* return a new utf-8 decoder */ return new CTcSrcFile(fp, new CCharmapToUniUTF8()); } /* if we read at least two bytes, try auto-detecting unicode */ if (siz >= 2) { CTcSrcFile *srcf; const char *const *cs_names; int bige; /* presume we won't find a byte-order marker */ srcf = 0; /* if the first bytes are 0xFF 0xFE, it's UCS-2 low-byte first */ if ((unsigned char)buf[0] == 0xFF && (unsigned char)buf[1] == 0xFE) { static const char *names[] = { "unicodel", "utf-16le", 0 }; /* create a UCS-2 little-endian reader */ srcf = new CTcSrcFile(fp, new CCharmapToUniUcs2Little()); bige = FALSE; cs_names = names; } /* if the first bytes are 0xFE 0xFF, it's UCS-2 high-byte first */ if ((unsigned char)buf[0] == 0xFE && (unsigned char)buf[1] == 0xFF) { static const char *names[] = { "unicodeb", "utf-16be", 0 }; /* create a UCS-2 little-endian reader */ srcf = new CTcSrcFile(fp, new CCharmapToUniUcs2Big()); bige = TRUE; cs_names = names; } /* if we found the byte-order marker, we know the character set */ if (srcf != 0) { uint skip; /* we at least want to skip the byte-order marker */ skip = 2; /* check to see if we have a '#charset' directive */ if (ucs_str_starts_with(buf + 2, siz - 2, "#charset ", bige, FALSE)) { char *p; size_t rem; /* scan past following spaces */ for (p = buf + 2 + 18, rem = siz - 2 - 18 ; rem >= 2 && (ucs_char_eq(p, ' ', bige, FALSE) || ucs_char_eq(p, '\t', bige, FALSE)) ; p += 2, rem -= 2) ; /* check for a '"' */ if (rem >= 2 && ucs_char_eq(p, '"', bige, FALSE)) { const char *const *n; /* skip the '"' */ p += 2; rem -= 2; /* * check for a match to any of the valid names for this * character set */ for (n = cs_names ; *n != 0 ; ++n) { /* if it's a match, stop scanning */ if (ucs_str_starts_with(p, rem, *n, bige, TRUE)) { size_t l; /* get the length of the name */ l = strlen(*n) * 2; /* check for a close quote */ if (rem >= l + 2 && ucs_char_eq(p + l, '"', bige, FALSE)) { /* skip the name and the quote */ p += l + 2; rem -= l + 2; /* skip the source text to this point */ skip = p - buf; /* stop scanning */ break; } } } } } /* seek just past the character set indicators */ osfseek(fp, startofs + skip, OSFSK_SET); /* return the file */ return srcf; } } /* * It doesn't appear to use UCS-2 encoding (at least, the file * doesn't start with a byte-order sensing sequence). Check to see * if the file starts with "#charset " in ASCII single-byte * characters. */ if (siz >= 9 && memcmp(buf, "#charset ", 9) == 0) { char *p; size_t rem; /* skip the #charset string and any following spaces */ for (p = buf + 9, rem = siz - 9 ; rem > 0 && (*p == ' ' || *p == '\t') ; ++p, --rem) ; /* make sure we're looking at a '"' */ if (rem != 0 && *p == '"') { char *charset_name; /* skip the open quote */ ++p; --rem; /* remember where the character set name starts */ charset_name = p; /* * find the closing quote, which must occur before a CR or * LF character */ for ( ; rem > 0 && *p != '"' && *p != 10 && *p != 13 ; ++p, --rem) ; /* make sure we found a matching quote */ if (rem != 0 && *p == '"') { /* seek just past the #charset string */ osfseek(fp, startofs + (p - buf) + 1, OSFSK_SET); /* * put a null terminator at the end of the character set * name */ *p = '\0'; /* create a mapper */ mapper = CCharmapToUni::load(res_loader, charset_name); /* * if that succeeded, return a reader for the mapper; * otherwise, simply proceed as though no #charset had * been present, so that we create a default mapper */ if (mapper != 0) { /* success - return a reader */ return new CTcSrcFile(fp, mapper); } else { /* tell the caller the #charset was invalid */ *charset_error = TRUE; } } } } /* * we didn't find any sensing codes, so seek back to the start of * the file */ osfseek(fp, startofs, OSFSK_SET); /* * We couldn't identify the file's character set based on anything * in the file, so create a mapper for the given default character * set. If there's not even a default character set defined, create * a plain ASCII mapper. */ if (default_charset != 0) mapper = CCharmapToUni::load(res_loader, default_charset); else mapper = new CCharmapToUniASCII(); /* check to see if we created a mapper */ if (mapper != 0) { /* return a source file reader based on the mapper */ return new CTcSrcFile(fp, mapper); } else { /* * we failed to create a mapper for the default character set - * flag the problem */ *default_charset_error = TRUE; /* close the input file */ osfcls(fp); /* return failure */ return 0; } }
static void listexit(osfildef *fp, char *msg) { if (fp != 0) osfcls(fp); errexit(msg, 1); }
/* * delete the file object */ CVmFile::~CVmFile() { /* if we still have an underlying OS file, close it */ if (fp_ != 0) osfcls(fp_); }
int main(int argc, char **argv) { int curarg; osfildef *fpin; osfildef *fpout; char tmpfile[OSFNMAX + 1]; char inbuf[OSFNMAX + 1]; char *p; char *infile; char buf[128]; opdef *oplist = (opdef *)0; opctxdef opctx; int do_create = FALSE; /* print main banner */ rscptf("TADS Resource Manager version 2.2.4\n"); rscptf("Copyright (c) 1992, 1999 by Michael J. Roberts. "); rscptf("All Rights Reserved.\n"); if (argc < 2) usage(); /* set default parsing options */ opctx.restype = RESTYPE_DFLT; opctx.flag = OPFADD | OPFDEL; opctx.doing_type = FALSE; /* scan file options (these come before the filename) */ for (curarg = 1 ; curarg < argc ; ++curarg) { /* check if it's an option - if not, stop looking */ if (argv[curarg][0] != '-') break; /* check the option */ if (!stricmp(argv[curarg], "-create")) { /* note that we want to create the file */ do_create = TRUE; } else { rscptf("unrecognized file option \"%s\"", argv[curarg]); errexit("", 1); } } /* get the file name */ infile = argv[curarg++]; strcpy(inbuf, infile); os_defext(inbuf, "gam"); /* open the file for reading, unless we're creating a new file */ if (do_create) { /* creating - we have no input file */ fpin = 0; } else if ((fpin = osfoprb(inbuf, OSFTGAME)) == 0) { /* * not creating, so the file must already exist - it doesn't, so * issue an error and quit */ errexit("unable to open resource file", 1); } /* * if no operations are desired, and we're not creating a new file, * just list the existing file's contents and quit */ if (curarg == argc && fpin != 0) { rscproc(fpin, (osfildef *)0, 0); osfcls(fpin); os_term(OSEXSUCC); } /* * Create an output file. If we're creating a new file, create the * file named on the command line; otherwise, create a temporary * file that we'll write to while working and then rename to the * original input filename after we've finished with the original * input file. */ if (do_create) { /* create the new file */ if ((fpout = osfopwb(inbuf, OSFTGAME)) == 0) errexit("unable to create file", 1); /* report the creation */ rscptf("\nFile created.\n"); } else { /* generate a temporary filename */ strcpy(tmpfile, inbuf); for (p = tmpfile + strlen(tmpfile) ; p > tmpfile && *(p-1) != ':' && *(p-1) != '\\' && *(p-1) != '/' ; --p); strcpy(p, "$TADSRSC.TMP"); /* open the temporary file */ if ((fpout = osfopwb(tmpfile, OSFTGAME)) == 0) errexit("unable to create temporary file", 1); } /* see if we need to read a response file */ if (curarg < argc && argv[curarg][0] == '@') { osfildef *argfp; int l; char *p; if (!(argfp = osfoprt(argv[curarg]+1, OSFTTEXT))) errexit("unable to open response file", 1); for (;;) { if (!osfgets(buf, sizeof(buf), argfp)) break; l = strlen(buf); if (l && buf[l-1] == '\n') buf[--l] = '\0'; for (p = buf ; t_isspace(*p) ; ++p); if (!*p) continue; oplist = addop(oplist, p, &opctx); } osfcls(argfp); } else { for ( ; curarg < argc ; ++curarg) oplist = addop(oplist, argv[curarg], &opctx); } /* process the resources */ oplist = rscproc(fpin, fpout, oplist); /* make sure they all got processed */ for ( ; oplist != 0 ; oplist = oplist->opnxt) { if (!(oplist->opflag & OPFDONE)) rscptf("warning: resource \"%s\" not found\n", oplist->opres); } /* close files */ if (fpin != 0) osfcls(fpin); if (fpout != 0) osfcls(fpout); /* * if we didn't create a new file, remove the original input file * and rename the temp file to the original file name */ if (!do_create) { /* remove the original input file */ if (remove(inbuf)) errexit("error deleting input file", 1); /* rename the temp file to the output file */ if (rename(tmpfile, inbuf)) errexit("error renaming temporary file", 1); } /* success */ os_term(OSEXSUCC); return OSEXSUCC; }
int main(int argc, char **argv) { osfildef *fp; const char *fname; const char *resname; tads_resinfo res_info; unsigned long rem; /* check usage */ if (argc != 3) { fprintf(stderr, "usage: resfind <filename> <resname>\n"); exit(2); } /* get the arguments */ fname = argv[1]; resname = argv[2]; /* open the file */ if ((fp = osfoprb(fname, OSFTGAME)) == 0 && (fp = osfoprb(fname, OSFTT3IMG)) == 0) { fprintf(stderr, "unable to open file \"%s\"\n", fname); exit(2); } /* find the resource */ if (!tads_find_resource_fp(fp, resname, &res_info)) { fprintf(stderr, "unable to find resource \"%s\"\n", resname); osfcls(fp); exit(1); } /* seek to the resource */ osfseek(fp, res_info.seek_pos, OSFSK_SET); /* copy the resource to standard output */ for (rem = res_info.siz ; rem != 0 ; ) { char buf[1024]; size_t cur; /* * read up to the buffer size or up to the resource size * remaining, whichever is smaller */ cur = sizeof(buf); if (cur > rem) cur = (size_t)rem; /* read the chunk */ if (osfrb(fp, buf, cur)) { fprintf(stderr, "error reading %u bytes from file\n", (unsigned)cur); osfcls(fp); exit(2); } /* copy the chunk to standard output */ fwrite(buf, cur, 1, stdout); /* deduct the amount we just read from the size remaining */ rem -= cur; } /* done with the file - close it */ osfcls(fp); /* success */ exit(0); return 0; }
/* * Perform base initialization. This is an internal routine called only * by higher-level initialization routines; we perform all of the * generic, configuration-independent initialization. */ void vm_init_base(vm_globals **vmg, const vm_init_options *opts) { vm_globals *vmg__; char disp_mapname[32]; char filename_mapname[32]; char filecont_mapname[32]; CResLoader *map_loader; int disp_map_err; const char *charset = opts->charset; /* * Allocate globals according to build-time configuration, then * assign the global pointer to a local named vmg__. This will * ensure that the globals are accessible for all of the different * build-time configurations. */ vmg__ = *vmg = vmglob_alloc(); /* initialize the error stack */ err_init(VM_ERR_STACK_BYTES); /* get the character map loader from the host interface */ map_loader = opts->hostifc->get_cmap_res_loader(); /* if an external message set hasn't been loaded, try loading one */ if (!err_is_message_file_loaded() && map_loader != 0) { osfildef *fp; /* try finding a message file */ fp = map_loader->open_res_file(VM_ERR_MSG_FNAME, 0, VM_ERR_MSG_RESTYPE); if (fp != 0) { /* * try loading it - if that fails, we'll just be left with * the built-in messages, so we won't have lost anything for * trying */ err_load_vm_message_file(fp); /* we're done with the file */ osfcls(fp); } } /* remember the host interface */ G_host_ifc = opts->hostifc; /* initialize the system debug log file name */ char path[OSFNMAX]; opts->hostifc->get_special_file_path(path, sizeof(path), OS_GSP_LOGFILE); os_build_full_path(G_syslogfile, sizeof(G_syslogfile), path, "tadslog.txt"); /* we don't have a resource loader for program resources yet */ G_res_loader = 0; /* create the object table */ VM_IF_ALLOC_PRE_GLOBAL(G_obj_table = new CVmObjTable()); G_obj_table->init(vmg0_); /* * Create the memory manager. Empirically, our hybrid heap allocator * is faster than the standard C++ run-time library's allocator on many * platforms, so use it instead of hte basic 'malloc' allocator. */ G_varheap = new CVmVarHeapHybrid(G_obj_table); // G_varheap = new CVmVarHeapMalloc(); to use the system 'malloc' instead G_mem = new CVmMemory(vmg_ G_varheap); /* create the undo manager */ G_undo = new CVmUndo(VM_UNDO_MAX_RECORDS, VM_UNDO_MAX_SAVEPTS); /* create the metafile and function set tables */ G_meta_table = new CVmMetaTable(5); G_bif_table = new CVmBifTable(5); /* initialize the metaclass registration tables */ vm_register_metaclasses(); /* initialize the TadsObject class */ CVmObjTads::class_init(vmg0_); /* create the byte-code interpreter */ VM_IFELSE_ALLOC_PRE_GLOBAL( G_interpreter = new CVmRun(VM_STACK_SIZE, vm_init_stack_reserve()), G_interpreter->init()); /* presume we won't create debugger information */ G_debugger = 0; G_srcf_table = 0; /* presume we don't have a network configuration */ G_net_config = 0; /* initialize the debugger if present */ vm_init_debugger(vmg0_); /* create the source file table */ G_srcf_table = new CVmSrcfTable(); /* create the pre-defined object mapper */ VM_IFELSE_ALLOC_PRE_GLOBAL(G_predef = new CVmPredef, G_predef->reset()); /* presume we're in normal execution mode (not preinit) */ G_preinit_mode = FALSE; /* allocate the TADS intrinsic function set's globals */ G_bif_tads_globals = new CVmBifTADSGlobals(vmg0_); /* allocate the BigNumber register cache */ G_bignum_cache = new CVmBigNumCache(32); /* no image loader yet */ G_image_loader = 0; /* * If the caller explicitly specified a character set, use it. * Otherwise, ask the OS layer for the default character set we * should use. */ if (charset == 0) { /* the user did not specify a mapping - ask the OS for the default */ os_get_charmap(disp_mapname, OS_CHARMAP_DISPLAY); /* use the name we got from the OS */ charset = disp_mapname; /* there's no explicit global character set name setting to store */ G_disp_cset_name = 0; } else { /* save the global character set name */ G_disp_cset_name = lib_copy_str(charset); } /* create the display character maps */ G_cmap_from_ui = CCharmapToUni::load(map_loader, charset); G_cmap_to_ui = CCharmapToLocal::load(map_loader, charset); /* create the filename character maps */ os_get_charmap(filename_mapname, OS_CHARMAP_FILENAME); G_cmap_from_fname = CCharmapToUni::load(map_loader, filename_mapname); G_cmap_to_fname = CCharmapToLocal::load(map_loader, filename_mapname); /* create the file-contents character maps */ os_get_charmap(filecont_mapname, OS_CHARMAP_FILECONTENTS); G_cmap_from_file = CCharmapToUni::load(map_loader, filecont_mapname); G_cmap_to_file = CCharmapToLocal::load(map_loader, filecont_mapname); /* * If the caller specified a separate log-file character set, create * the mapping. Otherwise, just use the to-file mapper for log files. */ if (opts->log_charset != 0) { /* load the specified log file output mapping */ G_cmap_to_log = CCharmapToLocal::load(map_loader, opts->log_charset); } else { /* no log file mapping is specified, so use the generic file map */ if ((G_cmap_to_log = G_cmap_to_file) != 0) G_cmap_to_log->add_ref(); } /* make a note of whether we had any problems loading the maps */ disp_map_err = (G_cmap_from_ui == 0 || G_cmap_to_ui == 0); /* if we failed to create any of the maps, load defaults */ if (G_cmap_from_ui == 0) G_cmap_from_ui = CCharmapToUni::load(map_loader, "us-ascii"); if (G_cmap_to_ui == 0) G_cmap_to_ui = CCharmapToLocal::load(map_loader, "us-ascii"); if (G_cmap_from_fname == 0) G_cmap_from_fname = CCharmapToUni::load(map_loader, "us-ascii"); if (G_cmap_to_fname == 0) G_cmap_to_fname = CCharmapToLocal::load(map_loader, "us-ascii"); if (G_cmap_from_file == 0) G_cmap_from_file = CCharmapToUni::load(map_loader, "us-ascii"); if (G_cmap_to_file == 0) G_cmap_to_file = CCharmapToLocal::load(map_loader, "us-ascii"); if (G_cmap_to_log == 0) G_cmap_to_log = CCharmapToLocal::load(map_loader, "us-ascii"); /* create the primary console */ G_console = opts->clientifc->create_console(VMGLOB_ADDR); /* * if we had any trouble opening the display character mapping file, * make a note that we are using a default mapping */ if (disp_map_err) { const char *msg; char buf[256]; /* get the message */ msg = err_get_msg(vm_messages, vm_message_count, VMERR_NO_CHARMAP_FILE, TRUE); /* format it */ sprintf(buf, msg, charset); /* display it */ opts->clientifc->display_error(VMGLOB_ADDR, 0, buf, TRUE); } }
/* * Given a saved game file, try to identify the game file that created the * saved game. */ static int vm_get_game_file_from_savefile(const char *savefile, char *fname, size_t fnamelen) { osfildef *fp; char buf[128]; int ret; size_t len; /* open the saved game file */ fp = osfoprb(savefile, OSFTBIN); /* if that failed, there's no way to read the game file name */ if (fp == 0) return FALSE; /* read the first few bytes */ if (osfrb(fp, buf, 16)) { /* * we couldn't even read that much, so it must not really be a * saved game file */ ret = FALSE; } else { /* check for a saved game signature we recognize */ if (memcmp(buf, "TADS2 save/g\012\015\032\000", 16) == 0) { /* * It's a TADS 2 saved game with embedded .GAM file * information. The filename immediately follows the signature * (the 15 bytes we just matched), with a two-byte length * prefix. Seek to the length prefix and read it. */ osfseek(fp, 16, OSFSK_SET); osfrb(fp, buf, 2); len = osrp2(buf); /* limit the read length to our caller's available buffer */ if (len > fnamelen - 1) len = fnamelen - 1; /* read the filename and null-terminate it */ osfrb(fp, fname, len); fname[len] = '\0'; /* success */ ret = TRUE; } else if (memcmp(buf, "T3-state-v", 10) == 0) { /* * It's a T3 saved state file. The image filename is always * embedded in this type of file, so seek back to the start of * the file and read the filename. * * Note that restore_get_image() returns zero on success, so we * want to return true if and only if that routine returns * zero. */ osfseek(fp, 0, OSFSK_SET); ret = (CVmSaveFile::restore_get_image(fp, fname, fnamelen) == 0); } else { /* * it's not a signature we know, so it must not be a saved * state file (at least not one we can deal with) */ ret = FALSE; } } /* we're done with the file now, so close it */ osfcls(fp); /* return the result */ return ret; }
int TADS2::cmap_load_internal(const char *filename) { osfildef *fp; static char sig1[] = CMAP_SIG_S100; char buf[256]; uchar lenbuf[2]; size_t len; int sysblk; // if there's no mapping file, use the default mapping if (filename == 0) { // initialize with the default mapping cmap_init_default(); // return success return 0; } // open the file fp = osfoprb(filename, OSFTCMAP); if (fp == 0) return 1; // check the signature if (osfrb(fp, buf, sizeof(sig1)) || memcmp(buf, sig1, sizeof(sig1)) != 0) { osfcls(fp); return 2; } // load the ID G_cmap_id[4] = '\0'; if (osfrb(fp, G_cmap_id, 4)) { osfcls(fp); return 3; } // load the long description if (osfrb(fp, lenbuf, 2) || (len = osrp2(lenbuf)) > sizeof(G_cmap_ldesc) || osfrb(fp, G_cmap_ldesc, len)) { osfcls(fp); return 4; } // load the two tables - input, then output if (osfrb(fp, G_cmap_input, sizeof(G_cmap_input)) || osfrb(fp, G_cmap_output, sizeof(G_cmap_output))) { osfcls(fp); return 5; } // read the next section header if (osfrb(fp, buf, 4)) { osfcls(fp); return 6; } // if it's "SYSI", read the system information string if (!memcmp(buf, "SYSI", 4)) { // read the length prefix, then the string if (osfrb(fp, lenbuf, 2) || (len = osrp2(lenbuf)) > sizeof(buf) || osfrb(fp, buf, len)) { osfcls(fp); return 7; } // we have a system information block sysblk = true; } else { // there's no system information block sysblk = false; } /* * call the OS code, so that it can do any system-dependent * initialization for the new character mapping */ os_advise_load_charmap(G_cmap_id, G_cmap_ldesc, sysblk ? buf : ""); // read the next section header if (sysblk && osfrb(fp, buf, 4)) { osfcls(fp); return 8; } // see if we have an entity list if (!memcmp(buf, "ENTY", 4)) { // read the entities for (;;) { unsigned int cval; char expansion[CMAP_MAX_ENTITY_EXPANSION]; // read the next item's length and character value if (osfrb(fp, buf, 4)) { osfcls(fp); return 9; } // decode the values len = osrp2(buf); cval = osrp2(buf+2); // if we've reached the zero marker, we're done if (len == 0 && cval == 0) break; // read the string if (len > CMAP_MAX_ENTITY_EXPANSION || osfrb(fp, expansion, len)) { osfcls(fp); return 10; } // tell the output code about the expansion tio_set_html_expansion(cval, expansion, len); } } /* * ignore anything else we find - if the file format is updated to * include extra information in the future, and this old code tries * to load an updated file, we'll just ignore the new information, * which should always be placed after the "SYSI" block (if present) * to ensure compatibility with past versions (such as this code) */ // no problems - close the file and return success osfcls(fp); return 0; }
/* write game to binary file */ static void fiowrt1(mcmcxdef *mctx, voccxdef *vctx, tokcxdef *tokctx, tokthdef *tab, uchar *fmts, uint fmtl, osfildef *fp, uint flags, objnum preinit, int extc, uint prpcnt, char *filever) { int i; int j; int k; uchar buf[TOKNAMMAX + 50]; errcxdef *ec = vctx->voccxerr; ulong fpos; int obj; vocidef ***vpg; vocidef **v; objnum *sc; vocdef *voc; vocwdef *vw; vocdef **vhsh; struct tm *tblock; time_t timer; fiowcxdef cbctx; /* callback context for toktheach */ uint xor_seed = 63; uint xor_inc = 64; char *vsnhdr; uint vsnlen; char fastnamebuf[OSFNMAX]; /* fast-load record temp file name */ long flag_seek; /* generate appropriate file version */ switch(filever[0]) { case 'a': /* generate .GAM compatible with pre-2.0.15 runtimes */ vsnhdr = FIOVSNHDR2; vsnlen = sizeof(FIOVSNHDR2); xor_seed = 17; /* use old xor values */ xor_inc = 29; break; case 'b': /* generate 2.0.15+ format */ vsnhdr = FIOVSNHDR3; vsnlen = sizeof(FIOVSNHDR3); break; case 'c': case '*': /* current version */ vsnhdr = FIOVSNHDR; vsnlen = sizeof(FIOVSNHDR); break; default: errsig(ec, ERR_WRTVSN); } /* write file header and version header */ if (osfwb(fp, FIOFILHDR, sizeof(FIOFILHDR)) || osfwb(fp, vsnhdr, vsnlen)) errsig(ec, ERR_WRTGAM); /* * write the flags - remember where we wrote it in case we need to * change the flags later */ flag_seek = osfpos(fp); oswp2(buf, flags); if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM); /* write the timestamp */ timer = time(NULL); tblock = localtime(&timer); strcpy(vctx->voccxtim, asctime(tblock)); if (osfwb(fp, vctx->voccxtim, (size_t)26)) errsig(ec, ERR_WRTGAM); /* write xor parameters if generating post 2.0.15 .GAM file */ if (filever[0] != 'a') { fpos = fiowhd(fp, ec, "\003XSI"); buf[0] = xor_seed; buf[1] = xor_inc; if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM); fiowcls(fp, ec, fpos); } /* write count of external functions */ if (extc) { fpos = fiowhd(fp, ec, "\006EXTCNT"); oswp2(buf, extc); /* write the external function count */ if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM); /* write XFCN-seek-location header if post 2.0.15 file version */ if (filever[0] != 'a') { oswp4(buf, 0); /* placeholder - TADSRSC sets this up later */ if (osfwb(fp, buf, 4)) errsig(ec, ERR_WRTGAM); } /* close the resource */ fiowcls(fp, ec, fpos); } /* * write the character set information, if a character set was * specified */ if (G_cmap_id[0] != '\0') { size_t len; /* this is not allowed with explicit file versions a, b, or c */ if (filever[0] == 'a' || filever[0] == 'b' || filever[0] == 'c') errsig(ec, ERR_VNOCTAB); /* write out the CHRSET resource header */ fpos = fiowhd(fp, ec, "\006CHRSET"); /* write out the ID and LDESC of the internal character set */ len = strlen(G_cmap_ldesc) + 1; oswp2(buf, len); if (osfwb(fp, G_cmap_id, 4) || osfwb(fp, buf, 2) || osfwb(fp, G_cmap_ldesc, len)) errsig(ec, ERR_WRTGAM); /* close the resource */ fiowcls(fp, ec, fpos); } /* write OBJ resource header */ fpos = fiowhd(fp, ec, "\003OBJ"); /* set up the symbol table callback context for writing the objects */ cbctx.fiowcxmem = mctx; cbctx.fiowcxerr = ec; cbctx.fiowcxfp = fp; cbctx.fiowcxund = 0; cbctx.fiowcxseed = xor_seed; cbctx.fiowcxinc = xor_inc; cbctx.fiowcxdebug = (flags & FIOFSYM); if (flags & FIOFFAST) { /* try creating the temp file */ cbctx.fiowcxffp = os_create_tempfile(0, fastnamebuf); /* if that failed, we don't have a fast-load table after all */ if (cbctx.fiowcxffp == 0) { long curpos; char flag_buf[2]; /* clear the fast-load flag */ flags &= ~FIOFFAST; /* * go back to the flags we wrote to the .gam file header, and * re-write the new flags without the fast-load bit - since * we're not going to write a fast-load table, we don't want * readers thinking they're going to find one */ /* first, remember where we are right now */ curpos = osfpos(fp); /* seek back to the flags and re-write the new flags */ osfseek(fp, flag_seek, OSFSK_SET); oswp2(flag_buf, flags); if (osfwb(fp, flag_buf, 2)) errsig(ec, ERR_WRTGAM); /* seek back to where we started */ osfseek(fp, curpos, OSFSK_SET); } } else cbctx.fiowcxffp = (osfildef *)0; cbctx.fiowcxflg = flags; /* go through symbol table, and write each object */ toktheach((toktdef *)tab, fiowrtobj, &cbctx); /* also write all objects created with 'new' */ for (vpg = vctx->voccxinh, i = 0 ; i < VOCINHMAX ; ++vpg, ++i) { objnum obj; if (!*vpg) continue; for (v = *vpg, obj = (i << 8), j = 0 ; j < 256 ; ++v, ++obj, ++j) { /* if the object was dynamically allocated, write it out */ if (*v && (*v)->vociflg & VOCIFNEW) { toksdef t; /* clear the 'new' flag, as this is a static object now */ (*v)->vociflg &= ~VOCIFNEW; /* set up a toksdef, and write it out */ t.tokstyp = TOKSTOBJ; t.toksval = obj; fiowrtobj(&cbctx, &t); } } } /* close the resource */ fiowcls(fp, ec, fpos); /* copy fast-load records to output file if applicable */ if (cbctx.fiowcxffp) { osfildef *fp2 = cbctx.fiowcxffp; char copybuf[1024]; ulong len; ulong curlen; /* start with resource header for fast-load records */ fpos = fiowhd(fp, ec, "\003FST"); /* get length of temp file, then rewind it */ len = osfpos(fp2); osfseek(fp2, 0L, OSFSK_SET); /* copy the whole thing to the output file */ while (len) { curlen = (len > sizeof(copybuf) ? sizeof(copybuf) : len); if (osfrb(fp2, copybuf, (size_t)curlen) || osfwb(fp, copybuf, (size_t)curlen)) errsig(ec, ERR_WRTGAM); len -= curlen; } /* close the fast-load resource */ fiowcls(fp, ec, fpos); /* close and delete temp file */ osfcls(fp2); osfdel_temp(fastnamebuf); } /* write vocabulary inheritance records - start with header */ fpos = fiowhd(fp, ec, "\003INH"); /* go through inheritance records and write to file */ for (vpg = vctx->voccxinh, i = 0 ; i < VOCINHMAX ; ++vpg, ++i) { if (!*vpg) continue; for (v = *vpg, obj = (i << 8), j = 0 ; j < 256 ; ++v, ++obj, ++j) { if (*v) { buf[0] = (*v)->vociflg; oswp2(buf + 1, obj); oswp2(buf + 3, (*v)->vociloc); oswp2(buf + 5, (*v)->vociilc); oswp2(buf + 7, (*v)->vocinsc); for (k = 0, sc = (*v)->vocisc ; k < (*v)->vocinsc ; ++sc, ++k) { if (k + 9 >= sizeof(buf)) errsig(ec, ERR_FIOMSC); oswp2(buf + 9 + (k << 1), (*sc)); } if (osfwb(fp, buf, 9 + (2 * (*v)->vocinsc))) errsig(ec, ERR_WRTGAM); } } } /* close resource */ fiowcls(fp, ec, fpos); /* write format strings */ if (fmtl) { fpos = fiowhd(fp, ec, "\006FMTSTR"); oswp2(buf, fmtl); if (flags & FIOFCRYPT) fioxor(fmts, fmtl, xor_seed, xor_inc); if (osfwb(fp, buf, 2) || osfwb(fp, fmts, fmtl)) errsig(ec, ERR_WRTGAM); fiowcls(fp, ec, fpos); } /* write preinit if preinit object was specified */ if (flags & FIOFPRE) { fpos = fiowhd(fp, ec, "\007PREINIT"); oswp2(buf, preinit); if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM); fiowcls(fp, ec, fpos); } /* write required objects out of voccxdef */ fpos = fiowhd(fp, ec, "\003REQ"); fiowrq(ec, fp, vctx->voccxme); fiowrq(ec, fp, vctx->voccxvtk); fiowrq(ec, fp, vctx->voccxstr); fiowrq(ec, fp, vctx->voccxnum); fiowrq(ec, fp, vctx->voccxprd); fiowrq(ec, fp, vctx->voccxvag); fiowrq(ec, fp, vctx->voccxini); fiowrq(ec, fp, vctx->voccxpre); fiowrq(ec, fp, vctx->voccxper); fiowrq(ec, fp, vctx->voccxprom); fiowrq(ec, fp, vctx->voccxpdis); fiowrq(ec, fp, vctx->voccxper2); fiowrq(ec, fp, vctx->voccxpdef); fiowrq(ec, fp, vctx->voccxpask); fiowrq(ec, fp, vctx->voccxppc); fiowrq(ec, fp, vctx->voccxpask2); fiowrq(ec, fp, vctx->voccxperp); fiowrq(ec, fp, vctx->voccxpostprom); fiowrq(ec, fp, vctx->voccxinitrestore); fiowrq(ec, fp, vctx->voccxpuv); fiowrq(ec, fp, vctx->voccxpnp); fiowrq(ec, fp, vctx->voccxpostact); fiowrq(ec, fp, vctx->voccxendcmd); fiowrq(ec, fp, vctx->voccxprecmd); fiowrq(ec, fp, vctx->voccxpask3); fiowrq(ec, fp, vctx->voccxpre2); fiowrq(ec, fp, vctx->voccxpdef2); fiowcls(fp, ec, fpos); /* write compound words */ if (vctx->voccxcpl) { fpos = fiowhd(fp, ec, "\004CMPD"); oswp2(buf, vctx->voccxcpl); if (flags & FIOFCRYPT) fioxor((uchar *)vctx->voccxcpp, (uint)vctx->voccxcpl, xor_seed, xor_inc); if (osfwb(fp, buf, 2) || osfwb(fp, vctx->voccxcpp, (uint)vctx->voccxcpl)) errsig(ec, ERR_WRTGAM); fiowcls(fp, ec, fpos); } /* write special words */ if (vctx->voccxspl) { fpos = fiowhd(fp, ec, "\010SPECWORD"); oswp2(buf, vctx->voccxspl); if (flags & FIOFCRYPT) fioxor((uchar *)vctx->voccxspp, (uint)vctx->voccxspl, xor_seed, xor_inc); if (osfwb(fp, buf, 2) || osfwb(fp, vctx->voccxspp, (uint)vctx->voccxspl)) errsig(ec, ERR_WRTGAM); fiowcls(fp, ec, fpos); } /* write vocabulary */ fpos = fiowhd(fp, ec, "\003VOC"); /* go through each hash chain */ for (i = 0, vhsh = vctx->voccxhsh ; i < VOCHASHSIZ ; ++i, ++vhsh) { /* go through each word in this hash chain */ for (voc = *vhsh ; voc ; voc = voc->vocnxt) { /* encrypt the word if desired */ if (flags & FIOFCRYPT) fioxor(voc->voctxt, (uint)(voc->voclen + voc->vocln2), xor_seed, xor_inc); /* go through each object relation attached to the word */ for (vw = vocwget(vctx, voc->vocwlst) ; vw ; vw = vocwget(vctx, vw->vocwnxt)) { /* clear the 'new' flag, as this is a static object now */ vw->vocwflg &= ~VOCFNEW; /* write out this object relation */ oswp2(buf, voc->voclen); oswp2(buf+2, voc->vocln2); oswp2(buf+4, vw->vocwtyp); oswp2(buf+6, vw->vocwobj); oswp2(buf+8, vw->vocwflg); if (osfwb(fp, buf, 10) || osfwb(fp, voc->voctxt, voc->voclen + voc->vocln2)) errsig(ec, ERR_WRTGAM); } } } fiowcls(fp, ec, fpos); /* write the symbol table, if desired */ if (flags & FIOFSYM) { fpos = fiowhd(fp, ec, "\006SYMTAB"); toktheach((toktdef *)tab, fiowrtsym, &cbctx); /* indicate last symbol with a length of zero */ buf[0] = 0; if (osfwb(fp, buf, 4)) errsig(ec, ERR_WRTGAM); fiowcls(fp, ec, fpos); } /* write line source chain, if desired */ if ((flags & (FIOFLIN | FIOFLIN2)) != 0 && vctx->voccxrun->runcxdbg != 0) { lindef *lin; /* write the SRC header */ fpos = fiowhd(fp, ec, "\003SRC"); /* write the line records */ for (lin = vctx->voccxrun->runcxdbg->dbgcxlin ; lin ; lin = lin->linnxt) { /* write this record */ if (linwrt(lin, fp)) errsig(ec, ERR_WRTGAM); } /* done with this section */ fiowcls(fp, ec, fpos); /* * if we have new-style line records, put a SRC2 header (with no * block contents) in the file, so that the debugger realizes * that it has new-style line records (with line numbers rather * than seek offsets) */ if ((flags & FIOFLIN2) != 0) { fpos = fiowhd(fp, ec, "\004SRC2"); fiowcls(fp, ec, fpos); } } /* if writing a precompiled header, write some more information */ if (flags & FIOFBIN) { /* write property count */ fpos = fiowhd(fp, ec, "\006PRPCNT"); oswp2(buf, prpcnt); if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM); fiowcls(fp, ec, fpos); /* write preprocessor symbol table */ fpos = fiowhd(fp, ec, "\006TADSPP"); tok_write_defines(tokctx, fp, ec); fiowcls(fp, ec, fpos); } /* write end-of-file resource header */ (void)fiowhd(fp, ec, "\004$EOF"); osfcls(fp); /* if there are undefined functions/objects, signal an error */ if (cbctx.fiowcxund) errsig(ec, ERR_UNDEF); }