bool vfeParsePathString (const UCS2String& path, UCS2String& volume, vector<UCS2String>& components, UCS2String& filename) { UCS2String q; if(path.empty() == true) return true; if(path[0] == '/') volume = '/'; for(size_t i = 0; i < path.length(); ++i) { if(path[i] == '/') { if(q.empty() == false) components.push_back(q); q.clear(); } else q += path[i]; } filename = q; return true; }
UCS2String TemporaryFile::SuggestName() { POV_ASSERT(!gTempPath.empty()); // TODO FIXME - This allows only one temporary file per process! // TODO FIXME - Avoid converting back and forth between UCS-2 and system-specific encoding. char str [POV_FILENAME_BUFFER_CHARS + 1] = ""; std::snprintf(str, POV_FILENAME_BUFFER_CHARS + 1, "%spov%d", UCS2toSysString(gTempPath).c_str(), int(getpid())); return SysToUCS2String(str); }
bool vfeParsePathString (const UCS2String& path, UCS2String& volume, vector<UCS2String>& components, UCS2String& filename) { char str [MAX_PATH * 4] ; volume.clear() ; filename.clear() ; components.clear() ; if (path.empty() == true) return (true); if (path.size () >= sizeof (str)) return (false) ; strcpy (str, UCS2toASCIIString (path).c_str ()) ; char lastch = str[strlen(str) - 1]; // now determine if it's a network or drive path. // (we could use the shlwapi functions here but I'd rather avoid the shlwapi.dll dependency). char *p2 = str ; char *p3 ; if ((strlen (str) > 1) && ((str [1] == ':') || (str[0] == '\\' && str[1] == '\\') || (str[0] == '/' && str[1] == '/'))) { if (str [1] == ':') { // if it's a drive reference the first character must be in range 'a' - 'z' if (!isalpha (str[0])) return (false) ; // currently we don't support relative paths if a volume is specified if ((str [2] != '\\') && (str [2] != '/')) return (false) ; volume = ASCIItoUCS2String (string (str).substr (0, 3).c_str()) ; p2 += 3 ; } else { // it's a UNC path ... look for the next separator p2 = strchr (str + 2, '\\'); p3 = strchr (str + 2, '/'); if ((p3 != NULL) && ((p2 == NULL) || (p2-str) > (p3-str))) p2 = p3; if (p2 == NULL) { // no separator; technically this is valid, but it's a relative reference // and as above we don't currently support this. return (false) ; } volume = ASCIItoUCS2String (string (str).substr (0, (size_t) (++p2 - str)).c_str()) ; } } else if ((str [0] == '\\') || (str [0] == '/')) { // it's a path relative to the root of the current drive. // we will use '\' as the volume name. // for volume-relative paths we also accept '/' as a path separator volume = ASCIItoUCS2String("\\"); p2++; } // p2 now points at the start of any path or file components // the first call to strtok will skip over any extra separators // at the start of the path for (char *p1 = strtok (p2, "\\/"); p1 != NULL; p1 = strtok (NULL, "/\\")) { if (*p1 == '\0') continue; if (p1[0] == '.' && p1[1] == '\0') continue; if (p1[0] == '.' && p1[1] == '.' && p1[2] == '\0') { // it's a relative directory reference ... see if we can pop a // path from components; if not we leave it in there since it // is permitted to refer to a directory above the CWD if ((components.empty() == false) && (components.back() != ASCIItoUCS2String(".."))) { components.pop_back(); continue; } } components.push_back (ASCIItoUCS2String (p1)) ; } // the filename, if present, will be the last entry in components. // we first check the last character of the supplied path to see // if it's a path separator char; if it is there's no filename. if (lastch == '\\' || lastch == '/') return true; if (components.empty() == false) { filename = components.back(); components.pop_back(); } return true ; }
bool Path::ParsePathString (UCS2String& volume, vector<UCS2String>& dirnames, UCS2String& filename, const UCS2String& path) { UCS2String stash; // Unless noted otherwise, all components are considered empty. volume.clear(); dirnames.clear(); filename.clear(); // Empty strings are considered valid path names, too. if (path.empty() == true) return true; UCS2String::const_iterator i = path.begin(); // Check for a volume identifier: // - If the second character is a colon, we presume the first two characters to identify the drive. In that case // we'll also check whether the following character is a path separator, indicating an absolute path on that // drive, in which case we'll also include a trailing separator to the drive letter. // - If the path starts with two identical path separator characters, we presume the string to be a UNC path, in // which case we set the volume identifier to the network share, including two leading and a trailing separator // characters. // - Otherwise, if the first character is a path separator, this indicates an absolute path on the current drive, // in which case we set the volume identifier to a single path separator character. // - In any other case, we presume the string to be a relative path, and set the volume identifier to an empty // string. if ((*i == POV_PATH_SEPARATOR) || (*i == POV_PATH_SEPARATOR_2)) { // String starts with a path separator; may be an absolute path on the current drive or a UNC path. // Stash the separator (use the canonical one, not the one actually used). stash += POV_PATH_SEPARATOR; ++i; if ((i != path.end()) && (*i == stash[0])) { // The second character is also an (identical) separator character, indicating a UNC path. // Stash another path separators (use the canonical one, not the one actually used). stash += POV_PATH_SEPARATOR; ++i; // Stash everything that follows, up to the next path separator. for (; (i != path.end()) && (*i != POV_PATH_SEPARATOR) && (*i != POV_PATH_SEPARATOR_2); ++i) stash += *i; // Currently, we don't support bare UNC share names without trailing separator character, // even though allegedly they are technically valid. if (i == path.end()) return false; // Stash another path separator (use the canonical one, not the one actually used) // to indicate an absolute path. stash += POV_PATH_SEPARATOR; ++i; } // If it's not a UNC path, at this point our stash contains a single path separator, // which is exactly what we intend to emit as the volume identifier in that case. // Emit whatever string we have stashed as the volume identifier. volume = stash; stash.clear(); } else if (isalpha (*i)) { // String starts with an ASCII letter; may be a relative path or a drive letter. // Stash the character, then go on to test what's next. stash += *i; ++i; if ((i != path.end()) && (*i == ':')) { // Yup, that's a drive letter. Add the colon to the stashed letter. stash += ':'; ++i; // Currently, we don't support relative paths if a volume is specified. if ((i == path.end()) || ((*i != POV_PATH_SEPARATOR) && (*i != POV_PATH_SEPARATOR_2))) return false; // Stash another path separator (use the canonical one, not the one actually used) // to indicate an absolute path. stash += POV_PATH_SEPARATOR; ++i; // Emit the stashed string as the volume identifier. volume = stash; stash.clear(); } // If it's not a drive letter, at this point we have only stashed the first letter, but // our index still points to the second one so the following algorithm will take care of it. } // Walk through the path string, stashing any non-separator characters. Whenever we hit a separator // character, emit the stashed characters (if any) as a directory name and clear the stash. // Also, as we go along, resolve the special directory names `.` and `..` if possible. // NB since we do not emit "empty" directory names, any sequence of consecutive separator // characters is effectively treated as a single separator character. for(; i != path.end(); ++i) { if ((*i == POV_PATH_SEPARATOR) || (*i == POV_PATH_SEPARATOR_2)) { if (!stash.empty() && !IsCurrentDir(stash)) { if (!dirnames.empty() && IsParentDir(stash)) dirnames.pop_back(); else dirnames.push_back (stash); stash.clear(); } } else stash += *i; } // Whatever is left in the stash is presumably the actual file name. // NB as a consequence of the algorithm chosen, any path name ending in a path separator // character will be emitted as a list of directories only, with the file name left empty. filename = stash; return true; }