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.