// FIXME: Fails on Windows if one of the paths does not specify a drive letter // E.g. c:\foo\bar and \foo\myfile.txt String getRelativePath(const String& fromAbsPath, const String& toAbsPath) { // Tokenize the paths StringVec fromTokens, toTokens; tokenize(posixPath(fromAbsPath), fromTokens, "/", "", "", "", "\\", false, false); tokenize(posixPath(toAbsPath), toTokens, "/", "", "", "", "\\", false, false); // Find the longest common prefix unsigned i = 0; StringVec::iterator fromIt = fromTokens.begin(); StringVec::iterator toIt = toTokens.begin(); for (; fromIt != fromTokens.end() && toIt != toTokens.end(); fromIt++, toIt++, i++) { if (!comparePaths(*fromIt, *toIt)) break; } if (i > 0) { // Assemble the relative path StringVec relPathTokens(fromTokens.size() - i, ".."); relPathTokens.insert(relPathTokens.end(), toIt, toTokens.end()); return joinStrings(relPathTokens, "/"); } else { // Paths have nothing in common, so no relative path exists (Windows-only) return sanitizePath(toAbsPath); } }
VError XMacFileSystemNotification::StartWatchingForChanges( const VFolder &inFolder, VFileSystemNotifier::EventKind inKindFilter, VFileSystemNotifier::IEventHandler *inHandler, sLONG inLatency ) { // We need to get the folder's path into an array for us to pass along to the OS call. VString posixPathString; inFolder.GetPath( posixPathString, FPS_POSIX); VStringConvertBuffer buffer( posixPathString, VTC_UTF_8); std::string posixPath( buffer.GetCPointer()); CFStringRef pathRef = posixPathString.MAC_RetainCFStringCopy(); CFArrayRef pathArray = CFArrayCreate( NULL, (const void **)&pathRef, 1, NULL ); FSEventStreamContext context = { 0 }; // The latency the user passed us is expressed in milliseconds, but the OS call requires the latency to be // expressed in seconds. CFTimeInterval latency = (inLatency / 1000.0); // Now we can make our change data object XMacChangeData *data = new XMacChangeData( inFolder.GetPath(), posixPath, inKindFilter, VTask::GetCurrent(), inHandler, this); context.info = data; data->fStreamRef = FSEventStreamCreate( NULL, &FSEventCallback, &context, pathArray, kFSEventStreamEventIdSinceNow, latency, kFSEventStreamCreateFlagNone ); if (data->fStreamRef == NULL) { CFRelease( pathArray ); CFRelease( pathRef ); ReleaseRefCountable( &data); return MAKE_NATIVE_VERROR( errno ); } // We also need to take an initial snapshot of the directory for us to compare again CreateDirectorySnapshot( posixPath, data->fSnapshot, true ); // Now we can add the data object we just made to our list fOwner->PushChangeData( data); // Next, we can schedule this to run on the main event loop FSEventStreamScheduleWithRunLoop( data->fStreamRef, CFRunLoopGetMain(), kCFRunLoopDefaultMode ); CFRelease( pathArray ); CFRelease( pathRef ); // Now that we've scheduled the stream to run on a helper thread, all that is left is to // start executing the stream VError err; if (FSEventStreamStart( data->fStreamRef )) { err = VE_OK; } else { err = MAKE_NATIVE_VERROR( errno ); } ReleaseRefCountable( &data); return err; }
String sb_fextension(const String& path) { String fixedPath = posixPath(path); String ext; size_t ext_pos = fixedPath.rfind("."); size_t dir_pos = fixedPath.rfind("/"); if (ext_pos != String::npos) { if (dir_pos == String::npos || (dir_pos != String::npos && dir_pos < ext_pos)) { ext = fixedPath.substr(ext_pos + 1); } } return ext; }
String sanitizePath(const String &path) { // Standardize slashes to POSIX style String fixedPath = posixPath(path); // Check if path is absolute bool isAbsolute = isAbsolutePath(fixedPath); // Tokenize path StringList pathComponents; std::size_t start = 0; do { std::size_t separator = (std::min)(fixedPath.find('/', start), fixedPath.length()); String token = fixedPath.substr(start, separator - start); if (token.empty() || token == ".") { // a/./b -> a/b and a//b -> a/b } else if (token == "..") { if (pathComponents.empty()) { // ../a -> ../a // /../a -> /a if (!isAbsolute) pathComponents.push_back(token); } else { // ../../a -> ../../a // a/../c -> c if (pathComponents.back() == "..") pathComponents.push_back(token); else pathComponents.pop_back(); } } else { pathComponents.push_back(token); } start = separator + 1; } while (start < path.length()); // Figure out if we need to add a leading slash String prefix; if (strBeginsWith(fixedPath, "/")) prefix = "/"; // Return reassembled path return prefix + joinStrings(pathComponents, "/"); }
String sb_fname(const String& path) { String fixedPath = posixPath(path); // Special case if (fixedPath == ".") return path; size_t ext_pos = fixedPath.rfind("."); size_t dir_pos = fixedPath.rfind("/"); if (ext_pos != String::npos) { if (dir_pos == String::npos || (dir_pos != String::npos && dir_pos < ext_pos)) { return fixedPath.substr(0, ext_pos); } } return path; }
String getcwd() { char cwd[PATH_MAX+1]; sbAssert(getcwd(cwd, sizeof(cwd)), "Failed to get CWD"); return posixPath(cwd); }