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