/*
 *   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;
    }
}
Example #3
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;
    }
}
Example #4
0
/*
 *   Deletion 
 */
CTcSrcFile::~CTcSrcFile()
{
    /* close my source file */
    if (fp_ != 0)
        osfcls(fp_);

    /* release my character mapper */
    if (mapper_ != 0)
        mapper_->release_ref();
}
Example #5
0
/*
 *   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);
        }
    }
}
Example #7
0
/*
 *   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;
}
Example #8
0
/* 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_;
}
Example #10
0
/*
 *   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;
}
Example #11
0
/*
 *   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;
}
Example #12
0
/*
 *   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);
}
Example #14
0
/*
 *   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;
}
Example #16
0
/*
 *   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);
}
Example #18
0
/*
 *   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;
}
Example #20
0
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;
}
Example #21
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);
    }
}
Example #22
0
/*
 *   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;
}
Example #23
0
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;
}
Example #24
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);
}