24#include <QDirIterator>
27#include <QRegularExpression>
32#include <sys/resource.h>
40#pragma comment(lib,"Shell32.lib")
46 list << QObject::tr(
"KB" ) << QObject::tr(
"MB" ) << QObject::tr(
"GB" ) << QObject::tr(
"TB" );
48 QStringListIterator i( list );
49 QString unit = QObject::tr(
"B" );
51 double fileSize = bytes;
52 while ( fileSize >= 1024.0 && i.hasNext() )
57 return QStringLiteral(
"%1 %2" ).arg( QString::number( fileSize,
'f', bytes >= 1048576 ? 2 : 0 ), unit );
62 const thread_local QRegularExpression rx( QStringLiteral(
"\\*\\.([a-zA-Z0-9\\.]+)" ) );
63 QStringList extensions;
64 QRegularExpressionMatchIterator matches = rx.globalMatch( filter );
66 while ( matches.hasNext() )
68 const QRegularExpressionMatch match = matches.next();
69 if ( match.hasMatch() )
71 QStringList newExtensions = match.capturedTexts();
72 newExtensions.pop_front();
73 extensions.append( newExtensions );
81 const thread_local QRegularExpression globPatternsRx( QStringLiteral(
".*\\((.*?)\\)$" ) );
82 const QRegularExpressionMatch matches = globPatternsRx.match( filter );
83 if ( matches.hasMatch() )
84 return matches.captured( 1 );
91 QFileInfo fi( fileName );
92 const QString name = fi.fileName();
93 const QStringList parts = filter.split( QStringLiteral(
";;" ) );
94 for (
const QString &part : parts )
96 const QStringList globPatterns =
wildcardsFromFilter( part ).split(
' ', Qt::SkipEmptyParts );
97 for (
const QString &glob : globPatterns )
99 const QString re = QRegularExpression::wildcardToRegularExpression( glob );
101 const QRegularExpression globRx( re );
102 if ( globRx.match( name ).hasMatch() )
111 if ( extensions.empty() || f.isEmpty() )
114 QString fileName = f;
116 for (
const QString &extension : std::as_const( extensions ) )
118 const QString extWithDot = extension.startsWith(
'.' ) ? extension :
'.' + extension;
119 if ( fileName.endsWith( extWithDot, Qt::CaseInsensitive ) )
128 const QString extension = extensions.at( 0 );
129 const QString extWithDot = extension.startsWith(
'.' ) ? extension :
'.' + extension;
130 fileName += extWithDot;
144 const thread_local QRegularExpression rx( QStringLiteral(
"[/\\\\\\?%\\*\\:\\|\"<>]" ) );
146 s.replace( rx, QStringLiteral(
"_" ) );
152 if ( path.isEmpty() )
156 QFileInfo fi( path );
158 currentPath = fi.dir();
160 currentPath = QDir( path );
162 QSet< QString > visited;
163 while ( !currentPath.exists() )
165 const QString parentPath = QDir::cleanPath( currentPath.absolutePath() + QStringLiteral(
"/.." ) );
166 if ( visited.contains( parentPath ) )
169 if ( parentPath.isEmpty() || parentPath == QLatin1String(
"." ) )
171 currentPath = QDir( parentPath );
172 visited << parentPath;
175 const QString res = QDir::cleanPath( currentPath.absolutePath() );
177 if ( res == QDir::currentPath() )
180 return res == QLatin1String(
"." ) ? QString() : res;
183QStringList
QgsFileUtils::findFile(
const QString &file,
const QString &basePath,
int maxClimbs,
int searchCeilling,
const QString ¤tDir )
186 QString originalFolder;
188 const QString fileName( basePath.isEmpty() ? QFileInfo( file ).fileName() : file );
189 const QString baseFolder( basePath.isEmpty() ? QFileInfo( file ).path() : basePath );
191 if ( QFileInfo( baseFolder ).isDir() )
193 folder = QDir( baseFolder ) ;
194 originalFolder = folder.absolutePath();
198 folder = QDir( QFileInfo( baseFolder ).absolutePath() );
199 originalFolder = folder.absolutePath();
202 QStringList searchedFolder = QStringList();
203 QString existingBase;
204 QString backupDirectory = QDir::currentPath();
205 QStringList foundFiles;
207 if ( !currentDir.isEmpty() && backupDirectory != currentDir && QDir( currentDir ).exists() )
208 QDir::setCurrent( currentDir );
211 while ( !folder.exists() && folder.absolutePath().count(
'/' ) > searchCeilling )
214 existingBase = folder.path();
215 if ( !folder.cdUp() )
216 folder = QFileInfo( existingBase ).absoluteDir();
220 if ( depth > ( maxClimbs + 4 ) )
223 bool folderExists = folder.exists();
225 if ( depth > maxClimbs )
228 if ( folder.absolutePath().count(
'/' ) < searchCeilling )
229 searchCeilling = folder.absolutePath().count(
'/' ) - 1;
231 while ( depth <= maxClimbs && folderExists && folder.absolutePath().count(
'/' ) >= searchCeilling )
234 QDirIterator localFinder( folder.path(), QStringList() << fileName, QDir::Files, QDirIterator::NoIteratorFlags );
235 searchedFolder.append( folder.absolutePath() );
236 if ( localFinder.hasNext() )
238 foundFiles << localFinder.next();
243 const QFileInfoList subdirs = folder.entryInfoList( QDir::AllDirs );
244 for (
const QFileInfo &subdir : subdirs )
246 if ( ! searchedFolder.contains( subdir.absolutePath() ) )
248 QDirIterator subDirFinder( subdir.path(), QStringList() << fileName, QDir::Files, QDirIterator::Subdirectories );
249 if ( subDirFinder.hasNext() )
251 QString possibleFile = subDirFinder.next();
252 if ( !subDirFinder.hasNext() )
254 foundFiles << possibleFile;
258 foundFiles << possibleFile;
259 while ( subDirFinder.hasNext() )
261 foundFiles << subDirFinder.next();
269 if ( depth > maxClimbs )
272 folderExists = folder.cdUp();
275 if ( QDir::currentPath() == currentDir && currentDir != backupDirectory )
276 QDir::setCurrent( backupDirectory );
282std::unique_ptr< wchar_t[] > pathToWChar(
const QString &path )
284 const QString nativePath = QDir::toNativeSeparators( path );
286 std::unique_ptr< wchar_t[] > pathArray(
new wchar_t[
static_cast< uint
>( nativePath.length() + 1 )] );
287 nativePath.toWCharArray( pathArray.get() );
288 pathArray[
static_cast< size_t >( nativePath.length() )] = 0;
293void fileAttributesOld( HANDLE handle, DWORD &fileAttributes,
bool &hasFileAttributes )
295 hasFileAttributes =
false;
296 BY_HANDLE_FILE_INFORMATION info;
297 if ( GetFileInformationByHandle( handle, &info ) )
299 hasFileAttributes =
true;
300 fileAttributes = info.dwFileAttributes;
305void fileAttributesNew( HANDLE handle, DWORD &fileAttributes,
bool &hasFileAttributes )
307 hasFileAttributes =
false;
309 _FILE_BASIC_INFO infoEx;
310 if ( GetFileInformationByHandleEx(
313 &infoEx,
sizeof( infoEx ) ) )
315 hasFileAttributes =
true;
316 fileAttributes = infoEx.FileAttributes;
321 fileAttributesOld( handle, fileAttributes, hasFileAttributes );
324 fileAttributesOld( handle, fileAttributes, hasFileAttributes );
328bool pathIsLikelyCloudStorage( QString path )
332 QDirIterator dirIt( path, QDir::Files );
333 if ( dirIt.hasNext() )
338 std::unique_ptr< wchar_t[] > pathArray = pathToWChar( path );
339 const HANDLE handle = CreateFileW( pathArray.get(), 0, FILE_SHARE_READ,
340 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
nullptr );
341 if ( handle != INVALID_HANDLE_VALUE )
343 bool hasFileAttributes =
false;
344 DWORD attributes = 0;
345 fileAttributesNew( handle, attributes, hasFileAttributes );
346 CloseHandle( handle );
347 if ( hasFileAttributes )
361 return ( attributes & FILE_ATTRIBUTE_RECALL_ON_OPEN )
362 || ( attributes & FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS );
374 std::unique_ptr< wchar_t[] > pathArray = pathToWChar( path );
375 const UINT type = GetDriveTypeW( pathArray.get() );
381 case DRIVE_NO_ROOT_DIR:
384 case DRIVE_REMOVABLE:
404 const QString originalPath = QDir::cleanPath( path );
405 QString currentPath = originalPath;
407 while ( currentPath != prevPath )
409 if ( pathIsLikelyCloudStorage( currentPath ) )
412 prevPath = currentPath;
413 currentPath = QFileInfo( currentPath ).path();
430 if ( path.contains( QLatin1String(
"fake_slow_path_for_unit_tests" ) ) )
463 for (
const QString &provider : providers )
469 for (
const QString &possibleSidecar : possibleSidecars )
471 if ( QFile::exists( possibleSidecar ) )
472 res.insert( possibleSidecar );
481 if ( !QFile::exists( oldPath ) )
483 error = QObject::tr(
"File does not exist" );
487 const QFileInfo oldPathInfo( oldPath );
491 const QString qmdPath = oldPathInfo.dir().filePath( oldPathInfo.completeBaseName() + QStringLiteral(
".qmd" ) );
492 if ( QFile::exists( qmdPath ) )
493 sidecars.insert( qmdPath );
497 const QString qmlPath = oldPathInfo.dir().filePath( oldPathInfo.completeBaseName() + QStringLiteral(
".qml" ) );
498 if ( QFile::exists( qmlPath ) )
499 sidecars.insert( qmlPath );
502 const QFileInfo newPathInfo( newPath );
506 errors.reserve( sidecars.size() );
508 for (
const QString &sidecar : std::as_const( sidecars ) )
510 const QFileInfo sidecarInfo( sidecar );
511 const QString newSidecarName = newPathInfo.dir().filePath( newPathInfo.completeBaseName() +
'.' + sidecarInfo.suffix() );
512 if ( newSidecarName != sidecar && QFile::exists( newSidecarName ) )
515 errors.append( QDir::toNativeSeparators( newSidecarName ) );
520 error = QObject::tr(
"Destination files already exist %1" ).arg( errors.join( QLatin1String(
", " ) ) );
524 if ( !QFile::rename( oldPath, newPath ) )
526 error = QObject::tr(
"Could not rename %1" ).arg( QDir::toNativeSeparators( oldPath ) );
530 for (
const QString &sidecar : std::as_const( sidecars ) )
532 const QFileInfo sidecarInfo( sidecar );
533 const QString newSidecarName = newPathInfo.dir().filePath( newPathInfo.completeBaseName() +
'.' + sidecarInfo.suffix() );
534 if ( newSidecarName == sidecar )
537 if ( !QFile::rename( sidecar, newSidecarName ) )
539 errors.append( QDir::toNativeSeparators( sidecar ) );
545 error = QObject::tr(
"Could not rename %1" ).arg( errors.join( QLatin1String(
", " ) ) );
554 struct rlimit rescLimit;
555 if ( getrlimit( RLIMIT_NOFILE, &rescLimit ) == 0 )
557 return rescLimit.rlim_cur;
568 DIR *dirp = opendir(
"/proc/self/fd" );
572 while (
struct dirent *entry = readdir( dirp ) )
574 if ( entry->d_type == DT_REG )
591 constexpr int SOME_MARGIN = 20;
592 return nFileCount > 0 && nFileLimit > 0 && nFileCount + filesToBeOpened > nFileLimit - SOME_MARGIN;
598 QString path = QDir::cleanPath( input );
599 if ( path.isEmpty() )
602 const QString fileName = QFileInfo( path ).fileName();
603 if ( !fileName.isEmpty() )
605 else if ( QFileInfo( path ).path() == path )
608 QString prevPath = path;
609 while ( ( path = QFileInfo( path ).path() ).length() < prevPath.length() )
611 const QString dirName = QDir( path ).dirName();
612 if ( dirName == QLatin1String(
"." ) )
615 result << ( !dirName.isEmpty() ? dirName : path );
619 std::reverse( result.begin(), result.end() );
623QString QgsFileUtils::uniquePath(
const QString &path )
625 if ( ! QFileInfo::exists( path ) )
630 QFileInfo info { path };
631 const QString suffix { info.completeSuffix() };
632 const QString pathPattern { QString( suffix.isEmpty() ? path : path.chopped( suffix.length() + 1 ) ).append( suffix.isEmpty() ? QStringLiteral(
"_%1" ) : QStringLiteral(
"_%1." ) ).append( suffix ) };
634 QString uniquePath { pathPattern.arg( i ) };
635 while ( QFileInfo::exists( uniquePath ) )
638 uniquePath = pathPattern.arg( i );
@ 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 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.