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;
295void fileAttributesOld( HANDLE handle, DWORD &fileAttributes,
bool &hasFileAttributes )
297 hasFileAttributes =
false;
298 BY_HANDLE_FILE_INFORMATION info;
299 if ( GetFileInformationByHandle( handle, &info ) )
301 hasFileAttributes =
true;
302 fileAttributes = info.dwFileAttributes;
307void fileAttributesNew( HANDLE handle, DWORD &fileAttributes,
bool &hasFileAttributes )
309 hasFileAttributes =
false;
311 _FILE_BASIC_INFO infoEx;
312 if ( GetFileInformationByHandleEx(
315 &infoEx,
sizeof( infoEx ) ) )
317 hasFileAttributes =
true;
318 fileAttributes = infoEx.FileAttributes;
323 fileAttributesOld( handle, fileAttributes, hasFileAttributes );
326 fileAttributesOld( handle, fileAttributes, hasFileAttributes );
330bool pathIsLikelyCloudStorage( QString path )
334 QDirIterator dirIt( path, QDir::Files );
335 if ( dirIt.hasNext() )
340 std::unique_ptr< wchar_t[] > pathArray = pathToWChar( path );
341 const HANDLE handle = CreateFileW( pathArray.get(), 0, FILE_SHARE_READ,
342 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
nullptr );
343 if ( handle != INVALID_HANDLE_VALUE )
345 bool hasFileAttributes =
false;
346 DWORD attributes = 0;
347 fileAttributesNew( handle, attributes, hasFileAttributes );
348 CloseHandle( handle );
349 if ( hasFileAttributes )
363 return ( attributes & FILE_ATTRIBUTE_RECALL_ON_OPEN )
364 || ( attributes & FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS );
376 std::unique_ptr< wchar_t[] > pathArray = pathToWChar( path );
377 const UINT type = GetDriveTypeW( pathArray.get() );
383 case DRIVE_NO_ROOT_DIR:
386 case DRIVE_REMOVABLE:
406 const QString originalPath = QDir::cleanPath( path );
407 QString currentPath = originalPath;
409 while ( currentPath != prevPath )
411 if ( pathIsLikelyCloudStorage( currentPath ) )
414 prevPath = currentPath;
415 currentPath = QFileInfo( currentPath ).path();
432 if ( path.contains( QLatin1String(
"fake_slow_path_for_unit_tests" ) ) )
465 for (
const QString &provider : providers )
471 for (
const QString &possibleSidecar : possibleSidecars )
473 if ( QFile::exists( possibleSidecar ) )
474 res.insert( possibleSidecar );
483 if ( !QFile::exists( oldPath ) )
485 error = QObject::tr(
"File does not exist" );
489 const QFileInfo oldPathInfo( oldPath );
493 const QString qmdPath = oldPathInfo.dir().filePath( oldPathInfo.completeBaseName() + QStringLiteral(
".qmd" ) );
494 if ( QFile::exists( qmdPath ) )
495 sidecars.insert( qmdPath );
499 const QString qmlPath = oldPathInfo.dir().filePath( oldPathInfo.completeBaseName() + QStringLiteral(
".qml" ) );
500 if ( QFile::exists( qmlPath ) )
501 sidecars.insert( qmlPath );
504 const QFileInfo newPathInfo( newPath );
508 errors.reserve( sidecars.size() );
510 for (
const QString &sidecar : std::as_const( sidecars ) )
512 const QFileInfo sidecarInfo( sidecar );
513 const QString newSidecarName = newPathInfo.dir().filePath( newPathInfo.completeBaseName() +
'.' + sidecarInfo.suffix() );
514 if ( newSidecarName != sidecar && QFile::exists( newSidecarName ) )
517 errors.append( QDir::toNativeSeparators( newSidecarName ) );
522 error = QObject::tr(
"Destination files already exist %1" ).arg( errors.join( QLatin1String(
", " ) ) );
526 if ( !QFile::rename( oldPath, newPath ) )
528 error = QObject::tr(
"Could not rename %1" ).arg( QDir::toNativeSeparators( oldPath ) );
532 for (
const QString &sidecar : std::as_const( sidecars ) )
534 const QFileInfo sidecarInfo( sidecar );
535 const QString newSidecarName = newPathInfo.dir().filePath( newPathInfo.completeBaseName() +
'.' + sidecarInfo.suffix() );
536 if ( newSidecarName == sidecar )
539 if ( !QFile::rename( sidecar, newSidecarName ) )
541 errors.append( QDir::toNativeSeparators( sidecar ) );
547 error = QObject::tr(
"Could not rename %1" ).arg( errors.join( QLatin1String(
", " ) ) );
556 struct rlimit rescLimit;
557 if ( getrlimit( RLIMIT_NOFILE, &rescLimit ) == 0 )
559 return rescLimit.rlim_cur;
568 int res =
static_cast<int>( QDir(
"/proc/self/fd" ).entryList().size() );
582 constexpr int SOME_MARGIN = 20;
583 return nFileCount > 0 && nFileLimit > 0 && nFileCount + filesToBeOpened > nFileLimit - SOME_MARGIN;
589 QString path = QDir::cleanPath( input );
590 if ( path.isEmpty() )
593 const QString fileName = QFileInfo( path ).fileName();
594 if ( !fileName.isEmpty() )
596 else if ( QFileInfo( path ).path() == path )
599 QString prevPath = path;
600 while ( ( path = QFileInfo( path ).path() ).length() < prevPath.length() )
602 const QString dirName = QDir( path ).dirName();
603 if ( dirName == QLatin1String(
"." ) )
606 result << ( !dirName.isEmpty() ? dirName : path );
610 std::reverse( result.begin(), result.end() );
616 if ( ! QFileInfo::exists( path ) )
621 QFileInfo info { path };
622 const QString suffix { info.completeSuffix() };
623 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.
static QString uniquePath(const QString &path)
Creates a unique file path name from a desired path by appending "_<n>" (where "<n>" is an integer nu...
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 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.