1
// -*- c-basic-offset: 2 -*-
3
Copyright (C) 1999 Torben Weis <weis@kde.org>
4
Copyright (C) 2005-2006 David Faure <faure@kde.org>
6
This library is free software; you can redistribute it and/or
7
modify it under the terms of the GNU Library General Public
8
License as published by the Free Software Foundation; either
9
version 2 of the License, or (at your option) any later version.
11
This library is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
Library General Public License for more details.
16
You should have received a copy of the GNU Library General Public License
17
along with this library; see the file COPYING.LIB. If not, write to
18
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19
Boston, MA 02110-1301, USA.
22
/// KDE4 TODO: maybe we should use QUrl::resolved()
25
* The currently active RFC for URL/URIs is RFC3986
26
* Previous (and now deprecated) RFCs are RFC1738 and RFC2396
31
// KDE_QT_ONLY is first used for dcop/client (e.g. marshalling)
45
#include <qstringlist.h>
47
#include <qmimedata.h>
48
#include <qtextcodec.h>
50
static QString cleanpath( const QString &_path, bool cleanDirSeparator, bool decodeDots )
52
if (_path.isEmpty()) return QString();
54
if (QDir::isRelativePath(_path))
55
return _path; // Don't mangle mailto-style URLs
59
int len = path.length();
64
static const QString &encodedDot = KGlobal::staticQString("%2e");
66
QString encodedDot("%2e");
68
if (path.indexOf(encodedDot, 0, Qt::CaseInsensitive) != -1)
71
static const QString &encodedDOT = KGlobal::staticQString("%2E"); // Uppercase!
73
QString encodedDOT("%2E");
75
path.replace(encodedDot, ".");
76
path.replace(encodedDOT, ".");
81
bool slash = (len && path[len-1] == QLatin1Char('/')) ||
82
(len > 1 && path[len-2] == QLatin1Char('/') && path[len-1] == QLatin1Char('.'));
84
// The following code cleans up directory path much like
85
// QDir::cleanPath() except it can be made to ignore multiple
86
// directory separators by setting the flag to false. That fixes
87
// bug# 15044, mail.altavista.com and other similar brain-dead server
88
// implementations that do not follow what has been specified in
91
int cdUp, orig_pos, pos;
95
while ( pos && (pos = path.lastIndexOf(QLatin1Char('/'),--pos)) != -1 )
97
len = orig_pos - pos - 1;
98
if ( len == 2 && path[pos+1] == '.' && path[pos+2] == '.' )
102
// Ignore any occurrences of '.'
103
// This includes entries that simply do not make sense like /..../
104
if ( (len || !cleanDirSeparator) &&
105
(len != 1 || path[pos+1] != '.' ) )
108
result.prepend(path.mid(pos, len+1));
116
#ifdef Q_WS_WIN // prepend drive letter if exists (js)
117
if (orig_pos >= 2 && isalpha(path[0].toLatin1()) && path[1]==':') {
118
result.prepend(QString(path[0])+':');
122
if ( result.isEmpty() )
124
else if ( slash && result[result.length()-1] != QLatin1Char('/') )
125
result.append(QChar('/'));
130
bool KUrl::isRelativeUrl(const QString &_url)
134
return QUrl( _url ).isRelative();
136
int len = _url.length();
137
if (!len) return true; // Very short relative URL.
138
const QChar *str = _url.unicode();
140
// Absolute URL must start with alpha-character
141
if (!isalpha(str[0].toLatin1()))
142
return true; // Relative URL
144
for(int i = 1; i < len; i++)
146
char c = str[i].toLatin1(); // Note: non-latin1 chars return 0!
148
return false; // Absolute URL
150
// Protocol part may only contain alpha, digit, + or -
151
if (!isalpha(c) && !isdigit(c) && (c != '+') && (c != '-'))
152
return true; // Relative URL
154
// URL did not contain ':'
155
return true; // Relative URL
158
KUrl::List::List(const KUrl &url)
163
KUrl::List::List(const QStringList &list)
165
for (QStringList::ConstIterator it = list.begin();
173
QStringList KUrl::List::toStringList() const
176
for( KUrl::List::ConstIterator it = begin();
180
lst.append( (*it).url() );
186
void KUrl::List::populateMimeData( QMimeData* mimeData,
187
const KUrl::MetaDataMap& metaData,
188
MimeDataFlags flags ) const
190
QList<QByteArray> urlStringList;
191
KUrl::List::ConstIterator uit = begin();
192
const KUrl::List::ConstIterator uEnd = end();
193
for ( ; uit != uEnd ; ++uit )
195
// Get each URL encoded in utf8 - and since we get it in escaped
196
// form on top of that, .toLatin1() is fine.
197
urlStringList.append( (*uit).toMimeDataString().toLatin1() );
200
QByteArray uriListData;
201
for ( QList<QByteArray>::const_iterator it = urlStringList.begin(), end = urlStringList.end()
202
; it != end ; ++it ) {
203
uriListData += (*it);
204
uriListData += "\r\n";
206
mimeData->setData( "text/uri-list", uriListData );
208
if ( ( flags & KUrl::NoTextExport ) == 0 )
210
QStringList prettyURLsList;
211
for ( uit = begin(); uit != uEnd ; ++uit )
212
prettyURLsList.append( (*uit).prettyUrl() );
214
QByteArray plainTextData = prettyURLsList.join( "\n" ).toLocal8Bit();
215
if( count() > 1 ) // terminate last line, unless it's the only line
216
plainTextData.append( "\n" );
217
mimeData->setData( "text/plain", plainTextData );
220
if ( !metaData.isEmpty() )
222
QByteArray metaDataData; // :)
223
for( KUrl::MetaDataMap::const_iterator it = metaData.begin(); it != metaData.end(); ++it )
225
metaDataData += it.key().toUtf8();
226
metaDataData += "$@@$";
227
metaDataData += it.value().toUtf8();
228
metaDataData += "$@@$";
230
mimeData->setData( "application/x-kio-metadata", metaDataData );
234
bool KUrl::List::canDecode( const QMimeData *mimeData )
236
return mimeData->hasFormat( "text/uri-list" ) || mimeData->hasFormat( "application/x-kde-urilist" );
239
QStringList KUrl::List::mimeDataTypes()
241
return QStringList()<<( "application/x-kde-urilist" )<<( "text/uri-list" );
244
KUrl::List KUrl::List::fromMimeData( const QMimeData *mimeData, KUrl::MetaDataMap* metaData )
247
// x-kde-urilist is the same format as text/uri-list, but contains
248
// KDE-aware urls, like media:/ and system:/, whereas text/uri-list is resolved to
249
// local files. So we look at it first for decoding, but we let apps set it when encoding.
250
QByteArray payload = mimeData->data( "application/x-kde-urilist" );
251
if ( payload.isEmpty() )
252
payload = mimeData->data( "text/uri-list" );
253
if ( !payload.isEmpty() ) {
255
const char* d = payload.data();
256
while ( c < payload.size() && d[c] ) {
259
while (c < payload.size() && d[c] && d[c]!='\r'
262
QByteArray s( d+f, c-f );
263
if ( s[0] != '#' ) // non-comment?
264
uris.append( KUrl::fromMimeDataByteArray( s ) );
266
while ( c < payload.size() && d[c] &&
267
( d[c] == '\n' || d[c] == '\r' ) )
273
const QByteArray metaDataPayload = mimeData->data( "application/x-kio-metadata" );
274
if ( !metaDataPayload.isEmpty() )
276
QString str = QString::fromUtf8( metaDataPayload );
277
Q_ASSERT( str.endsWith( "$@@$" ) );
278
str.truncate( str.length() - 4 );
279
const QStringList lst = str.split( "$@@$" );
280
QStringList::ConstIterator it = lst.begin();
281
bool readingKey = true; // true, then false, then true, etc.
283
for ( ; it != lst.end(); ++it ) {
287
metaData->insert( key, *it );
288
readingKey = !readingKey;
290
Q_ASSERT( readingKey ); // an odd number of items would be, well, odd ;-)
309
KUrl::KUrl( const QString &str )
312
if ( !str.isEmpty() ) {
313
if ( str[0] == QLatin1Char('/') || str[0] == QLatin1Char('~') )
316
setEncodedUrl( str.toUtf8(), QUrl::TolerantMode );
320
KUrl::KUrl( const char * str )
324
if ( str[0] == '/' || str[0] == '~' )
325
setPath( QString::fromUtf8( str ) );
327
setEncodedUrl( str, QUrl::TolerantMode );
331
KUrl::KUrl( const QByteArray& str )
334
if ( !str.isEmpty() ) {
335
if ( str[0] == '/' || str[0] == '~' )
336
setPath( QString::fromUtf8( str ) );
338
setEncodedUrl( str, QUrl::TolerantMode );
342
KUrl::KUrl( const KUrl& _u )
347
KUrl::KUrl( const QUrl &u )
352
KUrl::KUrl( const KUrl& _u, const QString& _rel_url )
356
if (_u.hasSubUrl()) // Operate on the last suburl, not the first
358
KUrl::List lst = split( _u );
359
KUrl u(lst.last(), _rel_url);
360
lst.erase( --lst.end() );
366
QString rUrl = _rel_url;
368
// WORKAROUND THE RFC 1606 LOOPHOLE THAT ALLOWS
369
// http:/index.html AS A VALID SYNTAX FOR RELATIVE
370
// URLS. ( RFC 2396 section 5.2 item # 3 )
371
int len = _u.scheme().length();
372
if ( !_u.host().isEmpty() && !rUrl.isEmpty() &&
373
rUrl.indexOf( _u.scheme(), 0, Qt::CaseInsensitive ) == 0 &&
374
rUrl[len] == ':' && (rUrl[len+1] != QLatin1Char('/') ||
375
(rUrl[len+1] == '/' && rUrl[len+2] != QLatin1Char('/'))) )
377
rUrl.remove( 0, rUrl.indexOf( ':' ) + 1 );
381
if ( rUrl.isEmpty() )
385
else if ( rUrl[0] == '#' )
388
QString strRef_encoded = rUrl.mid(1);
389
if ( strRef_encoded.isNull() )
390
strRef_encoded = ""; // we know there was an (empty) html ref, we saw the '#'
391
setFragment( strRef_encoded );
393
else if ( isRelativeUrl( rUrl ) )
396
setFragment( QString() );
397
setEncodedQuery( QByteArray() );
398
QString strPath = path();
399
if ( rUrl[0] == QLatin1Char('/') )
401
if ((rUrl.length() > 1) && (rUrl[1] == QLatin1Char('/')))
403
setHost( QString() );
405
// File protocol returns file:/// without host, strip // from rUrl
406
if ( _u.isLocalFile() )
411
else if ( rUrl[0] != '?' )
413
int pos = strPath.lastIndexOf( QLatin1Char('/') );
415
strPath.truncate(pos);
416
strPath += QLatin1Char('/');
420
if ( strPath.isEmpty() )
421
strPath = QLatin1Char('/');
424
//kDebug(126) << "url()=" << url() << " rUrl=" << rUrl << endl;
425
KUrl tmp( url() + rUrl);
426
//kDebug(126) << "assigning tmp=" << tmp.url() << endl;
428
cleanPath(KeepDirSeparators);
433
//kDebug(126) << "not relative; assigning tmp=" << tmp.url() << endl;
435
// Preserve userinfo if applicable.
436
if (!_u.userInfo().isEmpty() && userInfo().isEmpty()
437
&& (_u.host() == host()) && (_u.scheme() == scheme()))
439
setUserInfo( _u.userInfo() );
441
cleanPath(KeepDirSeparators);
445
bool KUrl::operator==( const KUrl& _u ) const
447
if ( !isValid() || !_u.isValid() )
449
return QUrl::operator==( _u );;
452
bool KUrl::operator==( const QString& _u ) const
455
return ( *this == u );
458
bool KUrl::cmp( const KUrl &u, bool ignore_trailing ) const
460
return equals( u, ignore_trailing ? CompareWithoutTrailingSlash : EqualsOptions(0) );
463
bool KUrl::equals( const KUrl &_u, const EqualsOptions& options ) const
465
if ( !isValid() || !_u.isValid() )
468
if ( options & CompareWithoutTrailingSlash || options & CompareWithoutFragment )
470
QString path1 = path((options & CompareWithoutTrailingSlash) ? RemoveTrailingSlash : LeaveTrailingSlash);
471
QString path2 = _u.path((options & CompareWithoutTrailingSlash) ? RemoveTrailingSlash : LeaveTrailingSlash);
472
if ( path1 != path2 )
475
if ( scheme() == _u.scheme() &&
476
authority() == _u.authority() && // user+pass+host+port
477
encodedQuery() == _u.encodedQuery() &&
478
(fragment() == _u.fragment() || options & CompareWithoutFragment ) )
484
return ( *this == _u );
487
void KUrl::setFileName( const QString& _txt )
489
setFragment( QString() );
491
while( i < _txt.length() && _txt[i] == QLatin1Char('/') )
493
QString tmp = i ? _txt.mid( i ) : _txt;
495
//QString path = m_strPath_encoded.isEmpty() ? m_strPath : m_strPath_encoded;
496
QString path = this->path();
497
if ( path.isEmpty() )
501
int lastSlash = path.lastIndexOf( QLatin1Char('/') );
502
if ( lastSlash == -1)
504
// The first character is not a '/' ???
505
// This looks strange ...
506
path = QLatin1Char('/');
508
else if ( !path.endsWith( QLatin1Char('/') ) )
509
path.truncate( lastSlash+1 ); // keep the "/"
512
if (m_strPath_encoded.isEmpty())
521
path += encode_string(tmp);
522
setEncodedPath( path );
528
void KUrl::cleanPath( const CleanPathOption& options )
530
//if (m_iUriMode != URL) return;
531
const QString newPath = cleanpath(path(), !(options & KeepDirSeparators), false);
532
if ( path() != newPath )
534
// WABA: Is this safe when "/../" is encoded with %?
535
//m_strPath_encoded = cleanpath(m_strPath_encoded, cleanDirSeparator, true);
538
static QString trailingSlash( KUrl::AdjustPathOption trailing, const QString &path )
540
QString result = path;
542
if ( trailing == KUrl::LeaveTrailingSlash )
544
else if ( trailing == KUrl::AddTrailingSlash )
546
int len = result.length();
547
if ( (len == 0) || (result[ len - 1 ] != QLatin1Char('/')) )
548
result += QLatin1Char('/');
551
else if ( trailing == KUrl::RemoveTrailingSlash )
555
int len = result.length();
556
while (len > 1 && result[ len - 1 ] == QLatin1Char('/'))
560
result.truncate( len );
569
void KUrl::adjustPath( AdjustPathOption trailing )
572
if (!m_strPath_encoded.isEmpty())
574
m_strPath_encoded = trailingSlash( _trailing, m_strPath_encoded );
577
const QString newPath = trailingSlash( trailing, path() );
578
if ( path() != newPath )
583
QString KUrl::encodedPathAndQuery( AdjustPathOption trailing , const EncodedPathAndQueryOptions &options) const
587
if (!m_strPath_encoded.isEmpty())
589
tmp = trailingSlash( _trailing, m_strPath_encoded );
594
tmp = path( trailing );
595
if ( (options & AvoidEmptyPath) && tmp.isEmpty() )
598
if (m_iUriMode == Mailto)
600
tmp = encode( tmp, 2 ); // encode neither @ nor /
604
tmp = encode( tmp, 1 ); // encode @ but not /
607
// The list of chars to exclude comes from QUrlPrivate::toEncoded
608
tmp = QString::fromLatin1( QUrl::toPercentEncoding( tmp, "!$&'()*+,;=:@/" ) );
611
#if defined( QT_KDE_QT_COPY ) || QT_VERSION >= 0x040200
614
if (!query().isEmpty())
617
tmp += query(); // includes the '?'
623
void KUrl::setEncodedPath( const QString& _txt, int encoding_hint )
625
m_strPath_encoded = _txt;
627
decode( m_strPath_encoded, m_strPath, m_strPath_encoded, encoding_hint );
628
// Throw away encoding for local files, makes file-operations faster.
629
if (m_strProtocol == "file")
630
m_strPath_encoded.clear();
632
if ( m_iUriMode == Auto )
637
void KUrl::setEncodedPathAndQuery( const QString& _txt )
639
int pos = _txt.indexOf( '?' );
642
setPath( QUrl::fromPercentEncoding( _txt.toLatin1() ) );
643
setEncodedQuery( QByteArray() );
647
setPath( QUrl::fromPercentEncoding( _txt.toLatin1() ).left( pos ) );
648
_setQuery( _txt.right( _txt.length() - pos - 1 ) );
652
QString KUrl::path( AdjustPathOption trailing ) const
654
return trailingSlash( trailing, path() );
657
bool KUrl::isLocalFile() const
659
if ( ( scheme() != "file" ) || hasSubUrl() )
662
if (host().isEmpty() || (host() == QLatin1String("localhost")))
665
char hostname[ 256 ];
666
hostname[ 0 ] = '\0';
667
if (!gethostname( hostname, 255 ))
668
hostname[sizeof(hostname)-1] = '\0';
670
for(char *p = hostname; *p; p++)
673
return (host() == hostname);
676
void KUrl::setFileEncoding(const QString &encoding)
683
if (!q.isEmpty() && (q[0] == '?'))
686
QStringList args = q.split('&', QString::SkipEmptyParts);
687
for(QStringList::Iterator it = args.begin();
690
QString s = QUrl::fromPercentEncoding( (*it).toLatin1() );
691
if (s.startsWith("charset="))
696
if (!encoding.isEmpty())
697
args.append("charset=" + QUrl::toPercentEncoding(encoding));
700
_setQuery(QString());
702
_setQuery(args.join("&"));
705
QString KUrl::fileEncoding() const
718
QStringList args = q.split('&', QString::SkipEmptyParts);
719
for(QStringList::ConstIterator it = args.begin();
723
QString s = QUrl::fromPercentEncoding((*it).toLatin1());
724
if (s.startsWith("charset="))
730
bool KUrl::hasSubUrl() const
732
if ( scheme().isEmpty() || !isValid() )
734
const QString ref = fragment();
737
if (ref.startsWith("gzip:"))
739
if (ref.startsWith("bzip:"))
741
if (ref.startsWith("bzip2:"))
743
if (ref.startsWith("tar:"))
745
if (ref.startsWith("ar:"))
747
if (ref.startsWith("zip:"))
749
if ( scheme() == "error" ) // anything that starts with error: has suburls
754
QString KUrl::url( AdjustPathOption trailing ) const
756
if ( trailing == AddTrailingSlash && !path().endsWith( QLatin1Char('/') ) ) {
757
// -1 and 0 are provided by QUrl, but not +1, so that one is a bit tricky.
758
// To avoid reimplementing toString() all over again, I just use another QUrl
759
// Let's hope this is fast, or not called often...
760
QUrl newUrl( *this );
761
newUrl.setPath( path() + QLatin1Char('/') );
762
return QString::fromLatin1( newUrl.toEncoded() ); // ### check
764
return QString::fromLatin1( toEncoded( trailing == RemoveTrailingSlash ? StripTrailingSlash : None ) ); // ## check encoding
767
QString KUrl::prettyUrl( AdjustPathOption trailing ) const
769
// Can't use toString(), it breaks urls with %23 in them (becomes '#', which is parsed back as a fragment)
770
// So prettyUrl is just url, with the password removed.
771
// We could replace some chars, like "%20" -> ' ', though?
772
if ( password().isEmpty() )
773
return url( trailing );
775
QUrl newUrl( *this );
776
newUrl.setPassword( QString() );
777
if ( trailing == AddTrailingSlash && !path().endsWith( QLatin1Char('/') ) ) {
778
// -1 and 0 are provided by QUrl, but not +1.
779
newUrl.setPath( path() + QLatin1Char('/') );
780
return QString::fromLatin1( newUrl.toEncoded() );
782
return QString::fromLatin1( newUrl.toEncoded( trailing == RemoveTrailingSlash ? StripTrailingSlash : None ) ); // ## check encoding
786
QString KUrl::prettyUrl( int _trailing, AdjustementFlags _flags) const
788
QString u = prettyUrl(_trailing);
789
if (_flags & StripFileProtocol && u.startsWith("file://")) {
792
return QDir::convertSeparators(u);
799
QString KUrl::pathOrUrl() const
801
if ( isLocalFile() && fragment().isNull() && encodedQuery().isNull() ) {
808
QString KUrl::toMimeDataString() const // don't fold this into populateMimeData, it's also needed by other code like konqdrag
813
// return url(0, KGlobal::locale()->fileEncodingMib());
814
// Can't do that anymore with QUrl....
815
// return url( 0, QTextCodec::codecForLocale()->mibEnum() );
818
// According to the XDND spec, file:/ URLs for DND must have
819
// the hostname part. But in really it just breaks many apps,
820
// so it's disabled for now.
821
const QString s = url( 0, KGlobal::locale()->fileEncodingMib() );
822
if( !s.startsWith( "file://" ))
825
if ( gethostname( hostname, 255 ) == 0 )
827
hostname[256] = '\0';
828
return QString( "file://" ) + hostname + s.mid( 5 );
834
if ( protocol() == "mailto" ) {
838
return url(/*0 , 106*/); // 106 is mib enum for utf8 codec
841
KUrl KUrl::fromMimeDataByteArray( const QByteArray& str )
843
if ( str.startsWith( "file:" ) )
844
return KUrl( str /*, QTextCodec::codecForLocale()->mibEnum()*/ );
846
return KUrl( str /*, 106*/ ); // 106 is mib enum for utf8 codec;
849
KUrl::List KUrl::split( const KUrl& _url )
859
u.setFragment( QString() );
863
url = KUrl(url.fragment());
867
ref = url.fragment();
868
#if defined( QT_KDE_QT_COPY ) || QT_VERSION >= 0x040200
869
hasRef = url.hasFragment();
871
hasRef = !ref.isEmpty();
879
// Set HTML ref in all URLs.
880
KUrl::List::Iterator it;
881
for( it = lst.begin() ; it != lst.end(); ++it )
883
(*it).setFragment( ref );
890
KUrl::List KUrl::split( const QString& _url )
892
return split(KUrl(_url));
895
KUrl KUrl::join( const KUrl::List & lst )
897
if (lst.isEmpty()) return KUrl();
902
QListIterator<KUrl> it(lst);
904
while (it.hasPrevious())
906
KUrl u(it.previous());
909
// ##### problem: this encodes the '#' into %23 every time,
910
// so at the 2nd level we get %2523, etc...
911
u.setFragment( tmp.url() );
921
QString KUrl::fileName( const DirectoryOptions& options ) const
924
if (hasSubUrl()) { // If we have a suburl, then return the filename from there
925
KUrl::List list = KUrl::split(*this);
926
return list.last().fileName(options);
928
const QString path = this->path();
930
int len = path.length();
934
if (!(options & ObeyTrailingSlash) )
936
while ( len >= 1 && path[ len - 1 ] == QLatin1Char('/') )
939
else if ( path[ len - 1 ] == QLatin1Char('/') )
942
// Does the path only consist of '/' characters ?
943
if ( len == 1 && path[ 0 ] == QLatin1Char('/') )
946
// Skip last n slashes
949
if (!m_strPath_encoded.isEmpty())
951
// This is hairy, we need the last unencoded slash.
952
// Count in the encoded string how many encoded slashes follow the last
954
int i = m_strPath_encoded.lastIndexOf( QLatin1Char('/'), len - 1 );
955
QString fileName_encoded = m_strPath_encoded.mid(i+1);
956
n += fileName_encoded.count("%2f", Qt::CaseInsensitive);
961
i = path.lastIndexOf( QLatin1Char('/'), i - 1 );
963
while (--n && (i > 0));
965
// If ( i == -1 ) => the first character is not a '/'
966
// So it's some URL like file:blah.tgz, return the whole path
968
if ( len == (int)path.length() )
971
// Might get here if _strip_trailing_slash is true
972
fname = path.left( len );
976
fname = path.mid( i + 1, len - i - 1 ); // TO CHECK
981
void KUrl::addPath( const QString& _txt )
985
KUrl::List lst = split( *this );
986
KUrl &u = lst.last();
992
//m_strPath_encoded.clear();
994
if ( _txt.isEmpty() )
997
QString strPath = path();
999
int len = strPath.length();
1000
// Add the trailing '/' if it is missing
1001
if ( _txt[0] != QLatin1Char('/') && ( len == 0 || strPath[ len - 1 ] != QLatin1Char('/') ) )
1002
strPath += QLatin1Char('/');
1004
// No double '/' characters
1006
const int _txtlen = _txt.length();
1007
if ( strPath.endsWith( QLatin1Char('/') ) )
1009
while ( ( i < _txtlen ) && ( _txt[i] == QLatin1Char('/') ) )
1013
setPath( strPath + _txt.mid( i ) );
1014
//kDebug(126)<<"addPath: resultpath="<<path()<<endl;
1017
QString KUrl::directory( const DirectoryOptions& options ) const
1019
QString result = path(); //m_strPath_encoded.isEmpty() ? m_strPath : m_strPath_encoded;
1020
if ( !(options & ObeyTrailingSlash) )
1021
result = trailingSlash( RemoveTrailingSlash, result );
1023
if ( result.isEmpty() || result == "/" )
1026
int i = result.lastIndexOf( "/" );
1027
// If ( i == -1 ) => the first character is not a '/'
1028
// So it's some URL like file:blah.tgz, with no path
1038
if ( options & AppendTrailingSlash )
1039
result = result.left( i + 1 );
1041
result = result.left( i );
1043
//if (!m_strPath_encoded.isEmpty())
1044
// result = decode(result);
1050
bool KUrl::cd( const QString& _dir )
1052
if ( _dir.isEmpty() || !isValid() )
1057
KUrl::List lst = split( *this );
1058
KUrl &u = lst.last();
1060
*this = join( lst );
1065
if ( _dir[0] == QLatin1Char('/') )
1067
//m_strPath_encoded.clear();
1069
setHTMLRef( QString() );
1070
setEncodedQuery( QByteArray() );
1074
// Users home directory on the local disk ?
1075
if ( ( _dir[0] == '~' ) && ( scheme() == "file" ))
1077
//m_strPath_encoded.clear();
1078
QString strPath = QDir::homePath();
1079
strPath += QLatin1Char('/');
1080
strPath += _dir.right( strPath.length() - 1 );
1082
setHTMLRef( QString() );
1083
setEncodedQuery( QByteArray() );
1088
// we always work on the past of the first url.
1089
// Sub URLs are not touched.
1091
// append '/' if necessary
1092
QString p = path(AddTrailingSlash);
1094
p = cleanpath( p, true, false );
1097
setHTMLRef( QString() );
1098
setEncodedQuery( QByteArray() );
1103
KUrl KUrl::upUrl( ) const
1105
if (!encodedQuery().isEmpty())
1108
u.setEncodedQuery(QByteArray());
1121
// We have a subURL.
1122
KUrl::List lst = split( *this );
1124
return KUrl(); // Huh?
1127
KUrl &u = lst.last();
1128
const QString old = u.path();
1130
if (u.path() != old)
1132
if (lst.count() == 1)
1139
QString KUrl::htmlRef() const
1143
return QUrl::fromPercentEncoding( ref().toLatin1() );
1146
List lst = split( *this );
1147
return QUrl::fromPercentEncoding( (*lst.begin()).ref().toLatin1() );
1150
QString KUrl::encodedHtmlRef() const
1157
List lst = split( *this );
1158
return (*lst.begin()).ref();
1161
void KUrl::setHTMLRef( const QString& _ref )
1165
setFragment( _ref );
1169
List lst = split( *this );
1171
(*lst.begin()).setFragment( _ref );
1173
*this = join( lst );
1176
bool KUrl::hasHTMLRef() const
1183
List lst = split( *this );
1184
return (*lst.begin()).hasRef();
1187
void KUrl::setDirectory( const QString &dir)
1189
if ( dir.endsWith("/"))
1195
void KUrl::setQuery( const QString &_txt )
1197
if (!_txt.isEmpty() && _txt[0] == '?')
1198
_setQuery( _txt.length() > 1 ? _txt.mid(1) : "" /*empty, not null*/ );
1203
void KUrl::_setQuery( const QString& query )
1205
setEncodedQuery( query.isNull() ? QByteArray() : query.toLatin1() ); // ### TODO encoding ok?
1208
QString KUrl::query() const
1210
#if defined( QT_KDE_QT_COPY ) || QT_VERSION >= 0x040200
1213
// For now we'll ignore the case of "a query but it's empty", waiting for Qt-4.2
1214
if (query().isEmpty())
1219
return QString( QChar( '?' ) ) + QString::fromAscii( encodedQuery() );
1222
bool urlcmp( const QString& _url1, const QString& _url2 )
1224
return QUrl( _url1, QUrl::TolerantMode ) == QUrl( _url2, QUrl::TolerantMode );
1227
if ( _url1.isEmpty() && _url2.isEmpty() )
1230
if ( _url1.isEmpty() || _url2.isEmpty() )
1233
KUrl::List list1 = KUrl::split( _url1 );
1234
KUrl::List list2 = KUrl::split( _url2 );
1237
if ( list1.isEmpty() || list2.isEmpty() )
1240
return ( list1 == list2 );
1244
bool urlcmp( const QString& _url1, const QString& _url2, const KUrl::EqualsOptions& _options )
1248
QUrl::FormattingOptions options = QUrl::None;
1249
if ( _options & KUrl::CompareWithoutTrailingSlash )
1250
options |= QUrl::StripTrailingSlash;
1251
if ( _options & KUrl::CompareWithoutFragment )
1252
options |= QUrl::RemoveFragment;
1253
return u1.toString( options ) == u2.toString( options );
1257
if ( _url1.isEmpty() && _url2.isEmpty() )
1260
if ( _url1.isEmpty() || _url2.isEmpty() )
1263
KUrl::List list1 = KUrl::split( _url1 );
1264
KUrl::List list2 = KUrl::split( _url2 );
1267
if ( list1.isEmpty() || list2.isEmpty() )
1270
int size = list1.count();
1271
if ( list2.count() != size )
1276
(*list1.begin()).setRef(QString());
1277
(*list2.begin()).setRef(QString());
1280
KUrl::List::Iterator it1 = list1.begin();
1281
KUrl::List::Iterator it2 = list2.begin();
1282
for( ; it1 != list1.end() ; ++it1, ++it2 )
1283
if ( !(*it1).equals( *it2, _ignore_trailing ) )
1290
KUrl KUrl::fromPathOrUrl( const QString& text )
1293
if ( !text.isEmpty() )
1295
if (!QDir::isRelativePath(text) || text[0] == '~')
1296
url.setPath( text );
1304
static QString _relativePath(const QString &base_dir, const QString &path, bool &isParent)
1306
QString _base_dir(QDir::cleanPath(base_dir));
1307
QString _path(QDir::cleanPath(path.isEmpty() || (path[0] != QLatin1Char('/')) ? _base_dir+'/'+path : path));
1309
if (_base_dir.isEmpty())
1312
if (_base_dir[_base_dir.length()-1] != QLatin1Char('/'))
1313
_base_dir.append(QLatin1Char('/') );
1315
QStringList list1 = _base_dir.split(QLatin1Char('/'), QString::SkipEmptyParts);
1316
QStringList list2 = _path.split(QLatin1Char('/'), QString::SkipEmptyParts);
1318
// Find where they meet
1320
int maxLevel = qMin(list1.count(), list2.count());
1321
while((level < maxLevel) && (list1[level] == list2[level])) level++;
1324
// Need to go down out of the first path to the common branch.
1325
for(int i = level; i < list1.count(); i++)
1326
result.append("../");
1328
// Now up up from the common branch to the second path.
1329
for(int i = level; i < list2.count(); i++)
1330
result.append(list2[i]).append("/");
1332
if ((level < list2.count()) && (path[path.length()-1] != QLatin1Char('/')))
1333
result.truncate(result.length()-1);
1335
isParent = (level == list1.count());
1340
QString KUrl::relativePath(const QString &base_dir, const QString &path, bool *isParent)
1342
bool parent = false;
1343
QString result = _relativePath(base_dir, path, parent);
1345
result.prepend("./");
1354
QString KUrl::relativeUrl(const KUrl &base_url, const KUrl &url)
1356
if ((url.protocol() != base_url.protocol()) ||
1357
(url.host() != base_url.host()) ||
1358
(url.port() && url.port() != base_url.port()) ||
1359
(url.hasUser() && url.user() != base_url.user()) ||
1360
(url.hasPass() && url.pass() != base_url.pass()))
1367
if ((base_url.path() != url.path()) || (base_url.query() != url.query()))
1370
QString basePath = base_url.directory(KUrl::ObeyTrailingSlash);
1371
relURL = _relativePath(basePath, url.path(), dummy); // was QUrl::toPercentEncoding() but why?
1372
relURL += url.query();
1378
relURL += url.ref();
1381
if ( relURL.isEmpty() )
1387
void KUrl::setPath( const QString& _path )
1389
if ( scheme().isEmpty() )
1390
setScheme( "file" );
1392
QString path = KShell::tildeExpand( _path );
1394
const QString& path = _path;
1396
QUrl::setPath( path );
1399
#if 0 // this would be if we didn't decode '+' into ' '
1400
QMap< QString, QString > KUrl::queryItems( int options ) const {
1401
QMap< QString, QString > result;
1402
const QList<QPair<QString, QString> > items = QUrl::queryItems();
1403
QPair<QString, QString> item;
1404
Q_FOREACH( item, items ) {
1405
result.insert( options & CaseInsensitiveKeys ? item.first.toLower() : item.first, item.second );
1411
QMap< QString, QString > KUrl::queryItems( const QueryItemsOptions &options ) const {
1412
const QString strQueryEncoded = encodedQuery();
1413
if ( strQueryEncoded.isEmpty() )
1414
return QMap<QString,QString>();
1416
QMap< QString, QString > result;
1417
const QStringList items = strQueryEncoded.split( '&', QString::SkipEmptyParts );
1418
for ( QStringList::const_iterator it = items.begin() ; it != items.end() ; ++it ) {
1419
const int equal_pos = (*it).indexOf( '=' );
1420
if ( equal_pos > 0 ) { // = is not the first char...
1421
QString name = (*it).left( equal_pos );
1422
if ( options & CaseInsensitiveKeys )
1423
name = name.toLower();
1424
QString value = (*it).mid( equal_pos + 1 );
1425
if ( value.isEmpty() )
1426
result.insert( name, QString::fromLatin1("") );
1428
// ### why is decoding name not necessary?
1429
value.replace( '+', ' ' ); // + in queries means space
1430
result.insert( name, QUrl::fromPercentEncoding( value.toLatin1() ) );
1432
} else if ( equal_pos < 0 ) { // no =
1433
QString name = (*it);
1434
if ( options & CaseInsensitiveKeys )
1435
name = name.toLower();
1436
result.insert( name, QString() );
1443
QString KUrl::queryItem( const QString& _item ) const
1445
const QString strQueryEncoded = encodedQuery();
1446
const QString item = _item + '=';
1447
if ( strQueryEncoded.length() <= 1 )
1450
const QStringList items = strQueryEncoded.split( '&', QString::SkipEmptyParts );
1451
const int _len = item.length();
1452
for ( QStringList::ConstIterator it = items.begin(); it != items.end(); ++it )
1454
if ( (*it).startsWith( item ) )
1456
if ( (*it).length() > _len )
1458
QString str = (*it).mid( _len );
1459
str.replace( '+', ' ' ); // + in queries means space.
1460
return QUrl::fromPercentEncoding( str.toLatin1() );
1463
return QString::fromLatin1("");
1470
void KUrl::addQueryItem( const QString& _item, const QString& _value )
1472
QString item = _item + '=';
1473
QString value = QUrl::toPercentEncoding( _value );
1475
QString strQueryEncoded = encodedQuery();
1476
if (!strQueryEncoded.isEmpty())
1477
strQueryEncoded += '&';
1478
strQueryEncoded += item + value;
1479
setEncodedQuery( strQueryEncoded.toLatin1() );
1482
void KUrl::populateMimeData( QMimeData* mimeData,
1483
const MetaDataMap& metaData,
1484
MimeDataFlags flags ) const
1486
KUrl::List lst( *this );
1487
lst.populateMimeData( mimeData, metaData, flags );
1490
bool KUrl::hasRef() const
1492
#if defined( QT_KDE_QT_COPY ) || QT_VERSION >= 0x040200
1493
return hasFragment();
1495
// For now we'll ignore the case of "a fragment but it's empty", waiting for Qt-4.2
1496
return !fragment().isEmpty();
1500
void KUrl::setRef( const QString& fragment )
1502
if ( fragment.isNull() )
1503
setFragment( fragment ); // pass null, not empty
1505
setFragment( QUrl::fromPercentEncoding( fragment.toLatin1() ) );