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