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