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