44 (
"vfs-case-sensitive",
    51  PRC_DESC(
"Set this true to make the VirtualFileSystem present the native "    52           "OS-provided filesystem as if it were a case-sensitive file "    53           "system, even if it is not (e.g. on Windows).  This variable "    54           "has no effect if the native filesystem is already case-sensitive, "    55           "and it has no effect on mounted multifile systems, which are "    56           "always case-sensitive.")),
    58   (
"vfs-implicit-pz", true,
    59    PRC_DESC(
"When this is true, the VirtualFileSystem will pretend a named "    60             "file exists even if it doesn't, as long as a filename with the "    61             "same name and the additional extension .pz does exist.  In this "    62             "case, the VirtualFileSystem will implicitly open the .pz file "    63             "and decompress it on-the-fly.")),
    65   (
"vfs-implicit-mf", false,
    66    PRC_DESC(
"When this is true, the VirtualFileSystem will automatically "    67             "mount multifiles on-the-fly when they are used as directories.  "    68             "For instance, opening the file /c/files/foo.mf/dirname/mytex.jpg "    69             "will implicitly retrieve a file named 'dirname/mytex.jpg' "    70             "within the multifile /c/files/foo.mf, even if the multifile "    71             "has not already been mounted.  This makes all of your multifiles "    72             "act like directories."))
    82 ~VirtualFileSystem() {
    93   return mount(new_mount, mount_point, flags);
   118       int flags, 
const string &password) {
   119   if (!physical_filename.
exists()) {
   120     express_cat->warning()
   121       << 
"Attempt to mount " << physical_filename << 
", not found.\n";
   128     return mount(new_mount, mount_point, flags);
   136     flags |= MF_read_only;
   137     if (!multifile->open_read(physical_filename)) {
   141     return mount(multifile, mount_point, flags);
   159            int flags, 
const string &password) {
   161   if (file == 
nullptr) {
   162     express_cat->warning()
   163       << 
"Attempt to mount " << virtual_filename << 
", not found.\n";
   167   if (file->is_directory()) {
   170     return mount(new_mount, mount_point, flags);
   179     flags |= MF_read_only;
   180     if (!multifile->open_read(virtual_filename)) {
   184     return mount(multifile, mount_point, flags);
   195   if (express_cat->is_debug()) {
   197       << 
"mount " << *
mount << 
" under " << mount_point << 
"\n";
   201   bool result = do_mount(
mount, mount_point, flags);
   213   Mounts::iterator ri, wi;
   214   wi = ri = _mounts.begin();
   215   while (ri != _mounts.end()) {
   219     if (
mount->is_exact_type(VirtualFileMountMultifile::get_class_type())) {
   224         if (express_cat->is_debug()) {
   226             << 
"unmount " << *
mount << 
" from " << 
mount->get_mount_point() << 
"\n";
   228         mount->_file_system = 
nullptr;
   241   int num_removed = _mounts.end() - wi;
   242   _mounts.erase(wi, _mounts.end());
   255   Mounts::iterator ri, wi;
   256   wi = ri = _mounts.begin();
   257   while (ri != _mounts.end()) {
   261     if (
mount->is_exact_type(VirtualFileMountSystem::get_class_type())) {
   266         if (express_cat->is_debug()) {
   268             << 
"unmount " << *
mount << 
" from " << 
mount->get_mount_point() << 
"\n";
   270         mount->_file_system = 
nullptr;
   277     } 
else if (
mount->is_exact_type(VirtualFileMountMultifile::get_class_type())) {
   282         if (express_cat->is_debug()) {
   284             << 
"unmount " << *
mount << 
" from " << 
mount->get_mount_point() << 
"\n";
   286         mount->_file_system = 
nullptr;
   300   int num_removed = _mounts.end() - wi;
   301   _mounts.erase(wi, _mounts.end());
   314   Mounts::iterator ri, wi;
   315   wi = ri = _mounts.begin();
   316   while (ri != _mounts.end()) {
   318     if ((*ri) == 
mount) {
   320       if (express_cat->is_debug()) {
   322           << 
"unmount " << *
mount << 
" from " << 
mount->get_mount_point() << 
"\n";
   324       (*ri)->_file_system = 
nullptr;
   333   int num_removed = _mounts.end() - wi;
   334   _mounts.erase(wi, _mounts.end());
   347   Filename nmp = normalize_mount_point(mount_point);
   348   Mounts::iterator ri, wi;
   349   wi = ri = _mounts.begin();
   350   while (ri != _mounts.end()) {
   354     if (
mount->get_mount_point() == nmp) {
   356       if (express_cat->is_debug()) {
   358           << 
"unmount " << *
mount << 
" from " << 
mount->get_mount_point() << 
"\n";
   360       mount->_file_system = 
nullptr;
   369   int num_removed = _mounts.end() - wi;
   370   _mounts.erase(wi, _mounts.end());
   384   for (ri = _mounts.begin(); ri != _mounts.end(); ++ri) {
   386     if (express_cat->is_debug()) {
   388         << 
"unmount " << *
mount << 
" from " << 
mount->get_mount_point() << 
"\n";
   390     mount->_file_system = 
nullptr;
   393   int num_removed = _mounts.size();
   403 int VirtualFileSystem::
   404 get_num_mounts()
 const {
   406   int result = _mounts.size();
   417   nassertd(n >= 0 && n < (
int)_mounts.size()) {
   434   if (new_directory == 
"/") {
   436     _cwd = new_directory;
   441   PT(
VirtualFile) file = do_get_file(new_directory, OF_status_only);
   442   if (file != 
nullptr && file->is_directory()) {
   443     _cwd = file->get_filename();
   471   PT(
VirtualFile) result = do_get_file(filename, OF_make_directory);
   473   nassertr_always(result != 
nullptr, 
false);
   474   return result->is_directory();
   489   string dirname = filename;
   490   size_t slash = dirname.find(
'/', 1);
   491   while (slash != string::npos) {
   492     Filename component(dirname.substr(0, slash));
   493     do_get_file(component, OF_make_directory);
   494     slash = dirname.find(
'/', slash + 1);
   498   PT(
VirtualFile) result = do_get_file(filename, OF_make_directory);
   500   nassertr_always(result != 
nullptr, 
false);
   501   return result->is_directory();
   517   int open_flags = status_only ? OF_status_only : 0;
   519   PT(
VirtualFile) result = do_get_file(filename, open_flags);
   533   PT(
VirtualFile) result = do_get_file(filename, OF_create_file);
   545           bool status_only)
 const {
   547     return get_file(filename, status_only);
   551   for (
int i = 0; i < num_directories; ++i) {
   562     if (found_file != 
nullptr) {
   578   if (file == 
nullptr) {
   582   return file->delete_file();
   599   PT(
VirtualFile) orig_file = do_get_file(orig_filename, OF_status_only);
   600   if (orig_file == 
nullptr) {
   605   PT(
VirtualFile) new_file = do_get_file(new_filename, OF_status_only | OF_allow_nonexist);
   606   if (new_file == 
nullptr) {
   613   return orig_file->rename_file(new_file);
   623   if (orig_file == 
nullptr) {
   628   if (new_file == 
nullptr) {
   632   return orig_file->copy_file(new_file);
   643                  const string &default_extension)
 const {
   647     found = 
find_file(filename, searchpath, 
true);
   649     if (found.is_null()) {
   652       if (filename.
get_extension().empty() && !default_extension.empty()) {
   655         found = 
find_file(try_ext, searchpath, 
true);
   665       if (filename.
get_extension().empty() && !default_extension.empty()) {
   673   if (!found.is_null()) {
   674     filename = found->get_original_filename();
   696     for (
int i = 0; i < num_directories; ++i) {
   723   Mounts::const_iterator mi;
   724   for (mi = _mounts.begin(); mi != _mounts.end(); ++mi) {
   743   if (_global_ptr == 
nullptr) {
   750     _global_ptr->
mount(
"/", 
"/", 0);
   753     _global_ptr->
chdir(ExecutionEnvironment::get_cwd());
   758        PRC_DESC(
"vfs-mount system-filename mount-point [options]"));
   761     for (
int i = 0; i < num_unique_values; i++) {
   774       size_t space = mount_desc.rfind(
' ');
   775       if (space == string::npos) {
   776         express_cat.warning()
   777           << 
"No space in vfs-mount descriptor: " << mount_desc << 
"\n";
   780         string mount_point = mount_desc.substr(space + 1);
   781         while (space > 0 && isspace(mount_desc[space - 1])) {
   784         mount_desc = mount_desc.substr(0, space);
   787         space = mount_desc.rfind(
' ');
   788         if (space != string::npos) {
   790           options = mount_point;
   791           mount_point = mount_desc.substr(space + 1);
   792           while (space > 0 && isspace(mount_desc[space - 1])) {
   795           mount_desc = mount_desc.substr(0, space);
   804         _global_ptr->
mount(physical_filename, mount_point, flags, password);
   809       (
"vfs-mount-ramdisk", 
"",
   810        PRC_DESC(
"vfs-mount-ramdisk mount-point [options]"));
   811     if (!vfs_mount_ramdisk.empty()) {
   812       string mount_point = vfs_mount_ramdisk;
   815       size_t space = mount_point.rfind(
' ');
   816       if (space != string::npos) {
   818         options = mount_point.substr(space + 1);
   819         while (space > 0 && isspace(mount_point[space - 1])) {
   822         mount_point = mount_point.substr(0, space);
   830       _global_ptr->
mount(ramdisk, mount_point, flags);
   849   if (file == 
nullptr) {
   852   istream *str = file->open_read_file(auto_unwrap);
   853   if (str != 
nullptr && str->fail()) {
   868   if (stream != 
nullptr) {
   873 #if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW)   875     (*global_operator_delete)(stream);
   893   if (file == 
nullptr) {
   896   ostream *str = file->open_write_file(auto_wrap, truncate);
   897   if (str != 
nullptr && str->fail()) {
   912   if (file == 
nullptr) {
   915   ostream *str = file->open_append_file();
   916   if (str != 
nullptr && str->fail()) {
   931   if (stream != 
nullptr) {
   932 #if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW)   934     (*global_operator_delete)(stream);
   949   if (file == 
nullptr) {
   952   iostream *str = file->open_read_write_file(truncate);
   953   if (str != 
nullptr && str->fail()) {
   968   if (file == 
nullptr) {
   971   iostream *str = file->open_read_append_file();
   972   if (str != 
nullptr && str->fail()) {
   987   if (stream != 
nullptr) {
   988 #if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW)   990     (*global_operator_delete)(stream);
  1002                                      const string &old_contents,
  1003                                      const string &new_contents) {
  1005   if (file == 
nullptr) {
  1009   return file->atomic_compare_and_exchange_contents(orig_contents, old_contents, new_contents);
  1018   if (file == 
nullptr) {
  1022   return file->atomic_read_contents(contents);
  1035   nassertv(!path.empty() && !path.
is_local());
  1037   Mounts::const_iterator mi;
  1038   for (mi = _mounts.begin(); mi != _mounts.end(); ++mi) {
  1041     string mount_point = 
mount->get_mount_point();
  1042     if (prefix.empty()) {
  1044       if (mount_point.find(
'/') == string::npos) {
  1047         names.push_back(mount_point);
  1050       if (mount_point.substr(0, prefix.length()) == prefix &&
  1051           mount_point.length() > prefix.length() &&
  1052           mount_point[prefix.length()] == 
'/') {
  1055         string basename = mount_point.substr(prefix.length());
  1056         if (basename.find(
'/') == string::npos) {
  1058           names.push_back(basename);
  1073   password = string();
  1077   size_t q = options.find(
',', p);
  1078   while (q != string::npos) {
  1082     q = options.find(
',', p);
  1093   if (option == 
"0" || option.empty()) {
  1095   } 
else if (option == 
"ro") {
  1096     flags |= MF_read_only;
  1097   } 
else if (option.substr(0, 3) == 
"pw:") {
  1098     password = option.substr(3);
  1100     express_cat.warning()
  1101       << 
"Invalid option on vfs-mount: \"" << option << 
"\"\n";
  1113 normalize_mount_point(
const Filename &mount_point)
 const {
  1119   nassertr(!nmp.empty() && nmp[0] == 
'/', nmp);
  1126 bool VirtualFileSystem::
  1128   nassertr(
mount->_file_system == 
nullptr, 
false);
  1129   mount->_file_system = 
this;
  1130   mount->_mount_point = normalize_mount_point(mount_point);
  1131   mount->_mount_flags = flags;
  1132   _mounts.push_back(
mount);
  1142 do_get_file(
const Filename &filename, 
int open_flags)
 const {
  1143   if (filename.empty()) {
  1147   if (pathname.is_local()) {
  1148     pathname = 
Filename(_cwd, filename);
  1150       pathname.set_text();
  1153   pathname.standardize();
  1157   Filename strpath_pz = strpath + 
".pz";
  1166   unsigned int start_seq = _mount_seq;
  1168   size_t i = _mounts.size();
  1173     if (strpath == mount_point) {
  1176       if (consider_match(found_file, composite_file, 
mount, 
"", pathname,
  1177                          false, open_flags)) {
  1180     } 
else if (mount_point.empty()) {
  1182       if (consider_match(found_file, composite_file, 
mount, strpath,
  1183                          pathname, 
false, open_flags)) {
  1187       if (vfs_implicit_pz) {
  1188         if (consider_match(found_file, composite_file, 
mount, strpath_pz,
  1189                            pathname, 
true, open_flags)) {
  1195     } 
else if (strpath.length() > mount_point.length() &&
  1196                mount_point == strpath.substr(0, mount_point.length()) &&
  1197                strpath[mount_point.length()] == 
'/') {
  1199       Filename local_filename = strpath.substr(mount_point.length() + 1);
  1200       Filename local_filename_pz = strpath_pz.substr(mount_point.length() + 1);
  1201       if (consider_match(found_file, composite_file, 
mount, local_filename,
  1202                          pathname, 
false, open_flags)) {
  1206       if (vfs_implicit_pz) {
  1208         if (consider_match(found_file, composite_file, 
mount, local_filename_pz,
  1209                            pathname, 
true, open_flags)) {
  1218     if (start_seq != _mount_seq) {
  1219       start_seq = _mount_seq;
  1224   if (found_file == 
nullptr && vfs_implicit_mf) {
  1229     if (start_seq != _mount_seq) {
  1232       return do_get_file(filename, open_flags);
  1236 #if defined(_WIN32) && !defined(NDEBUG)  1240     if (filename.length() > 2 && isalpha(filename[0]) && filename[1] == 
':' &&
  1241         (filename[2] == 
'\\' || filename[2] == 
'/')) {
  1244       if (corrected_fn.
exists()) {
  1245         express_cat.warning()
  1246           << 
"Filename uses Windows-style path: " << filename << 
"\n";
  1247         express_cat.warning()
  1248           << 
"  expected Unix-style path: " << corrected_fn << 
"\n";
  1266 bool VirtualFileSystem::
  1269                const Filename &original_filename, 
bool implicit_pz_file,
  1270                int open_flags)
 const {
  1272     mount->make_virtual_file(local_filename, original_filename, 
false, open_flags);
  1273   if (!vfile->has_file() && ((open_flags & OF_allow_nonexist) == 0)) {
  1278   if (found_file == 
nullptr) {
  1281     if (!found_file->
is_directory() || ((open_flags & OF_make_directory) != 0)) {
  1287     if (implicit_pz_file) {
  1289       found_file = 
nullptr;
  1295     if (!vfile->is_directory()) {
  1300     if (!implicit_pz_file) {
  1303       if (composite_file == 
nullptr) {
  1308         found_file = composite_file;
  1327 bool VirtualFileSystem::
  1328 consider_mount_mf(
const Filename &filename) {
  1330   if (dirname.empty() || dirname == filename) {
  1342     PT(
VirtualFile) file = do_get_file(dirname, 
false);
  1343     if (file == 
nullptr || !file->is_regular_file()) {
  1350     istream *stream = file->open_read_file(
false);
  1351     if (stream == 
nullptr) {
  1360     if (!multifile->open_read(streamw, 
true)) {
  1367       << 
"Implicitly mounting " << dirname << 
"\n";
  1371     return do_mount(new_mount, dirname, MF_read_only);
  1375   return consider_mount_mf(dirname);
 Filename get_filename_index(int index) const
If the pattern flag is set for this Filename and the filename string actually includes a sequence of ...
bool copy_file(const Filename &orig_filename, const Filename &new_filename)
Attempts to copy the contents of the indicated file to the indicated file.
std::iostream * open_read_append_file(const Filename &filename)
Works like open_read_write_file(), but the file is opened in append mode.
PointerTo< VirtualFile > find_file(const Filename &filename, const DSearchPath &searchpath, bool status_only=false) const
Uses the indicated search path to find the file within the file system.
std::string get_dirname() const
Returns the directory part of the filename.
void set_extension(const std::string &s)
Replaces the file extension.
bool rename_file(const Filename &orig_filename, const Filename &new_filename)
Attempts to move or rename the indicated file or directory.
Multifile * get_multifile() const
Returns the Multifile pointer that this mount object is based on.
int find_all_files(const Filename &filename, const DSearchPath &searchpath, DSearchPath::Results &results) const
Searches all the directories in the search list for the indicated file, in order.
static void close_read_write_file(std::iostream *stream)
Closes a file opened by a previous call to open_read_write_file().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
size_t get_num_unique_values() const
Returns the number of unique values in the variable.
Maps an actual OS directory into the VirtualFileSystem.
A hierarchy of directories and files that appears to be one continuous file system,...
std::istream * open_read_file(const Filename &filename, bool auto_unwrap) const
Convenience function; returns a newly allocated istream if the file exists and can be read,...
void add_component(VirtualFile *file)
Adds one more component to the composite directory.
void set_type(Type type)
Sets the type of the file represented by the filename.
void set_binary()
Indicates that the filename represents a binary file.
void set_original_filename(const Filename &filename)
Stores the original filename that was used to locate this VirtualFile.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void init_libexpress()
Initializes the library.
bool atomic_read_contents(const Filename &filename, std::string &contents) const
See Filename::atomic_read_contents().
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
bool make_directory_full(const Filename &filename)
Attempts to create a directory within the file system.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
The abstract base class for a file or directory within the VirtualFileSystem.
bool is_fully_qualified() const
Returns true if the filename is fully qualified, e.g.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class is similar to ConfigVariable, but it reports its value as a list of strings.
PointerTo< VirtualFile > create_file(const Filename &filename)
Attempts to create a file by the indicated name in the filesystem, if possible, and returns it.
void standardize()
Converts the filename to standard form by replacing consecutive slashes with a single slash,...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Simulates an actual directory on disk with in-memory storage.
Type get_type() const
Returns the type of the file represented by the filename, as previously set by set_type().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
static void parse_options(const std::string &options, int &flags, std::string &password)
Parses all of the option flags in the options list on the vfs-mount Config.prc line.
get_num_directories
Returns the number of directories on the search list.
bool exists(const Filename &filename) const
Convenience function; returns true if the named file exists.
bool mount(Multifile *multifile, const Filename &mount_point, int flags)
Mounts the indicated Multifile at the given mount point.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool is_directory(const Filename &filename) const
Convenience function; returns true if the named file exists and is a directory.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
The name of a file, such as a texture file or an Egg file.
This is a convenience class to specialize ConfigVariable as a string type.
static std::string expand_string(const std::string &str)
Reads the string, looking for environment variable names marked by a $.
This class provides a locking wrapper around an arbitrary istream pointer.
bool make_directory(const Filename &filename)
Attempts to create a directory within the file system.
virtual bool is_directory() const
Returns true if this file represents a directory (and scan_directory() may be called),...
std::string get_fullpath() const
Returns the entire filename: directory, basename, extension.
bool chdir(const Filename &new_directory)
Changes the current directory.
std::ostream * open_append_file(const Filename &filename)
Works like open_write_file(), but the file is opened in append mode.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
void scan_mount_points(vector_string &names, const Filename &path) const
Adds to names a list of all the mount points in use that are one directory below path,...
const Filename & get_physical_filename() const
Returns the name of the source file on the OS filesystem of the directory or file that is mounted.
int unmount_point(const Filename &mount_point)
Unmounts all systems attached to the given mount point from the file system.
void set_encryption_password(const std::string &encryption_password)
Specifies the password that will be used to encrypt subfiles subsequently added to the multifile,...
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
const Filename & get_original_filename() const
Returns the original filename as it was used to locate this VirtualFile.
int unmount(Multifile *multifile)
Unmounts all appearances of the indicated Multifile from the file system.
std::string get_unique_value(size_t n) const
Returns the nth unique value of the variable.
bool is_local() const
Returns true if the filename is local, e.g.
bool is_text() const
Returns true if the Filename has been indicated to represent a text file via a previous call to set_t...
std::string get_extension() const
Returns the file extension.
std::string get_basename() const
Returns the basename part of the filename.
static void close_write_file(std::ostream *stream)
Closes a file opened by a previous call to open_write_file().
static void parse_option(const std::string &option, int &flags, std::string &password)
Parses one of the option flags in the options list on the vfs-mount Config.prc line.
bool mount_loop(const Filename &virtual_filename, const Filename &mount_point, int flags, const std::string &password="")
This is similar to mount(), but it receives the name of a Multifile that already appears within the v...
bool is_directory() const
Returns true if the filename exists and is a directory name, false otherwise.
std::ostream * open_write_file(const Filename &filename, bool auto_wrap, bool truncate)
Convenience function; returns a newly allocated ostream if the file exists and can be written,...
The abstract base class for a mount definition used within a VirtualFileSystem.
std::iostream * open_read_write_file(const Filename &filename, bool truncate)
Convenience function; returns a newly allocated iostream if the file exists and can be written,...
get_directory
Returns the nth directory on the search list.
A file that contains a set of files.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void write(std::ostream &out) const
Print debugging information.
This class stores a list of directories that can be searched, in order, to locate a particular file.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Filename get_cwd() const
Returns the current directory name.
get_mount
Returns the nth mount in the system.
int unmount_all()
Unmounts all files from the file system.
const Filename & get_multifile_name() const
Returns the filename of the Multifile, if it is available.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool delete_file(const Filename &filename)
Attempts to delete the indicated file or directory.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A composite directory within the VirtualFileSystem: this maps to more than one directory on different...
Maps a Multifile's contents into the VirtualFileSystem.
bool atomic_compare_and_exchange_contents(const Filename &filename, std::string &orig_contents, const std::string &old_contents, const std::string &new_contents)
See Filename::atomic_compare_and_exchange_contents().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
void add_file(const Filename &file)
Adds a new file to the result list.
static Filename from_os_specific(const std::string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes,...