00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "kurlcompletion.h"
00027
00028 #include <config.h>
00029
00030 #include <stdlib.h>
00031 #include <assert.h>
00032 #include <limits.h>
00033
00034 #include <QtCore/QMutableStringListIterator>
00035 #include <QtCore/QRegExp>
00036 #include <QtCore/QTimer>
00037 #include <QtCore/QDir>
00038 #include <QtCore/QFile>
00039 #include <QtCore/QTextIStream>
00040 #include <QtCore/QThread>
00041 #include <QtGui/QActionEvent>
00042
00043 #include <kapplication.h>
00044 #include <kauthorized.h>
00045 #include <kdebug.h>
00046 #include <kurl.h>
00047 #include <kio/job.h>
00048 #include <kprotocolmanager.h>
00049 #include <kconfig.h>
00050 #include <kglobal.h>
00051 #include <kde_file.h>
00052
00053 #include <sys/types.h>
00054 #include <dirent.h>
00055 #include <unistd.h>
00056 #include <sys/stat.h>
00057 #include <pwd.h>
00058 #include <time.h>
00059 #include <sys/param.h>
00060 #include <kconfiggroup.h>
00061
00062 static bool expandTilde(QString &);
00063 static bool expandEnv(QString &);
00064
00065 static QString unescape(const QString &text);
00066
00067
00068
00069 #define MODE_EXE (S_IXUSR | S_IXGRP | S_IXOTH)
00070
00071
00072 enum ComplType {CTNone=0, CTEnv, CTUser, CTMan, CTExe, CTFile, CTUrl, CTInfo};
00073
00074 class CompletionThread;
00075
00081 class CompletionMatchEvent : public QEvent
00082 {
00083 public:
00084 CompletionMatchEvent( CompletionThread *thread ) :
00085 QEvent( uniqueType() ),
00086 m_completionThread( thread )
00087 {}
00088
00089 CompletionThread *completionThread() const { return m_completionThread; }
00090 static Type uniqueType() { return Type(User + 61080); }
00091
00092 private:
00093 CompletionThread *m_completionThread;
00094 };
00095
00096 class CompletionThread : public QThread
00097 {
00098 protected:
00099 CompletionThread( KUrlCompletion *receiver ) :
00100 QThread(),
00101 m_receiver( receiver ),
00102 m_terminationRequested( false )
00103 {}
00104
00105 public:
00106 void requestTermination() { m_terminationRequested = true; }
00107 QStringList matches() const { return m_matches; }
00108
00109 protected:
00110 void addMatch( const QString &match ) { m_matches.append( match ); }
00111 bool terminationRequested() const { return m_terminationRequested; }
00112 void done()
00113 {
00114 if ( !m_terminationRequested )
00115 qApp->postEvent( m_receiver, new CompletionMatchEvent( this ) );
00116 else
00117 deleteLater();
00118 }
00119
00120 private:
00121 KUrlCompletion *m_receiver;
00122 QStringList m_matches;
00123 bool m_terminationRequested;
00124 };
00125
00131 class UserListThread : public CompletionThread
00132 {
00133 public:
00134 UserListThread( KUrlCompletion *receiver ) :
00135 CompletionThread( receiver )
00136 {}
00137
00138 protected:
00139 virtual void run()
00140 {
00141 static const QChar tilde = '~';
00142
00143 struct passwd *pw;
00144 while ( ( pw = ::getpwent() ) && !terminationRequested() )
00145 addMatch( tilde + QString::fromLocal8Bit( pw->pw_name ) );
00146
00147 ::endpwent();
00148
00149 addMatch( QString( tilde ) );
00150
00151 done();
00152 }
00153 };
00154
00155 class DirectoryListThread : public CompletionThread
00156 {
00157 public:
00158 DirectoryListThread( KUrlCompletion *receiver,
00159 const QStringList &dirList,
00160 const QString &filter,
00161 bool onlyExe,
00162 bool onlyDir,
00163 bool noHidden,
00164 bool appendSlashToDir ) :
00165 CompletionThread( receiver ),
00166 m_dirList( dirList ),
00167 m_filter( filter ),
00168 m_onlyExe( onlyExe ),
00169 m_onlyDir( onlyDir ),
00170 m_noHidden( noHidden ),
00171 m_appendSlashToDir( appendSlashToDir )
00172 {}
00173
00174 virtual void run();
00175
00176 private:
00177 QStringList m_dirList;
00178 QString m_filter;
00179 bool m_onlyExe;
00180 bool m_onlyDir;
00181 bool m_noHidden;
00182 bool m_appendSlashToDir;
00183 };
00184
00185 void DirectoryListThread::run()
00186 {
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199 DIR *dir = 0;
00200
00201 for ( QStringList::ConstIterator it = m_dirList.begin();
00202 it != m_dirList.end() && !terminationRequested();
00203 ++it )
00204 {
00205
00206
00207 if ( !dir ) {
00208 dir = ::opendir( QFile::encodeName( *it ) );
00209 if ( ! dir ) {
00210 kDebug() << "Failed to open dir:" << *it;
00211 return;
00212 }
00213 }
00214
00215
00216
00217
00218
00219 QString path = QDir::currentPath();
00220 QDir::setCurrent( *it );
00221
00222
00223
00224
00225
00226 #ifndef HAVE_READDIR_R
00227 KDE_struct_dirent *dirEntry = 0;
00228 while ( !terminationRequested() &&
00229 (dirEntry = KDE_readdir( dir)))
00230 #else
00231 struct dirent *dirPosition = (struct dirent *) malloc( sizeof( struct dirent ) + MAXPATHLEN + 1 );
00232 struct dirent *dirEntry = 0;
00233 while ( !terminationRequested() &&
00234 ::readdir_r( dir, dirPosition, &dirEntry ) == 0 && dirEntry )
00235 #endif
00236
00237 {
00238
00239
00240 if ( dirEntry->d_name[0] == '.' && m_noHidden )
00241 continue;
00242
00243
00244
00245 if ( dirEntry->d_name[0] == '.' && dirEntry->d_name[1] == '\0' )
00246 continue;
00247
00248
00249
00250 if ( dirEntry->d_name[0] == '.' && dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0' )
00251 continue;
00252
00253 QString file = QFile::decodeName( dirEntry->d_name );
00254
00255 if ( m_filter.isEmpty() || file.startsWith( m_filter ) ) {
00256
00257 if ( m_onlyExe || m_onlyDir || m_appendSlashToDir ) {
00258 KDE_struct_stat sbuff;
00259
00260 if ( KDE_stat( dirEntry->d_name, &sbuff ) == 0 ) {
00261
00262
00263
00264 if ( m_onlyExe && ( sbuff.st_mode & MODE_EXE ) == 0 )
00265 continue;
00266
00267
00268
00269 if ( m_onlyDir && !S_ISDIR( sbuff.st_mode ) )
00270 continue;
00271
00272
00273
00274 if ( m_appendSlashToDir && S_ISDIR( sbuff.st_mode ) )
00275 file.append( QLatin1Char( '/' ) );
00276
00277 }
00278 else {
00279 kDebug() << "Could not stat file" << file;
00280 continue;
00281 }
00282 }
00283
00284 addMatch( file );
00285 }
00286 }
00287
00288
00289
00290 QDir::setCurrent( path );
00291
00292 ::closedir( dir );
00293 dir = 0;
00294 #ifdef HAVE_READDIR_R
00295 free( dirPosition );
00296 #endif
00297 }
00298
00299 done();
00300 }
00301
00304
00305
00306 class KUrlCompletionPrivate
00307 {
00308 public:
00309 KUrlCompletionPrivate(KUrlCompletion *parent)
00310 : q(parent),
00311 url_auto_completion(true),
00312 userListThread(0),
00313 dirListThread(0)
00314 {
00315 }
00316
00317 ~KUrlCompletionPrivate();
00318
00319 void _k_slotEntries( KIO::Job*, const KIO::UDSEntryList& );
00320 void _k_slotIOFinished( KJob* );
00321
00322 class MyURL;
00323 bool userCompletion(const MyURL &url, QString *match);
00324 bool envCompletion(const MyURL &url, QString *match);
00325 bool exeCompletion(const MyURL &url, QString *match);
00326 bool fileCompletion(const MyURL &url, QString *match);
00327 bool urlCompletion(const MyURL &url, QString *match);
00328
00329 bool isAutoCompletion();
00330
00331
00332 QString listDirectories(const QStringList &,
00333 const QString &,
00334 bool only_exe = false,
00335 bool only_dir = false,
00336 bool no_hidden = false,
00337 bool stat_files = true);
00338
00339 void listUrls( const QList<KUrl *> &urls,
00340 const QString &filter = QString(),
00341 bool only_exe = false,
00342 bool no_hidden = false );
00343
00344 void addMatches( const QStringList & );
00345 QString finished();
00346
00347 void init();
00348
00349 void setListedUrl(int compl_type ,
00350 const QString& dir = QString(),
00351 const QString& filter = QString(),
00352 bool no_hidden = false );
00353
00354 bool isListedUrl( int compl_type ,
00355 const QString& dir = QString(),
00356 const QString& filter = QString(),
00357 bool no_hidden = false );
00358
00359 KUrlCompletion *q;
00360 QList<KUrl*> list_urls;
00361
00362 bool onlyLocalProto;
00363
00364
00365 bool url_auto_completion;
00366
00367
00368
00369 bool popup_append_slash;
00370
00371
00372 QString last_path_listed;
00373 QString last_file_listed;
00374 QString last_prepend;
00375 int last_compl_type;
00376 int last_no_hidden;
00377
00378 QString cwd;
00379
00380 KUrlCompletion::Mode mode;
00381 bool replace_env;
00382 bool replace_home;
00383 bool complete_url;
00384
00385 KIO::ListJob *list_job;
00386
00387 QString prepend;
00388 QString compl_text;
00389
00390
00391 bool list_urls_only_exe;
00392 bool list_urls_no_hidden;
00393 QString list_urls_filter;
00394
00395 CompletionThread *userListThread;
00396 CompletionThread *dirListThread;
00397 };
00398
00399 KUrlCompletionPrivate::~KUrlCompletionPrivate()
00400 {
00401 if ( userListThread )
00402 userListThread->requestTermination();
00403 if ( dirListThread )
00404 dirListThread->requestTermination();
00405 }
00406
00409
00410
00411
00412 class KUrlCompletionPrivate::MyURL
00413 {
00414 public:
00415 MyURL(const QString &url, const QString &cwd);
00416 MyURL(const MyURL &url);
00417 ~MyURL();
00418
00419 KUrl *kurl() const { return m_kurl; }
00420
00421 QString protocol() const { return m_kurl->protocol(); }
00422
00423 QString dir() const { return m_kurl->directory(KUrl::AppendTrailingSlash|KUrl::ObeyTrailingSlash); }
00424 QString file() const { return m_kurl->fileName(KUrl::ObeyTrailingSlash); }
00425
00426
00427 QString url() const { return m_url; }
00428
00429
00430 bool isURL() const { return m_isURL; }
00431
00432 void filter( bool replace_user_dir, bool replace_env );
00433
00434 private:
00435 void init(const QString &url, const QString &cwd);
00436
00437 KUrl *m_kurl;
00438 QString m_url;
00439 bool m_isURL;
00440 };
00441
00442 KUrlCompletionPrivate::MyURL::MyURL(const QString &_url, const QString &cwd)
00443 {
00444 init(_url, cwd);
00445 }
00446
00447 KUrlCompletionPrivate::MyURL::MyURL(const MyURL &_url)
00448 {
00449 m_kurl = new KUrl( *(_url.m_kurl) );
00450 m_url = _url.m_url;
00451 m_isURL = _url.m_isURL;
00452 }
00453
00454 void KUrlCompletionPrivate::MyURL::init(const QString &_url, const QString &cwd)
00455 {
00456
00457 m_url = _url;
00458
00459
00460 QString url_copy = _url;
00461
00462
00463 if ( url_copy.startsWith( QLatin1Char('#') ) ) {
00464 if ( url_copy.length() > 1 && url_copy.at(1) == QLatin1Char('#') )
00465 url_copy.replace( 0, 2, QLatin1String("info:") );
00466 else
00467 url_copy.replace( 0, 1, QLatin1String("man:") );
00468 }
00469
00470
00471 QRegExp protocol_regex = QRegExp( "^(?![A-Za-z]:)[^/\\s\\\\]*:" );
00472
00473
00474
00475 if ( protocol_regex.indexIn( url_copy ) == 0 )
00476 {
00477 m_kurl = new KUrl( url_copy );
00478 m_isURL = true;
00479 }
00480 else
00481 {
00482 m_isURL = false;
00483 if ( cwd.isEmpty() )
00484 {
00485 m_kurl = new KUrl;
00486 if ( !QDir::isRelativePath(url_copy) ||
00487 url_copy.at(0) == QLatin1Char('$') ||
00488 url_copy.at(0) == QLatin1Char('~') )
00489 m_kurl->setPath( url_copy );
00490 else
00491 *m_kurl = url_copy;
00492 }
00493 else
00494 {
00495 KUrl base = cwd;
00496 base.adjustPath(KUrl::AddTrailingSlash);
00497
00498 if ( !QDir::isRelativePath(url_copy) ||
00499 url_copy.at(0) == QLatin1Char('~') ||
00500 url_copy.at(0) == QLatin1Char('$') )
00501 {
00502 m_kurl = new KUrl;
00503 m_kurl->setPath( url_copy );
00504 }
00505 else
00506 {
00507
00508 m_kurl = new KUrl( base );
00509 m_kurl->addPath( url_copy );
00510 }
00511 }
00512 }
00513 }
00514
00515 KUrlCompletionPrivate::MyURL::~MyURL()
00516 {
00517 delete m_kurl;
00518 }
00519
00520 void KUrlCompletionPrivate::MyURL::filter( bool replace_user_dir, bool replace_env )
00521 {
00522 QString d = dir() + file();
00523 if ( replace_user_dir ) expandTilde( d );
00524 if ( replace_env ) expandEnv( d );
00525 m_kurl->setPath( d );
00526 }
00527
00530
00531
00532
00533 KUrlCompletion::KUrlCompletion() : KCompletion(),d(new KUrlCompletionPrivate(this))
00534 {
00535 d->init();
00536 }
00537
00538
00539 KUrlCompletion::KUrlCompletion( Mode _mode ) : KCompletion(),d(new KUrlCompletionPrivate(this))
00540 {
00541 d->init();
00542 setMode ( _mode );
00543 }
00544
00545 KUrlCompletion::~KUrlCompletion()
00546 {
00547 stop();
00548 delete d;
00549 }
00550
00551
00552 void KUrlCompletionPrivate::init()
00553 {
00554
00555 cwd = QDir::homePath();
00556
00557 replace_home = true;
00558 replace_env = true;
00559 last_no_hidden = false;
00560 last_compl_type = 0;
00561 list_job = 0L;
00562 mode = KUrlCompletion::FileCompletion;
00563
00564
00565 KConfigGroup cg( KGlobal::config(), "URLCompletion" );
00566
00567 url_auto_completion = cg.readEntry("alwaysAutoComplete", true);
00568 popup_append_slash = cg.readEntry("popupAppendSlash", true);
00569 onlyLocalProto = cg.readEntry("LocalProtocolsOnly", false);
00570 }
00571
00572 void KUrlCompletion::setDir(const QString &_dir)
00573 {
00574 d->cwd = _dir;
00575 }
00576
00577 QString KUrlCompletion::dir() const
00578 {
00579 return d->cwd;
00580 }
00581
00582 KUrlCompletion::Mode KUrlCompletion::mode() const
00583 {
00584 return d->mode;
00585 }
00586
00587 void KUrlCompletion::setMode( Mode _mode )
00588 {
00589 d->mode = _mode;
00590 }
00591
00592 bool KUrlCompletion::replaceEnv() const
00593 {
00594 return d->replace_env;
00595 }
00596
00597 void KUrlCompletion::setReplaceEnv( bool replace )
00598 {
00599 d->replace_env = replace;
00600 }
00601
00602 bool KUrlCompletion::replaceHome() const
00603 {
00604 return d->replace_home;
00605 }
00606
00607 void KUrlCompletion::setReplaceHome( bool replace )
00608 {
00609 d->replace_home = replace;
00610 }
00611
00612
00613
00614
00615
00616
00617 QString KUrlCompletion::makeCompletion(const QString &text)
00618 {
00619
00620
00621 KUrlCompletionPrivate::MyURL url(text, d->cwd);
00622
00623 d->compl_text = text;
00624
00625
00626
00627 int toRemove = url.file().length() - url.kurl()->query().length();
00628 if ( url.kurl()->hasRef() )
00629 toRemove += url.kurl()->ref().length() + 1;
00630 d->prepend = text.left( text.length() - toRemove );
00631 d->complete_url = url.isURL();
00632
00633 QString aMatch;
00634
00635
00636
00637 if ( d->replace_env && d->envCompletion( url, &aMatch ) )
00638 return aMatch;
00639
00640
00641
00642 if ( d->replace_home && d->userCompletion( url, &aMatch ) )
00643 return aMatch;
00644
00645
00646 url.filter( d->replace_home, d->replace_env );
00647
00648
00649
00650
00651
00652
00653 if ( d->mode == ExeCompletion ) {
00654
00655
00656 if ( d->exeCompletion( url, &aMatch ) )
00657 return aMatch;
00658
00659
00660
00661
00662 if ( d->urlCompletion( url, &aMatch ) )
00663 return aMatch;
00664 }
00665 else {
00666
00667
00668 if ( d->fileCompletion( url, &aMatch ) )
00669 return aMatch;
00670
00671
00672
00673 if ( d->urlCompletion( url, &aMatch ) )
00674 return aMatch;
00675 }
00676
00677 d->setListedUrl( CTNone );
00678 stop();
00679
00680 return QString();
00681 }
00682
00683
00684
00685
00686
00687
00688
00689 QString KUrlCompletionPrivate::finished()
00690 {
00691 if ( last_compl_type == CTInfo )
00692 return q->KCompletion::makeCompletion( compl_text.toLower() );
00693 else
00694 return q->KCompletion::makeCompletion( compl_text );
00695 }
00696
00697
00698
00699
00700
00701
00702
00703 bool KUrlCompletion::isRunning() const
00704 {
00705 return d->list_job || (d->dirListThread && !d->dirListThread->isFinished());
00706 }
00707
00708
00709
00710
00711
00712
00713 void KUrlCompletion::stop()
00714 {
00715 if ( d->list_job ) {
00716 d->list_job->kill();
00717 d->list_job = 0L;
00718 }
00719
00720 while ( !d->list_urls.isEmpty() ) {
00721 delete d->list_urls.takeFirst();
00722 }
00723
00724 if ( d->dirListThread ) {
00725 d->dirListThread->requestTermination();
00726 d->dirListThread = 0;
00727 }
00728 }
00729
00730
00731
00732
00733 void KUrlCompletionPrivate::setListedUrl( int complType,
00734 const QString& directory,
00735 const QString& filter,
00736 bool no_hidden )
00737 {
00738 last_compl_type = complType;
00739 last_path_listed = directory;
00740 last_file_listed = filter;
00741 last_no_hidden = (int)no_hidden;
00742 last_prepend = prepend;
00743 }
00744
00745 bool KUrlCompletionPrivate::isListedUrl( int complType,
00746 const QString& directory,
00747 const QString& filter,
00748 bool no_hidden )
00749 {
00750 return last_compl_type == complType
00751 && ( last_path_listed == directory
00752 || (directory.isEmpty() && last_path_listed.isEmpty()) )
00753 && ( filter.startsWith(last_file_listed)
00754 || (filter.isEmpty() && last_file_listed.isEmpty()) )
00755 && last_no_hidden == (int)no_hidden
00756 && last_prepend == prepend;
00757 }
00758
00759
00760
00761
00762
00763
00764 bool KUrlCompletionPrivate::isAutoCompletion()
00765 {
00766 return q->completionMode() == KGlobalSettings::CompletionAuto
00767 || q->completionMode() == KGlobalSettings::CompletionPopup
00768 || q->completionMode() == KGlobalSettings::CompletionMan
00769 || q->completionMode() == KGlobalSettings::CompletionPopupAuto;
00770 }
00773
00774
00775
00776 bool KUrlCompletionPrivate::userCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
00777 {
00778 if ( url.protocol() != QLatin1String("file")
00779 || !url.dir().isEmpty()
00780 || !url.file().startsWith( QLatin1Char('~') ) )
00781 return false;
00782
00783 if ( !isListedUrl( CTUser ) ) {
00784 q->stop();
00785 q->clear();
00786
00787 if ( !userListThread ) {
00788 userListThread = new UserListThread( q );
00789 userListThread->start();
00790
00791
00792
00793
00794 userListThread->wait( 200 );
00795 QStringList l = userListThread->matches();
00796 addMatches( l );
00797 }
00798 }
00799 *pMatch = finished();
00800 return true;
00801 }
00802
00805
00806
00807
00808 #ifndef Q_OS_WIN
00809 extern char **environ;
00810 #endif
00811
00812 bool KUrlCompletionPrivate::envCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
00813 {
00814 if ( url.file().isEmpty() || url.file().at(0) != QLatin1Char('$') )
00815 return false;
00816
00817 if ( !isListedUrl( CTEnv ) ) {
00818 q->stop();
00819 q->clear();
00820
00821 char **env = environ;
00822
00823 QString dollar = QLatin1String("$");
00824
00825 QStringList l;
00826
00827 while ( *env ) {
00828 QString s = QString::fromLocal8Bit( *env );
00829
00830 int pos = s.indexOf(QLatin1Char('='));
00831
00832 if ( pos == -1 )
00833 pos = s.length();
00834
00835 if ( pos > 0 )
00836 l.append( dollar + s.left(pos) );
00837
00838 env++;
00839 }
00840
00841 addMatches( l );
00842 }
00843
00844 setListedUrl( CTEnv );
00845
00846 *pMatch = finished();
00847 return true;
00848 }
00849
00852
00853
00854
00855 bool KUrlCompletionPrivate::exeCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
00856 {
00857 if ( url.protocol() != QLatin1String("file") )
00858 return false;
00859
00860 QString directory = unescape( url.dir() );
00861
00862
00863
00864
00865
00866
00867
00868
00869 QStringList dirList;
00870
00871 if ( !QDir::isRelativePath(directory) ) {
00872
00873 dirList.append( directory );
00874 }
00875 else if ( !directory.isEmpty() && !cwd.isEmpty() ) {
00876
00877 dirList.append( cwd + QLatin1Char('/') + directory );
00878 }
00879 else if ( !url.file().isEmpty() ) {
00880
00881 dirList = QString::fromLocal8Bit(qgetenv("PATH")).split(
00882 KPATH_SEPARATOR,QString::SkipEmptyParts);
00883
00884 QStringList::Iterator it = dirList.begin();
00885
00886 for ( ; it != dirList.end(); ++it )
00887 it->append(QLatin1Char('/'));
00888 }
00889
00890
00891 bool no_hidden_files = url.file().isEmpty() || url.file().at(0) != QLatin1Char('.');
00892
00893
00894
00895 if ( !isListedUrl( CTExe, directory, url.file(), no_hidden_files ) )
00896 {
00897 q->stop();
00898 q->clear();
00899
00900 setListedUrl( CTExe, directory, url.file(), no_hidden_files );
00901
00902 *pMatch = listDirectories( dirList, url.file(), true, false, no_hidden_files );
00903 }
00904 else if ( !q->isRunning() ) {
00905 *pMatch = finished();
00906 }
00907 else {
00908 if ( dirListThread )
00909 setListedUrl( CTExe, directory, url.file(), no_hidden_files );
00910 *pMatch = QString();
00911 }
00912
00913 return true;
00914 }
00915
00918
00919
00920
00921 bool KUrlCompletionPrivate::fileCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
00922 {
00923 if ( url.protocol() != QLatin1String("file") )
00924 return false;
00925
00926 QString directory = unescape( url.dir() );
00927
00928 if (url.url().length() && url.url().at(0) == QLatin1Char('.'))
00929 {
00930 if (url.url().length() == 1)
00931 {
00932 *pMatch = ( q->completionMode() == KGlobalSettings::CompletionMan )?
00933 QLatin1String(".") :
00934 QLatin1String("..");
00935 return true;
00936 }
00937 else if (url.url().length() == 2 && url.url().at(1) == QLatin1Char('.'))
00938 {
00939 *pMatch = QLatin1String("..");
00940 return true;
00941 }
00942 }
00943
00944
00945
00946
00947
00948
00949
00950
00951
00952 QStringList dirList;
00953
00954 if ( !QDir::isRelativePath(directory) ) {
00955
00956 dirList.append( directory );
00957 }
00958 else if ( !cwd.isEmpty() ) {
00959
00960 QString dirToAdd = cwd;
00961 if ( !directory.isEmpty() ) {
00962 if ( !cwd.endsWith('/') )
00963 dirToAdd.append( QLatin1Char('/') );
00964 dirToAdd.append( directory );
00965 }
00966 dirList.append( dirToAdd );
00967 }
00968
00969
00970 bool no_hidden_files = !url.file().startsWith( QLatin1Char('.') );
00971
00972
00973
00974 if ( !isListedUrl( CTFile, directory, QString(), no_hidden_files ) )
00975 {
00976 q->stop();
00977 q->clear();
00978
00979 setListedUrl( CTFile, directory, QString(), no_hidden_files );
00980
00981
00982 bool append_slash = ( popup_append_slash
00983 && (q->completionMode() == KGlobalSettings::CompletionPopup ||
00984 q->completionMode() == KGlobalSettings::CompletionPopupAuto ) );
00985
00986 bool only_dir = ( mode == KUrlCompletion::DirCompletion );
00987
00988 *pMatch = listDirectories( dirList, QString(), false, only_dir, no_hidden_files,
00989 append_slash );
00990 }
00991 else if ( !q->isRunning() ) {
00992 *pMatch = finished();
00993 }
00994 else {
00995 *pMatch = QString();
00996 }
00997
00998 return true;
00999 }
01000
01003
01004
01005
01006 bool KUrlCompletionPrivate::urlCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
01007 {
01008
01009 if (onlyLocalProto && KProtocolInfo::protocolClass(url.protocol()) != QLatin1String(":local"))
01010 return false;
01011
01012
01013 KUrl url_cwd( cwd );
01014
01015
01016 KUrl url_dir( url_cwd, url.kurl()->url() );
01017
01018
01019
01020
01021
01022
01023
01024 bool man_or_info = ( url_dir.protocol() == QLatin1String("man")
01025 || url_dir.protocol() == QLatin1String("info") );
01026
01027 if ( !url_dir.isValid()
01028 || !KProtocolManager::supportsListing( url_dir )
01029 || ( !man_or_info
01030 && ( url_dir.directory(KUrl::AppendTrailingSlash|KUrl::ObeyTrailingSlash).isEmpty()
01031 || ( isAutoCompletion()
01032 && !url_auto_completion ) ) ) ) {
01033 return false;
01034 }
01035
01036 url_dir.setFileName(QString());
01037
01038
01039 QString directory = unescape( url_dir.directory(KUrl::AppendTrailingSlash|KUrl::ObeyTrailingSlash) );
01040
01041 url_dir.setPath( directory );
01042
01043
01044
01045 if ( !isListedUrl( CTUrl, url_dir.prettyUrl(), url.file() ) )
01046 {
01047 q->stop();
01048 q->clear();
01049
01050 setListedUrl( CTUrl, url_dir.prettyUrl(), QString() );
01051
01052 QList<KUrl*> url_list;
01053 url_list.append( new KUrl( url_dir ) );
01054
01055 listUrls( url_list, QString(), false );
01056
01057 *pMatch = QString();
01058 }
01059 else if ( !q->isRunning() ) {
01060 *pMatch = finished();
01061 }
01062 else {
01063 *pMatch = QString();
01064 }
01065
01066 return true;
01067 }
01068
01071
01072
01073
01074
01075
01076
01077
01078
01079 void KUrlCompletionPrivate::addMatches( const QStringList &matchList )
01080 {
01081 QStringList::ConstIterator it = matchList.begin();
01082 QStringList::ConstIterator end = matchList.end();
01083
01084 if ( complete_url )
01085 for ( ; it != end; ++it )
01086 q->addItem( prepend + QUrl::toPercentEncoding(*it) );
01087 else
01088 for ( ; it != end; ++it )
01089 q->addItem( prepend + (*it));
01090 }
01091
01092
01093
01094
01095
01096
01097
01098
01099
01100
01101
01102
01103
01104 QString KUrlCompletionPrivate::listDirectories(
01105 const QStringList &dirList,
01106 const QString &filter,
01107 bool only_exe,
01108 bool only_dir,
01109 bool no_hidden,
01110 bool append_slash_to_dir)
01111 {
01112 assert( !q->isRunning() );
01113
01114 if ( qgetenv("KURLCOMPLETION_LOCAL_KIO").isEmpty() ) {
01115
01116
01117
01118
01119
01120 if ( dirListThread )
01121 dirListThread->requestTermination();
01122
01123 QStringList dirs;
01124
01125 for ( QStringList::ConstIterator it = dirList.begin();
01126 it != dirList.end();
01127 ++it )
01128 {
01129 KUrl url;
01130 url.setPath(*it);
01131 if ( KAuthorized::authorizeUrlAction( QLatin1String("list"), KUrl(), url ) )
01132 dirs.append( *it );
01133 }
01134
01135 dirListThread = new DirectoryListThread( q, dirs, filter, only_exe, only_dir,
01136 no_hidden, append_slash_to_dir );
01137 dirListThread->start();
01138 dirListThread->wait( 200 );
01139 addMatches( dirListThread->matches() );
01140
01141 return finished();
01142 }
01143
01144
01145
01146
01147 QList<KUrl*> url_list;
01148
01149 QStringList::ConstIterator it = dirList.begin();
01150
01151 for ( ; it != dirList.end(); ++it ) {
01152 url_list.append( new KUrl( *it ) );
01153 }
01154
01155 listUrls( url_list, filter, only_exe, no_hidden );
01156
01157
01158 return QString();
01159 }
01160
01161
01162
01163
01164
01165
01166
01167
01168
01169 void KUrlCompletionPrivate::listUrls(
01170 const QList<KUrl *> &urls,
01171 const QString &filter,
01172 bool only_exe,
01173 bool no_hidden )
01174 {
01175 assert( list_urls.isEmpty() );
01176 assert( list_job == 0L );
01177
01178 list_urls = urls;
01179 list_urls_filter = filter;
01180 list_urls_only_exe = only_exe;
01181 list_urls_no_hidden = no_hidden;
01182
01183
01184
01185
01186
01187
01188
01189
01190 _k_slotIOFinished(0);
01191 }
01192
01193
01194
01195
01196
01197
01198 void KUrlCompletionPrivate::_k_slotEntries(KIO::Job*, const KIO::UDSEntryList& entries)
01199 {
01200 QStringList matchList;
01201
01202 KIO::UDSEntryList::ConstIterator it = entries.begin();
01203 const KIO::UDSEntryList::ConstIterator end = entries.end();
01204
01205 QString filter = list_urls_filter;
01206
01207 int filter_len = filter.length();
01208
01209
01210
01211 for (; it != end; ++it) {
01212 const KIO::UDSEntry& entry = *it;
01213 const QString url = entry.stringValue( KIO::UDSEntry::UDS_URL );
01214
01215 QString entry_name;
01216 if (!url.isEmpty()) {
01217
01218 entry_name = KUrl(url).fileName();
01219 } else {
01220 entry_name = entry.stringValue( KIO::UDSEntry::UDS_NAME );
01221 }
01222
01223
01224
01225 if ( (!entry_name.isEmpty() && entry_name.at(0) == QLatin1Char('.')) &&
01226 ( list_urls_no_hidden ||
01227 entry_name.length() == 1 ||
01228 ( entry_name.length() == 2 && entry_name.at(1) == QLatin1Char('.') ) ) )
01229 continue;
01230
01231 const bool isDir = entry.isDir();
01232
01233 if ( mode == KUrlCompletion::DirCompletion && !isDir )
01234 continue;
01235
01236 if ( filter_len == 0 || entry_name.left(filter_len) == filter ) {
01237 if ( isDir )
01238 entry_name.append( QLatin1Char( '/' ) );
01239
01240 const bool isExe = entry.numberValue( KIO::UDSEntry::UDS_ACCESS ) & MODE_EXE;
01241 if ( isExe || !list_urls_only_exe )
01242 matchList.append( entry_name );
01243 }
01244 }
01245
01246 addMatches( matchList );
01247 }
01248
01249
01250
01251
01252
01253
01254
01255
01256
01257 void KUrlCompletionPrivate::_k_slotIOFinished( KJob * job )
01258 {
01259 assert( job == list_job );
01260
01261 if ( list_urls.isEmpty() ) {
01262
01263 list_job = 0L;
01264
01265 finished();
01266
01267 }
01268 else {
01269
01270 KUrl *kurl = list_urls.takeFirst();
01271
01272
01273
01274
01275
01276 list_job = KIO::listDir( *kurl, KIO::HideProgressInfo );
01277 list_job->addMetaData("no-auth-prompt", "true");
01278
01279 assert( list_job );
01280
01281 q->connect( list_job,
01282 SIGNAL(result(KJob*)),
01283 SLOT(_k_slotIOFinished(KJob*)) );
01284
01285 q->connect( list_job,
01286 SIGNAL( entries( KIO::Job*, const KIO::UDSEntryList&)),
01287 SLOT( _k_slotEntries( KIO::Job*, const KIO::UDSEntryList&)) );
01288
01289 delete kurl;
01290 }
01291 }
01292
01295
01296
01297
01298
01299
01300
01301
01302
01303
01304 void KUrlCompletion::postProcessMatch( QString *pMatch ) const
01305 {
01306
01307
01308 if ( !pMatch->isEmpty() ) {
01309
01310
01311
01312 if ( d->last_compl_type == CTFile
01313 && pMatch->at( pMatch->length()-1 ) != QLatin1Char('/') )
01314 {
01315 QString copy;
01316
01317 if ( pMatch->startsWith( QLatin1String("file:") ) )
01318 copy = KUrl(*pMatch).path();
01319 else
01320 copy = *pMatch;
01321
01322 expandTilde( copy );
01323 expandEnv( copy );
01324 if ( QDir::isRelativePath(copy) )
01325 copy.prepend( d->cwd + QLatin1Char('/') );
01326
01327
01328
01329 KDE_struct_stat sbuff;
01330
01331 QByteArray file = QFile::encodeName( copy );
01332
01333 if ( KDE_stat( file.data(), &sbuff ) == 0 ) {
01334 if ( S_ISDIR ( sbuff.st_mode ) )
01335 pMatch->append( QLatin1Char( '/' ) );
01336 }
01337 else {
01338 kDebug() << "Could not stat file" << copy;
01339 }
01340 }
01341 }
01342 }
01343
01344 void KUrlCompletion::postProcessMatches( QStringList * ) const
01345 {
01346
01347
01348
01349 }
01350
01351 void KUrlCompletion::postProcessMatches( KCompletionMatches * ) const
01352 {
01353
01354
01355
01356 }
01357
01358 void KUrlCompletion::customEvent(QEvent *e)
01359 {
01360 if ( e->type() == CompletionMatchEvent::uniqueType() ) {
01361
01362 CompletionMatchEvent *matchEvent = static_cast<CompletionMatchEvent *>( e );
01363
01364 matchEvent->completionThread()->wait();
01365
01366 if ( !d->isListedUrl( CTUser ) ) {
01367 stop();
01368 clear();
01369 d->addMatches( matchEvent->completionThread()->matches() );
01370 }
01371
01372 d->setListedUrl( CTUser );
01373
01374 if ( d->userListThread == matchEvent->completionThread() )
01375 d->userListThread = 0;
01376
01377 if ( d->dirListThread == matchEvent->completionThread() )
01378 d->dirListThread = 0;
01379
01380 delete matchEvent->completionThread();
01381 }
01382 }
01383
01384
01385 QString KUrlCompletion::replacedPath( const QString& text, bool replaceHome, bool replaceEnv )
01386 {
01387 if ( text.isEmpty() )
01388 return text;
01389
01390 KUrlCompletionPrivate::MyURL url( text, QString() );
01391 if ( !url.kurl()->isLocalFile() )
01392 return text;
01393
01394 url.filter( replaceHome, replaceEnv );
01395 return url.dir() + url.file();
01396 }
01397
01398
01399 QString KUrlCompletion::replacedPath( const QString& text ) const
01400 {
01401 return replacedPath( text, d->replace_home, d->replace_env );
01402 }
01403
01406
01407
01408
01409
01410
01411
01412
01413
01414 static bool expandEnv( QString &text )
01415 {
01416
01417
01418 int pos = 0;
01419
01420 bool expanded = false;
01421
01422 while ( (pos = text.indexOf(QLatin1Char('$'), pos)) != -1 ) {
01423
01424
01425
01426 if ( pos > 0 && text.at(pos-1) == QLatin1Char('\\') ) {
01427 pos++;
01428 }
01429
01430
01431 else {
01432
01433
01434 int pos2 = text.indexOf( QLatin1Char(' '), pos+1 );
01435 int pos_tmp = text.indexOf( QLatin1Char('/'), pos+1 );
01436
01437 if ( pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2) )
01438 pos2 = pos_tmp;
01439
01440 if ( pos2 == -1 )
01441 pos2 = text.length();
01442
01443
01444
01445
01446 if ( pos2 >= 0 ) {
01447 int len = pos2 - pos;
01448 QString key = text.mid( pos+1, len-1);
01449 QString value =
01450 QString::fromLocal8Bit( qgetenv(key.toLocal8Bit()) );
01451
01452 if ( !value.isEmpty() ) {
01453 expanded = true;
01454 text.replace( pos, len, value );
01455 pos = pos + value.length();
01456 }
01457 else {
01458 pos = pos2;
01459 }
01460 }
01461 }
01462 }
01463
01464 return expanded;
01465 }
01466
01467
01468
01469
01470
01471
01472
01473 static bool expandTilde(QString &text)
01474 {
01475 if ( text.isEmpty() || ( text.at(0) != QLatin1Char('~') ))
01476 return false;
01477
01478 bool expanded = false;
01479
01480
01481
01482 int pos2 = text.indexOf( QLatin1Char(' '), 1 );
01483 int pos_tmp = text.indexOf( QLatin1Char('/'), 1 );
01484
01485 if ( pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2) )
01486 pos2 = pos_tmp;
01487
01488 if ( pos2 == -1 )
01489 pos2 = text.length();
01490
01491
01492
01493 if ( pos2 >= 0 ) {
01494
01495 QString user = text.mid( 1, pos2-1 );
01496 QString dir;
01497
01498
01499
01500 if ( user.isEmpty() ) {
01501 dir = QDir::homePath();
01502 }
01503
01504
01505 else {
01506 struct passwd *pw = ::getpwnam( user.toLocal8Bit() );
01507
01508 if ( pw )
01509 dir = QFile::decodeName( pw->pw_dir );
01510
01511 ::endpwent();
01512 }
01513
01514 if ( !dir.isEmpty() ) {
01515 expanded = true;
01516 text.replace(0, pos2, dir);
01517 }
01518 }
01519
01520 return expanded;
01521 }
01522
01523
01524
01525
01526
01527
01528
01529 static QString unescape(const QString &text)
01530 {
01531 QString result;
01532
01533 for (int pos = 0; pos < text.length(); pos++)
01534 if ( text.at(pos) != QLatin1Char('\\') )
01535 result.insert( result.length(), text.at(pos) );
01536
01537 return result;
01538 }
01539
01540 #include "kurlcompletion.moc"