From kfm-devel Fri Jan 18 16:44:14 2008 From: David Faure Date: Fri, 18 Jan 2008 16:44:14 +0000 To: kfm-devel Subject: Better solution for allowing slash in filenames Message-Id: <200801181744.14452.faure () kde ! org> X-MARC-Message: https://marc.info/?l=kfm-devel&m=120067470101987 MIME-Version: 1 Content-Type: multipart/mixed; boundary="--Boundary-00=_edNkHdXRPSSk0rF" --Boundary-00=_edNkHdXRPSSk0rF Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline KDE 3.x and KDE 4.0 allow slashes in filenames by encoding them as %2F (and % as %%), a nice trick by Torben in KDE-1, but which leads to unexpected behavior (like "foo%" and "foo%%" both showing up as "foo%" in konqueror, the real path and the visible filename being different, etc). I found a much better solution to allow '/' without all this escaping: using a unicode character that looks like a slash but that isn't '/': QChar(0x2044), also known as "FRACTION SLASH". Looks quite nice IMHO: http://web.davidfaure.fr/kde/slash.jpg Ah... of course there's one downside: if you name a file "http://www.kde.org" you can't copy paste that name into the webbrowser as a URL, since the slashes are not real slashes anymore. Oh well, corner case of the corner case... -- David Faure, faure@kde.org, sponsored by Trolltech to work on KDE, Konqueror (http://www.konqueror.org), and KOffice (http://www.koffice.org). --Boundary-00=_edNkHdXRPSSk0rF Content-Type: text/x-diff; charset="us-ascii"; name="slash.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="slash.diff" Index: kio/global.cpp =================================================================== --- kio/global.cpp (revision 762501) +++ kio/global.cpp (working copy) @@ -132,41 +132,14 @@ KIO_EXPORT QString KIO::itemsSummaryStri KIO_EXPORT QString KIO::encodeFileName( const QString & _str ) { QString str( _str ); - - int i = 0; - while ( ( i = str.indexOf( '%', i ) ) != -1 ) { - str.replace( i, 1, "%%"); - i += 2; - } - while ( ( i = str.indexOf( '/' ) ) != -1 ) - str.replace( i, 1, "%2f"); + str.replace('/', QChar(0x2044)); // "Fraction slash" return str; } KIO_EXPORT QString KIO::decodeFileName( const QString & _str ) { - const int len = _str.length(); - QString text; - text.reserve(len); - for ( int i = 0; i < len ; ++i ) { - if ( _str[i] == '%' && i+1 < len ) { - const QChar nextChar = _str[i+1]; - if ( nextChar == '%' ) { // %% -> % - text.append(QLatin1Char('%')); - ++i; - } else if ( nextChar == '2' && (i+2 / - text.append(QLatin1Char('/')); - i += 2; - } else { - text.append(QLatin1Char('%')); - } - } else { - text.append(_str[i]); - } - } - text.squeeze(); - - return text; + // Nothing to decode. "Fraction slash" is fine in filenames. + return _str; } KIO_EXPORT QString KIO::Job::errorString() const Index: tests/kfileitemtest.cpp =================================================================== --- tests/kfileitemtest.cpp (revision 762501) +++ tests/kfileitemtest.cpp (working copy) @@ -208,15 +208,9 @@ void KFileItemTest::testDecodeFileName_d QTest::addColumn("filename"); QTest::addColumn("expectedText"); - QTest::newRow("no %") << "filename" << "filename"; - QTest::newRow("%2f at end") << "foo%2f" << "foo/"; - QTest::newRow("%2f at begin") << "%2f" << "/"; - QTest::newRow("%2F") << "foo%2Fbar" << "foo/bar"; - QTest::newRow("%%") << "%%" << "%"; - QTest::newRow("%%%%") << "%%%%" << "%%"; - QTest::newRow("%x") << "%x" << "%x"; - QTest::newRow("%x%") << "%x%" << "%x%"; - QTest::newRow("%2f as result") << "foo%%2f" << "foo%2f"; + QTest::newRow("simple") << "filename" << "filename"; + QTest::newRow("/ at end") << QString("foo") + QChar(0x2044) << QString("foo") + QChar(0x2044); + QTest::newRow("/ at begin") << QString(QChar(0x2044)) << QString(QChar(0x2044)); } @@ -232,14 +226,9 @@ void KFileItemTest::testEncodeFileName_d QTest::addColumn("text"); QTest::addColumn("expectedFileName"); - QTest::newRow("no %") << "filename" << "filename"; - QTest::newRow("%2f at end") << "foo/" << "foo%2f"; - QTest::newRow("%2f at begin") << "/" << "%2f"; - QTest::newRow("%%") << "%" << "%%"; - QTest::newRow("%%%%") << "%%" << "%%%%"; - QTest::newRow("%x") << "%x" << "%%x"; - QTest::newRow("%x%") << "%x%" << "%%x%%"; - QTest::newRow("%2f as input") << "foo%2f" << "foo%%2f"; + QTest::newRow("simple") << "filename" << "filename"; + QTest::newRow("/ at end") << "foo/" << QString("foo") + QChar(0x2044); + QTest::newRow("/ at begin") << "/" << QString(QChar(0x2044)); } void KFileItemTest::testEncodeFileName() Index: tests/kdirmodeltest.cpp =================================================================== --- tests/kdirmodeltest.cpp (revision 762501) +++ tests/kdirmodeltest.cpp (working copy) @@ -45,7 +45,7 @@ void KDirModelTest::initTestCase() * PATH/toplevelfile_1 * PATH/toplevelfile_2 * PATH/toplevelfile_3 - * PATH/file%2fslash + * PATH/specialchars%: * PATH/subdir * PATH/subdir/testfile * PATH/subdir/subsubdir @@ -55,7 +55,7 @@ void KDirModelTest::initTestCase() m_topLevelFileNames << "toplevelfile_1" << "toplevelfile_2" << "toplevelfile_3" - << "file%2fslash"; + << "specialchars%:"; foreach(QString f, m_topLevelFileNames) { createTestFile(path+f); } @@ -87,13 +87,13 @@ void KDirModelTest::fillModel( bool relo m_fileIndex = idx; else if (item.url().fileName() == "toplevelfile_2") m_secondFileIndex = idx; - else if (item.url().fileName().endsWith("slash")) - m_slashFileIndex = idx; + else if (item.url().fileName().startsWith("special")) + m_specialFileIndex = idx; } QVERIFY(m_dirIndex.isValid()); QVERIFY(m_fileIndex.isValid()); QVERIFY(m_secondFileIndex.isValid()); - QVERIFY(m_slashFileIndex.isValid()); + QVERIFY(m_specialFileIndex.isValid()); // Now list subdir/ QVERIFY(m_dirModel.canFetchMore(m_dirIndex)); @@ -179,8 +179,8 @@ void KDirModelTest::testNames() QString fileName = m_dirModel.data(m_fileIndex, Qt::DisplayRole).toString(); QCOMPARE(fileName, QString("toplevelfile_1")); - QString slashFileName = m_dirModel.data(m_slashFileIndex, Qt::DisplayRole).toString(); - QCOMPARE(slashFileName, QString("file/slash")); + QString specialFileName = m_dirModel.data(m_specialFileIndex, Qt::DisplayRole).toString(); + QCOMPARE(specialFileName, QString("specialchars%:")); QString dirName = m_dirModel.data(m_dirIndex, Qt::DisplayRole).toString(); QCOMPARE(dirName, QString("subdir")); Index: tests/kdirmodeltest.h =================================================================== --- tests/kdirmodeltest.h (revision 762501) +++ tests/kdirmodeltest.h (working copy) @@ -58,7 +58,7 @@ private: KTempDir m_tempDir; KDirModel m_dirModel; QModelIndex m_fileIndex; - QModelIndex m_slashFileIndex; + QModelIndex m_specialFileIndex; QModelIndex m_secondFileIndex; QModelIndex m_dirIndex; QModelIndex m_fileInDirIndex; --Boundary-00=_edNkHdXRPSSk0rF--