/* * 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; }
/* * 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; }
/* * 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; }