/* * 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; }
/* * 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; }
/* * Is the given file in the given directory? */ int os_is_file_in_dir(const char *filename, const char *path, int allow_subdirs, int match_self) { char filename_buf[OSFNMAX], path_buf[OSFNMAX]; size_t flen, plen; int endsep = FALSE; /* absolute-ize the filename, if necessary */ if (!os_is_file_absolute(filename)) { os_get_abs_filename(filename_buf, sizeof(filename_buf), filename); filename = filename_buf; } /* absolute-ize the path, if necessary */ if (!os_is_file_absolute(path)) { os_get_abs_filename(path_buf, sizeof(path_buf), path); path = path_buf; } /* * canonicalize the paths, to remove .. and . elements - this will make * it possible to directly compare the path strings */ safe_strcpy(filename_buf, sizeof(filename_buf), filename); canonicalize_path(filename_buf); filename = filename_buf; safe_strcpy(path_buf, sizeof(path_buf), path); canonicalize_path(path_buf); path = path_buf; /* get the length of the filename and the length of the path */ flen = strlen(filename); plen = strlen(path); /* * If the path ends in a separator character, ignore that. Exception: * if the path is a root path, leave the separator intact. */ if (plen > 0 && (path[plen-1] == '\\' || path[plen-1] == '/') && !is_root_path(path)) --plen; /* if the path still ends in a path separator, so note */ endsep = (plen > 0 && (path[plen-1] == '\\' || path[plen-1] == '/')); /* * if the names match, return true if and only if the caller wants * us to match the directory to itself */ if (flen == plen && memicmp(filename, path, flen) == 0) return match_self; /* * Check that the filename has 'path' as its path prefix. First, check * that the leading substring of the filename matches 'path', ignoring * case. Note that we need the filename to be at least two characters * longer than the path: it must have a path separator after the path * name, and at least one character for a filename past that. * Exception: if the path already ends in a path separator, we don't * need to add another one, so the filename just needs to be one * character longer. */ if (flen < plen + (endsep ? 1 : 2) || !patheq(filename, path, plen)) return FALSE; /* * Okay, 'path' is the leading substring of 'filename'; next make sure * that this prefix actually ends at a path separator character in the * filename. (This is necessary so that we don't confuse "c:\a\b.txt" * as matching "c:\abc\d.txt" - if we only matched the "c:\a" prefix, * we'd miss the fact that the file is actually in directory "c:\abc", * not "c:\a".) If the path itself ends in a path separator, we've * made this check already simply by the substring match. */ if (!endsep && (filename[plen] != '\\' && filename[plen] != '/')) return FALSE; /* * We're good on the path prefix - we definitely have a file that's * within the 'path' directory or one of its subdirectories. If we're * allowed to match on subdirectories, we already have our answer * (true). If we're not allowed to match subdirectories, we still have * one more check, which is that the rest of the filename is free of * path separator charactres. If it is, we have a file that's directly * in the 'path' directory; otherwise it's in a subdirectory of 'path' * and thus isn't a match. */ if (allow_subdirs) { /* * filename is in the 'path' directory or one of its * subdirectories, and we're allowed to match on subdirectories, so * we have a match */ return TRUE; } else { const char *p; /* * We're not allowed to match subdirectories, so scan the rest of * the filename for path separators. If we find any, the file is * in a subdirectory of 'path' rather than directly in 'path' * itself, so it's not a match. If we don't find any separators, * we have a file directly in 'path', so it's a match. */ for (p = filename + plen + (endsep ? 1 : 0) ; *p != '\0' && *p != '/' && *p != '\\' ; ++p) ; /* * if we reached the end of the string without finding a path * separator character, it's a match */ return (*p == '\0'); } }