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#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
95 const QStringList globPatterns =
wildcardsFromFilter( part ).split(
' ', QString::SkipEmptyParts );
97 const QStringList globPatterns =
wildcardsFromFilter( part ).split(
' ', Qt::SkipEmptyParts );
99 for (
const QString &glob : globPatterns )
101 const QString re = QRegularExpression::wildcardToRegularExpression( glob );
103 const QRegularExpression globRx( re );
104 if ( globRx.match( name ).hasMatch() )
113 if ( extensions.empty() || f.isEmpty() )
116 QString fileName = f;
118 for (
const QString &extension : std::as_const( extensions ) )
120 const QString extWithDot = extension.startsWith(
'.' ) ? extension :
'.' + extension;
121 if ( fileName.endsWith( extWithDot, Qt::CaseInsensitive ) )
130 const QString extension = extensions.at( 0 );
131 const QString extWithDot = extension.startsWith(
'.' ) ? extension :
'.' + extension;
132 fileName += extWithDot;
146 const thread_local QRegularExpression rx( QStringLiteral(
"[/\\\\\\?%\\*\\:\\|\"<>]" ) );
148 s.replace( rx, QStringLiteral(
"_" ) );
154 if ( path.isEmpty() )
158 QFileInfo fi( path );
160 currentPath = fi.dir();
162 currentPath = QDir( path );
164 QSet< QString > visited;
165 while ( !currentPath.exists() )
167 const QString parentPath = QDir::cleanPath( currentPath.absolutePath() + QStringLiteral(
"/.." ) );
168 if ( visited.contains( parentPath ) )
171 if ( parentPath.isEmpty() || parentPath == QLatin1String(
"." ) )
173 currentPath = QDir( parentPath );
174 visited << parentPath;
177 const QString res = QDir::cleanPath( currentPath.absolutePath() );
179 if ( res == QDir::currentPath() )
182 return res == QLatin1String(
"." ) ? QString() : res;
185QStringList
QgsFileUtils::findFile(
const QString &file,
const QString &basePath,
int maxClimbs,
int searchCeilling,
const QString ¤tDir )
188 QString originalFolder;
190 const QString fileName( basePath.isEmpty() ? QFileInfo( file ).fileName() : file );
191 const QString baseFolder( basePath.isEmpty() ? QFileInfo( file ).path() : basePath );
193 if ( QFileInfo( baseFolder ).isDir() )
195 folder = QDir( baseFolder ) ;
196 originalFolder = folder.absolutePath();
200 folder = QDir( QFileInfo( baseFolder ).absolutePath() );
201 originalFolder = folder.absolutePath();
204 QStringList searchedFolder = QStringList();
205 QString existingBase;
206 QString backupDirectory = QDir::currentPath();
207 QStringList foundFiles;
209 if ( !currentDir.isEmpty() && backupDirectory != currentDir && QDir( currentDir ).exists() )
210 QDir::setCurrent( currentDir );
213 while ( !folder.exists() && folder.absolutePath().count(
'/' ) > searchCeilling )
216 existingBase = folder.path();
217 if ( !folder.cdUp() )
218 folder = QFileInfo( existingBase ).absoluteDir();
222 if ( depth > ( maxClimbs + 4 ) )
225 bool folderExists = folder.exists();
227 if ( depth > maxClimbs )
230 if ( folder.absolutePath().count(
'/' ) < searchCeilling )
231 searchCeilling = folder.absolutePath().count(
'/' ) - 1;
233 while ( depth <= maxClimbs && folderExists && folder.absolutePath().count(
'/' ) >= searchCeilling )
236 QDirIterator localFinder( folder.path(), QStringList() << fileName, QDir::Files, QDirIterator::NoIteratorFlags );
237 searchedFolder.append( folder.absolutePath() );
238 if ( localFinder.hasNext() )
240 foundFiles << localFinder.next();
245 const QFileInfoList subdirs = folder.entryInfoList( QDir::AllDirs );
246 for (
const QFileInfo &subdir : subdirs )
248 if ( ! searchedFolder.contains( subdir.absolutePath() ) )
250 QDirIterator subDirFinder( subdir.path(), QStringList() << fileName, QDir::Files, QDirIterator::Subdirectories );
251 if ( subDirFinder.hasNext() )
253 QString possibleFile = subDirFinder.next();
254 if ( !subDirFinder.hasNext() )
256 foundFiles << possibleFile;
260 foundFiles << possibleFile;
261 while ( subDirFinder.hasNext() )
263 foundFiles << subDirFinder.next();
271 if ( depth > maxClimbs )
274 folderExists = folder.cdUp();
277 if ( QDir::currentPath() == currentDir && currentDir != backupDirectory )
278 QDir::setCurrent( backupDirectory );
284std::unique_ptr< wchar_t[] > pathToWChar(
const QString &path )
286 const QString nativePath = QDir::toNativeSeparators( path );
288 std::unique_ptr< wchar_t[] > pathArray(
new wchar_t[
static_cast< uint
>( nativePath.length() + 1 )] );
289 nativePath.toWCharArray( pathArray.get() );
290 pathArray[
static_cast< size_t >( nativePath.length() )] = 0;
300 std::unique_ptr< wchar_t[] > pathArray = pathToWChar( path );
301 const UINT type = GetDriveTypeW( pathArray.get() );
307 case DRIVE_NO_ROOT_DIR:
310 case DRIVE_REMOVABLE:
330 const QString originalPath = QDir::cleanPath( path );
331 QString currentPath = originalPath;
333 while ( currentPath != prevPath )
335 prevPath = currentPath;
336 currentPath = QFileInfo( currentPath ).path();
352 if ( path.contains( QLatin1String(
"fake_slow_path_for_unit_tests" ) ) )
384 for (
const QString &provider : providers )
390 for (
const QString &possibleSidecar : possibleSidecars )
392 if ( QFile::exists( possibleSidecar ) )
393 res.insert( possibleSidecar );
402 if ( !QFile::exists( oldPath ) )
404 error = QObject::tr(
"File does not exist" );
408 const QFileInfo oldPathInfo( oldPath );
412 const QString qmdPath = oldPathInfo.dir().filePath( oldPathInfo.completeBaseName() + QStringLiteral(
".qmd" ) );
413 if ( QFile::exists( qmdPath ) )
414 sidecars.insert( qmdPath );
418 const QString qmlPath = oldPathInfo.dir().filePath( oldPathInfo.completeBaseName() + QStringLiteral(
".qml" ) );
419 if ( QFile::exists( qmlPath ) )
420 sidecars.insert( qmlPath );
423 const QFileInfo newPathInfo( newPath );
427 errors.reserve( sidecars.size() );
429 for (
const QString &sidecar : std::as_const( sidecars ) )
431 const QFileInfo sidecarInfo( sidecar );
432 const QString newSidecarName = newPathInfo.dir().filePath( newPathInfo.completeBaseName() +
'.' + sidecarInfo.suffix() );
433 if ( newSidecarName != sidecar && QFile::exists( newSidecarName ) )
436 errors.append( QDir::toNativeSeparators( newSidecarName ) );
441 error = QObject::tr(
"Destination files already exist %1" ).arg( errors.join( QLatin1String(
", " ) ) );
445 if ( !QFile::rename( oldPath, newPath ) )
447 error = QObject::tr(
"Could not rename %1" ).arg( QDir::toNativeSeparators( oldPath ) );
451 for (
const QString &sidecar : std::as_const( sidecars ) )
453 const QFileInfo sidecarInfo( sidecar );
454 const QString newSidecarName = newPathInfo.dir().filePath( newPathInfo.completeBaseName() +
'.' + sidecarInfo.suffix() );
455 if ( newSidecarName == sidecar )
458 if ( !QFile::rename( sidecar, newSidecarName ) )
460 errors.append( QDir::toNativeSeparators( sidecar ) );
466 error = QObject::tr(
"Could not rename %1" ).arg( errors.join( QLatin1String(
", " ) ) );
475 struct rlimit rescLimit;
476 if ( getrlimit( RLIMIT_NOFILE, &rescLimit ) == 0 )
478 return rescLimit.rlim_cur;
487 int res =
static_cast<int>( QDir(
"/proc/self/fd" ).entryList().size() );
501 constexpr int SOME_MARGIN = 20;
502 return nFileCount > 0 && nFileLimit > 0 && nFileCount + filesToBeOpened > nFileLimit - SOME_MARGIN;
508 QString path = QDir::cleanPath( input );
509 if ( path.isEmpty() )
512 const QString fileName = QFileInfo( path ).fileName();
513 if ( !fileName.isEmpty() )
515 else if ( QFileInfo( path ).path() == path )
518 QString prevPath = path;
519 while ( ( path = QFileInfo( path ).path() ).length() < prevPath.length() )
521 const QString dirName = QDir( path ).dirName();
522 if ( dirName == QLatin1String(
"." ) )
525 result << ( !dirName.isEmpty() ? dirName : path );
529 std::reverse( result.begin(), result.end() );
@ 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.
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 filt...
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 bool fileMatchesFilter(const QString &fileName, const QString &filter)
Returns true if the given fileName matches a file filter string.
static Qgis::DriveType driveType(const QString &path) SIP_THROW(QgsNotSupportedException)
Returns the drive type for the given path.
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.