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