Пример #1
0
/*
 *   Log an error, part 1: show the message prefix.  This should be called
 *   before formatting the text of the message, and part 2 should be called
 *   after formatting the message text.  Returns a pointer to the message
 *   template text.  
 */
static const char *log_msg_internal_1(
    CTcTokFileDesc *linedesc, long linenum,
    int *err_counter, int *warn_counter, int *first_error, int *first_warning,
    unsigned long options, const int *suppress_list, size_t suppress_cnt,
    tc_severity_t severity, int err)
{
    const char *msg;
    const char *prefix;

    /*
     *   If this is a warning or a pedantic warning, and it's in the list of
     *   suppressed messages, ignore it entirely.  
     */
    if (severity == TC_SEV_PEDANTIC || severity == TC_SEV_WARNING)
    {
        size_t rem;
        const int *p;

        /* scan the suppress list */
        for (p = suppress_list, rem = suppress_cnt ; rem != 0 ; ++p, --rem)
        {
            /* check for a match */
            if (*p == err)
            {
                /* it's in the suppress list - ignore the error */
                return 0;
            }
        }
    }

    /* increment the appropriate counter */
    switch(severity)
    {
    case TC_SEV_INFO:
        /* 
         *   we don't need to count informational messages, and no prefix is
         *   required 
         */
        prefix = "";
        break;

    case TC_SEV_PEDANTIC:
        /* 
         *   if we're not in "pedantic" mode, or we're not even showing
         *   regular errors, ignore it 
         */
        if (!(options & TCMAIN_ERR_PEDANTIC)
            || !(options & TCMAIN_ERR_WARNINGS))
            return 0;

        /* if this is the first warning, remember the code */
        if (*warn_counter == 0 && first_warning != 0)
            *first_warning = err;

        /* count it */
        ++(*warn_counter);

        /* set the prefix */
        prefix = "warning";
        break;

    case TC_SEV_WARNING:
        /* if we're suppressing warnings, ignore it */
        if (!(options & TCMAIN_ERR_WARNINGS))
            return 0;

        /* if this is the first warning, remember the code */
        if (*warn_counter == 0 && first_warning != 0)
            *first_warning = err;

        /* count the warning */
        ++(*warn_counter);

        /* use an appropriate prefix */
        prefix = "warning";
        break;

    case TC_SEV_ERROR:
        /* if this is the first error, remember the code */
        if (*err_counter == 0 && first_error != 0)
            *first_error = err;

        /* count the error */
        ++(*err_counter);

        /* use an appropriate prefix */
        prefix = "error";
        break;

    case TC_SEV_FATAL:
        /* if this is the first error, remember the code */
        if (*err_counter == 0 && first_error != 0)
            *first_error = err;

        /* count this as an error */
        ++(*err_counter);

        /* use an appropriate prefix */
        prefix = "fatal error";
        break;

    case TC_SEV_INTERNAL:
        /* if this is the first error, remember the code */
        if (*err_counter == 0 && first_error != 0)
            *first_error = err;

        /* count this as an error */
        ++(*err_counter);

        /* use an appropriate prefix */
        prefix = "internal error";
        break;
    }

    /* display the current parsing position, if available */
    if (linedesc != 0)
    {
        const char *fname;
        char qu_buf[OSFNMAX*2 + 2];

        /* get the filename from the source descriptor */
        fname = linedesc->get_fname();

        /* 
         *   if we're in test reporting mode, show only the root part of the
         *   filename 
         */
        if ((options & TCMAIN_ERR_TESTMODE) != 0)
            fname = os_get_root_name((char *)fname);

        /* if they want quoted filenames, quote the filename */
        if ((options & TCMAIN_ERR_FNAME_QU) != 0)
        {
            const char *src;
            char *dst;

            /* quote each character of the filename */
            for (src = fname, qu_buf[0] = '"', dst = qu_buf + 1 ;
                 *src != '\0' ; )
            {
                /* if this is a quote character, stutter it */
                if (*src == '"')
                    *dst++ = '"';

                /* add this character */
                *dst++ = *src++;
            }

            /* add a closing quote and trailing null */
            *dst++ = '"';
            *dst = '\0';

            /* use the quoted version of the filename */
            fname = qu_buf;
        }

        /* show the filename and line number prefix */
        G_hostifc->print_err("%s(%ld): ", fname, linenum);
    }

    /* display the error type prefix */
    G_hostifc->print_err("%s", prefix);

    /* add the error number, if we're showing error numbers */
    if ((options & TCMAIN_ERR_NUMBERS) != 0 && severity != TC_SEV_INFO)
        G_hostifc->print_err(" %u", (unsigned int)err);

    /* add a colon and a space for separation */
    G_hostifc->print_err(": ");

    /* get the error message */
    msg = tcerr_get_msg(err, (options & TCMAIN_ERR_VERBOSE) != 0);
    if (msg == 0)
        msg = "[Unable to find message text for this error code.  "
              "This might indicate an internal problem with the compiler, "
              "or it might be caused by an installation problem that is "
              "preventing the compiler from finding an external message "
              "file that it requires.]";

    /* return the message text, so the caller can format it with arguments */
    return msg;
}
Пример #2
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;
}
Пример #3
0
/*
 *   File mode 
 */
int osfmode(const char *fname, int follow_links,
            unsigned long *mode, unsigned long *attrs)
{
    struct _stat s;
    const char *root, *p;
    static const char *devices[] = {
        "con", "prn", "aux", "nul", "com#", "lpt#"
    };
    int i;
    char rbuf[32];
    unsigned long imode, iattrs;

    /* 
     *   if the caller doesn't want the 'mode' or 'attrs' values, set the
     *   pointers to point to internal buffers so that we don't have to worry
     *   about null OUT variables beyond this point 
     */
    if (mode == 0)
        mode = &imode;
    if (attrs == 0)
        attrs = &iattrs;

    /* clear the mode bits in case we don't find a match */
    *mode = 0;

    /* 
     *   Check for the reserved DOS device filenames.  Windows device name
     *   handling is a bit bizarre and, frankly, stupid: if the root filename
     *   (excluding the extension) is one of the special DOS device names,
     *   the entire rest of the name (drive, path, extension) is ignored, and
     *   the reserved name takes precedence.  So, pull out the root name
     *   after the path, then strip off any extension, so that we have the
     *   part of the name that Windows treats as a device name.  Run through
     *   the fixed list of reserved names.  If we find a match, the name is
     *   definitely a device name; but then we have to check if the device
     *   actually exists on this system to determine if we should return
     *   "device" or "unknown".
     */
    root = os_get_root_name((char *)fname);
    for (p = root ; *p && *p != ':' && *p != '.' ; ++p) ;

    /* extract the root name into a buffer */
    safe_strcpyl(rbuf, sizeof(rbuf), root, p - root);

    /* look for a device name match */
    for (i = 0 ; i < sizeof(devices)/sizeof(devices[0]) ; ++i)
    {
        /* check the root name against the current device name */
        if (devname_eq(rbuf, devices[i]))
        {
            FILE *fp;
            
            /* 
             *   We got a match, so this file refers to a device.  Now we
             *   have to check to see if the device actually exists on this
             *   system.  That's surprisingly difficult; there doesn't seem
             *   to exist a Windows API that returns consistent results.  The
             *   only sure way seems to be to try opening the file.  Some
             *   devices are read-only and some are write-only, so we need to
             *   try both modes.
             *   
             *   Opening the file as a device doesn't seem ideal for a number
             *   of reasons, such as the potential for false negatives due to
             *   sharing violations.  But there doesn't seem to be any other
             *   reliable way to find out if the file is openable.
             */
            *attrs = 0;
            fp = _fsopen(rbuf, "r", _SH_DENYNO);
            if (fp != 0)
            {
                *attrs |= OSFATTR_READ;
                fclose(fp);
            }
            fp = _fsopen(rbuf, "w", _SH_DENYNO);
            if (fp != 0)
            {
                *attrs |= OSFATTR_WRITE;
                fclose(fp);
            }
            if (*attrs != 0)
            {
                /* it exists - return "char mode device" */
                *mode = OSFMODE_CHAR;
                return TRUE;
            }

            /* it's a device name, but it doesn't exist - return failure */
            return FALSE;
        }
    }
    
    /* get the file status information; return false on failure */
    if (_stat(fname, &s) < 0)
        return FALSE;

    /* got it - return the mode information */
    *mode = (unsigned long)s.st_mode;

    /* get the file attributes */
    *attrs = oss_get_file_attrs(fname);

    /* indicate success */
    return TRUE;
}