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;
}
Example #3
0
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;
}