/* * Open a resource file given the resource path. */ osfildef *CResLoader::open_res_file(const char *respath, const char *deflib, const char *exerestype) { char filepath[OSFNMAX]; osfildef *fp; /* * Look for the resource as an external file. If we have a root * directory, look for the file under that directory; otherwise, * look for it in the current directory. */ if (root_dir_ != 0) { char fname[OSFNMAX]; /* get the resource name as a file path */ os_cvt_url_dir(fname, sizeof(fname), respath, FALSE); /* build a full path from the root directory and the resource path */ os_build_full_path(filepath, sizeof(filepath), root_dir_, fname); } else { /* get the resource name as a file path */ os_cvt_url_dir(filepath, sizeof(filepath), respath, FALSE); } /* try opening the file */ fp = osfoprb(filepath, OSFTBIN); /* if we didn't find it, try looking in the default library */ if (fp == 0 && deflib != 0) { char fname[OSFNMAX]; /* convert from URL notation to local path conventions */ os_cvt_url_dir(fname, sizeof(fname), deflib, FALSE); /* build the full path, starting in the root resource directory */ os_build_full_path(filepath, sizeof(filepath), root_dir_, fname); /* add the default resource library extension */ os_defext(filepath, "t3r"); /* try opening the file */ fp = open_lib_res(filepath, respath); } /* if we still didn't find it, try looking in the executable */ if (fp == 0 && exerestype != 0) fp = open_exe_res(respath, exerestype); /* return the result */ return fp; }
/* * Empty a local directory */ static void empty_dir(VMG_ const char *dir) { /* open the directory search */ osdirhdl_t dirhdl; if (os_open_dir(dir, &dirhdl)) { err_try { /* keep going until we're out of files */ char fname[OSFNMAX]; while (os_read_dir(dirhdl, fname, sizeof(fname))) { /* get the full path */ char path[OSFNMAX]; os_build_full_path(path, sizeof(path), dir, fname); /* get the mode */ unsigned long fmode; unsigned long fattr; if (osfmode(path, FALSE, &fmode, &fattr)) { /* check whether it's a directory or an ordinary file */ if ((fmode & OSFMODE_DIR) != 0) { /* * directory - skip the special '.' and '..' links, * since they'd get us stuck in a loop */ os_specfile_t st = os_is_special_file(fname); if (st != OS_SPECFILE_SELF && st != OS_SPECFILE_PARENT) { /* recursively empty the directory */ empty_dir(vmg_ path); /* remove this directory */ if (!os_rmdir(path)) err_throw(VMERR_DELETE_FILE); } } else { /* ordinary file - delete it */ if (osfdel(path)) err_throw(VMERR_DELETE_FILE); } } } } err_finally { /* close the directory search handle */ os_close_dir(dirhdl); } err_end; }
/* * Scan a resource filename. We build the full filename by combining the * enclosing library path and the given filename, then we call * scan_full_resource(). */ void CTcLibParser::scan_resource(const char *val) { char rel_path[OSFNMAX]; char full_name[OSFNMAX]; /* convert the value from a URL-style path to a local path */ os_cvt_url_dir(rel_path, sizeof(rel_path), val, FALSE); /* build the full name */ os_build_full_path(full_name, sizeof(full_name), lib_path_, rel_path); /* call the full resource filename scanner */ scan_full_resource(val, full_name); }
/* * Open a network file */ CVmNetFile *CVmNetFile::open(VMG_ const vm_val_t *val, const vm_rcdesc *rc, int mode, os_filetype_t typ, const char *mime_type) { vm_val_t filespec; /* check for a TadsObject implementing getFilename */ if (G_predef->filespec_getFilename != VM_INVALID_PROP && val->typ == VM_OBJ && CVmObjTads::is_tadsobj_obj(vmg_ val->val.obj)) { /* call getFilename - the return value is the file spec */ G_interpreter->get_prop( vmg_ 0, val, G_predef->filespec_getFilename, val, 0, rc); /* the result is the real file spec */ filespec = *G_interpreter->get_r0(); } else { /* it's not a TadsObject, so it must directly have the file name */ filespec = *val; } /* check the argument type */ CVmNetFile *nf = 0; if (filespec.typ == VM_OBJ && CVmObjTemporaryFile::is_tmpfil_obj(vmg_ filespec.val.obj)) { /* temporary file object - get the object, properly cast */ CVmObjTemporaryFile *tmp = (CVmObjTemporaryFile *)vm_objp( vmg_ filespec.val.obj); /* if the temporary file object is invalid, it's an error */ if (tmp->get_fname() == 0) err_throw(VMERR_CREATE_FILE); /* create the local file descriptor for the temp file path */ nf = open_local(vmg_ tmp->get_fname(), 0, mode, typ); /* mark it as a temp file */ nf->is_temp = TRUE; } else { /* anything else has to be a string */ char fname[OSFNMAX]; CVmBif::get_str_val_fname(vmg_ fname, sizeof(fname), CVmBif::get_str_val(vmg_ &filespec)); /* * if it's a local file, and it has a relative path, explicitly * apply the image file path as the default working directory */ if (!os_is_file_absolute(fname) && !is_net_mode(vmg0_)) { /* build the full, absolute name based on the image file path */ char fname_abs[OSFNMAX]; os_build_full_path(fname_abs, sizeof(fname_abs), G_image_loader->get_path(), fname); /* replace the relative path with the new absolute path */ lib_strcpy(fname, sizeof(fname), fname_abs); } /* create the regular network file descriptor */ nf = open(vmg_ fname, 0, mode, typ, mime_type); } /* if they gave us an object as our file spec, remember it */ if (nf != 0 && val->typ == VM_OBJ) nf->filespec = val->val.obj; /* return the network file descriptor */ return nf; }
/* * Open a network file */ CVmNetFile *CVmNetFile::open(VMG_ const vm_val_t *val, const vm_rcdesc *rc, int mode, os_filetype_t typ, const char *mime_type) { vm_val_t filespec; /* check for a TadsObject implementing getFilename */ if (G_predef->filespec_getFilename != VM_INVALID_PROP && vm_val_cast_ok(CVmObjTads, val)) { /* call getFilename - the return value is the file spec */ G_interpreter->get_prop( vmg_ 0, val, G_predef->filespec_getFilename, val, 0, rc); /* the result is the real file spec */ filespec = *G_interpreter->get_r0(); } else { /* it's not a TadsObject, so it must directly have the file name */ filespec = *val; } /* check the file spec argument type */ CVmNetFile *nf = 0; CVmObjTemporaryFile *tmp = 0; CVmObjFileName *ofn = 0; if ((tmp = vm_val_cast(CVmObjTemporaryFile, &filespec)) != 0) { /* if the temporary file object is invalid, it's an error */ if (tmp->get_fname() == 0) err_throw(VMERR_CREATE_FILE); /* create the local file descriptor for the temp file path */ nf = open_local(vmg_ tmp->get_fname(), 0, mode, typ); /* mark it as a temp file */ nf->is_temp = TRUE; } else if (filespec.is_numeric(vmg0_) || ((ofn = vm_val_cast(CVmObjFileName, &filespec)) != 0 && ofn->is_special_file())) { /* * It's a special file ID, either as an integer or as a FileName * wrapping a special file int. Get the value. */ int32_t sfid = (ofn != 0 ? ofn->get_sfid() : filespec.num_to_int(vmg0_)); /* resolve the file system path for the given special file ID */ char fname[OSFNMAX] = { '\0' }; if (!CVmObjFile::sfid_to_path(vmg_ fname, sizeof(fname), sfid)) err_throw(VMERR_BAD_VAL_BIF); /* create the special file descriptor */ nf = open(vmg_ fname, sfid, mode, typ, mime_type); } else { /* anything else has to be a string */ char fname[OSFNMAX]; CVmBif::get_fname_val(vmg_ fname, sizeof(fname), &filespec); /* * if it's a local file, and it has a relative path, explicitly * apply the image file path as the default working directory */ if (!os_is_file_absolute(fname) && !is_net_mode(vmg0_)) { /* build the full, absolute name based on the file base path */ char fnabs[OSFNMAX]; os_build_full_path(fnabs, sizeof(fnabs), G_file_path, fname); /* replace the relative path with the new absolute path */ lib_strcpy(fname, sizeof(fname), fnabs); } /* create the regular network file descriptor */ nf = open(vmg_ fname, 0, mode, typ, mime_type); } /* if they gave us an object as our file spec, remember it */ if (nf != 0 && val->typ == VM_OBJ) nf->filespec = val->val.obj; /* return the network file descriptor */ return nf; }
/* Get the local time zone name, as a zoneinfo database key. Many * modern Unix systems use zoneinfo keys as they native timezone * setting, but there are several different ways of storing this on a * per-process and system-wide basis. We'll try looking for the common * mechanisms. */ int os_get_zoneinfo_key( char *buf, size_t buflen ) { /* First, try the TZ environment variable. This is used on nearly * all Unix-alikes for a per-process timezone setting, although * it will only contain a zoneinfo key in newer versions. There * are several possible formats for specifying a zoneinfo key: * * TZ=/usr/share/zoneinfo/America/Los_Angeles * - A full absolute path name to a tzinfo file. We'll sense * this by looking for "/zoneinfo/" in the string, and if we * find it, we'll return the portion after /zoneinfo/. * * TZ=America/Los_Angeles * - Just the zoneinfo key, without a path. If we find a string * that contains all alphabetics, undersores, and slashes, and * has at least one internal slash but doesn't start with a * slash, we probably have a zoneinfo key. We'll see if we * can find a matching file in the usual zoneinfo database * locations: /etc/zoneinfo, /usr/share/zoneinfo; if we can, * we'll return the key name, otherwise we'll assume this * isn't actually a zoneinfo key but just happens to look like * one in terms of format. * * TZ=:America/Los_Angeles * TZ=:/etc/zoneinfo/America/Los_Angeles * - POSIX systems generally use the ":" prefix to signify that * this is a zoneinfo path rather than the old-style "EST5EDT" * type of self-contained zone description. If we see a colon * prefix with a relative path (properly formed in terms of * its character content), we'll simply assume this is a * zoneinfo key without even checking for an existing file, * since there's not much else it could be. If we see an * absolute path, we'll search it for /zoneinfo/ and return * the portion after this, again without checking for an * existing file. */ const char *tz = getenv("TZ"); if (tz != 0 && tz[0] != '\0') { /* check that the string is formatted like a zoneinfo key */ #define tzcharok(c) (isalpha(c) != 0 || (c) == '/' || (c) == '_') int fmt_ok = TRUE; const char *p; fmt_ok &= (tz[0] == ':' || tzcharok(tz[0])); for (p = tz + 1 ; *p != '\0' ; ++p) fmt_ok &= tzcharok(*p); /* proceed only if it has the right format */ if (fmt_ok) { /* check for a leading ':', per POSIX */ if (tz[0] == ':') { /* yes, we have a leading ':', so it's almost certainly * a zoneinfo key; if it's an absolute path, find the * part after /zoneinfo/ */ if (tz[1] == '/') { /* absolute form - look for /zoneinfo/ */ const char *z = strstr(tz, "/zoneinfo/"); if (z != 0) { /* found it - return the part after /zoneinfo/ */ safe_strcpy(buf, buflen, z + 10); return TRUE; } } else { /* relative path - return as-is minus the colon */ safe_strcpy(buf, buflen, tz + 1); return TRUE; } } else { /* There's no colon, so it *might* be a zoneinfo key. * If it's an absolute path containing /zoneinfo/, it's * a solid bet. If it's a relative path, look to see * if we can find a file in one of the usual zoneinfo * database locations. */ if (tz[0] == '/') { /* absolute path - check for /zoneinfo/ */ const char *z = strstr(tz, "/zoneinfo/"); if (z != 0) { /* found it - return the part after /zoneinfo/ */ safe_strcpy(buf, buflen, z + 10); return TRUE; } } else { /* relative path - look for a tzinfo file in the * usual locations */ static const char *dirs[] = { "/etc/zoneinfo", "/usr/share/zoneinfo", 0 }; const char **dir; for (dir = dirs ; *dir != 0 ; ++dir) { /* build this full path */ char fbuf[OSFNMAX]; os_build_full_path(fbuf, sizeof(fbuf), *dir, tz); /* check for a file at this location */ if (!osfacc(fbuf)) { /* got it - looks like a good zoneinfo key */ safe_strcpy(buf, buflen, tz); return TRUE; } } } } } } /* No luck with TZ, so try the system-wide settings next. * * If a file called /etc/timezone exists, it's usually a one-line * text file containing the zoneinfo key. Read and return its * contents. */ FILE *fp; if ((fp = fopen("/etc/timezone", "r")) != 0) { /* read the one-liner */ char lbuf[256]; int ok = FALSE; if (fgets(lbuf, sizeof(lbuf), fp) != 0) { /* strip any trailing newline */ size_t l = strlen(lbuf); if (l != 0 && lbuf[l-1] == '\n') lbuf[l-1] = '\0'; /* if it's in absolute format, return the part after * /zoneinfo/; otherwise just return the string */ if (lbuf[0] == '/') { /* absoltue path - find /zoneinfo/ */ const char *z = strstr(lbuf, "/zoneinfo/"); if (z != 0) { safe_strcpy(buf, buflen, z + 10); ok = TRUE; } } else { /* relative notation - return it as-is */ safe_strcpy(buf, buflen, lbuf); ok = TRUE; } } /* we're done with the file */ fclose(fp); /* if we got our result, return success */ if (ok) return TRUE; } /* If /etc/sysconfig/clock exists, read it and look for a line * starting with ZONE=. This contains the zoneinfo key. */ if ((fp = fopen("/etc/sysconfig/clock", "r")) != 0) { /* scan the file for ZONE=... */ int ok = FALSE; for (;;) { /* read the next line */ char lbuf[256]; if (fgets(lbuf, sizeof(lbuf), fp) == 0) break; /* skip leading spaces */ const char *p; for (p = lbuf ; isspace(*p) ; ++p) ; /* check for ZONE */ if (memicmp(p, "zone", 4) != 0) continue; /* skip spaces after ZONE */ for (p += 4 ; isspace(*p) ; ++p) ; /* check for '=' */ if (*p != '=') continue; /* skip spaces after the '=' */ for (++p ; isspace(*p) ; ++p) ; /* if it's in absolute form, look for /zoneinfo/ */ if (*p == '/') { const char *z = strstr(p, "/zoneinfo/"); if (z != 0) { safe_strcpy(buf, buflen, z + 10); ok = TRUE; } } else { /* relative notation - it's the zoneinfo key */ safe_strcpy(buf, buflen, p); ok = TRUE; } /* that's our ZONE line, so we're done scanning the file */ break; } /* done with the file */ fclose(fp); /* if we got our result, return success */ if (ok) return TRUE; } /* If /etc/localtime is a symbolic link, the linked file is the * actual zoneinfo file. Resolve the link and return the portion * of the path after "/zoneinfo/". */ #if 0 static const char *elt = "/etc/localtime"; unsigned long mode; char linkbuf[OSFNMAX]; const char *zi; if (osfmode(elt, FALSE, &mode, NULL) && (mode & OSFMODE_LINK) != 0 && os_resolve_symlink(elt, linkbuf, sizeof(linkbuf)) && (zi = strstr(linkbuf, "/zoneinfo/")) != 0) { /* it's a link containing /zoneinfo/, so return the portion * after /zoneinfo/ */ safe_strcpy(buf, buflen, zi + 10); return TRUE; } #endif /* well, we're out of ideas - return failure */ return FALSE; }
/* * 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); } }