23#include <QRegularExpression>
27#include <QDirIterator>
31#include <sys/resource.h>
38#pragma comment(lib,"Shell32.lib")
44 list << QObject::tr(
"KB" ) << QObject::tr(
"MB" ) << QObject::tr(
"GB" ) << QObject::tr(
"TB" );
46 QStringListIterator i( list );
47 QString unit = QObject::tr(
"B" );
49 double fileSize = bytes;
50 while ( fileSize >= 1024.0 && i.hasNext() )
55 return QStringLiteral(
"%1 %2" ).arg( QString::number( fileSize,
'f', bytes >= 1048576 ? 2 : 0 ), unit );
60 const thread_local QRegularExpression rx( QStringLiteral(
"\\*\\.([a-zA-Z0-9\\.]+)" ) );
61 QStringList extensions;
62 QRegularExpressionMatchIterator matches = rx.globalMatch( filter );
64 while ( matches.hasNext() )
66 const QRegularExpressionMatch match = matches.next();
67 if ( match.hasMatch() )
69 QStringList newExtensions = match.capturedTexts();
70 newExtensions.pop_front();
71 extensions.append( newExtensions );
79 const thread_local QRegularExpression globPatternsRx( QStringLiteral(
".*\\((.*?)\\)$" ) );
80 const QRegularExpressionMatch matches = globPatternsRx.match( filter );
81 if ( matches.hasMatch() )
82 return matches.captured( 1 );
89 QFileInfo fi( fileName );
90 const QString name = fi.fileName();
91 const QStringList parts = filter.split( QStringLiteral(
";;" ) );
92 for (
const QString &part : parts )
94 const QStringList globPatterns =
wildcardsFromFilter( part ).split(
' ', Qt::SkipEmptyParts );
95 for (
const QString &glob : globPatterns )
97 const QString re = QRegularExpression::wildcardToRegularExpression( glob );
99 const QRegularExpression globRx( re );
100 if ( globRx.match( name ).hasMatch() )
109 if ( extensions.empty() || f.isEmpty() )
112 QString fileName = f;
114 for (
const QString &extension : std::as_const( extensions ) )
116 const QString extWithDot = extension.startsWith(
'.' ) ? extension :
'.' + extension;
117 if ( fileName.endsWith( extWithDot, Qt::CaseInsensitive ) )
126 const QString extension = extensions.at( 0 );
127 const QString extWithDot = extension.startsWith(
'.' ) ? extension :
'.' + extension;
128 fileName += extWithDot;
142 const thread_local QRegularExpression rx( QStringLiteral(
"[/\\\\\\?%\\*\\:\\|\"<>]" ) );
144 s.replace( rx, QStringLiteral(
"_" ) );
150 if ( path.isEmpty() )
154 QFileInfo fi( path );
156 currentPath = fi.dir();
158 currentPath = QDir( path );
160 QSet< QString > visited;
161 while ( !currentPath.exists() )
163 const QString parentPath = QDir::cleanPath( currentPath.absolutePath() + QStringLiteral(
"/.." ) );
164 if ( visited.contains( parentPath ) )
167 if ( parentPath.isEmpty() || parentPath == QLatin1String(
"." ) )
169 currentPath = QDir( parentPath );
170 visited << parentPath;
173 const QString res = QDir::cleanPath( currentPath.absolutePath() );
175 if ( res == QDir::currentPath() )
178 return res == QLatin1String(
"." ) ? QString() : res;
181QStringList
QgsFileUtils::findFile(
const QString &file,
const QString &basePath,
int maxClimbs,
int searchCeilling,
const QString ¤tDir )
184 QString originalFolder;
186 const QString fileName( basePath.isEmpty() ? QFileInfo( file ).fileName() : file );
187 const QString baseFolder( basePath.isEmpty() ? QFileInfo( file ).path() : basePath );
189 if ( QFileInfo( baseFolder ).isDir() )
191 folder = QDir( baseFolder ) ;
192 originalFolder = folder.absolutePath();
196 folder = QDir( QFileInfo( baseFolder ).absolutePath() );
197 originalFolder = folder.absolutePath();
200 QStringList searchedFolder = QStringList();
201 QString existingBase;
202 QString backupDirectory = QDir::currentPath();
203 QStringList foundFiles;
205 if ( !currentDir.isEmpty() && backupDirectory != currentDir && QDir( currentDir ).exists() )
206 QDir::setCurrent( currentDir );
209 while ( !folder.exists() && folder.absolutePath().count(
'/' ) > searchCeilling )
212 existingBase = folder.path();
213 if ( !folder.cdUp() )
214 folder = QFileInfo( existingBase ).absoluteDir();
218 if ( depth > ( maxClimbs + 4 ) )
221 bool folderExists = folder.exists();
223 if ( depth > maxClimbs )
226 if ( folder.absolutePath().count(
'/' ) < searchCeilling )
227 searchCeilling = folder.absolutePath().count(
'/' ) - 1;
229 while ( depth <= maxClimbs && folderExists && folder.absolutePath().count(
'/' ) >= searchCeilling )
232 QDirIterator localFinder( folder.path(), QStringList() << fileName, QDir::Files, QDirIterator::NoIteratorFlags );
233 searchedFolder.append( folder.absolutePath() );
234 if ( localFinder.hasNext() )
236 foundFiles << localFinder.next();
241 const QFileInfoList subdirs = folder.entryInfoList( QDir::AllDirs );
242 for (
const QFileInfo &subdir : subdirs )
244 if ( ! searchedFolder.contains( subdir.absolutePath() ) )
246 QDirIterator subDirFinder( subdir.path(), QStringList() << fileName, QDir::Files, QDirIterator::Subdirectories );
247 if ( subDirFinder.hasNext() )
249 QString possibleFile = subDirFinder.next();
250 if ( !subDirFinder.hasNext() )
252 foundFiles << possibleFile;
256 foundFiles << possibleFile;
257 while ( subDirFinder.hasNext() )
259 foundFiles << subDirFinder.next();
267 if ( depth > maxClimbs )
270 folderExists = folder.cdUp();
273 if ( QDir::currentPath() == currentDir && currentDir != backupDirectory )
274 QDir::setCurrent( backupDirectory );
280std::unique_ptr< wchar_t[] > pathToWChar(
const QString &path )
282 const QString nativePath = QDir::toNativeSeparators( path );
284 std::unique_ptr< wchar_t[] > pathArray(
new wchar_t[
static_cast< uint
>( nativePath.length() + 1 )] );
285 nativePath.toWCharArray( pathArray.get() );
286 pathArray[
static_cast< size_t >( nativePath.length() )] = 0;
291void fileAttributesOld( HANDLE handle, DWORD &fileAttributes,
bool &hasFileAttributes )
293 hasFileAttributes =
false;
294 BY_HANDLE_FILE_INFORMATION info;
295 if ( GetFileInformationByHandle( handle, &info ) )
297 hasFileAttributes =
true;
298 fileAttributes = info.dwFileAttributes;
303void fileAttributesNew( HANDLE handle, DWORD &fileAttributes,
bool &hasFileAttributes )
305 hasFileAttributes =
false;
307 _FILE_BASIC_INFO infoEx;
308 if ( GetFileInformationByHandleEx(
311 &infoEx,
sizeof( infoEx ) ) )
313 hasFileAttributes =
true;
314 fileAttributes = infoEx.FileAttributes;
319 fileAttributesOld( handle, fileAttributes, hasFileAttributes );
322 fileAttributesOld( handle, fileAttributes, hasFileAttributes );
326bool pathIsLikelyCloudStorage( QString path )
330 QDirIterator dirIt( path, QDir::Files );
331 if ( dirIt.hasNext() )
336 std::unique_ptr< wchar_t[] > pathArray = pathToWChar( path );
337 const HANDLE handle = CreateFileW( pathArray.get(), 0, FILE_SHARE_READ,
338 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
nullptr );
339 if ( handle != INVALID_HANDLE_VALUE )
341 bool hasFileAttributes =
false;
342 DWORD attributes = 0;
343 fileAttributesNew( handle, attributes, hasFileAttributes );
344 CloseHandle( handle );
345 if ( hasFileAttributes )
359 return ( attributes & FILE_ATTRIBUTE_RECALL_ON_OPEN )
360 || ( attributes & FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS );
372 std::unique_ptr< wchar_t[] > pathArray = pathToWChar( path );
373 const UINT type = GetDriveTypeW( pathArray.get() );
379 case DRIVE_NO_ROOT_DIR:
382 case DRIVE_REMOVABLE:
402 const QString originalPath = QDir::cleanPath( path );
403 QString currentPath = originalPath;
405 while ( currentPath != prevPath )
407 if ( pathIsLikelyCloudStorage( currentPath ) )
410 prevPath = currentPath;
411 currentPath = QFileInfo( currentPath ).path();
428 if ( path.contains( QLatin1String(
"fake_slow_path_for_unit_tests" ) ) )
461 for (
const QString &provider : providers )
467 for (
const QString &possibleSidecar : possibleSidecars )
469 if ( QFile::exists( possibleSidecar ) )
470 res.insert( possibleSidecar );
479 if ( !QFile::exists( oldPath ) )
481 error = QObject::tr(
"File does not exist" );
485 const QFileInfo oldPathInfo( oldPath );
489 const QString qmdPath = oldPathInfo.dir().filePath( oldPathInfo.completeBaseName() + QStringLiteral(
".qmd" ) );
490 if ( QFile::exists( qmdPath ) )
491 sidecars.insert( qmdPath );
495 const QString qmlPath = oldPathInfo.dir().filePath( oldPathInfo.completeBaseName() + QStringLiteral(
".qml" ) );
496 if ( QFile::exists( qmlPath ) )
497 sidecars.insert( qmlPath );
500 const QFileInfo newPathInfo( newPath );
504 errors.reserve( sidecars.size() );
506 for (
const QString &sidecar : std::as_const( sidecars ) )
508 const QFileInfo sidecarInfo( sidecar );
509 const QString newSidecarName = newPathInfo.dir().filePath( newPathInfo.completeBaseName() +
'.' + sidecarInfo.suffix() );
510 if ( newSidecarName != sidecar && QFile::exists( newSidecarName ) )
513 errors.append( QDir::toNativeSeparators( newSidecarName ) );
518 error = QObject::tr(
"Destination files already exist %1" ).arg( errors.join( QLatin1String(
", " ) ) );
522 if ( !QFile::rename( oldPath, newPath ) )
524 error = QObject::tr(
"Could not rename %1" ).arg( QDir::toNativeSeparators( oldPath ) );
528 for (
const QString &sidecar : std::as_const( sidecars ) )
530 const QFileInfo sidecarInfo( sidecar );
531 const QString newSidecarName = newPathInfo.dir().filePath( newPathInfo.completeBaseName() +
'.' + sidecarInfo.suffix() );
532 if ( newSidecarName == sidecar )
535 if ( !QFile::rename( sidecar, newSidecarName ) )
537 errors.append( QDir::toNativeSeparators( sidecar ) );
543 error = QObject::tr(
"Could not rename %1" ).arg( errors.join( QLatin1String(
", " ) ) );
552 struct rlimit rescLimit;
553 if ( getrlimit( RLIMIT_NOFILE, &rescLimit ) == 0 )
555 return rescLimit.rlim_cur;
564 int res =
static_cast<int>( QDir(
"/proc/self/fd" ).entryList().size() );
578 constexpr int SOME_MARGIN = 20;
579 return nFileCount > 0 && nFileLimit > 0 && nFileCount + filesToBeOpened > nFileLimit - SOME_MARGIN;
585 QString path = QDir::cleanPath( input );
586 if ( path.isEmpty() )
589 const QString fileName = QFileInfo( path ).fileName();
590 if ( !fileName.isEmpty() )
592 else if ( QFileInfo( path ).path() == path )
595 QString prevPath = path;
596 while ( ( path = QFileInfo( path ).path() ).length() < prevPath.length() )
598 const QString dirName = QDir( path ).dirName();
599 if ( dirName == QLatin1String(
"." ) )
602 result << ( !dirName.isEmpty() ? dirName : path );
606 std::reverse( result.begin(), result.end() );
612 if ( ! QFileInfo::exists( path ) )
617 QFileInfo info { path };
618 const QString suffix { info.completeSuffix() };
619 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.