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; }
void XMacFileSystemNotification::CreateDirectorySnapshot( const std::string& inPosixPath, SnapshotListing &outList, bool inRecursive ) { xbox_assert(inPosixPath.at(inPosixPath.size()-1) == '/'); DirectoryListing *ret = new DirectoryListing(); struct dirent **entries = NULL; // This is an array of directory entry pointers // Now we can scan the directory for a file listing in alphabetical order int numentries = scandir( inPosixPath.c_str(), &entries, NULL, alphasort ); // Next, we can loop over all of the entries, and add them to our snapshot list. However, // we don't want to add listings for the "this directory" and "parent directory" object that // scandir returns to us, so we'll skip those/ for( int i = 0; i < numentries; i++) { if (strcmp( entries[ i ]->d_name, "." ) != 0 && strcmp( entries[ i ]->d_name, ".." ) != 0 && strcmp( entries[ i ]->d_name, ".DS_Store" ) != 0 ) { std::string name( entries[ i ]->d_name); std::string filePath( inPosixPath); filePath += name; struct stat s_obj; if (lstat( filePath.c_str(), &s_obj ) == 0) { FileInfo data; data.isDirectory = (entries[ i ]->d_type == DT_DIR); data.st_mtimespec = s_obj.st_mtimespec; (*ret)[name] = data; if (inRecursive && entries[ i ]->d_type == DT_DIR) { // We have another directory to process, so we will recurse. filePath += "/"; CreateDirectorySnapshot( filePath, outList, inRecursive ); } } } free( entries[i]); } free( entries); // We will free the data from the list elsewhere, which frees the data from // the call to scandir as well. outList[inPosixPath] = ret; }
void OpFileSystem::CreateDirectorySnapshot( const NrpText& directory, const NrpText& templateName, const NrpText& itemName, IniFile* ini, const NrpText& funcUpdate ) { _wfinddata_t fdata; intptr_t hFile; assert( directory.size() ); if( directory.size() ) { hFile = _wfindfirst( ( CheckEndSlash( directory )+ anyFile ).ToWide(), &fdata); while( hFile ) { if ( !( firstEntry == fdata.name || secondEntry == fdata.name ) )// это удалять не надо if ((( fdata.attrib & _A_SUBDIR ) == _A_SUBDIR ) || ( fdata.attrib == _A_SUBDIR ))// найдена папка { CreateDirectorySnapshot( CheckEndSlash( directory ) + NrpText( fdata.name ), templateName, itemName, ini, funcUpdate ); } else// иначе найден файл { if( _wcsicmp( itemName.ToWide(), fdata.name ) == 0 ) { NrpText fileName = CheckEndSlash( directory )+ fdata.name; IniFile rv( fileName ); int number= ini->Get( SECTION_OPTIONS, templateName + numTemplate, (int)0 ); NrpText intName = rv.Get( SECTION_PROPERTIES, INrpConfig::uniqTemplate, NrpText( "" ) ); ini->Set( SECTION_OPTIONS, CreateKeyName( number ), intName ); ini->Set( SECTION_OPTIONS, templateName + NrpText( (int)number ), fileName ); ini->Set( SECTION_OPTIONS, templateName + numTemplate, number+1 ); if( funcUpdate.size() && GetTickCount() - lastTimeUpdate > 500 ) { lastTimeUpdate = GetTickCount(); CNrpScript::Instance().DoString( funcUpdate + "(" + NrpText( number ) + ")" ); } } } if( _wfindnext( hFile, &fdata) != 0 ) break; } } _findclose( hFile ); }
bool XMacFileSystemNotification::BuildChangeList( XMacChangeData *inData, const std::string& inPosixPath) { // We want to scan the directory we're watching, and compare it against the // snapshot we've stored. If a file exists in the scandir list, but not the // snapshot, then it's a newly added file. If a file exists in the snapshot, // but not the scandir, then it's a file that's been deleted. If the file // exists in both snapshots, but the modification date has changed (by checking // with a call to lstat), then it's a file modification. // // Unfortunately, it is impossible for us to tell whether a file has been renamed, // so that operation will just look like a delete/add pair to the user. DirectoryListing *old_snapshot = NULL; DirectoryListing *new_snapshot = NULL; // Find the directory we care about in our old snapshot SnapshotListing::iterator directory_in_old_snapshot = inData->fSnapshot.find( inPosixPath); if (directory_in_old_snapshot != inData->fSnapshot.end()) old_snapshot = directory_in_old_snapshot->second; if (!old_snapshot) return false; // Take an entirely new snapshot, just for the directory we care about SnapshotListing directory_for_new_snapshot; CreateDirectorySnapshot( inPosixPath, directory_for_new_snapshot, false ); new_snapshot = directory_for_new_snapshot.find( inPosixPath)->second; if (!new_snapshot) return false; bool bFoundChanges = false; for (DirectoryListing::iterator file_from_new_snapshot = new_snapshot->begin(); file_from_new_snapshot != new_snapshot->end(); ++file_from_new_snapshot) { // We've been given a node of snapshot data from the new snapshot. We want to see // if we can find this piece of information in the old snapshot's list now. If we // can find it, then we need to check the modification date information as well. DirectoryListing::iterator file_from_old_snapshot = old_snapshot->find( file_from_new_snapshot->first ); if (file_from_old_snapshot != old_snapshot->end()) { if (!file_from_old_snapshot->second.isDirectory && // Directories cannot be modified, only added or deleted (file_from_old_snapshot->second.st_mtimespec.tv_sec != file_from_new_snapshot->second.st_mtimespec.tv_sec || file_from_old_snapshot->second.st_mtimespec.tv_nsec != file_from_new_snapshot->second.st_mtimespec.tv_nsec)) { // The modification times are different, so push this node onto the file change // stack so that we can process it later bFoundChanges |= AddChangeToList( inData, inPosixPath, file_from_new_snapshot->first, file_from_new_snapshot->second.isDirectory, VFileSystemNotifier::kFileModified); } } else { // We couldn't find the node in our old snapshot, so this means the file has been added to the directory. So // push the new node onto the file add stack so that we can process it later. bFoundChanges |= AddChangeToList( inData, inPosixPath, file_from_new_snapshot->first, file_from_new_snapshot->second.isDirectory, VFileSystemNotifier::kFileAdded); } } // We're not out of the woods quite yet. We still need to determine if any files have been // deleted. In this case, we need to loop over all of the old files and see if we can find // anything in the list of new files to match. If we can't, then we have a delete operation. // But, we only need to do this if the user actually said they care about file deletions. if (inData->fFilters & VFileSystemNotifier::kFileDeleted) { for (DirectoryListing::iterator iter = old_snapshot->begin(); iter != old_snapshot->end(); ++iter) { DirectoryListing::iterator results = new_snapshot->find( iter->first ); if (results == new_snapshot->end()) { // We found a deletion to add to the list bFoundChanges |= AddChangeToList( inData, inPosixPath, iter->first, iter->second.isDirectory, VFileSystemNotifier::kFileDeleted); } } } // Destroy the data we snagged for the new snapshot -- we're going to regenerate the snapshot // entirely whenever there is a change. It's ineffecient, but it also means we don't have to // track nearly as many edge cases in the code. ClearDirectorySnapshot( directory_for_new_snapshot ); return bFoundChanges; }