[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kfm-devel
Subject:    Better solution for allowing slash in filenames
From:       David Faure <faure () kde ! org>
Date:       2008-01-18 16:44:14
Message-ID: 200801181744.14452.faure () kde ! org
[Download RAW message or body]

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).

["slash.diff" (text/x-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<len) && _str[i+2].toLower()=='f' ) { // %2f -> /
-                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<QString>("filename");
     QTest::addColumn<QString>("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<QString>("text");
     QTest::addColumn<QString>("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;


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic