23#include <QRegularExpression> 
   27#include <QDirIterator> 
   31#include <sys/resource.h> 
   39#pragma comment(lib,"Shell32.lib") 
   45  list << QObject::tr( 
"KB" ) << QObject::tr( 
"MB" ) << QObject::tr( 
"GB" ) << QObject::tr( 
"TB" );
 
   47  QStringListIterator i( list );
 
   48  QString unit = QObject::tr( 
"B" );
 
   50  double fileSize = bytes;
 
   51  while ( fileSize >= 1024.0 && i.hasNext() )
 
   56  return QStringLiteral( 
"%1 %2" ).arg( QString::number( fileSize, 
'f', bytes >= 1048576 ? 2 : 0 ), unit );
 
 
   61  const thread_local QRegularExpression rx( QStringLiteral( 
"\\*\\.([a-zA-Z0-9\\.]+)" ) );
 
   62  QStringList extensions;
 
   63  QRegularExpressionMatchIterator matches = rx.globalMatch( filter );
 
   65  while ( matches.hasNext() )
 
   67    const QRegularExpressionMatch match = matches.next();
 
   68    if ( match.hasMatch() )
 
   70      QStringList newExtensions = match.capturedTexts();
 
   71      newExtensions.pop_front(); 
 
   72      extensions.append( newExtensions );
 
 
   80  const thread_local QRegularExpression globPatternsRx( QStringLiteral( 
".*\\((.*?)\\)$" ) );
 
   81  const QRegularExpressionMatch matches = globPatternsRx.match( filter );
 
   82  if ( matches.hasMatch() )
 
   83    return matches.captured( 1 );
 
 
   90  QFileInfo fi( fileName );
 
   91  const QString name = fi.fileName();
 
   92  const QStringList parts = filter.split( QStringLiteral( 
";;" ) );
 
   93  for ( 
const QString &part : parts )
 
   95    const QStringList globPatterns = 
wildcardsFromFilter( part ).split( 
' ', Qt::SkipEmptyParts );
 
   96    for ( 
const QString &glob : globPatterns )
 
   98      const QString re = QRegularExpression::wildcardToRegularExpression( glob );
 
  100      const QRegularExpression globRx( re );
 
  101      if ( globRx.match( name ).hasMatch() )
 
 
  110  if ( extensions.empty() || f.isEmpty() )
 
  113  QString fileName = f;
 
  115  for ( 
const QString &extension : std::as_const( extensions ) )
 
  117    const QString extWithDot = extension.startsWith( 
'.' ) ? extension : 
'.' + extension;
 
  118    if ( fileName.endsWith( extWithDot, Qt::CaseInsensitive ) )
 
  127    const QString extension = extensions.at( 0 );
 
  128    const QString extWithDot = extension.startsWith( 
'.' ) ? extension : 
'.' + extension;
 
  129    fileName += extWithDot;
 
 
  143  const thread_local QRegularExpression rx( QStringLiteral( 
"[/\\\\\\?%\\*\\:\\|\"<>]" ) );
 
  145  s.replace( rx, QStringLiteral( 
"_" ) );
 
 
  151  if ( path.isEmpty() )
 
  155  QFileInfo fi( path );
 
  157    currentPath = fi.dir();
 
  159    currentPath = QDir( path );
 
  161  QSet< QString > visited;
 
  162  while ( !currentPath.exists() )
 
  164    const QString parentPath = QDir::cleanPath( currentPath.absolutePath() + QStringLiteral( 
"/.." ) );
 
  165    if ( visited.contains( parentPath ) )
 
  168    if ( parentPath.isEmpty() || parentPath == QLatin1String( 
"." ) )
 
  170    currentPath = QDir( parentPath );
 
  171    visited << parentPath;
 
  174  const QString res = QDir::cleanPath( currentPath.absolutePath() );
 
  176  if ( res == QDir::currentPath() )
 
  179  return res == QLatin1String( 
"." ) ? QString() : res;
 
 
  182QStringList 
QgsFileUtils::findFile( 
const QString &file, 
const QString &basePath, 
int maxClimbs, 
int searchCeilling, 
const QString ¤tDir )
 
  185  QString originalFolder;
 
  187  const QString fileName( basePath.isEmpty() ? QFileInfo( file ).fileName() : file );
 
  188  const QString baseFolder( basePath.isEmpty() ? QFileInfo( file ).path() : basePath );
 
  190  if ( QFileInfo( baseFolder ).isDir() )
 
  192    folder = QDir( baseFolder ) ;
 
  193    originalFolder = folder.absolutePath();
 
  197    folder = QDir( QFileInfo( baseFolder ).absolutePath() );
 
  198    originalFolder = folder.absolutePath();
 
  201  QStringList searchedFolder = QStringList();
 
  202  QString existingBase;
 
  203  QString backupDirectory = QDir::currentPath();
 
  204  QStringList foundFiles;
 
  206  if ( !currentDir.isEmpty() && backupDirectory != currentDir && QDir( currentDir ).exists() )
 
  207    QDir::setCurrent( currentDir );
 
  210  while ( !folder.exists() && folder.absolutePath().count( 
'/' ) > searchCeilling )
 
  213    existingBase = folder.path();
 
  214    if ( !folder.cdUp() )
 
  215      folder = QFileInfo( existingBase ).absoluteDir(); 
 
  219    if ( depth > ( maxClimbs + 4 ) ) 
 
  222  bool folderExists = folder.exists();
 
  224  if ( depth > maxClimbs )
 
  227  if ( folder.absolutePath().count( 
'/' ) < searchCeilling )
 
  228    searchCeilling = folder.absolutePath().count( 
'/' ) - 1;
 
  230  while ( depth <= maxClimbs && folderExists && folder.absolutePath().count( 
'/' ) >= searchCeilling )
 
  233    QDirIterator localFinder( folder.path(), QStringList() << fileName, QDir::Files, QDirIterator::NoIteratorFlags );
 
  234    searchedFolder.append( folder.absolutePath() );
 
  235    if ( localFinder.hasNext() )
 
  237      foundFiles << localFinder.next();
 
  242    const QFileInfoList subdirs = folder.entryInfoList( QDir::AllDirs );
 
  243    for ( 
const QFileInfo &subdir : subdirs )
 
  245      if ( ! searchedFolder.contains( subdir.absolutePath() ) )
 
  247        QDirIterator subDirFinder( subdir.path(), QStringList() << fileName, QDir::Files, QDirIterator::Subdirectories );
 
  248        if ( subDirFinder.hasNext() )
 
  250          QString possibleFile = subDirFinder.next();
 
  251          if ( !subDirFinder.hasNext() )
 
  253            foundFiles << possibleFile;
 
  257          foundFiles << possibleFile;
 
  258          while ( subDirFinder.hasNext() )
 
  260            foundFiles << subDirFinder.next();
 
  268    if ( depth > maxClimbs )
 
  271    folderExists = folder.cdUp();
 
  274  if ( QDir::currentPath() == currentDir && currentDir != backupDirectory )
 
  275    QDir::setCurrent( backupDirectory );
 
 
  281std::unique_ptr< wchar_t[] > pathToWChar( 
const QString &path )
 
  283  const QString nativePath = QDir::toNativeSeparators( path );
 
  285  std::unique_ptr< wchar_t[] > pathArray( 
new wchar_t[
static_cast< uint
>( nativePath.length() + 1 )] );
 
  286  nativePath.toWCharArray( pathArray.get() );
 
  287  pathArray[
static_cast< size_t >( nativePath.length() )] = 0;
 
  292void fileAttributesOld( HANDLE handle, DWORD &fileAttributes, 
bool &hasFileAttributes )
 
  294  hasFileAttributes = 
false;
 
  295  BY_HANDLE_FILE_INFORMATION info;
 
  296  if ( GetFileInformationByHandle( handle, &info ) )
 
  298    hasFileAttributes = 
true;
 
  299    fileAttributes = info.dwFileAttributes;
 
  304void fileAttributesNew( HANDLE handle, DWORD &fileAttributes, 
bool &hasFileAttributes )
 
  306  hasFileAttributes = 
false;
 
  308  _FILE_BASIC_INFO infoEx;
 
  309  if ( GetFileInformationByHandleEx(
 
  312         &infoEx, 
sizeof( infoEx ) ) )
 
  314    hasFileAttributes = 
true;
 
  315    fileAttributes = infoEx.FileAttributes;
 
  320    fileAttributesOld( handle, fileAttributes, hasFileAttributes );
 
  323  fileAttributesOld( handle, fileAttributes, hasFileAttributes );
 
  327bool pathIsLikelyCloudStorage( QString path )
 
  331  QDirIterator dirIt( path, QDir::Files );
 
  332  if ( dirIt.hasNext() )
 
  337  std::unique_ptr< wchar_t[] > pathArray = pathToWChar( path );
 
  338  const HANDLE handle = CreateFileW( pathArray.get(), 0, FILE_SHARE_READ,
 
  339                                     nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 
nullptr );
 
  340  if ( handle != INVALID_HANDLE_VALUE )
 
  342    bool hasFileAttributes = 
false;
 
  343    DWORD attributes = 0;
 
  344    fileAttributesNew( handle, attributes, hasFileAttributes );
 
  345    CloseHandle( handle );
 
  346    if ( hasFileAttributes )
 
  360      return ( attributes & FILE_ATTRIBUTE_RECALL_ON_OPEN )
 
  361             || ( attributes & FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS );
 
  373    std::unique_ptr< wchar_t[] > pathArray = pathToWChar( path );
 
  374    const UINT type = GetDriveTypeW( pathArray.get() );
 
  380      case DRIVE_NO_ROOT_DIR:
 
  383      case DRIVE_REMOVABLE:
 
  403  const QString originalPath = QDir::cleanPath( path );
 
  404  QString currentPath = originalPath;
 
  406  while ( currentPath != prevPath )
 
  408    if ( pathIsLikelyCloudStorage( currentPath ) )
 
  411    prevPath = currentPath;
 
  412    currentPath = QFileInfo( currentPath ).path();
 
 
  429  if ( path.contains( QLatin1String( 
"fake_slow_path_for_unit_tests" ) ) )
 
 
  462  for ( 
const QString &provider : providers )
 
  468      for ( 
const QString &possibleSidecar : possibleSidecars )
 
  470        if ( QFile::exists( possibleSidecar ) )
 
  471          res.insert( possibleSidecar );
 
 
  480  if ( !QFile::exists( oldPath ) )
 
  482    error = QObject::tr( 
"File does not exist" );
 
  486  const QFileInfo oldPathInfo( oldPath );
 
  490    const QString qmdPath = oldPathInfo.dir().filePath( oldPathInfo.completeBaseName() + QStringLiteral( 
".qmd" ) );
 
  491    if ( QFile::exists( qmdPath ) )
 
  492      sidecars.insert( qmdPath );
 
  496    const QString qmlPath = oldPathInfo.dir().filePath( oldPathInfo.completeBaseName() + QStringLiteral( 
".qml" ) );
 
  497    if ( QFile::exists( qmlPath ) )
 
  498      sidecars.insert( qmlPath );
 
  501  const QFileInfo newPathInfo( newPath );
 
  505  errors.reserve( sidecars.size() );
 
  507  for ( 
const QString &sidecar : std::as_const( sidecars ) )
 
  509    const QFileInfo sidecarInfo( sidecar );
 
  510    const QString newSidecarName = newPathInfo.dir().filePath( newPathInfo.completeBaseName() + 
'.' + sidecarInfo.suffix() );
 
  511    if ( newSidecarName != sidecar && QFile::exists( newSidecarName ) )
 
  514      errors.append( QDir::toNativeSeparators( newSidecarName ) );
 
  519    error = QObject::tr( 
"Destination files already exist %1" ).arg( errors.join( QLatin1String( 
", " ) ) );
 
  523  if ( !QFile::rename( oldPath, newPath ) )
 
  525    error = QObject::tr( 
"Could not rename %1" ).arg( QDir::toNativeSeparators( oldPath ) );
 
  529  for ( 
const QString &sidecar : std::as_const( sidecars ) )
 
  531    const QFileInfo sidecarInfo( sidecar );
 
  532    const QString newSidecarName = newPathInfo.dir().filePath( newPathInfo.completeBaseName() + 
'.' + sidecarInfo.suffix() );
 
  533    if ( newSidecarName == sidecar )
 
  536    if ( !QFile::rename( sidecar, newSidecarName ) )
 
  538      errors.append( QDir::toNativeSeparators( sidecar ) );
 
  544    error = QObject::tr( 
"Could not rename %1" ).arg( errors.join( QLatin1String( 
", " ) ) );
 
 
  553  struct rlimit rescLimit;
 
  554  if ( getrlimit( RLIMIT_NOFILE, &rescLimit ) == 0 )
 
  556    return rescLimit.rlim_cur;
 
 
  567  DIR *dirp = opendir( 
"/proc/self/fd" );
 
  571  while ( 
struct dirent *entry = readdir( dirp ) )
 
  573    if ( entry->d_type == DT_REG )
 
 
  590  constexpr int SOME_MARGIN = 20;
 
  591  return nFileCount > 0 && nFileLimit > 0 && nFileCount + filesToBeOpened > nFileLimit - SOME_MARGIN;
 
 
  597  QString path = QDir::cleanPath( input );
 
  598  if ( path.isEmpty() )
 
  601  const QString fileName = QFileInfo( path ).fileName();
 
  602  if ( !fileName.isEmpty() )
 
  604  else if ( QFileInfo( path ).path() == path )
 
  607  QString prevPath = path;
 
  608  while ( ( path = QFileInfo( path ).path() ).length() < prevPath.length() )
 
  610    const QString dirName = QDir( path ).dirName();
 
  611    if ( dirName == QLatin1String( 
"." ) )
 
  614    result << ( !dirName.isEmpty() ? dirName : path );
 
  618  std::reverse( result.begin(), result.end() );
 
 
  624  if ( ! QFileInfo::exists( path ) )
 
  629  QFileInfo info { path };
 
  630  const QString suffix { info.completeSuffix() };
 
  631  const QString pathPattern { QString( suffix.isEmpty() ? path : path.chopped( suffix.length() + 1 ) ).append( suffix.isEmpty() ? QStringLiteral( 
"_%1" ) : QStringLiteral( 
"_%1." ) ).append( suffix ) };
 
 
@ Cloud
Cloud storage – files may be remote or locally stored, depending on user configuration.
 
@ Removable
Removable drive.
 
@ IncludeMetadataFile
Indicates that any associated .qmd metadata file should be included with the operation.
 
@ IncludeStyleFile
Indicates that any associated .qml styling file should be included with the operation.
 
QFlags< FileOperationFlag > FileOperationFlags
File operation flags.
 
static QString uniquePath(const QString &path)
Creates a unique file path name from a desired path by appending _<n> (where <n> is an integer number...
 
static QString stringToSafeFilename(const QString &string)
Converts a string to a safe filename, replacing characters which are not safe for filenames with an '...
 
static QStringList findFile(const QString &file, const QString &basepath=QString(), int maxClimbs=4, int searchCeiling=4, const QString ¤tDir=QString())
Will check basepath in an outward spiral up to maxClimbs levels to check if file exists.
 
static int openedFileCount()
Returns the number of currently opened files by the process.
 
static QString wildcardsFromFilter(const QString &filter)
Given a filter string like GeoTIFF Files (*.tiff *.tif), extracts the wildcard portion of this filter...
 
static bool renameDataset(const QString &oldPath, const QString &newPath, QString &error, Qgis::FileOperationFlags flags=Qgis::FileOperationFlag::IncludeMetadataFile|Qgis::FileOperationFlag::IncludeStyleFile)
Renames the dataset at oldPath to newPath, renaming both the file at oldPath and all associated sidec...
 
static QSet< QString > sidecarFilesForPath(const QString &path)
Returns a list of the sidecar files which exist for the dataset a the specified path.
 
static bool pathIsSlowDevice(const QString &path)
Returns true if the specified path is assumed to reside on a slow device, e.g.
 
static bool isCloseToLimitOfOpenedFiles(int filesToBeOpened=1)
Returns whether when opening new file(s) will reach, or nearly reach, the limit of simultaneously ope...
 
static Qgis::DriveType driveType(const QString &path)
Returns the drive type for the given path.
 
static bool fileMatchesFilter(const QString &fileName, const QString &filter)
Returns true if the given fileName matches a file filter string.
 
static QString ensureFileNameHasExtension(const QString &fileName, const QStringList &extensions)
Ensures that a fileName ends with an extension from the provided list of extensions.
 
static QString representFileSize(qint64 bytes)
Returns the human size from bytes.
 
static QString addExtensionFromFilter(const QString &fileName, const QString &filter)
Ensures that a fileName ends with an extension from the specified filter string.
 
static int openedFileLimit()
Returns the limit of simultaneously opened files by the process.
 
static QStringList splitPathToComponents(const QString &path)
Given a file path, returns a list of all the components leading to that path.
 
static QString findClosestExistingPath(const QString &path)
Returns the top-most existing folder from path.
 
static QStringList extensionsFromFilter(const QString &filter)
Returns a list of the extensions contained within a file filter string.
 
Custom exception class which is raised when an operation is not supported.
 
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
 
QStringList providerList() const
Returns list of available providers by their keys.
 
QgsProviderMetadata * providerMetadata(const QString &providerKey) const
Returns metadata of the provider or nullptr if not found.