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