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