• Skip to content
  • Skip to link menu
KDE 4.7 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KDECore

kurl.cpp
Go to the documentation of this file.
00001 /*
00002     Copyright (C) 1999 Torben Weis <weis@kde.org>
00003     Copyright (C) 2005-2006 David Faure <faure@kde.org>
00004 
00005     This library is free software; you can redistribute it and/or
00006     modify it under the terms of the GNU Library General Public
00007     License as published by the Free Software Foundation; either
00008     version 2 of the License, or (at your option) any later version.
00009 
00010     This library is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013     Library General Public License for more details.
00014 
00015     You should have received a copy of the GNU Library General Public License
00016     along with this library; see the file COPYING.LIB.  If not, write to
00017     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018     Boston, MA 02110-1301, USA.
00019 */
00020 
00022 
00023 /*
00024  * The currently active RFC for URL/URIs is RFC3986
00025  * Previous (and now deprecated) RFCs are RFC1738 and RFC2396
00026  */
00027 
00028 #include "kurl.h"
00029 
00030 #include <kdebug.h>
00031 #include <kglobal.h>
00032 #include <kshell.h>
00033 
00034 #include <stdio.h>
00035 #include <assert.h>
00036 #include <ctype.h>
00037 #include <stdlib.h>
00038 #include <unistd.h>
00039 
00040 #include <QtCore/QDir>
00041 #include <QtCore/QMutableStringListIterator>
00042 #include <QtCore/QRegExp>
00043 #include <QtCore/QMimeData>
00044 #include <QtCore/QTextCodec>
00045 
00046 #ifdef DEBUG_KURL
00047 static int kurlDebugArea() { static int s_area = KDebug::registerArea("kdecore (KUrl)"); return s_area; }
00048 #endif
00049 
00050 static QString cleanpath( const QString &_path, bool cleanDirSeparator, bool decodeDots )
00051 {
00052   if (_path.isEmpty())
00053       return QString();
00054 
00055   if (QFileInfo(_path).isRelative())
00056      return _path; // Don't mangle mailto-style URLs
00057 
00058   QString path = _path;
00059 
00060   int len = path.length();
00061 
00062   if (decodeDots)
00063   {
00064      static const QString &encodedDot = KGlobal::staticQString("%2e");
00065      if (path.indexOf(encodedDot, 0, Qt::CaseInsensitive) != -1)
00066      {
00067         static const QString &encodedDOT = KGlobal::staticQString("%2E"); // Uppercase!
00068         path.replace(encodedDot, QString(QLatin1Char('.')));
00069         path.replace(encodedDOT, QString(QLatin1Char('.')));
00070         len = path.length();
00071      }
00072   }
00073 
00074   const bool slash = (len && path[len-1] == QLatin1Char('/')) ||
00075                      (len > 1 && path[len-2] == QLatin1Char('/') && path[len-1] == QLatin1Char('.'));
00076 
00077   // The following code cleans up directory path much like
00078   // QDir::cleanPath() except it can be made to ignore multiple
00079   // directory separators by setting the flag to false.  That fixes
00080   // bug# 15044, mail.altavista.com and other similar brain-dead server
00081   // implementations that do not follow what has been specified in
00082   // RFC 2396!! (dA)
00083   QString result;
00084   int cdUp, orig_pos, pos;
00085 
00086   cdUp = 0;
00087   pos = orig_pos = len;
00088   while ( pos && (pos = path.lastIndexOf(QLatin1Char('/'),--pos)) != -1 )
00089   {
00090     len = orig_pos - pos - 1;
00091     if ( len == 2 && path[pos+1] == QLatin1Char('.') && path[pos+2] == QLatin1Char('.') )
00092       cdUp++;
00093     else
00094     {
00095       // Ignore any occurrences of '.'
00096       // This includes entries that simply do not make sense like /..../
00097       if ( (len || !cleanDirSeparator) &&
00098            (len != 1 || path[pos+1] != QLatin1Char('.') ) )
00099       {
00100           if ( !cdUp )
00101               result.prepend(path.mid(pos, len+1));
00102           else
00103               cdUp--;
00104       }
00105     }
00106     orig_pos = pos;
00107   }
00108 
00109 #ifdef Q_WS_WIN // prepend drive letter if exists (js)
00110   if (orig_pos >= 2 && path[0].isLetter() && path[1] == QLatin1Char(':') ) {
00111     result.prepend(QString(path[0]) + QLatin1Char(':') );
00112   }
00113 #endif
00114 
00115   if ( result.isEmpty() )
00116     result = QLatin1Char('/');
00117   else if ( slash && result[result.length()-1] != QLatin1Char('/') )
00118        result.append(QLatin1Char('/'));
00119 
00120   return result;
00121 }
00122 
00123 #ifdef Q_WS_WIN
00124 
00125 // returns true if provided arguments desinate letter+colon or double slash
00126 #define IS_DRIVE_OR_DOUBLESLASH(isletter, char1, char2, colon, slash) \
00127   ((isletter && char2 == colon) || (char1 == slash && char2 == slash))
00128 
00129 // Removes file:/// or file:// or file:/ or / prefix assuming that str
00130 // is (nonempty) Windows absolute path with a drive letter or double slash.
00131 // If there was file protocol, the path is decoded from percent encoding
00132 static QString removeSlashOrFilePrefix(const QString& str)
00133 {
00134   // FIXME this should maybe be replaced with some (faster?)/nicer logic
00135   const int len = str.length();
00136   if (str[0]==QLatin1Char('f')) {
00137     if ( len > 10 && str.startsWith( QLatin1String( "file:///" ) )
00138          && IS_DRIVE_OR_DOUBLESLASH(str[8].isLetter(), str[8], str[9], QLatin1Char(':'), QLatin1Char('/')) )
00139       return QUrl::fromPercentEncoding( str.toLatin1() ).mid(8);
00140     else if ( len > 9 && str.startsWith( QLatin1String( "file://" ) )
00141               && IS_DRIVE_OR_DOUBLESLASH(str[7].isLetter(), str[7], str[8], QLatin1Char(':'), QLatin1Char('/')) )
00142       return QUrl::fromPercentEncoding( str.toLatin1() ).mid(7);
00143     else if ( len > 8 && str.startsWith( QLatin1String( "file:/" ) )
00144               && IS_DRIVE_OR_DOUBLESLASH(str[6].isLetter(), str[6], str[7], QLatin1Char(':'), QLatin1Char('/')) )
00145       return QUrl::fromPercentEncoding( str.toLatin1() ).mid(6);
00146   }
00147   /* No 'else' here since there can be "f:/" path. */
00148 
00149   /* '/' + drive letter or // */
00150   if ( len > 2 && str[0] == QLatin1Char('/')
00151        && IS_DRIVE_OR_DOUBLESLASH(str[1].isLetter(), str[1], str[2], QLatin1Char(':'), QLatin1Char('/')) )
00152     return str.mid(1);
00153   /* drive letter or // */
00154   else if ( len >= 2 && IS_DRIVE_OR_DOUBLESLASH(str[0].isLetter(), str[0], str[1], QLatin1Char(':'), QLatin1Char('/')) )
00155     return str;
00156   return QString();
00157 }
00158 #endif
00159 
00160 bool KUrl::isRelativeUrl(const QString &_url)
00161 {
00162   int len = _url.length();
00163   if (!len) return true; // Very short relative URL.
00164   const QChar *str = _url.unicode();
00165 
00166   // Absolute URL must start with alpha-character
00167   if (!isalpha(str[0].toLatin1()))
00168      return true; // Relative URL
00169 
00170   for(int i = 1; i < len; i++)
00171   {
00172      char c = str[i].toLatin1(); // Note: non-latin1 chars return 0!
00173      if (c == ':')
00174         return false; // Absolute URL
00175 
00176      // Protocol part may only contain alpha, digit, + or -
00177      if (!isalpha(c) && !isdigit(c) && (c != '+') && (c != '-'))
00178         return true; // Relative URL
00179   }
00180   // URL did not contain ':'
00181   return true; // Relative URL
00182 }
00183 
00184 KUrl::List::List(const KUrl &url)
00185 {
00186     append( url );
00187 }
00188 
00189 KUrl::List::List(const QList<KUrl> &list)
00190     : QList<KUrl>(list)
00191 {
00192 }
00193 
00194 KUrl::List::List(const QList<QUrl> &list)
00195 {
00196     foreach(const QUrl& url, list) {
00197         append(KUrl(url));
00198     }
00199 }
00200 
00201 KUrl::List::List(const QStringList &list)
00202 {
00203   for (QStringList::ConstIterator it = list.begin();
00204        it != list.end();
00205        ++it)
00206     {
00207       append( KUrl(*it) );
00208     }
00209 }
00210 
00211 QStringList KUrl::List::toStringList() const
00212 {
00213     return toStringList(KUrl::LeaveTrailingSlash);
00214 }
00215 
00216 QStringList KUrl::List::toStringList(KUrl::AdjustPathOption trailing) const
00217 {
00218     QStringList lst;
00219     for(KUrl::List::ConstIterator it = constBegin();
00220          it != constEnd(); ++it) {
00221         lst.append(it->url(trailing));
00222     }
00223     return lst;
00224 }
00225 
00226 static QByteArray uriListData(const KUrl::List& urls)
00227 {
00228     QList<QByteArray> urlStringList;
00229     KUrl::List::ConstIterator uit = urls.constBegin();
00230     const KUrl::List::ConstIterator uEnd = urls.constEnd();
00231     for (; uit != uEnd ; ++uit) {
00232         // Get each URL encoded in utf8 - and since we get it in escaped
00233         // form on top of that, .toLatin1() is fine.
00234         urlStringList.append((*uit).toMimeDataString().toLatin1());
00235     }
00236 
00237     QByteArray uriListData;
00238     for (int i = 0, n = urlStringList.count(); i < n; ++i) {
00239       uriListData += urlStringList.at(i);
00240         if (i < n-1)
00241           uriListData += "\r\n";
00242     }
00243     return uriListData;
00244 }
00245 
00246 static const char s_kdeUriListMime[] = "application/x-kde4-urilist";
00247 
00248 void KUrl::List::populateMimeData( QMimeData* mimeData,
00249                                    const KUrl::MetaDataMap& metaData,
00250                                    MimeDataFlags flags ) const
00251 {
00252     mimeData->setData(QString::fromLatin1("text/uri-list"), uriListData(*this));
00253 
00254     if ( ( flags & KUrl::NoTextExport ) == 0 )
00255     {
00256         QStringList prettyURLsList;
00257         KUrl::List::ConstIterator uit = constBegin();
00258         const KUrl::List::ConstIterator uEnd = constEnd();
00259         for ( ; uit != uEnd ; ++uit ) {
00260             QString prettyURL = (*uit).prettyUrl();
00261             if ( (*uit).protocol() == QLatin1String("mailto") ) {
00262                 prettyURL = (*uit).path(); // remove mailto: when pasting into konsole
00263             }
00264             prettyURLsList.append( prettyURL );
00265         }
00266 
00267         QByteArray plainTextData = prettyURLsList.join(QString(QLatin1Char('\n'))).toLocal8Bit();
00268         if( count() > 1 ) // terminate last line, unless it's the only line
00269             plainTextData.append( "\n" );
00270         mimeData->setData( QString::fromLatin1("text/plain"), plainTextData );
00271     }
00272 
00273     if ( !metaData.isEmpty() )
00274     {
00275         QByteArray metaDataData; // :)
00276         for( KUrl::MetaDataMap::const_iterator it = metaData.begin(); it != metaData.end(); ++it )
00277         {
00278             metaDataData += it.key().toUtf8();
00279             metaDataData += "$@@$";
00280             metaDataData += it.value().toUtf8();
00281             metaDataData += "$@@$";
00282         }
00283         mimeData->setData( QString::fromLatin1("application/x-kio-metadata"), metaDataData );
00284     }
00285 }
00286 
00287 
00288 void KUrl::List::populateMimeData(const KUrl::List& mostLocalUrls,
00289                                   QMimeData* mimeData,
00290                                   const KUrl::MetaDataMap& metaData,
00291                                   MimeDataFlags flags) const
00292 {
00293     // Export the most local urls as text/uri-list and plain text.
00294     mostLocalUrls.populateMimeData(mimeData, metaData, flags);
00295 
00296     mimeData->setData(QString::fromLatin1(s_kdeUriListMime), uriListData(*this));
00297 }
00298 
00299 bool KUrl::List::canDecode( const QMimeData *mimeData )
00300 {
00301     return mimeData->hasFormat(QString::fromLatin1("text/uri-list")) ||
00302         mimeData->hasFormat(QString::fromLatin1(s_kdeUriListMime));
00303 }
00304 
00305 QStringList KUrl::List::mimeDataTypes()
00306 {
00307     return QStringList() << QString::fromLatin1(s_kdeUriListMime) << QString::fromLatin1("text/uri-list");
00308 }
00309 
00310 
00311 KUrl::List KUrl::List::fromMimeData(const QMimeData *mimeData,
00312                                     DecodeOptions decodeOptions,
00313                                     KUrl::MetaDataMap* metaData)
00314 {
00315 
00316     KUrl::List uris;
00317     const char* firstMimeType = s_kdeUriListMime;
00318     const char* secondMimeType = "text/uri-list";
00319     if (decodeOptions == PreferLocalUrls) {
00320         qSwap(firstMimeType, secondMimeType);
00321     }
00322     QByteArray payload = mimeData->data(QString::fromLatin1(firstMimeType));
00323     if (payload.isEmpty())
00324         payload = mimeData->data(QString::fromLatin1(secondMimeType));
00325     if ( !payload.isEmpty() ) {
00326         int c = 0;
00327         const char* d = payload.constData();
00328         while ( c < payload.size() && d[c] ) {
00329             int f = c;
00330             // Find line end
00331             while (c < payload.size() && d[c] && d[c]!='\r'
00332                    && d[c] != '\n')
00333                 c++;
00334             QByteArray s( d+f, c-f );
00335             if ( s[0] != '#' ) // non-comment?
00336                 uris.append( KUrl::fromMimeDataByteArray( s ) );
00337             // Skip junk
00338             while ( c < payload.size() && d[c] &&
00339                     ( d[c] == '\n' || d[c] == '\r' ) )
00340                 ++c;
00341         }
00342     }
00343     if ( metaData )
00344     {
00345         const QByteArray metaDataPayload = mimeData->data(QLatin1String("application/x-kio-metadata"));
00346         if ( !metaDataPayload.isEmpty() )
00347         {
00348             QString str = QString::fromUtf8( metaDataPayload );
00349             Q_ASSERT(str.endsWith(QLatin1String("$@@$")));
00350             str.truncate( str.length() - 4 );
00351             const QStringList lst = str.split(QLatin1String("$@@$"));
00352             QStringList::ConstIterator it = lst.begin();
00353             bool readingKey = true; // true, then false, then true, etc.
00354             QString key;
00355             for ( ; it != lst.end(); ++it ) {
00356                 if ( readingKey )
00357                     key = *it;
00358                 else
00359                     metaData->insert( key, *it );
00360                 readingKey = !readingKey;
00361             }
00362             Q_ASSERT( readingKey ); // an odd number of items would be, well, odd ;-)
00363         }
00364     }
00365 
00366     return uris;
00367 }
00368 
00369 KUrl::List KUrl::List::fromMimeData( const QMimeData *mimeData, KUrl::MetaDataMap* metaData )
00370 {
00371     return fromMimeData(mimeData, PreferKdeUrls, metaData);
00372 }
00373 
00374 KUrl::List::operator QVariant() const
00375 {
00376   return qVariantFromValue(*this);
00377 }
00378 
00379 KUrl::List::operator QList<QUrl>() const
00380 {
00381     QList<QUrl> list;
00382     foreach(const KUrl& url, *this) {
00383         list << url;
00384     }
00385     return list;
00386 }
00387 
00389 
00390 KUrl::KUrl()
00391     : QUrl(), d(0)
00392 {
00393 }
00394 
00395 KUrl::~KUrl()
00396 {
00397 }
00398 
00399 
00400 KUrl::KUrl( const QString &str )
00401   : QUrl(), d(0)
00402 {
00403   if ( !str.isEmpty() ) {
00404 #ifdef Q_WS_WIN
00405 #ifdef DEBUG_KURL
00406     kDebug(kurlDebugArea()) << "KUrl::KUrl ( const QString &str = " << str.toAscii().data() << " )";
00407 #endif
00408     QString pathToSet;
00409     // when it starts with file:// it's a url and must be valid. we don't care if the
00410     // path exist/ is valid or not
00411     if (!str.startsWith(QLatin1String("file://")))
00412       pathToSet = removeSlashOrFilePrefix( QDir::fromNativeSeparators(str) );
00413     if ( !pathToSet.isEmpty() ) {
00414       // we have a prefix indicating this is a local URL
00415       // remember the possible query using _setEncodedUrl(), then set up the correct path without query protocol part
00416       int index = pathToSet.lastIndexOf(QLatin1Char('?'));
00417       if (index == -1)
00418         setPath( pathToSet );
00419       else {
00420         setPath( pathToSet.left( index ) );
00421         _setQuery( pathToSet.mid( index + 1 ) );
00422       }
00423       return;
00424     }
00425 #endif
00426     if ( str[0] == QLatin1Char('/') || str[0] == QLatin1Char('~') )
00427       setPath( str );
00428     else {
00429       _setEncodedUrl( str.toUtf8() );
00430     }
00431   }
00432 }
00433 
00434 KUrl::KUrl( const char * str )
00435   : QUrl(), d(0)
00436 {
00437 #ifdef Q_WS_WIN
00438   // true if @a c is letter
00439   #define IS_LETTER(c) \
00440     ((c >= QLatin1Char('A') && c <= QLatin1Char('Z')) || (c >= QLatin1Char('a') && c <= QLatin1Char('z')))
00441 
00442   // like IS_DRIVE_OR_DOUBLESLASH, but slash is prepended
00443   #define IS_SLASH_AND_DRIVE_OR_DOUBLESLASH_0 \
00444     ( QLatin1Char(str[0]) == QLatin1Char('/') && IS_DRIVE_OR_DOUBLESLASH(IS_LETTER(QLatin1Char(str[1])), QLatin1Char(str[1]), QLatin1Char(str[2]), QLatin1Char(':'), QLatin1Char('/')) )
00445 
00446   // like IS_DRIVE_OR_DOUBLESLASH, with characters == str[0] and str[1]
00447   #define IS_DRIVE_OR_DOUBLESLASH_0 \
00448     ( IS_DRIVE_OR_DOUBLESLASH(IS_LETTER(QLatin1Char(str[0])), QLatin1Char(str[0]), QLatin1Char(str[1]), QLatin1Char(':'), QLatin1Char('/')) )
00449 
00450 #if defined(DEBUG_KURL)
00451   kDebug(kurlDebugArea()) << "KUrl::KUrl " << " " << str;
00452 #endif
00453   if ( str && str[0] && str[1] && str[2] ) {
00454     if ( IS_SLASH_AND_DRIVE_OR_DOUBLESLASH_0 )
00455       setPath( QString::fromUtf8( str+1 ) );
00456     else if ( IS_DRIVE_OR_DOUBLESLASH_0 )
00457       setPath( QString::fromUtf8( str ) );
00458   }
00459 #endif
00460   if ( str && str[0] ) {
00461     if ( str[0] == '/' || str[0] == '~' )
00462       setPath( QString::fromUtf8( str ) );
00463     else
00464       _setEncodedUrl( str );
00465   }
00466 }
00467 
00468 KUrl::KUrl( const QByteArray& str )
00469    : QUrl(), d(0)
00470 {
00471   if ( !str.isEmpty() ) {
00472 #ifdef Q_WS_WIN
00473 #ifdef DEBUG_KURL
00474     kDebug(kurlDebugArea()) << "KUrl::KUrl " << " " << str.data();
00475 #endif
00476     if ( IS_SLASH_AND_DRIVE_OR_DOUBLESLASH_0 )
00477       setPath( QString::fromUtf8( str.mid( 1 ) ) );
00478     else if ( IS_DRIVE_OR_DOUBLESLASH_0 )
00479       setPath( QString::fromUtf8( str ) );
00480 #else
00481     if ( str[0] == '/' || str[0] == '~' )
00482       setPath( QString::fromUtf8( str ) );
00483 #endif
00484     else
00485       _setEncodedUrl( str );
00486   }
00487 }
00488 
00489 KUrl::KUrl( const KUrl& _u )
00490     : QUrl( _u ), d(0)
00491 {
00492 #if defined(Q_WS_WIN) && defined(DEBUG_KURL)
00493     kDebug(kurlDebugArea()) << "KUrl::KUrl(KUrl) " << " path " << _u.path() << " toLocalFile " << _u.toLocalFile();
00494 #endif
00495 }
00496 
00497 KUrl::KUrl( const QUrl &u )
00498     : QUrl( u ), d(0)
00499 {
00500 #if defined(Q_WS_WIN) && defined(DEBUG_KURL)
00501     kDebug(kurlDebugArea()) << "KUrl::KUrl(Qurl) " << " path " << u.path() << " toLocalFile " << u.toLocalFile();
00502 #endif
00503 }
00504 
00505 KUrl::KUrl( const KUrl& _u, const QString& _rel_url )
00506    : QUrl(), d(0)
00507 {
00508 #if defined(Q_WS_WIN) && defined(DEBUG_KURL)
00509     kDebug(kurlDebugArea()) << "KUrl::KUrl(KUrl,QString rel_url) " << " path " << _u.path() << " toLocalFile " << _u.toLocalFile();
00510 #endif
00511 #if 0
00512   if (_u.hasSubUrl()) // Operate on the last suburl, not the first
00513   {
00514     KUrl::List lst = split( _u );
00515     KUrl u(lst.last(), _rel_url);
00516     lst.erase( --lst.end() );
00517     lst.append( u );
00518     *this = join( lst );
00519     return;
00520   }
00521 #endif
00522   QString rUrl = _rel_url;
00523 
00524   // WORKAROUND THE RFC 1606 LOOPHOLE THAT ALLOWS
00525   // http:/index.html AS A VALID SYNTAX FOR RELATIVE
00526   // URLS. ( RFC 2396 section 5.2 item # 3 )
00527   const int len = _u.scheme().length();
00528   if ( !_u.host().isEmpty() && !rUrl.isEmpty() &&
00529        rUrl.indexOf( _u.scheme(), 0, Qt::CaseInsensitive ) == 0 &&
00530        rUrl[len] == QLatin1Char(':') && (rUrl[len+1] != QLatin1Char('/') ||
00531        (rUrl[len+1] == QLatin1Char('/') && rUrl[len+2] != QLatin1Char('/'))) )
00532   {
00533     rUrl.remove( 0, rUrl.indexOf( QLatin1Char(':') ) + 1 );
00534   }
00535 
00536 
00537   if ( rUrl.isEmpty() )
00538   {
00539     *this = _u;
00540   }
00541   else if ( rUrl[0] == QLatin1Char('#') )
00542   {
00543     *this = _u;
00544     QByteArray strRef_encoded = rUrl.mid(1).toLatin1();
00545     if ( strRef_encoded.isNull() )
00546         strRef_encoded = ""; // we know there was an (empty) html ref, we saw the '#'
00547     setEncodedFragment( strRef_encoded );
00548   }
00549   else if ( isRelativeUrl( rUrl ) )
00550   {
00551     *this = _u;
00552     setFragment( QString() );
00553     setEncodedQuery( QByteArray() );
00554     QString strPath = path();
00555     if ( rUrl[0] == QLatin1Char('/') )
00556     {
00557         if ((rUrl.length() > 1) && (rUrl[1] == QLatin1Char('/')))
00558         {
00559             setHost( QString() );
00560             setPort( -1 );
00561             // File protocol returns file:/// without host, strip // from rUrl
00562             if ( _u.isLocalFile() )
00563                 rUrl.remove(0, 2);
00564         }
00565         strPath.clear();
00566     }
00567     else if ( rUrl[0] != QLatin1Char('?') )
00568     {
00569        const int pos = strPath.lastIndexOf( QLatin1Char('/') );
00570        if (pos >= 0)
00571           strPath.truncate(pos);
00572        strPath += QLatin1Char('/');
00573     }
00574     else
00575     {
00576        if ( strPath.isEmpty() )
00577           strPath = QLatin1Char('/');
00578     }
00579     setPath( strPath );
00580     //kDebug(kurlDebugArea()) << "url()=" << url() << " rUrl=" << rUrl;
00581     const KUrl tmp( url() + rUrl);
00582     //kDebug(kurlDebugArea()) << "assigning tmp=" << tmp.url();
00583     *this = tmp;
00584     cleanPath(KeepDirSeparators);
00585   }
00586   else
00587   {
00588     const KUrl tmp( rUrl );
00589     //kDebug(kurlDebugArea()) << "not relative; assigning tmp=" << tmp.url();
00590     *this = tmp;
00591     // Preserve userinfo if applicable.
00592     if (!_u.userInfo().isEmpty() && userInfo().isEmpty()
00593         && (_u.host() == host()) && (_u.scheme() == scheme()))
00594     {
00595        setUserInfo( _u.userInfo() );
00596     }
00597     cleanPath(KeepDirSeparators);
00598   }
00599 }
00600 
00601 KUrl& KUrl::operator=( const KUrl& _u )
00602 {
00603   QUrl::operator=( _u );
00604   return *this;
00605 }
00606 
00607 bool KUrl::operator==( const KUrl& _u ) const
00608 {
00609   return QUrl::operator==( _u );
00610 }
00611 
00612 bool KUrl::operator==( const QString& _u ) const
00613 {
00614   KUrl u( _u );
00615   return ( *this == u );
00616 }
00617 
00618 KUrl::operator QVariant() const
00619 {
00620   return qVariantFromValue(*this);
00621 }
00622 
00623 #ifndef KDE_NO_DEPRECATED
00624 bool KUrl::cmp( const KUrl &u, bool ignore_trailing ) const
00625 {
00626   return equals( u, ignore_trailing ? CompareWithoutTrailingSlash : EqualsOptions(0) );
00627 }
00628 #endif
00629 
00630 bool KUrl::equals( const KUrl &_u, const EqualsOptions& options ) const
00631 {
00632   if ( !isValid() || !_u.isValid() )
00633     return false;
00634 
00635   if ( options & CompareWithoutTrailingSlash || options & CompareWithoutFragment )
00636   {
00637     QString path1 = path((options & CompareWithoutTrailingSlash) ? RemoveTrailingSlash : LeaveTrailingSlash);
00638     QString path2 = _u.path((options & CompareWithoutTrailingSlash) ? RemoveTrailingSlash : LeaveTrailingSlash);
00639 
00640     if (options & AllowEmptyPath) {
00641         if (path1 == QLatin1String("/"))
00642             path1.clear();
00643         if (path2 == QLatin1String("/"))
00644             path2.clear();
00645     }
00646 
00647 #ifdef Q_WS_WIN
00648     const bool bLocal1 = isLocalFile();
00649     const bool bLocal2 = _u.isLocalFile();
00650     if ( !bLocal1 && bLocal2 || bLocal1 && !bLocal2 )
00651       return false;
00652     // local files are case insensitive
00653     if ( bLocal1 && bLocal2 && 0 != QString::compare( path1, path2, Qt::CaseInsensitive ) )
00654       return false;
00655 #endif
00656     if ( path1 != path2 )
00657       return false;
00658 
00659     if ( scheme() == _u.scheme() &&
00660          authority() == _u.authority() && // user+pass+host+port
00661          encodedQuery() == _u.encodedQuery() &&
00662          (fragment() == _u.fragment() || options & CompareWithoutFragment )    )
00663       return true;
00664 
00665     return false;
00666   }
00667 
00668   return ( *this == _u );
00669 }
00670 
00671 QString KUrl::protocol() const
00672 {
00673     return scheme().toLower();
00674 }
00675 
00676 void KUrl::setProtocol( const QString& proto )
00677 {
00678     setScheme( proto );
00679 }
00680 
00681 QString KUrl::user() const
00682 {
00683     return userName();
00684 }
00685 
00686 void KUrl::setUser( const QString& user )
00687 {
00688     setUserName( user );
00689 }
00690 
00691 bool KUrl::hasUser() const
00692 {
00693     return !userName().isEmpty();
00694 }
00695 
00696 QString KUrl::pass() const
00697 {
00698     return password();
00699 }
00700 
00701 void KUrl::setPass( const QString& pass )
00702 {
00703     setPassword( pass );
00704 }
00705 
00706 bool KUrl::hasPass() const
00707 {
00708     return !password().isEmpty();
00709 }
00710 
00711 bool KUrl::hasHost() const
00712 {
00713     return !host().isEmpty();
00714 }
00715 
00716 bool KUrl::hasPath() const
00717 {
00718     return !path().isEmpty();
00719 }
00720 
00721 KUrl KUrl::fromPath( const QString& text )
00722 {
00723     KUrl u;
00724     u.setPath( text );
00725     return u;
00726 }
00727 
00728 void KUrl::setFileName( const QString& _txt )
00729 {
00730   setFragment( QString() );
00731   int i = 0;
00732   while( i < _txt.length() && _txt[i] == QLatin1Char('/') )
00733       ++i;
00734   QString tmp = i ? _txt.mid( i ) : _txt;
00735 
00736   QString path = this->path();
00737   if ( path.isEmpty() )
00738 #ifdef Q_OS_WIN
00739     path = isLocalFile() ? QDir::rootPath() : QLatin1String("/");
00740 #else
00741     path = QDir::rootPath();
00742 #endif
00743   else
00744   {
00745     int lastSlash = path.lastIndexOf( QLatin1Char('/') );
00746     if ( lastSlash == -1)
00747       path.clear(); // there's only the file name, remove it
00748     else if ( !path.endsWith( QLatin1Char('/') ) )
00749       path.truncate( lastSlash+1 ); // keep the "/"
00750   }
00751 
00752   path += tmp;
00753   setPath( path );
00754 
00755   cleanPath();
00756 }
00757 
00758 void KUrl::cleanPath( const CleanPathOption& options )
00759 {
00760   //if (m_iUriMode != URL) return;
00761   const QString newPath = cleanpath(path(), !(options & KeepDirSeparators), false);
00762   if ( path() != newPath )
00763       setPath( newPath );
00764   // WABA: Is this safe when "/../" is encoded with %?
00765   //m_strPath_encoded = cleanpath(m_strPath_encoded, cleanDirSeparator, true);
00766 }
00767 
00768 static QString trailingSlash( KUrl::AdjustPathOption trailing, const QString &path )
00769 {
00770   if ( trailing == KUrl::LeaveTrailingSlash ) {
00771     return path;
00772   }
00773 
00774   QString result = path;
00775 
00776   if ( trailing == KUrl::AddTrailingSlash )
00777   {
00778     int len = result.length();
00779     if ( (len == 0) || (result[ len - 1 ] != QLatin1Char('/')) )
00780       result += QLatin1Char('/');
00781     return result;
00782   }
00783   else if ( trailing == KUrl::RemoveTrailingSlash )
00784   {
00785     if ( result == QLatin1String("/") )
00786       return result;
00787     int len = result.length();
00788     while (len > 1 && result[ len - 1 ] == QLatin1Char('/'))
00789     {
00790       len--;
00791     }
00792     result.truncate( len );
00793     return result;
00794   }
00795   else {
00796     assert( 0 );
00797     return result;
00798   }
00799 }
00800 
00801 void KUrl::adjustPath( AdjustPathOption trailing )
00802 {
00803 #if 0
00804   if (!m_strPath_encoded.isEmpty())
00805   {
00806      m_strPath_encoded = trailingSlash( _trailing, m_strPath_encoded );
00807   }
00808 #endif
00809   const QString newPath = trailingSlash( trailing, path() );
00810   if ( path() != newPath )
00811       setPath( newPath );
00812 }
00813 
00814 
00815 QString KUrl::encodedPathAndQuery( AdjustPathOption trailing , const EncodedPathAndQueryOptions &options) const
00816 {
00817     QString encodedPath;
00818 #ifdef Q_OS_WIN
00819     // see KUrl::path()
00820     if (isLocalFile()) {
00821         // ### this is probably broken
00822         encodedPath = trailingSlash(trailing, QUrl::toLocalFile());
00823         encodedPath = QString::fromLatin1(QUrl::toPercentEncoding(encodedPath, "!$&'()*+,;=:@/"));
00824     } else {
00825         encodedPath = trailingSlash(trailing, QString::fromLatin1(QUrl::encodedPath()));
00826     }
00827 #else
00828     encodedPath = trailingSlash(trailing, QString::fromLatin1(QUrl::encodedPath()));
00829 #endif
00830 
00831     if ((options & AvoidEmptyPath) && encodedPath.isEmpty()) {
00832         encodedPath.append(QLatin1Char('/'));
00833     }
00834 
00835     if (hasQuery()) {
00836         return encodedPath + QLatin1Char('?') + QString::fromLatin1(encodedQuery());
00837     } else {
00838         return encodedPath;
00839     }
00840 }
00841 
00842 #if 0
00843 void KUrl::setEncodedPath( const QString& _txt, int encoding_hint )
00844 {
00845   m_strPath_encoded = _txt;
00846 
00847   decode( m_strPath_encoded, m_strPath, m_strPath_encoded, encoding_hint );
00848   // Throw away encoding for local files, makes file-operations faster.
00849   if (m_strProtocol == "file")
00850      m_strPath_encoded.clear();
00851 
00852   if ( m_iUriMode == Auto )
00853     m_iUriMode = URL;
00854 }
00855 #endif
00856 
00857 void KUrl::setEncodedPathAndQuery( const QString& _txt )
00858 {
00859   const int pos = _txt.indexOf(QLatin1Char('?'));
00860   if ( pos == -1 )
00861   {
00862     setPath( QUrl::fromPercentEncoding( _txt.toLatin1() ) );
00863     setEncodedQuery( QByteArray() );
00864   }
00865   else
00866   {
00867     setPath( QUrl::fromPercentEncoding(_txt.toLatin1().left(pos)) );
00868     _setQuery( _txt.right( _txt.length() - pos - 1 ) );
00869   }
00870 }
00871 
00872 QString KUrl::path( AdjustPathOption trailing ) const
00873 {
00874 #ifdef Q_WS_WIN
00875 #ifdef DEBUG_KURL
00876   kWarning() << (isLocalFile() ? "converted to local file - the related call should be converted to toLocalFile()" : "") << QUrl::path();
00877 #endif
00878   return trailingSlash( trailing, isLocalFile() ? QUrl::toLocalFile() : QUrl::path() );
00879 #else
00880   return trailingSlash( trailing, QUrl::path() );
00881 #endif
00882 }
00883 
00884 QString KUrl::toLocalFile( AdjustPathOption trailing ) const
00885 {
00886     if (hasHost() && isLocalFile()) {
00887         KUrl urlWithoutHost(*this);
00888         urlWithoutHost.setHost(QString());
00889         return trailingSlash(trailing, urlWithoutHost.toLocalFile());
00890     }
00891 #ifdef __GNUC__
00892 #warning FIXME: Remove #ifdef below once upstream bug, QTBUG-20322, is fixed. Also see BR# 194746.
00893 #endif
00894 #ifndef Q_WS_WIN
00895     if (isLocalFile()) {
00896         return trailingSlash(trailing, QUrl::path());
00897     }
00898 #endif
00899     return trailingSlash(trailing, QUrl::toLocalFile());
00900 }
00901 
00902 inline static bool hasSubUrl( const QUrl& url );
00903 
00904 static inline bool isLocalFile( const QUrl& url )
00905 {
00906   if ( ( url.scheme() != QLatin1String("file") ) || hasSubUrl( url ) )
00907      return false;
00908 
00909   if (url.host().isEmpty() || (url.host() == QLatin1String("localhost")))
00910      return true;
00911 
00912   char hostname[ 256 ];
00913   hostname[ 0 ] = '\0';
00914   if (!gethostname( hostname, 255 ))
00915      hostname[sizeof(hostname)-1] = '\0';
00916 
00917   for(char *p = hostname; *p; p++)
00918      *p = tolower(*p);
00919 
00920   return (url.host() == QString::fromLatin1( hostname ));
00921 }
00922 
00923 bool KUrl::isLocalFile() const
00924 {
00925   return ::isLocalFile( *this );
00926 }
00927 
00928 void KUrl::setFileEncoding(const QString &encoding)
00929 {
00930   if (!isLocalFile())
00931      return;
00932 
00933   QString q = query();
00934 
00935   if (!q.isEmpty() && q[0] == QLatin1Char('?'))
00936      q = q.mid(1);
00937 
00938   QStringList args = q.split(QLatin1Char('&'), QString::SkipEmptyParts);
00939   for(QStringList::Iterator it = args.begin();
00940       it != args.end();)
00941   {
00942       QString s = QUrl::fromPercentEncoding( (*it).toLatin1() );
00943       if (s.startsWith(QLatin1String("charset=")))
00944          it = args.erase(it);
00945       else
00946          ++it;
00947   }
00948   if (!encoding.isEmpty())
00949       args.append(QLatin1String("charset=") + QString::fromLatin1(QUrl::toPercentEncoding(encoding)));
00950 
00951   if (args.isEmpty())
00952      _setQuery(QString());
00953   else
00954      _setQuery(args.join(QString(QLatin1Char('&'))));
00955 }
00956 
00957 QString KUrl::fileEncoding() const
00958 {
00959   if (!isLocalFile())
00960      return QString();
00961 
00962   QString q = query();
00963 
00964   if (q.isEmpty())
00965      return QString();
00966 
00967   if (q[0] == QLatin1Char('?'))
00968      q = q.mid(1);
00969 
00970   const QStringList args = q.split(QLatin1Char('&'), QString::SkipEmptyParts);
00971   for(QStringList::ConstIterator it = args.begin();
00972       it != args.end();
00973       ++it)
00974   {
00975       QString s = QUrl::fromPercentEncoding((*it).toLatin1());
00976       if (s.startsWith(QLatin1String("charset=")))
00977          return s.mid(8);
00978   }
00979   return QString();
00980 }
00981 
00982 inline static bool hasSubUrl( const QUrl& url )
00983 {
00984   // The isValid call triggers QUrlPrivate::validate which needs the full encoded url,
00985   // all this takes too much time for isLocalFile()
00986   const QString scheme = url.scheme();
00987   if ( scheme.isEmpty() /*|| !isValid()*/ )
00988     return false;
00989   const QString ref( url.fragment() );
00990   if (ref.isEmpty())
00991      return false;
00992   switch ( ref.at(0).unicode() ) {
00993   case 'g':
00994     if ( ref.startsWith(QLatin1String("gzip:")) )
00995       return true;
00996     break;
00997   case 'b':
00998     if ( ref.startsWith(QLatin1String("bzip:")) || ref.startsWith(QLatin1String("bzip2:")) )
00999       return true;
01000     break;
01001   case 'l':
01002     if ( ref.startsWith(QLatin1String("lzma:")) )
01003       return true;
01004     break;
01005   case 'x':
01006     if ( ref.startsWith(QLatin1String("xz:")) )
01007       return true;
01008     break;
01009   case 't':
01010     if ( ref.startsWith(QLatin1String("tar:")) )
01011       return true;
01012     break;
01013   case 'a':
01014     if ( ref.startsWith(QLatin1String("ar:")) )
01015       return true;
01016     break;
01017   case 'z':
01018     if ( ref.startsWith(QLatin1String("zip:")) )
01019       return true;
01020     break;
01021   default:
01022     break;
01023   }
01024   if ( scheme == QLatin1String("error") ) // anything that starts with error: has suburls
01025      return true;
01026   return false;
01027 }
01028 
01029 bool KUrl::hasSubUrl() const
01030 {
01031   return ::hasSubUrl( *this );
01032 }
01033 
01034 QString KUrl::url( AdjustPathOption trailing ) const
01035 {
01036   if (QString::compare(scheme(), QLatin1String("mailto"), Qt::CaseInsensitive) == 0) {
01037       // mailto urls should be prettified, see the url183433 testcase.
01038       return prettyUrl(trailing);
01039   }
01040   if ( trailing == AddTrailingSlash && !path().endsWith( QLatin1Char('/') ) ) {
01041       // -1 and 0 are provided by QUrl, but not +1, so that one is a bit tricky.
01042       // To avoid reimplementing toEncoded() all over again, I just use another QUrl
01043       // Let's hope this is fast, or not called often...
01044       QUrl newUrl( *this );
01045       newUrl.setPath( path() + QLatin1Char('/') );
01046       return QString::fromLatin1(newUrl.toEncoded());
01047   }
01048   else if ( trailing == RemoveTrailingSlash) {
01049       const QString cleanedPath = trailingSlash(trailing, path());
01050       if (cleanedPath == QLatin1String("/")) {
01051           if (path() != QLatin1String("/")) {
01052               QUrl fixedUrl = *this;
01053               fixedUrl.setPath(cleanedPath);
01054               return QLatin1String(fixedUrl.toEncoded(None));
01055           }
01056           return QLatin1String(toEncoded(None));
01057       }
01058   }
01059   return QString::fromLatin1(toEncoded(trailing == RemoveTrailingSlash ? StripTrailingSlash : None));
01060 }
01061 
01062 static QString toPrettyPercentEncoding(const QString &input, bool forFragment)
01063 {
01064   QString result;
01065   result.reserve(input.length());
01066   for (int i = 0; i < input.length(); ++i) {
01067     const QChar c = input.at(i);
01068     register ushort u = c.unicode();
01069     if (u < 0x20
01070         || (!forFragment && u == '?') // don't escape '?' in fragments, not needed and wrong (#173101)
01071         || u == '#' || u == '%'
01072         || (u == ' ' && (i+1 == input.length() || input.at(i+1).unicode() == ' '))) {
01073       static const char hexdigits[] = "0123456789ABCDEF";
01074       result += QLatin1Char('%');
01075       result += QLatin1Char(hexdigits[(u & 0xf0) >> 4]);
01076       result += QLatin1Char(hexdigits[u & 0xf]);
01077     } else {
01078       result += c;
01079     }
01080   }
01081 
01082   return result;
01083 }
01084 
01085 QString KUrl::prettyUrl( AdjustPathOption trailing ) const
01086 {
01087   // reconstruct the URL in a "pretty" form
01088   // a "pretty" URL is NOT suitable for data transfer. It's only for showing data to the user.
01089   // however, it must be parseable back to its original state, since
01090   // notably Konqueror displays it in the Location address.
01091 
01092   // A pretty URL is the same as a normal URL, except that:
01093   // - the password is removed
01094   // - the hostname is shown in Unicode (as opposed to ACE/Punycode)
01095   // - the pathname and fragment parts are shown in Unicode (as opposed to %-encoding)
01096   QString result = scheme();
01097   if (!result.isEmpty())
01098   {
01099     if(!authority().isEmpty() || result == QLatin1String("file"))
01100         result += QLatin1String("://");
01101     else
01102         result += QLatin1Char(':');
01103   }
01104 
01105   QString tmp = userName();
01106   if (!tmp.isEmpty()) {
01107     result += QString::fromLatin1(QUrl::toPercentEncoding(tmp));
01108     result += QLatin1Char('@');
01109   }
01110 
01111   // Check if host is an ipv6 address
01112   tmp = host();
01113   if (tmp.contains(QLatin1Char(':')))
01114     result += QLatin1Char('[') + tmp + QLatin1Char(']');
01115   else
01116     result += tmp;
01117 
01118   if (port() != -1) {
01119     result += QLatin1Char(':');
01120     result += QString::number(port());
01121   }
01122 
01123   tmp = path();
01124 #ifdef Q_WS_WIN
01125   if (isLocalFile())
01126     tmp.prepend(QLatin1Char('/')); // KUrl::path() returns toLocalFile() on windows so we need to add the / back to create a proper url
01127 #endif
01128   result += toPrettyPercentEncoding(tmp, false);
01129 
01130   // adjust the trailing slash, if necessary
01131   if (trailing == AddTrailingSlash && !tmp.endsWith(QLatin1Char('/')))
01132     result += QLatin1Char('/');
01133   else if (trailing == RemoveTrailingSlash && tmp.length() > 1 && tmp.endsWith(QLatin1Char('/')))
01134     result.chop(1);
01135 
01136   if (hasQuery()) {
01137     result += QLatin1Char('?');
01138     result += QString::fromLatin1(encodedQuery());
01139   }
01140 
01141   if (hasFragment()) {
01142     result += QLatin1Char('#');
01143     result += toPrettyPercentEncoding(fragment(), true);
01144   }
01145 
01146   return result;
01147 }
01148 
01149 #if 0
01150 QString KUrl::prettyUrl( int _trailing, AdjustementFlags _flags) const
01151 {
01152   QString u = prettyUrl(_trailing);
01153   if (_flags & StripFileProtocol && u.startsWith("file://")) {
01154     u.remove(0, 7);
01155 #ifdef Q_WS_WIN
01156     return QDir::convertSeparators(u);
01157 #endif
01158   }
01159   return u;
01160 }
01161 #endif
01162 
01163 QString KUrl::pathOrUrl() const
01164 {
01165     return pathOrUrl(LeaveTrailingSlash);
01166 }
01167 
01168 QString KUrl::pathOrUrl(AdjustPathOption trailing) const
01169 {
01170   if ( isLocalFile() && fragment().isNull() && encodedQuery().isNull() ) {
01171     return toLocalFile(trailing);
01172   } else {
01173     return prettyUrl(trailing);
01174   }
01175 }
01176 
01177 // Used for text/uri-list in the mime data
01178 QString KUrl::toMimeDataString() const // don't fold this into populateMimeData, it's also needed by other code like konqdrag
01179 {
01180   if ( isLocalFile() )
01181   {
01182 #if 1
01183     return url();
01184 #else
01185     // According to the XDND spec, file:/ URLs for DND must have
01186     // the hostname part. But in really it just breaks many apps,
01187     // so it's disabled for now.
01188     const QString s = url( 0, KGlobal::locale()->fileEncodingMib() );
01189     if( !s.startsWith( QLatin1String ( "file://" ) ))
01190     {
01191         char hostname[257];
01192         if ( gethostname( hostname, 255 ) == 0 )
01193         {
01194             hostname[256] = '\0';
01195             return QString( "file://" ) + hostname + s.mid( 5 );
01196         }
01197     }
01198 #endif
01199   }
01200 
01201   if (hasPass()) {
01202     KUrl safeUrl(*this);
01203     safeUrl.setPassword(QString());
01204     return safeUrl.url();
01205   }
01206   return url();
01207 }
01208 
01209 KUrl KUrl::fromMimeDataByteArray( const QByteArray& str )
01210 {
01211   if ( str.startsWith( "file:" ) ) // krazy:exclude=strings
01212     return KUrl( str /*, QTextCodec::codecForLocale()->mibEnum()*/ );
01213 
01214   return KUrl( str /*, 106*/ ); // 106 is mib enum for utf8 codec;
01215 }
01216 
01217 KUrl::List KUrl::split( const KUrl& _url )
01218 {
01219   QString ref;
01220   bool hasRef;
01221   KUrl::List lst;
01222   KUrl url = _url;
01223 
01224   while(true)
01225   {
01226      KUrl u = url;
01227      u.setFragment( QString() );
01228      lst.append(u);
01229      if (url.hasSubUrl())
01230      {
01231         url = KUrl(url.fragment());
01232      }
01233      else
01234      {
01235         ref = url.fragment();
01236         hasRef = url.hasFragment();
01237         break;
01238      }
01239   }
01240 
01241   if ( hasRef )
01242   {
01243     // Set HTML ref in all URLs.
01244     KUrl::List::Iterator it;
01245     for( it = lst.begin() ; it != lst.end(); ++it )
01246     {
01247       (*it).setFragment( ref );
01248     }
01249   }
01250 
01251   return lst;
01252 }
01253 
01254 KUrl::List KUrl::split( const QString& _url )
01255 {
01256   return split(KUrl(_url));
01257 }
01258 
01259 KUrl KUrl::join( const KUrl::List & lst )
01260 {
01261   if (lst.isEmpty()) return KUrl();
01262   KUrl tmp;
01263 
01264   bool first = true;
01265   QListIterator<KUrl> it(lst);
01266   it.toBack();
01267   while (it.hasPrevious())
01268   {
01269      KUrl u(it.previous());
01270      if (!first) {
01271          u.setEncodedFragment(tmp.url().toLatin1() /* TODO double check encoding */);
01272      }
01273      tmp = u;
01274 
01275      first = false;
01276   }
01277 
01278   return tmp;
01279 }
01280 
01281 QString KUrl::fileName( const DirectoryOptions& options ) const
01282 {
01283   Q_ASSERT( options != 0 ); //Disallow options == false
01284   QString fname;
01285   if (hasSubUrl()) { // If we have a suburl, then return the filename from there
01286     const KUrl::List list = KUrl::split(*this);
01287     return list.last().fileName(options);
01288   }
01289   const QString path = this->path();
01290 
01291   int len = path.length();
01292   if ( len == 0 )
01293     return fname;
01294 
01295   if (!(options & ObeyTrailingSlash) )
01296   {
01297     while ( len >= 1 && path[ len - 1 ] == QLatin1Char('/') )
01298       len--;
01299   }
01300   else if ( path[ len - 1 ] == QLatin1Char('/') )
01301     return fname;
01302 
01303   // Does the path only consist of '/' characters ?
01304   if ( len == 1 && path[ 0 ] == QLatin1Char('/') )
01305     return fname;
01306 
01307   // Skip last n slashes
01308   int n = 1;
01309 #if 0
01310   if (!m_strPath_encoded.isEmpty())
01311   {
01312      // This is hairy, we need the last unencoded slash.
01313      // Count in the encoded string how many encoded slashes follow the last
01314      // unencoded one.
01315      int i = m_strPath_encoded.lastIndexOf( QLatin1Char('/'), len - 1 );
01316      QString fileName_encoded = m_strPath_encoded.mid(i+1);
01317      n += fileName_encoded.count("%2f", Qt::CaseInsensitive);
01318   }
01319 #endif
01320   int i = len;
01321   do {
01322     i = path.lastIndexOf( QLatin1Char('/'), i - 1 );
01323   }
01324   while (--n && (i > 0));
01325 
01326   // If ( i == -1 ) => the first character is not a '/'
01327   // So it's some URL like file:blah.tgz, return the whole path
01328   if ( i == -1 ) {
01329     if ( len == (int)path.length() )
01330       fname = path;
01331     else
01332       // Might get here if _strip_trailing_slash is true
01333       fname = path.left( len );
01334   }
01335   else
01336   {
01337      fname = path.mid( i + 1, len - i - 1 ); // TO CHECK
01338   }
01339   return fname;
01340 }
01341 
01342 void KUrl::addPath( const QString& _txt )
01343 {
01344   if (hasSubUrl())
01345   {
01346      KUrl::List lst = split( *this );
01347      KUrl &u = lst.last();
01348      u.addPath(_txt);
01349      *this = join( lst );
01350      return;
01351   }
01352 
01353   //m_strPath_encoded.clear();
01354 
01355   if ( _txt.isEmpty() )
01356     return;
01357 
01358   QString strPath = path();
01359   int i = 0;
01360   int len = strPath.length();
01361   // Add the trailing '/' if it is missing
01362   if ( _txt[0] != QLatin1Char('/') && ( len == 0 || strPath[ len - 1 ] != QLatin1Char('/') ) )
01363     strPath += QLatin1Char('/');
01364 
01365   // No double '/' characters
01366   i = 0;
01367   const int _txtlen = _txt.length();
01368   if ( strPath.endsWith( QLatin1Char('/') ) )
01369   {
01370     while ( ( i < _txtlen ) && ( _txt[i] == QLatin1Char('/') ) )
01371       ++i;
01372   }
01373 
01374   setPath( strPath + _txt.mid( i ) );
01375   //kDebug(kurlDebugArea())<<"addPath: resultpath="<<path();
01376 }
01377 
01378 QString KUrl::directory( const DirectoryOptions& options ) const
01379 {
01380   Q_ASSERT( options != 0 ); //Disallow options == false
01381   QString result = path(); //m_strPath_encoded.isEmpty() ? m_strPath : m_strPath_encoded;
01382   if ( !(options & ObeyTrailingSlash) )
01383     result = trailingSlash( RemoveTrailingSlash, result );
01384 
01385   if ( result.isEmpty() || result == QLatin1String ( "/" ) )
01386     return result;
01387 
01388   int i = result.lastIndexOf( QLatin1Char('/') );
01389   // If ( i == -1 ) => the first character is not a '/'
01390   // So it's some URL like file:blah.tgz, with no path
01391   if ( i == -1 )
01392     return QString();
01393 
01394   if ( i == 0 )
01395   {
01396     return QString(QLatin1Char('/'));
01397   }
01398 
01399 #ifdef Q_WS_WIN
01400   if ( i == 2 && result[1] == QLatin1Char(':') )
01401   {
01402     return result.left(3);
01403   }
01404 #endif
01405 
01406   if ( options & AppendTrailingSlash )
01407     result = result.left( i + 1 );
01408   else
01409     result = result.left( i );
01410 
01411   //if (!m_strPath_encoded.isEmpty())
01412   //  result = decode(result);
01413 
01414   return result;
01415 }
01416 
01417 
01418 bool KUrl::cd( const QString& _dir )
01419 {
01420   if ( _dir.isEmpty() || !isValid() )
01421     return false;
01422 
01423   if (hasSubUrl())
01424   {
01425      KUrl::List lst = split( *this );
01426      KUrl &u = lst.last();
01427      u.cd(_dir);
01428      *this = join( lst );
01429      return true;
01430   }
01431 
01432   // absolute path ?
01433 #ifdef Q_OS_WIN
01434   if ( !QFileInfo(_dir).isRelative() )
01435 #else
01436   if ( _dir[0] == QLatin1Char('/') )
01437 #endif
01438   {
01439     //m_strPath_encoded.clear();
01440     setPath( _dir );
01441     setHTMLRef( QString() );
01442     setEncodedQuery( QByteArray() );
01443     return true;
01444   }
01445 
01446   // Users home directory on the local disk ?
01447   if (_dir[0] == QLatin1Char('~') && scheme() == QLatin1String ("file"))
01448   {
01449     //m_strPath_encoded.clear();
01450     QString strPath = QDir::homePath();
01451     strPath += QLatin1Char('/');
01452     strPath += _dir.right( strPath.length() - 1 );
01453     setPath( strPath );
01454     setHTMLRef( QString() );
01455     setEncodedQuery( QByteArray() );
01456     return true;
01457   }
01458 
01459   // relative path
01460   // we always work on the past of the first url.
01461   // Sub URLs are not touched.
01462 
01463   // append '/' if necessary
01464   QString p = path(AddTrailingSlash);
01465   p += _dir;
01466   p = cleanpath( p, true, false );
01467   setPath( p );
01468 
01469   setHTMLRef( QString() );
01470   setEncodedQuery( QByteArray() );
01471 
01472   return true;
01473 }
01474 
01475 KUrl KUrl::upUrl( ) const
01476 {
01477   if (!isValid() || isRelative())
01478     return KUrl();
01479 
01480   if (!encodedQuery().isEmpty())
01481   {
01482      KUrl u(*this);
01483      u.setEncodedQuery(QByteArray());
01484      return u;
01485   }
01486 
01487   if (!hasSubUrl())
01488   {
01489      KUrl u(*this);
01490      u.cd(QLatin1String("../"));
01491      return u;
01492   }
01493 
01494   // We have a subURL.
01495   KUrl::List lst = split( *this );
01496   if (lst.isEmpty())
01497       return KUrl(); // Huh?
01498   while (true)
01499   {
01500      KUrl &u = lst.last();
01501      const QString old = u.path();
01502      u.cd(QLatin1String("../"));
01503      if (u.path() != old)
01504          break; // Finished.
01505      if (lst.count() == 1)
01506          break; // Finished.
01507      lst.removeLast();
01508   }
01509   return join( lst );
01510 }
01511 
01512 QString KUrl::htmlRef() const
01513 {
01514   if ( !hasSubUrl() )
01515   {
01516       return fragment();
01517   }
01518 
01519   const List lst = split( *this );
01520   return (*lst.begin()).fragment();
01521 }
01522 
01523 QString KUrl::encodedHtmlRef() const
01524 {
01525   if ( !hasSubUrl() )
01526   {
01527     return ref();
01528   }
01529 
01530   const List lst = split( *this );
01531   return (*lst.begin()).ref();
01532 }
01533 
01534 void KUrl::setHTMLRef( const QString& _ref )
01535 {
01536   if ( !hasSubUrl() )
01537   {
01538     setFragment( _ref );
01539     return;
01540   }
01541 
01542   List lst = split( *this );
01543 
01544   (*lst.begin()).setFragment( _ref );
01545 
01546   *this = join( lst );
01547 }
01548 
01549 bool KUrl::hasHTMLRef() const
01550 {
01551   if ( !hasSubUrl() )
01552   {
01553     return hasRef();
01554   }
01555 
01556   const List lst = split( *this );
01557   return (*lst.begin()).hasRef();
01558 }
01559 
01560 void KUrl::setDirectory( const QString &dir)
01561 {
01562   if ( dir.endsWith(QLatin1Char('/')))
01563      setPath(dir);
01564   else
01565      setPath(dir + QLatin1Char('/'));
01566 }
01567 
01568 void KUrl::setQuery( const QString &_txt )
01569 {
01570   if (!_txt.isEmpty() && _txt[0] == QLatin1Char('?'))
01571     _setQuery( _txt.length() > 1 ? _txt.mid(1) : QString::fromLatin1("") /*empty, not null*/ );
01572   else
01573     _setQuery( _txt );
01574 }
01575 
01576 void KUrl::_setQuery( const QString& query )
01577 {
01578     if ( query.isNull() ) {
01579         setEncodedQuery( QByteArray() );
01580     } else if ( query.isEmpty() ) {
01581         setEncodedQuery("");
01582     } else {
01583         setEncodedQuery( query.toLatin1() ); // already percent-escaped, so toLatin1 is ok
01584     }
01585 }
01586 
01587 QString KUrl::query() const
01588 {
01589   if (!hasQuery()) {
01590     return QString();
01591   }
01592   return QString(QLatin1Char('?')) + QString::fromLatin1(encodedQuery());
01593 }
01594 
01595 void KUrl::_setEncodedUrl(const QByteArray& url)
01596 {
01597   setEncodedUrl(url, QUrl::TolerantMode);
01598   if (!isValid()) // see unit tests referring to N183630/task 183874
01599     setUrl(QString::fromUtf8(url), QUrl::TolerantMode);
01600 }
01601 
01602 #ifndef KDE_NO_DEPRECATED
01603 bool urlcmp( const QString& _url1, const QString& _url2 )
01604 {
01605   return QUrl( _url1, QUrl::TolerantMode ) == QUrl( _url2, QUrl::TolerantMode );
01606 #if 0
01607   // Both empty ?
01608   if ( _url1.isEmpty() && _url2.isEmpty() )
01609     return true;
01610   // Only one empty ?
01611   if ( _url1.isEmpty() || _url2.isEmpty() )
01612     return false;
01613 
01614   KUrl::List list1 = KUrl::split( _url1 );
01615   KUrl::List list2 = KUrl::split( _url2 );
01616 
01617   // Malformed ?
01618   if ( list1.isEmpty() || list2.isEmpty() )
01619     return false;
01620 
01621   return ( list1 == list2 );
01622 #endif
01623 }
01624 #endif
01625 
01626 #ifndef KDE_NO_DEPRECATED
01627 bool urlcmp( const QString& _url1, const QString& _url2, const KUrl::EqualsOptions& _options )
01628 {
01629     // Both empty ?
01630     if (_url1.isEmpty() && _url2.isEmpty())
01631         return true;
01632     // Only one empty ?
01633     if (_url1.isEmpty() || _url2.isEmpty())
01634         return false;
01635 
01636     KUrl u1(_url1);
01637     KUrl u2(_url2);
01638     return u1.equals(u2, _options);
01639 
01640 #if 0 // kde3 code that supported nested urls
01641 
01642   KUrl::List list1 = KUrl::split( _url1 );
01643   KUrl::List list2 = KUrl::split( _url2 );
01644 
01645   // Malformed ?
01646   if ( list1.isEmpty() || list2.isEmpty() )
01647     return false;
01648 
01649   int size = list1.count();
01650   if ( list2.count() != size )
01651     return false;
01652 
01653   if ( _ignore_ref )
01654   {
01655     (*list1.begin()).setRef(QString());
01656     (*list2.begin()).setRef(QString());
01657   }
01658 
01659   KUrl::List::Iterator it1 = list1.begin();
01660   KUrl::List::Iterator it2 = list2.begin();
01661   for( ; it1 != list1.end() ; ++it1, ++it2 )
01662     if ( !(*it1).equals( *it2, _ignore_trailing ) )
01663       return false;
01664   return true;
01665 #endif
01666 }
01667 #endif
01668 
01669 // static
01670 #ifndef KDE_NO_DEPRECATED
01671 KUrl KUrl::fromPathOrUrl( const QString& text )
01672 {
01673     KUrl url;
01674     if ( !text.isEmpty() )
01675     {
01676         if (!QDir::isRelativePath(text) || text[0] == QLatin1Char('~'))
01677             url.setPath( text );
01678         else
01679             url = KUrl( text );
01680     }
01681 
01682     return url;
01683 }
01684 #endif
01685 
01686 static QString _relativePath(const QString &base_dir, const QString &path, bool &isParent)
01687 {
01688    QString _base_dir(QDir::cleanPath(base_dir));
01689    QString _path(QDir::cleanPath(path.isEmpty() || (path[0] != QLatin1Char('/')) ? _base_dir+QLatin1Char('/')+path : path));
01690 
01691    if (_base_dir.isEmpty())
01692       return _path;
01693 
01694    if (_base_dir[_base_dir.length()-1] != QLatin1Char('/'))
01695       _base_dir.append(QLatin1Char('/') );
01696 
01697    const QStringList list1 = _base_dir.split(QLatin1Char('/'), QString::SkipEmptyParts);
01698    const QStringList list2 = _path.split(QLatin1Char('/'), QString::SkipEmptyParts);
01699 
01700    // Find where they meet
01701    int level = 0;
01702    int maxLevel = qMin(list1.count(), list2.count());
01703    while((level < maxLevel) && (list1[level] == list2[level])) level++;
01704 
01705    QString result;
01706    // Need to go down out of the first path to the common branch.
01707    for(int i = level; i < list1.count(); i++)
01708       result.append(QLatin1String("../"));
01709 
01710    // Now up up from the common branch to the second path.
01711    for(int i = level; i < list2.count(); i++)
01712       result.append(list2[i]).append(QLatin1Char('/'));
01713 
01714    if ((level < list2.count()) && (path[path.length()-1] != QLatin1Char('/')))
01715       result.truncate(result.length()-1);
01716 
01717    isParent = (level == list1.count());
01718 
01719    return result;
01720 }
01721 
01722 QString KUrl::relativePath(const QString &base_dir, const QString &path, bool *isParent)
01723 {
01724    bool parent = false;
01725    QString result = _relativePath(base_dir, path, parent);
01726    if (parent)
01727       result.prepend(QLatin1String("./"));
01728 
01729    if (isParent)
01730       *isParent = parent;
01731 
01732    return result;
01733 }
01734 
01735 
01736 QString KUrl::relativeUrl(const KUrl &base_url, const KUrl &url)
01737 {
01738    if ((url.protocol() != base_url.protocol()) ||
01739        (url.host() != base_url.host()) ||
01740        (url.port() && url.port() != base_url.port()) ||
01741        (url.hasUser() && url.user() != base_url.user()) ||
01742        (url.hasPass() && url.pass() != base_url.pass()))
01743    {
01744       return url.url();
01745    }
01746 
01747    QString relURL;
01748 
01749    if ((base_url.path() != url.path()) || (base_url.query() != url.query()))
01750    {
01751       bool dummy;
01752       QString basePath = base_url.directory(KUrl::ObeyTrailingSlash);
01753       relURL = _relativePath(basePath, url.path(), dummy); // was QUrl::toPercentEncoding() but why?
01754       relURL += url.query();
01755    }
01756 
01757    if ( url.hasRef() )
01758    {
01759       relURL += QLatin1Char('#');
01760       relURL += url.ref();
01761    }
01762 
01763    if ( relURL.isEmpty() )
01764       return QLatin1String("./");
01765 
01766    return relURL;
01767 }
01768 
01769 void KUrl::setPath( const QString& _path )
01770 {
01771 #if defined(Q_WS_WIN) && defined(DEBUG_KURL)
01772     kDebug(kurlDebugArea()) << "KUrl::setPath " << " " << _path.toAscii().data();
01773 #endif
01774     if ( scheme().isEmpty() )
01775         setScheme( QLatin1String( "file" ) );
01776     QString path = KShell::tildeExpand( _path );
01777     if (path.isEmpty())
01778         path = _path;
01779 #ifdef Q_WS_WIN
01780     const int len = path.length();
01781     if( len == 2 && IS_LETTER(path[0]) && path[1] == QLatin1Char(':') )
01782         path += QLatin1Char('/');
01783     //This is necessary because QUrl has the "path" part including the first slash
01784     //Without this QUrl doesn't understand that this is a path, and some operations fail
01785     //e.g. C:/blah needs to become /C:/blah
01786     else
01787     if( len > 0 && path[0] != QLatin1Char('/') && scheme() == QLatin1String( "file" ) )
01788         path = QLatin1Char('/') + path;
01789 #endif
01790     QUrl::setPath( path );
01791 }
01792 
01793 #if 0 // this would be if we didn't decode '+' into ' '
01794 QMap< QString, QString > KUrl::queryItems( int options ) const {
01795   QMap< QString, QString > result;
01796   const QList<QPair<QString, QString> > items = QUrl::queryItems();
01797   QPair<QString, QString> item;
01798   Q_FOREACH( item, items ) {
01799       result.insert( options & CaseInsensitiveKeys ? item.first.toLower() : item.first, item.second );
01800   }
01801   return result;
01802 }
01803 #endif
01804 
01805 QMap< QString, QString > KUrl::queryItems( const QueryItemsOptions &options ) const
01806 {
01807   const QString strQueryEncoded = QString::fromLatin1(encodedQuery());
01808   if ( strQueryEncoded.isEmpty() )
01809     return QMap<QString,QString>();
01810 
01811   QMap< QString, QString > result;
01812   const QStringList items = strQueryEncoded.split( QLatin1Char('&'), QString::SkipEmptyParts );
01813   for ( QStringList::const_iterator it = items.begin() ; it != items.end() ; ++it ) {
01814     const int equal_pos = (*it).indexOf(QLatin1Char('='));
01815     if ( equal_pos > 0 ) { // = is not the first char...
01816       QString name = (*it).left( equal_pos );
01817       if ( options & CaseInsensitiveKeys )
01818     name = name.toLower();
01819       QString value = (*it).mid( equal_pos + 1 );
01820       if ( value.isEmpty() )
01821         result.insert( name, QString::fromLatin1("") );
01822       else {
01823     // ### why is decoding name not necessary?
01824     value.replace( QLatin1Char('+'), QLatin1Char(' ') ); // + in queries means space
01825     result.insert( name, QUrl::fromPercentEncoding( value.toLatin1() ) );
01826       }
01827     } else if ( equal_pos < 0 ) { // no =
01828       QString name = (*it);
01829       if ( options & CaseInsensitiveKeys )
01830     name = name.toLower();
01831       result.insert( name, QString() );
01832     }
01833   }
01834 
01835   return result;
01836 }
01837 
01838 QString KUrl::queryItem( const QString& _item ) const
01839 {
01840   const QString strQueryEncoded = QString::fromLatin1(encodedQuery());
01841   const QString item = _item + QLatin1Char('=');
01842   if ( strQueryEncoded.length() <= 1 )
01843     return QString();
01844 
01845   const QStringList items = strQueryEncoded.split( QString(QLatin1Char('&')), QString::SkipEmptyParts );
01846   const int _len = item.length();
01847   for ( QStringList::ConstIterator it = items.begin(); it != items.end(); ++it )
01848   {
01849     if ( (*it).startsWith( item ) )
01850     {
01851       if ( (*it).length() > _len )
01852       {
01853         QString str = (*it).mid( _len );
01854         str.replace( QLatin1Char('+'), QLatin1Char(' ') ); // + in queries means space.
01855         return QUrl::fromPercentEncoding( str.toLatin1() );
01856       }
01857       else // empty value
01858         return QString::fromLatin1("");
01859     }
01860   }
01861 
01862   return QString();
01863 }
01864 
01865 void KUrl::addQueryItem( const QString& _item, const QString& _value )
01866 {
01867   QString item = _item + QLatin1Char('=');
01868   QString value = QString::fromLatin1(QUrl::toPercentEncoding(_value));
01869 
01870   QString strQueryEncoded = QString::fromLatin1(encodedQuery());
01871   if (!strQueryEncoded.isEmpty())
01872      strQueryEncoded += QLatin1Char('&');
01873   strQueryEncoded += item + value;
01874   setEncodedQuery( strQueryEncoded.toLatin1() );
01875 }
01876 
01877 void KUrl::populateMimeData( QMimeData* mimeData,
01878                              const MetaDataMap& metaData,
01879                              MimeDataFlags flags ) const
01880 {
01881   KUrl::List lst( *this );
01882   lst.populateMimeData( mimeData, metaData, flags );
01883 }
01884 
01885 bool KUrl::hasRef() const
01886 {
01887   return hasFragment();
01888 }
01889 
01890 void KUrl::setRef( const QString& fragment )
01891 {
01892   if ( fragment.isEmpty() ) // empty or null
01893     setFragment( fragment );
01894   else
01895     setFragment( QUrl::fromPercentEncoding( fragment.toLatin1() ) );
01896 }
01897 
01898 QString KUrl::ref() const
01899 {
01900   if ( fragment().isNull() )
01901     return QString();
01902   else
01903     return QString::fromLatin1( QUrl::toPercentEncoding( fragment() ) );
01904 }
01905 
01906 bool KUrl::isParentOf( const KUrl& u ) const
01907 {
01908   return QUrl::isParentOf( u ) || equals( u, CompareWithoutTrailingSlash );
01909 }
01910 
01911 uint qHash(const KUrl& kurl)
01912 {
01913   // qHash(kurl.url()) was the worse implementation possible, since QUrl::toEncoded()
01914   // had to concatenate the bits of the url into the full url every time.
01915 
01916   return qHash(kurl.protocol()) ^ qHash(kurl.path()) ^ qHash(kurl.fragment()) ^ qHash(kurl.query());
01917 }

KDECore

Skip menu "KDECore"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Modules
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal