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