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