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 <tqstring.h>
00032 #include <tqstringlist.h>
00033 #include <tqvaluelist.h>
00034 #include <tqregexp.h>
00035 #include <tqtimer.h>
00036 #include <tqdir.h>
00037 #include <tqfile.h>
00038 #include <tqtextstream.h>
00039 #include <tqdeepcopy.h>
00040 #include <tqthread.h>
00041
00042 #include <tdeapplication.h>
00043 #include <kdebug.h>
00044 #include <kcompletion.h>
00045 #include <kurl.h>
00046 #include <tdeio/jobclasses.h>
00047 #include <tdeio/job.h>
00048 #include <kprotocolinfo.h>
00049 #include <tdeconfig.h>
00050 #include <tdeglobal.h>
00051 #include <tdelocale.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(TQString &);
00065 static bool expandEnv(TQString &);
00066
00067 static TQString unescape(const TQString &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 TQCustomEvent
00084 {
00085 public:
00086 CompletionMatchEvent( CompletionThread *thread ) :
00087 TQCustomEvent( 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 TQThread
00099 {
00100 protected:
00101 CompletionThread( KURLCompletion *receiver ) :
00102 TQThread(),
00103 m_receiver( receiver ),
00104 m_terminationRequested( false )
00105 {}
00106
00107 public:
00108 void requestTermination() { m_terminationRequested = true; }
00109 TQDeepCopy<TQStringList> matches() const { return m_matches; }
00110
00111 protected:
00112 void addMatch( const TQString &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 TQStringList 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 TQChar tilde = '~';
00144
00145 struct passwd *pw;
00146 while ( ( pw = ::getpwent() ) && !terminationRequested() )
00147 addMatch( tilde + TQString::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 TQStringList &dirList,
00162 const TQString &filter,
00163 bool onlyExe,
00164 bool onlyDir,
00165 bool noHidden,
00166 bool appendSlashToDir ) :
00167 CompletionThread( receiver ),
00168 m_dirList( TQDeepCopy<TQStringList>( dirList ) ),
00169 m_filter( TQDeepCopy<TQString>( 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 TQStringList m_dirList;
00180 TQString 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 ( TQStringList::ConstIterator it = m_dirList.begin();
00204 it != m_dirList.end() && !terminationRequested();
00205 ++it )
00206 {
00207
00208
00209 if ( !dir ) {
00210 dir = ::opendir( TQFile::encodeName( *it ) );
00211 if ( ! dir ) {
00212 kdDebug() << "Failed to open dir: " << *it << endl;
00213 done();
00214 return;
00215 }
00216 }
00217
00218
00219
00220
00221
00222 TQString path = TQDir::currentDirPath();
00223 TQDir::setCurrent( *it );
00224
00225
00226
00227
00228
00229 #ifndef HAVE_READDIR_R
00230 struct dirent *dirEntry = 0;
00231 while ( !terminationRequested() &&
00232 (dirEntry = ::readdir( dir)))
00233 #else
00234 #if !defined(MAXPATHLEN) && defined(__GNU__)
00235 #define MAXPATHLEN UCHAR_MAX
00236 #endif
00237 struct dirent *dirPosition = (struct dirent *) malloc( sizeof( struct dirent ) + MAXPATHLEN + 1 );
00238 struct dirent *dirEntry = 0;
00239 while ( !terminationRequested() &&
00240 ::readdir_r( dir, dirPosition, &dirEntry ) == 0 && dirEntry )
00241 #endif
00242
00243 {
00244
00245
00246 if ( dirEntry->d_name[0] == '.' && m_noHidden )
00247 continue;
00248
00249
00250
00251 if ( dirEntry->d_name[0] == '.' && dirEntry->d_name[1] == '\0' )
00252 continue;
00253
00254
00255
00256 if ( dirEntry->d_name[0] == '.' && dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0' )
00257 continue;
00258
00259 TQString file = TQFile::decodeName( dirEntry->d_name );
00260
00261 if ( m_filter.isEmpty() || file.startsWith( m_filter ) ) {
00262
00263 if ( m_onlyExe || m_onlyDir || m_appendSlashToDir ) {
00264 KDE_struct_stat sbuff;
00265
00266 if ( KDE_stat( dirEntry->d_name, &sbuff ) == 0 ) {
00267
00268
00269
00270 if ( m_onlyExe && ( sbuff.st_mode & MODE_EXE ) == 0 )
00271 continue;
00272
00273
00274
00275 if ( m_onlyDir && !S_ISDIR( sbuff.st_mode ) )
00276 continue;
00277
00278
00279
00280 if ( m_appendSlashToDir && S_ISDIR( sbuff.st_mode ) )
00281 file.append( '/' );
00282
00283 }
00284 else {
00285 kdDebug() << "Could not stat file " << file << endl;
00286 continue;
00287 }
00288 }
00289
00290 addMatch( file );
00291 }
00292 }
00293
00294
00295
00296 TQDir::setCurrent( path );
00297
00298 ::closedir( dir );
00299 dir = 0;
00300 #ifdef HAVE_READDIR_R
00301 free( dirPosition );
00302 #endif
00303 }
00304
00305 done();
00306 }
00307
00310
00311
00312
00313 class KURLCompletion::MyURL
00314 {
00315 public:
00316 MyURL(const TQString &url, const TQString &cwd);
00317 MyURL(const MyURL &url);
00318 ~MyURL();
00319
00320 KURL *kurl() const { return m_kurl; }
00321
00322 TQString protocol() const { return m_kurl->protocol(); }
00323
00324 TQString dir() const { return m_kurl->directory(false, false); }
00325 TQString file() const { return m_kurl->fileName(false); }
00326
00327
00328 TQString url() const { return m_url; }
00329
00330
00331 bool isURL() const { return m_isURL; }
00332
00333 void filter( bool replace_user_dir, bool replace_env );
00334
00335 private:
00336 void init(const TQString &url, const TQString &cwd);
00337
00338 KURL *m_kurl;
00339 TQString m_url;
00340 bool m_isURL;
00341 };
00342
00343 KURLCompletion::MyURL::MyURL(const TQString &url, const TQString &cwd)
00344 {
00345 init(url, cwd);
00346 }
00347
00348 KURLCompletion::MyURL::MyURL(const MyURL &url)
00349 {
00350 m_kurl = new KURL( *(url.m_kurl) );
00351 m_url = url.m_url;
00352 m_isURL = url.m_isURL;
00353 }
00354
00355 void KURLCompletion::MyURL::init(const TQString &url, const TQString &cwd)
00356 {
00357
00358 m_url = url;
00359
00360
00361 TQString url_copy = url;
00362
00363
00364 if ( url_copy[0] == '#' ) {
00365 if ( url_copy[1] == '#' )
00366 url_copy.replace( 0, 2, TQString("info:") );
00367 else
00368 url_copy.replace( 0, 1, TQString("man:") );
00369 }
00370
00371
00372 TQRegExp protocol_regex = TQRegExp( "^[^/\\s\\\\]*:" );
00373
00374
00375
00376 if ( protocol_regex.search( url_copy ) == 0 )
00377 {
00378 m_kurl = new KURL( url_copy );
00379 m_isURL = true;
00380 }
00381 else
00382 {
00383 m_isURL = false;
00384 if ( cwd.isEmpty() )
00385 {
00386 m_kurl = new KURL();
00387 if ( !TQDir::isRelativePath(url_copy) || url_copy[0] == '$' || url_copy[0] == '~' )
00388 m_kurl->setPath( url_copy );
00389 else
00390 *m_kurl = url_copy;
00391 }
00392 else
00393 {
00394 KURL base = KURL::fromPathOrURL( cwd );
00395 base.adjustPath(+1);
00396
00397 if ( !TQDir::isRelativePath(url_copy) || url_copy[0] == '~' || url_copy[0] == '$' )
00398 {
00399 m_kurl = new KURL();
00400 m_kurl->setPath( url_copy );
00401 }
00402 else
00403 {
00404
00405 m_kurl = new KURL( base );
00406 m_kurl->addPath( url_copy );
00407 }
00408 }
00409 }
00410 }
00411
00412 KURLCompletion::MyURL::~MyURL()
00413 {
00414 delete m_kurl;
00415 }
00416
00417 void KURLCompletion::MyURL::filter( bool replace_user_dir, bool replace_env )
00418 {
00419 TQString d = dir() + file();
00420 if ( replace_user_dir ) expandTilde( d );
00421 if ( replace_env ) expandEnv( d );
00422 m_kurl->setPath( d );
00423 }
00424
00427
00428
00429 class KURLCompletionPrivate
00430 {
00431 public:
00432 KURLCompletionPrivate() : url_auto_completion(true),
00433 userListThread(0),
00434 dirListThread(0) {}
00435 ~KURLCompletionPrivate();
00436
00437 TQValueList<KURL*> list_urls;
00438
00439 bool onlyLocalProto;
00440
00441
00442 bool url_auto_completion;
00443
00444
00445
00446 bool popup_append_slash;
00447
00448
00449 TQString last_path_listed;
00450 TQString last_file_listed;
00451 TQString last_prepend;
00452 int last_compl_type;
00453 int last_no_hidden;
00454
00455 TQString cwd;
00456
00457 KURLCompletion::Mode mode;
00458 bool replace_env;
00459 bool replace_home;
00460 bool complete_url;
00461
00462 TDEIO::ListJob *list_job;
00463
00464 TQString prepend;
00465 TQString compl_text;
00466
00467
00468 bool list_urls_only_exe;
00469 bool list_urls_no_hidden;
00470 TQString list_urls_filter;
00471
00472 CompletionThread *userListThread;
00473 CompletionThread *dirListThread;
00474 };
00475
00476 KURLCompletionPrivate::~KURLCompletionPrivate()
00477 {
00478 if ( userListThread )
00479 userListThread->requestTermination();
00480 if ( dirListThread )
00481 dirListThread->requestTermination();
00482 }
00483
00486
00487
00488
00489 KURLCompletion::KURLCompletion() : TDECompletion()
00490 {
00491 init();
00492 }
00493
00494
00495 KURLCompletion::KURLCompletion( Mode mode ) : TDECompletion()
00496 {
00497 init();
00498 setMode ( mode );
00499 }
00500
00501 KURLCompletion::~KURLCompletion()
00502 {
00503 stop();
00504 delete d;
00505 }
00506
00507
00508 void KURLCompletion::init()
00509 {
00510 d = new KURLCompletionPrivate;
00511
00512 d->cwd = TQDir::homeDirPath();
00513
00514 d->replace_home = true;
00515 d->replace_env = true;
00516 d->last_no_hidden = false;
00517 d->last_compl_type = 0;
00518 d->list_job = 0L;
00519 d->mode = KURLCompletion::FileCompletion;
00520
00521
00522 TDEConfig *c = TDEGlobal::config();
00523 TDEConfigGroupSaver cgs( c, "URLCompletion" );
00524
00525 d->url_auto_completion = c->readBoolEntry("alwaysAutoComplete", true);
00526 d->popup_append_slash = c->readBoolEntry("popupAppendSlash", true);
00527 d->onlyLocalProto = c->readBoolEntry("LocalProtocolsOnly", false);
00528 }
00529
00530 void KURLCompletion::setDir(const TQString &dir)
00531 {
00532 d->cwd = dir;
00533 }
00534
00535 TQString KURLCompletion::dir() const
00536 {
00537 return d->cwd;
00538 }
00539
00540 KURLCompletion::Mode KURLCompletion::mode() const
00541 {
00542 return d->mode;
00543 }
00544
00545 void KURLCompletion::setMode( Mode mode )
00546 {
00547 d->mode = mode;
00548 }
00549
00550 bool KURLCompletion::replaceEnv() const
00551 {
00552 return d->replace_env;
00553 }
00554
00555 void KURLCompletion::setReplaceEnv( bool replace )
00556 {
00557 d->replace_env = replace;
00558 }
00559
00560 bool KURLCompletion::replaceHome() const
00561 {
00562 return d->replace_home;
00563 }
00564
00565 void KURLCompletion::setReplaceHome( bool replace )
00566 {
00567 d->replace_home = replace;
00568 }
00569
00570
00571
00572
00573
00574
00575 TQString KURLCompletion::makeCompletion(const TQString &text)
00576 {
00577
00578
00579 MyURL url(text, d->cwd);
00580
00581 d->compl_text = text;
00582
00583
00584
00585 int toRemove = url.file().length() - url.kurl()->query().length();
00586 if ( url.kurl()->hasRef() )
00587 toRemove += url.kurl()->ref().length() + 1;
00588 d->prepend = text.left( text.length() - toRemove );
00589 d->complete_url = url.isURL();
00590
00591 TQString match;
00592
00593
00594
00595 if ( d->replace_env && envCompletion( url, &match ) )
00596 return match;
00597
00598
00599
00600 if ( d->replace_home && userCompletion( url, &match ) )
00601 return match;
00602
00603
00604 url.filter( d->replace_home, d->replace_env );
00605
00606
00607
00608
00609
00610
00611 if ( d->mode == ExeCompletion ) {
00612
00613
00614 if ( exeCompletion( url, &match ) )
00615 return match;
00616
00617
00618
00619
00620 if ( urlCompletion( url, &match ) )
00621 return match;
00622 }
00623 else if ( d->mode == SystemExeCompletion ) {
00624
00625
00626 if ( systemexeCompletion( url, &match ) )
00627 return match;
00628
00629
00630
00631
00632 if ( urlCompletion( url, &match ) )
00633 return match;
00634 }
00635 else {
00636
00637
00638 if ( fileCompletion( url, &match ) )
00639 return match;
00640
00641
00642
00643 if ( urlCompletion( url, &match ) )
00644 return match;
00645 }
00646
00647 setListedURL( CTNone );
00648 stop();
00649
00650 return TQString::null;
00651 }
00652
00653
00654
00655
00656
00657
00658
00659 TQString KURLCompletion::finished()
00660 {
00661 if ( d->last_compl_type == CTInfo )
00662 return TDECompletion::makeCompletion( d->compl_text.lower() );
00663 else
00664 return TDECompletion::makeCompletion( d->compl_text );
00665 }
00666
00667
00668
00669
00670
00671
00672
00673 bool KURLCompletion::isRunning() const
00674 {
00675 return d->list_job || (d->dirListThread && !d->dirListThread->finished());
00676 }
00677
00678
00679
00680
00681
00682
00683 void KURLCompletion::stop()
00684 {
00685 if ( d->list_job ) {
00686 d->list_job->kill();
00687 d->list_job = 0L;
00688 }
00689
00690 if ( !d->list_urls.isEmpty() ) {
00691 TQValueList<KURL*>::Iterator it = d->list_urls.begin();
00692 for ( ; it != d->list_urls.end(); it++ )
00693 delete (*it);
00694 d->list_urls.clear();
00695 }
00696
00697 if ( d->dirListThread ) {
00698 d->dirListThread->requestTermination();
00699 d->dirListThread = 0;
00700 }
00701 }
00702
00703
00704
00705
00706 void KURLCompletion::setListedURL( int complType,
00707 const TQString& dir,
00708 const TQString& filter,
00709 bool no_hidden )
00710 {
00711 d->last_compl_type = complType;
00712 d->last_path_listed = dir;
00713 d->last_file_listed = filter;
00714 d->last_no_hidden = (int)no_hidden;
00715 d->last_prepend = d->prepend;
00716 }
00717
00718 bool KURLCompletion::isListedURL( int complType,
00719 const TQString& dir,
00720 const TQString& filter,
00721 bool no_hidden )
00722 {
00723 return d->last_compl_type == complType
00724 && ( d->last_path_listed == dir
00725 || (dir.isEmpty() && d->last_path_listed.isEmpty()) )
00726 && ( filter.startsWith(d->last_file_listed)
00727 || (filter.isEmpty() && d->last_file_listed.isEmpty()) )
00728 && d->last_no_hidden == (int)no_hidden
00729 && d->last_prepend == d->prepend;
00730 }
00731
00732
00733
00734
00735
00736
00737 bool KURLCompletion::isAutoCompletion()
00738 {
00739 return completionMode() == TDEGlobalSettings::CompletionAuto
00740 || completionMode() == TDEGlobalSettings::CompletionPopup
00741 || completionMode() == TDEGlobalSettings::CompletionMan
00742 || completionMode() == TDEGlobalSettings::CompletionPopupAuto;
00743 }
00746
00747
00748
00749 bool KURLCompletion::userCompletion(const MyURL &url, TQString *match)
00750 {
00751 if ( url.protocol() != "file"
00752 || !url.dir().isEmpty()
00753 || url.file().at(0) != '~' )
00754 return false;
00755
00756 if ( !isListedURL( CTUser ) ) {
00757 stop();
00758 clear();
00759
00760 if ( !d->userListThread ) {
00761 d->userListThread = new UserListThread( this );
00762 d->userListThread->start();
00763
00764
00765
00766
00767 d->userListThread->wait( 200 );
00768 TQStringList l = d->userListThread->matches();
00769 addMatches( l );
00770 }
00771 }
00772 *match = finished();
00773 return true;
00774 }
00775
00778
00779
00780
00781 #if !defined(__OpenBSD__) && !defined(__FreeBSD__)
00782 extern char **environ;
00783 #endif
00784
00785 bool KURLCompletion::envCompletion(const MyURL &url, TQString *match)
00786 {
00787 #if defined(__OpenBSD__) || defined(__FreeBSD__)
00788 return false;
00789 #else
00790 if ( url.file().at(0) != '$' )
00791 return false;
00792
00793 if ( !isListedURL( CTEnv ) ) {
00794 stop();
00795 clear();
00796
00797 char **env = environ;
00798
00799 TQString dollar = TQString("$");
00800
00801 TQStringList l;
00802
00803 while ( *env ) {
00804 TQString s = TQString::fromLocal8Bit( *env );
00805
00806 int pos = s.find('=');
00807
00808 if ( pos == -1 )
00809 pos = s.length();
00810
00811 if ( pos > 0 )
00812 l.append( dollar + s.left(pos) );
00813
00814 env++;
00815 }
00816
00817 addMatches( l );
00818 }
00819
00820 setListedURL( CTEnv );
00821
00822 *match = finished();
00823 return true;
00824 #endif
00825 }
00826
00829
00830
00831
00832 bool KURLCompletion::exeCompletion(const MyURL &url, TQString *match)
00833 {
00834 if ( url.protocol() != "file" )
00835 return false;
00836
00837 TQString dir = url.dir();
00838
00839 dir = unescape( dir );
00840
00841
00842
00843
00844
00845
00846
00847
00848 TQStringList dirList;
00849
00850 if ( !TQDir::isRelativePath(dir) ) {
00851
00852 dirList.append( dir );
00853 }
00854 else if ( !dir.isEmpty() && !d->cwd.isEmpty() ) {
00855
00856 dirList.append( d->cwd + '/' + dir );
00857 }
00858 else if ( !url.file().isEmpty() ) {
00859
00860 dirList = TQStringList::split(KPATH_SEPARATOR,
00861 TQString::fromLocal8Bit(::getenv("PATH")));
00862
00863 TQStringList::Iterator it = dirList.begin();
00864
00865 for ( ; it != dirList.end(); it++ )
00866 (*it).append('/');
00867 }
00868
00869
00870 bool no_hidden_files = url.file().at(0) != '.';
00871
00872
00873
00874 if ( !isListedURL( CTExe, dir, url.file(), no_hidden_files ) )
00875 {
00876 stop();
00877 clear();
00878
00879 setListedURL( CTExe, dir, url.file(), no_hidden_files );
00880
00881 *match = listDirectories( dirList, url.file(), true, false, no_hidden_files );
00882 }
00883 else if ( !isRunning() ) {
00884 *match = finished();
00885 }
00886 else {
00887 if ( d->dirListThread )
00888 setListedURL( CTExe, dir, url.file(), no_hidden_files );
00889 *match = TQString::null;
00890 }
00891
00892 return true;
00893 }
00894
00897
00898
00899
00900 bool KURLCompletion::systemexeCompletion(const MyURL &url, TQString *match)
00901 {
00902 if ( url.protocol() != "file" )
00903 return false;
00904
00905 TQString dir = url.dir();
00906
00907 dir = unescape( dir );
00908
00909
00910
00911
00912
00913
00914
00915
00916 TQStringList dirList;
00917
00918 if ( !url.file().isEmpty() ) {
00919
00920 dirList = TQStringList::split(KPATH_SEPARATOR,
00921 TQString::fromLocal8Bit(::getenv("PATH")));
00922
00923 TQStringList::Iterator it = dirList.begin();
00924
00925 for ( ; it != dirList.end(); it++ )
00926 (*it).append('/');
00927 }
00928
00929
00930 bool no_hidden_files = url.file().at(0) != '.';
00931
00932
00933
00934 if ( !isListedURL( CTExe, dir, url.file(), no_hidden_files ) )
00935 {
00936 stop();
00937 clear();
00938
00939 setListedURL( CTExe, dir, url.file(), no_hidden_files );
00940
00941 *match = listDirectories( dirList, url.file(), true, false, no_hidden_files );
00942 }
00943 else if ( !isRunning() ) {
00944 *match = finished();
00945 }
00946 else {
00947 if ( d->dirListThread )
00948 setListedURL( CTExe, dir, url.file(), no_hidden_files );
00949 *match = TQString::null;
00950 }
00951
00952 return true;
00953 }
00954
00957
00958
00959
00960 bool KURLCompletion::fileCompletion(const MyURL &url, TQString *match)
00961 {
00962 if ( url.protocol() != "file" )
00963 return false;
00964
00965 TQString dir = url.dir();
00966
00967 if (url.url()[0] == '.')
00968 {
00969 if (url.url().length() == 1)
00970 {
00971 *match =
00972 ( completionMode() == TDEGlobalSettings::CompletionMan )? "." : "..";
00973 return true;
00974 }
00975 if (url.url().length() == 2 && url.url()[1]=='.')
00976 {
00977 *match="..";
00978 return true;
00979 }
00980 }
00981
00982
00983
00984 dir = unescape( dir );
00985
00986
00987
00988
00989
00990
00991
00992 TQStringList dirList;
00993
00994 if ( !TQDir::isRelativePath(dir) ) {
00995
00996 dirList.append( dir );
00997 }
00998 else if ( !d->cwd.isEmpty() ) {
00999
01000 dirList.append( d->cwd + '/' + dir );
01001 }
01002
01003
01004 bool no_hidden_files = ( url.file().at(0) != '.' );
01005
01006
01007
01008 if ( !isListedURL( CTFile, dir, "", no_hidden_files ) )
01009 {
01010 stop();
01011 clear();
01012
01013 setListedURL( CTFile, dir, "", no_hidden_files );
01014
01015
01016 bool append_slash = ( d->popup_append_slash
01017 && (completionMode() == TDEGlobalSettings::CompletionPopup ||
01018 completionMode() == TDEGlobalSettings::CompletionPopupAuto ) );
01019
01020 bool only_dir = ( d->mode == DirCompletion );
01021
01022 *match = listDirectories( dirList, "", false, only_dir, no_hidden_files,
01023 append_slash );
01024 }
01025 else if ( !isRunning() ) {
01026 *match = finished();
01027 }
01028 else {
01029 *match = TQString::null;
01030 }
01031
01032 return true;
01033 }
01034
01037
01038
01039
01040 bool KURLCompletion::urlCompletion(const MyURL &url, TQString *match)
01041 {
01042
01043 if (d->onlyLocalProto && KProtocolInfo::protocolClass(url.protocol()) != ":local")
01044 return false;
01045
01046
01047 KURL url_cwd = KURL::fromPathOrURL( d->cwd );
01048
01049
01050 KURL url_dir( url_cwd, url.kurl()->url() );
01051
01052
01053
01054
01055
01056
01057
01058 bool man_or_info = ( url_dir.protocol() == TQString("man")
01059 || url_dir.protocol() == TQString("info") );
01060
01061 if ( !url_dir.isValid()
01062 || !KProtocolInfo::supportsListing( url_dir )
01063 || ( !man_or_info
01064 && ( url_dir.directory(false,false).isEmpty()
01065 || ( isAutoCompletion()
01066 && !d->url_auto_completion ) ) ) ) {
01067 return false;
01068 }
01069
01070 url_dir.setFileName("");
01071
01072
01073 TQString dir = url_dir.directory( false, false );
01074
01075 dir = unescape( dir );
01076
01077 url_dir.setPath( dir );
01078
01079
01080
01081 if ( !isListedURL( CTUrl, url_dir.prettyURL(), url.file() ) )
01082 {
01083 stop();
01084 clear();
01085
01086 setListedURL( CTUrl, url_dir.prettyURL(), "" );
01087
01088 TQValueList<KURL*> url_list;
01089 url_list.append( new KURL( url_dir ) );
01090
01091 listURLs( url_list, "", false );
01092
01093 *match = TQString::null;
01094 }
01095 else if ( !isRunning() ) {
01096 *match = finished();
01097 }
01098 else {
01099 *match = TQString::null;
01100 }
01101
01102 return true;
01103 }
01104
01107
01108
01109
01110
01111
01112
01113
01114
01115 void KURLCompletion::addMatches( const TQStringList &matches )
01116 {
01117 TQStringList::ConstIterator it = matches.begin();
01118 TQStringList::ConstIterator end = matches.end();
01119
01120 if ( d->complete_url )
01121 for ( ; it != end; it++ )
01122 addItem( d->prepend + KURL::encode_string(*it));
01123 else
01124 for ( ; it != end; it++ )
01125 addItem( d->prepend + (*it));
01126 }
01127
01128
01129
01130
01131
01132
01133
01134
01135
01136
01137
01138
01139
01140 TQString KURLCompletion::listDirectories(
01141 const TQStringList &dirList,
01142 const TQString &filter,
01143 bool only_exe,
01144 bool only_dir,
01145 bool no_hidden,
01146 bool append_slash_to_dir)
01147 {
01148 assert( !isRunning() );
01149
01150 if ( !::getenv("KURLCOMPLETION_LOCAL_TDEIO") ) {
01151
01152
01153
01154
01155
01156 if ( d->dirListThread )
01157 d->dirListThread->requestTermination();
01158
01159 TQStringList dirs;
01160
01161 for ( TQStringList::ConstIterator it = dirList.begin();
01162 it != dirList.end();
01163 ++it )
01164 {
01165 KURL url;
01166 url.setPath(*it);
01167 if ( kapp->authorizeURLAction( "list", KURL(), url ) )
01168 dirs.append( *it );
01169 }
01170
01171 d->dirListThread = new DirectoryListThread( this, dirs, filter, only_exe, only_dir,
01172 no_hidden, append_slash_to_dir );
01173 d->dirListThread->start();
01174 d->dirListThread->wait( 200 );
01175 addMatches( d->dirListThread->matches() );
01176
01177 return finished();
01178 }
01179 else {
01180
01181
01182
01183
01184 TQValueList<KURL*> url_list;
01185
01186 TQStringList::ConstIterator it = dirList.begin();
01187
01188 for ( ; it != dirList.end(); it++ )
01189 url_list.append( new KURL(*it) );
01190
01191 listURLs( url_list, filter, only_exe, no_hidden );
01192
01193
01194 return TQString::null;
01195 }
01196 }
01197
01198
01199
01200
01201
01202
01203
01204
01205
01206 void KURLCompletion::listURLs(
01207 const TQValueList<KURL *> &urls,
01208 const TQString &filter,
01209 bool only_exe,
01210 bool no_hidden )
01211 {
01212 assert( d->list_urls.isEmpty() );
01213 assert( d->list_job == 0L );
01214
01215 d->list_urls = urls;
01216 d->list_urls_filter = filter;
01217 d->list_urls_only_exe = only_exe;
01218 d->list_urls_no_hidden = no_hidden;
01219
01220
01221
01222
01223
01224
01225
01226
01227 slotIOFinished(0L);
01228 }
01229
01230
01231
01232
01233
01234
01235 void KURLCompletion::slotEntries(TDEIO::Job*, const TDEIO::UDSEntryList& entries)
01236 {
01237 TQStringList matches;
01238
01239 TDEIO::UDSEntryListConstIterator it = entries.begin();
01240 TDEIO::UDSEntryListConstIterator end = entries.end();
01241
01242 TQString filter = d->list_urls_filter;
01243
01244 int filter_len = filter.length();
01245
01246
01247
01248 for (; it != end; ++it) {
01249 TQString name;
01250 TQString url;
01251 bool is_exe = false;
01252 bool is_dir = false;
01253
01254 TDEIO::UDSEntry e = *it;
01255 TDEIO::UDSEntry::ConstIterator it_2 = e.begin();
01256
01257 for( ; it_2 != e.end(); it_2++ ) {
01258 switch ( (*it_2).m_uds ) {
01259 case TDEIO::UDS_NAME:
01260 name = (*it_2).m_str;
01261 break;
01262 case TDEIO::UDS_ACCESS:
01263 is_exe = ((*it_2).m_long & MODE_EXE) != 0;
01264 break;
01265 case TDEIO::UDS_FILE_TYPE:
01266 is_dir = ((*it_2).m_long & S_IFDIR) != 0;
01267 break;
01268 case TDEIO::UDS_URL:
01269 url = (*it_2).m_str;
01270 break;
01271 }
01272 }
01273
01274 if (!url.isEmpty()) {
01275
01276 name = KURL(url).fileName();
01277 }
01278
01279
01280
01281 if ( name[0] == '.' &&
01282 ( d->list_urls_no_hidden ||
01283 name.length() == 1 ||
01284 ( name.length() == 2 && name[1] == '.' ) ) )
01285 continue;
01286
01287 if ( d->mode == DirCompletion && !is_dir )
01288 continue;
01289
01290 if ( filter_len == 0 || name.left(filter_len) == filter ) {
01291 if ( is_dir )
01292 name.append( '/' );
01293
01294 if ( is_exe || !d->list_urls_only_exe )
01295 matches.append( name );
01296 }
01297 }
01298
01299 addMatches( matches );
01300 }
01301
01302
01303
01304
01305
01306
01307
01308
01309
01310 void KURLCompletion::slotIOFinished( TDEIO::Job * job )
01311 {
01312
01313
01314 assert( job == d->list_job );
01315
01316 if ( d->list_urls.isEmpty() ) {
01317
01318 d->list_job = 0L;
01319
01320 finished();
01321
01322 }
01323 else {
01324
01325 KURL *kurl = d->list_urls.first();
01326
01327 d->list_urls.remove( kurl );
01328
01329
01330
01331 d->list_job = TDEIO::listDir( *kurl, false );
01332 d->list_job->addMetaData("no-auth-prompt", "true");
01333
01334 assert( d->list_job );
01335
01336 connect( d->list_job,
01337 TQT_SIGNAL(result(TDEIO::Job*)),
01338 TQT_SLOT(slotIOFinished(TDEIO::Job*)) );
01339
01340 connect( d->list_job,
01341 TQT_SIGNAL( entries( TDEIO::Job*, const TDEIO::UDSEntryList&)),
01342 TQT_SLOT( slotEntries( TDEIO::Job*, const TDEIO::UDSEntryList&)) );
01343
01344 delete kurl;
01345 }
01346 }
01347
01350
01351
01352
01353
01354
01355
01356
01357
01358
01359 void KURLCompletion::postProcessMatch( TQString *match ) const
01360 {
01361
01362
01363 if ( !match->isEmpty() ) {
01364
01365
01366
01367 if ( d->last_compl_type == CTFile )
01368 adjustMatch( *match );
01369 }
01370 }
01371
01372 void KURLCompletion::adjustMatch( TQString& match ) const
01373 {
01374 if ( match.at( match.length()-1 ) != '/' )
01375 {
01376 TQString copy;
01377
01378 if ( match.startsWith( TQString("file:") ) )
01379 copy = KURL(match).path();
01380 else
01381 copy = match;
01382
01383 expandTilde( copy );
01384 expandEnv( copy );
01385 if ( TQDir::isRelativePath(copy) )
01386 copy.prepend( d->cwd + '/' );
01387
01388
01389
01390 KDE_struct_stat sbuff;
01391
01392 TQCString file = TQFile::encodeName( copy );
01393
01394 if ( KDE_stat( (const char*)file, &sbuff ) == 0 ) {
01395 if ( S_ISDIR ( sbuff.st_mode ) )
01396 match.append( '/' );
01397 }
01398 else {
01399 kdDebug() << "Could not stat file " << copy << endl;
01400 }
01401 }
01402 }
01403
01404 void KURLCompletion::postProcessMatches( TQStringList * matches ) const
01405 {
01406 if ( !matches->isEmpty() && d->last_compl_type == CTFile ) {
01407 TQStringList::Iterator it = matches->begin();
01408 for (; it != matches->end(); ++it ) {
01409 adjustMatch( (*it) );
01410 }
01411 }
01412 }
01413
01414 void KURLCompletion::postProcessMatches( TDECompletionMatches * matches ) const
01415 {
01416 if ( !matches->isEmpty() && d->last_compl_type == CTFile ) {
01417 TDECompletionMatches::Iterator it = matches->begin();
01418 for (; it != matches->end(); ++it ) {
01419 adjustMatch( (*it).value() );
01420 }
01421 }
01422 }
01423
01424 void KURLCompletion::customEvent(TQCustomEvent *e)
01425 {
01426 if ( e->type() == CompletionMatchEvent::uniqueType() ) {
01427
01428 CompletionMatchEvent *event = static_cast<CompletionMatchEvent *>( e );
01429
01430 event->completionThread()->wait();
01431
01432 if ( !isListedURL( CTUser ) ) {
01433 stop();
01434 clear();
01435 addMatches( event->completionThread()->matches() );
01436 }
01437
01438 setListedURL( CTUser );
01439
01440 if ( d->userListThread == event->completionThread() )
01441 d->userListThread = 0;
01442
01443 if ( d->dirListThread == event->completionThread() )
01444 d->dirListThread = 0;
01445
01446 delete event->completionThread();
01447 }
01448 }
01449
01450
01451 TQString KURLCompletion::replacedPath( const TQString& text, bool replaceHome, bool replaceEnv )
01452 {
01453 if ( text.isEmpty() )
01454 return text;
01455
01456 MyURL url( text, TQString::null );
01457 if ( !url.kurl()->isLocalFile() )
01458 return text;
01459
01460 url.filter( replaceHome, replaceEnv );
01461 return url.dir() + url.file();
01462 }
01463
01464
01465 TQString KURLCompletion::replacedPath( const TQString& text )
01466 {
01467 return replacedPath( text, d->replace_home, d->replace_env );
01468 }
01469
01472
01473
01474
01475
01476
01477
01478
01479
01480 static bool expandEnv( TQString &text )
01481 {
01482
01483
01484 int pos = 0;
01485
01486 bool expanded = false;
01487
01488 while ( (pos = text.find('$', pos)) != -1 ) {
01489
01490
01491
01492 if ( text[pos-1] == '\\' ) {
01493 pos++;
01494 }
01495
01496
01497 else {
01498
01499
01500 int pos2 = text.find( ' ', pos+1 );
01501 int pos_tmp = text.find( '/', pos+1 );
01502
01503 if ( pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2) )
01504 pos2 = pos_tmp;
01505
01506 if ( pos2 == -1 )
01507 pos2 = text.length();
01508
01509
01510
01511
01512 if ( pos2 >= 0 ) {
01513 int len = pos2 - pos;
01514 TQString key = text.mid( pos+1, len-1);
01515 TQString value =
01516 TQString::fromLocal8Bit( ::getenv(key.local8Bit()) );
01517
01518 if ( !value.isEmpty() ) {
01519 expanded = true;
01520 text.replace( pos, len, value );
01521 pos = pos + value.length();
01522 }
01523 else {
01524 pos = pos2;
01525 }
01526 }
01527 }
01528 }
01529
01530 return expanded;
01531 }
01532
01533
01534
01535
01536
01537
01538
01539 static bool expandTilde(TQString &text)
01540 {
01541 if ( text[0] != '~' )
01542 return false;
01543
01544 bool expanded = false;
01545
01546
01547
01548 int pos2 = text.find( ' ', 1 );
01549 int pos_tmp = text.find( '/', 1 );
01550
01551 if ( pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2) )
01552 pos2 = pos_tmp;
01553
01554 if ( pos2 == -1 )
01555 pos2 = text.length();
01556
01557
01558
01559 if ( pos2 >= 0 ) {
01560
01561 TQString user = text.mid( 1, pos2-1 );
01562 TQString dir;
01563
01564
01565
01566 if ( user.isEmpty() ) {
01567 dir = TQDir::homeDirPath();
01568 }
01569
01570
01571 else {
01572 struct passwd *pw = ::getpwnam( user.local8Bit() );
01573
01574 if ( pw )
01575 dir = TQFile::decodeName( pw->pw_dir );
01576
01577 ::endpwent();
01578 }
01579
01580 if ( !dir.isEmpty() ) {
01581 expanded = true;
01582 text.replace(0, pos2, dir);
01583 }
01584 }
01585
01586 return expanded;
01587 }
01588
01589
01590
01591
01592
01593
01594
01595 static TQString unescape(const TQString &text)
01596 {
01597 TQString result;
01598
01599 for (uint pos = 0; pos < text.length(); pos++)
01600 if ( text[pos] != '\\' )
01601 result.insert( result.length(), text[pos] );
01602
01603 return result;
01604 }
01605
01606 void KURLCompletion::virtual_hook( int id, void* data )
01607 { TDECompletion::virtual_hook( id, data ); }
01608
01609 #include "kurlcompletion.moc"
01610