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