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