Retain non-bounding double quotes in paths (#62906)

* split paths on space(s) between quotes without stripping quotes

Fixes #62837

* only strip outside quotes from paths

Fixes #62837

* only strip bookend quotes from paths

Fixes #62837

* additional tests for updated path parsing

Fixes #62837
This commit is contained in:
Tom Christian 2025-08-27 02:34:05 -07:00 committed by GitHub
parent 0323f7fc5a
commit 0503771bf1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 57 additions and 8 deletions

View File

@ -82,17 +82,46 @@ QString QgsFileWidget::filePath()
QStringList QgsFileWidget::splitFilePaths( const QString &path )
{
QStringList paths;
const thread_local QRegularExpression partsRegex = QRegularExpression( QStringLiteral( "\"\\s+\"" ) );
const QStringList pathParts = path.split( partsRegex, Qt::SkipEmptyParts );
QStringList pathParts;
// Iterate over regular expression matches in the path instead of splitting the path on an expression.
// Splitting on an expression discards the string parts matching the expression.
// We want to split on spaces between double quotes without discarding double quotes around spaces.
// The decision whether to discard double quotes is made later, based on each isolated split path.
const thread_local QRegularExpression partSeparatorsRegex = QRegularExpression( QStringLiteral( "(?:\")(\\s+)(?:\")" ) );
QRegularExpressionMatchIterator partSeparatorMatches = partSeparatorsRegex.globalMatch( path );
int substringStart = 0;
while ( partSeparatorMatches.hasNext() )
{
QRegularExpressionMatch match = partSeparatorMatches.next();
int substringEnd = match.capturedStart() + 1;
int substringLength = substringEnd - substringStart;
pathParts.append( path.mid( substringStart, substringLength ) );
substringStart = match.capturedEnd() - 1;
if ( !partSeparatorMatches.hasNext() )
{
pathParts.append( path.mid( substringStart ) );
}
}
if ( pathParts.length() == 0 )
{
pathParts.append( path );
}
const thread_local QRegularExpression cleanRe( QStringLiteral( "(^\\s*\")|(\"\\s*)" ) );
paths.reserve( pathParts.size() );
QStringList paths;
const thread_local QRegularExpression doubleQuoteWrappedRegex( QStringLiteral( "(?:^\\s*\")(.+)(?:\"\\s*$)" ) );
for ( const QString &pathsPart : pathParts )
{
QString cleaned = pathsPart;
cleaned.remove( cleanRe );
paths.append( cleaned );
QRegularExpressionMatch match = doubleQuoteWrappedRegex.match( pathsPart );
QString finalPath;
if ( match.hasMatch() )
{
finalPath = match.captured( 1 );
}
else
{
finalPath = pathsPart;
}
paths.append( finalPath );
}
return paths;
}

View File

@ -209,6 +209,26 @@ void TestQgsFileWidget::testSplitFilePaths()
QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( " \"%1\" \"%1\" " ).arg( path ) ), QStringList() << path << path );
QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( "\"%1\" \"%1\" " ).arg( path ) ), QStringList() << path << path );
QCOMPARE( QgsFileWidget::splitFilePaths( path ), QStringList() << path );
const QString pathPrefixed = QString( TEST_DATA_DIR + QStringLiteral( "ZARR:\"/path/to/store.zarr\"" ) );
QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( "\"%1\" \"%1\"" ).arg( pathPrefixed ) ), QStringList() << pathPrefixed << pathPrefixed );
QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( "\"%1\" \"%1\"" ).arg( pathPrefixed ) ), QStringList() << pathPrefixed << pathPrefixed );
QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( " \"%1\" \"%1\"" ).arg( pathPrefixed ) ), QStringList() << pathPrefixed << pathPrefixed );
QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( " \"%1\" \"%1\" " ).arg( pathPrefixed ) ), QStringList() << pathPrefixed << pathPrefixed );
QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( "\"%1\" \"%1\" " ).arg( pathPrefixed ) ), QStringList() << pathPrefixed << pathPrefixed );
QCOMPARE( QgsFileWidget::splitFilePaths( pathPrefixed ), QStringList() << pathPrefixed );
QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( "\"%1\" \"%2\"" ).arg( path, pathPrefixed ) ), QStringList() << path << pathPrefixed );
QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( "\"%1\" \"%2\"" ).arg( path, pathPrefixed ) ), QStringList() << path << pathPrefixed );
QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( " \"%1\" \"%2\"" ).arg( path, pathPrefixed ) ), QStringList() << path << pathPrefixed );
QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( " \"%1\" \"%2\" " ).arg( path, pathPrefixed ) ), QStringList() << path << pathPrefixed );
QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( "\"%1\" \"%2\" " ).arg( path, pathPrefixed ) ), QStringList() << path << pathPrefixed );
QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( "\"%1\" \"%2\"" ).arg( pathPrefixed, path ) ), QStringList() << pathPrefixed << path );
QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( "\"%1\" \"%2\"" ).arg( pathPrefixed, path ) ), QStringList() << pathPrefixed << path );
QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( " \"%1\" \"%2\"" ).arg( pathPrefixed, path ) ), QStringList() << pathPrefixed << path );
QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( " \"%1\" \"%2\" " ).arg( pathPrefixed, path ) ), QStringList() << pathPrefixed << path );
QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( "\"%1\" \"%2\" " ).arg( pathPrefixed, path ) ), QStringList() << pathPrefixed << path );
}
QGSTEST_MAIN( TestQgsFileWidget )