• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KIO

kurlcompletion.cpp

Go to the documentation of this file.
00001 /* -*- indent-tabs-mode: t; tab-width: 4; c-basic-offset:4 -*-
00002 
00003    This file is part of the KDE libraries
00004    Copyright (C) 2000 David Smith <dsmith@algonet.se>
00005    Copyright (C) 2004 Scott Wheeler <wheeler@kde.org>
00006 
00007    This class was inspired by a previous KUrlCompletion by
00008    Henner Zeller <zeller@think.de>
00009 
00010    This library is free software; you can redistribute it and/or
00011    modify it under the terms of the GNU Library General Public
00012    License as published by the Free Software Foundation; either
00013    version 2 of the License, or (at your option) any later version.
00014 
00015    This library is distributed in the hope that it will be useful,
00016    but WITHOUT ANY WARRANTY; without even the implied warranty of
00017    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00018    Library General Public License for more details.
00019 
00020    You should have received a copy of the GNU Library General Public License
00021    along with this library; see the file COPYING.LIB.   If not, write to
00022    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00023    Boston, MA 02110-1301, USA.
00024 */
00025 
00026 #include "kurlcompletion.h"
00027 
00028 #include <config.h>
00029 
00030 #include <stdlib.h>
00031 #include <assert.h>
00032 #include <limits.h>
00033 
00034 #include <QtCore/QMutableStringListIterator>
00035 #include <QtCore/QRegExp>
00036 #include <QtCore/QTimer>
00037 #include <QtCore/QDir>
00038 #include <QtCore/QFile>
00039 #include <QtCore/QTextIStream>
00040 #include <QtCore/QThread>
00041 #include <QtGui/QActionEvent>
00042 
00043 #include <kapplication.h>
00044 #include <kauthorized.h>
00045 #include <kdebug.h>
00046 #include <kurl.h>
00047 #include <kio/job.h>
00048 #include <kprotocolmanager.h>
00049 #include <kconfig.h>
00050 #include <kglobal.h>
00051 #include <kde_file.h>
00052 
00053 #include <sys/types.h>
00054 #include <dirent.h>
00055 #include <unistd.h>
00056 #include <sys/stat.h>
00057 #include <pwd.h>
00058 #include <time.h>
00059 #include <sys/param.h>
00060 #include <kconfiggroup.h>
00061 
00062 #ifdef Q_WS_WIN
00063 #include <kkernel_win.h>
00064 #endif
00065 
00066 static bool expandTilde(QString &);
00067 static bool expandEnv(QString &);
00068 
00069 static QString unescape(const QString &text);
00070 
00071 // Permission mask for files that are executable by
00072 // user, group or other
00073 #define MODE_EXE (S_IXUSR | S_IXGRP | S_IXOTH)
00074 
00075 // Constants for types of completion
00076 enum ComplType {CTNone=0, CTEnv, CTUser, CTMan, CTExe, CTFile, CTUrl, CTInfo};
00077 
00078 class CompletionThread;
00079 
00082 // KUrlCompletionPrivate
00083 //
00084 class KUrlCompletionPrivate
00085 {
00086 public:
00087     KUrlCompletionPrivate(KUrlCompletion *parent)
00088     : q(parent),
00089       url_auto_completion(true),
00090       userListThread(0),
00091       dirListThread(0)
00092   {
00093   }
00094 
00095     ~KUrlCompletionPrivate();
00096 
00097     void _k_slotEntries( KIO::Job*, const KIO::UDSEntryList& );
00098     void _k_slotIOFinished( KJob* );
00099 
00100     class MyURL;
00101     bool userCompletion(const MyURL &url, QString *match);
00102     bool envCompletion(const MyURL &url, QString *match);
00103     bool exeCompletion(const MyURL &url, QString *match);
00104     bool fileCompletion(const MyURL &url, QString *match);
00105     bool urlCompletion(const MyURL &url, QString *match);
00106 
00107     bool isAutoCompletion();
00108 
00109     // List the next dir in m_dirs
00110     QString listDirectories(const QStringList &,
00111                             const QString &,
00112                             bool only_exe = false,
00113                             bool only_dir = false,
00114                             bool no_hidden = false,
00115                             bool stat_files = true);
00116 
00117     void listUrls( const QList<KUrl *> &urls,
00118                        const QString &filter = QString(),
00119                        bool only_exe = false,
00120                        bool no_hidden = false );
00121 
00122     void addMatches( const QStringList & );
00123     QString finished();
00124 
00125     void init();
00126 
00127     void setListedUrl(int compl_type /* enum ComplType */,
00128                       const QString& dir = QString(),
00129                       const QString& filter = QString(),
00130                       bool no_hidden = false );
00131 
00132     bool isListedUrl( int compl_type /* enum ComplType */,
00133                       const QString& dir = QString(),
00134                       const QString& filter = QString(),
00135                       bool no_hidden = false );
00136 
00137   KUrlCompletion *q;
00138     QList<KUrl*> list_urls;
00139 
00140     bool onlyLocalProto;
00141 
00142     // urlCompletion() in Auto/Popup mode?
00143     bool url_auto_completion;
00144 
00145     // Append '/' to directories in Popup mode?
00146     // Doing that stat's all files and is slower
00147     bool popup_append_slash;
00148 
00149     // Keep track of currently listed files to avoid reading them again
00150     QString last_path_listed;
00151     QString last_file_listed;
00152     QString last_prepend;
00153     int last_compl_type;
00154     int last_no_hidden;
00155 
00156     QString cwd; // "current directory" = base dir for completion
00157 
00158     KUrlCompletion::Mode mode; // ExeCompletion, FileCompletion, DirCompletion
00159     bool replace_env;
00160     bool replace_home;
00161     bool complete_url; // if true completing a URL (i.e. 'prepend' is a URL), otherwise a path
00162 
00163     KIO::ListJob *list_job; // kio job to list directories
00164 
00165     QString prepend; // text to prepend to listed items
00166     QString compl_text; // text to pass on to KCompletion
00167 
00168     // Filters for files read with  kio
00169     bool list_urls_only_exe; // true = only list executables
00170     bool list_urls_no_hidden;
00171     QString list_urls_filter; // filter for listed files
00172 
00173     CompletionThread *userListThread;
00174     CompletionThread *dirListThread;
00175 };
00176 
00182 class CompletionMatchEvent : public QEvent
00183 {
00184 public:
00185     CompletionMatchEvent( CompletionThread *thread ) :
00186         QEvent( uniqueType() ),
00187         m_completionThread( thread )
00188     {}
00189 
00190     CompletionThread *completionThread() const { return m_completionThread; }
00191     static Type uniqueType() { return Type(User + 61080); }
00192 
00193 private:
00194     CompletionThread *m_completionThread;
00195 };
00196 
00197 class CompletionThread : public QThread
00198 {
00199 protected:
00200     CompletionThread( KUrlCompletionPrivate *receiver ) :
00201         QThread(),
00202         m_prepend( receiver->prepend ),
00203         m_complete_url( receiver->complete_url ),
00204         m_receiver( receiver ),
00205         m_terminationRequested( false )
00206     {}
00207 
00208 public:
00209     void requestTermination() { m_terminationRequested = true; }
00210     QStringList matches() const { return m_matches; }
00211 
00212 protected:
00213     void addMatch( const QString &match ) { m_matches.append( match ); }
00214     bool terminationRequested() const { return m_terminationRequested; }
00215     void done()
00216     {
00217         if ( !m_terminationRequested )
00218             qApp->postEvent( m_receiver->q, new CompletionMatchEvent( this ) );
00219         else
00220             deleteLater();
00221     }
00222 
00223     const QString m_prepend;
00224     const bool m_complete_url; // if true completing a URL (i.e. 'm_prepend' is a URL), otherwise a path
00225 
00226 private:
00227     KUrlCompletionPrivate *m_receiver;
00228     QStringList m_matches;
00229     bool m_terminationRequested;
00230 };
00231 
00237 class UserListThread : public CompletionThread
00238 {
00239 public:
00240     UserListThread( KUrlCompletionPrivate *receiver ) :
00241         CompletionThread( receiver )
00242     {}
00243 
00244 protected:
00245     virtual void run()
00246     {
00247         static const QChar tilde = '~';
00248 
00249         // we don't need to handle prepend here, right? ~user is always at pos 0
00250         assert(m_prepend.isEmpty());
00251         struct passwd *pw;
00252         while ( ( pw = ::getpwent() ) && !terminationRequested() )
00253             addMatch( tilde + QString::fromLocal8Bit( pw->pw_name ) );
00254 
00255         ::endpwent();
00256 
00257         addMatch( QString( tilde ) );
00258 
00259         done();
00260     }
00261 };
00262 
00263 class DirectoryListThread : public CompletionThread
00264 {
00265 public:
00266     DirectoryListThread( KUrlCompletionPrivate *receiver,
00267                          const QStringList &dirList,
00268                          const QString &filter,
00269                          bool onlyExe,
00270                          bool onlyDir,
00271                          bool noHidden,
00272                          bool appendSlashToDir ) :
00273         CompletionThread( receiver ),
00274         m_dirList( dirList ),
00275         m_filter(  filter  ),
00276         m_onlyExe( onlyExe ),
00277         m_onlyDir( onlyDir ),
00278         m_noHidden( noHidden ),
00279         m_appendSlashToDir( appendSlashToDir )
00280     {}
00281 
00282     virtual void run();
00283 
00284 private:
00285     QStringList m_dirList;
00286     QString m_filter;
00287     bool m_onlyExe;
00288     bool m_onlyDir;
00289     bool m_noHidden;
00290     bool m_appendSlashToDir;
00291 };
00292 
00293 #ifdef Q_WS_WIN
00294 
00295 void DirectoryListThread::run()
00296 {
00297   WIN32_FIND_DATAW find_data;
00298 
00299   QString prepend_slash;
00300 // TODO: doesn't work yet in kurlcombobox yet
00301 //  QString prepend_backslash;
00302 
00303   if( !m_prepend.isEmpty() ) {
00304     prepend_slash = m_prepend;
00305     if( !prepend_slash.endsWith( QLatin1Char( '/' ) ) )
00306       prepend_slash += QLatin1Char( '/' );
00307   }
00308 //  prepend_backslash = prepend_slash;
00309 //  prepend_backslash.replace( '/', '\\' );
00310 
00311   Q_FOREACH( const QString &dir_, m_dirList )
00312   {
00313     QString dir = dir_;
00314     if( !dir.endsWith( '/' ) )
00315       dir += QLatin1Char( '/' );
00316     dir += QLatin1String( "*.*" );
00317     HANDLE hFind = FindFirstFileW( ( LPCWSTR ) dir.utf16(), &find_data );
00318     if( hFind == INVALID_HANDLE_VALUE ) {
00319       qDebug() << "Failed to open dir:" << dir;
00320       return;
00321     }
00322     do {
00323       // Skip hidden files if m_noHidden is true
00324       if( ( find_data.cFileName[0] == '.' && m_noHidden ) ||
00325           ( find_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) == FILE_ATTRIBUTE_HIDDEN )
00326         continue;
00327 
00328       // Skip "."
00329 
00330       if ( find_data.cFileName[0] == '.' && find_data.cFileName[1] == '\0' )
00331         continue;
00332 
00333       // Skip ".."
00334 
00335       if ( find_data.cFileName[0] == '.' && find_data.cFileName[1] == '.' && find_data.cFileName[2] == '\0' )
00336         continue;
00337 
00338       // Verify directory
00339 
00340       if ( m_onlyDir && ( find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) == 0 )
00341         continue;
00342 
00343       QString file = QString::fromUtf16( ( const ushort * ) find_data.cFileName );
00344 
00345       bool appendSlash = false;
00346 
00347       if ( m_filter.isEmpty() || file.startsWith( m_filter ) ) {
00348 
00349         if ( m_onlyExe || m_onlyDir || m_appendSlashToDir ) {
00350 
00351           // Verify executable
00352 
00353           if ( m_onlyExe && !isExecutable( file ) )
00354             continue;
00355 
00356           // Add '/' to directories
00357 
00358           if ( m_appendSlashToDir && ( find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) == FILE_ATTRIBUTE_DIRECTORY )
00359             appendSlash = true;
00360         }
00361 
00362         if( appendSlash ) {
00363           addMatch( prepend_slash + file + QLatin1Char( '/' ) );
00364 //          addMatch( prepend_backslash + file + QLatin1Char( '\\' ));
00365         } else {
00366           addMatch( prepend_slash + file );
00367 //          addMatch( prepend_backslash + file );
00368         }
00369 
00370       }
00371 
00372     }
00373 
00374     while( FindNextFileW( hFind, &find_data ) );
00375 
00376   }
00377 
00378   done();
00379 }
00380 
00381 #else   // Q_WS_WIN
00382 
00383 void DirectoryListThread::run()
00384 {
00385     // Thread safety notes:
00386     //
00387     // There very possibly may be thread safety issues here, but I've done a check
00388     // of all of the things that would seem to be problematic.  Here are a few
00389     // things that I have checked to be safe here (some used indirectly):
00390     //
00391     // QDir::currentPath(), QDir::setCurrent(), QFile::decodeName(), QFile::encodeName()
00392     // QString::fromLocal8Bit(), QString::toLocal8Bit(), QTextCodec::codecForLocale()
00393     //
00394     // Also see (for POSIX functions):
00395     // http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_09.html
00396 
00397     DIR *dir = 0;
00398 
00399     for ( QStringList::ConstIterator it = m_dirList.constBegin();
00400           it != m_dirList.constEnd() && !terminationRequested();
00401           ++it )
00402     {
00403         // Open the next directory
00404 
00405         if ( !dir ) {
00406             dir = ::opendir( QFile::encodeName( *it ) );
00407             if ( ! dir ) {
00408                 kDebug() << "Failed to open dir:" << *it;
00409                 return;
00410             }
00411         }
00412 
00413         // A trick from KIO that helps performance by a little bit:
00414         // chdir to the directory so we won't have to deal with full paths
00415         // with stat()
00416 
00417         QString path = QDir::currentPath();
00418         QDir::setCurrent( *it );
00419 
00420         // Loop through all directory entries
00421         // Solaris and IRIX dirent structures do not allocate space for d_name. On
00422         // systems that do (HP-UX, Linux, Tru64 UNIX), we overallocate space but
00423         // that's ok.
00424 #ifndef HAVE_READDIR_R
00425         KDE_struct_dirent *dirEntry = 0;
00426         while ( !terminationRequested() &&
00427                 (dirEntry = KDE_readdir( dir)))
00428 #else
00429         struct dirent *dirPosition = (struct dirent *) malloc( sizeof( struct dirent ) + MAXPATHLEN + 1 );
00430         struct dirent *dirEntry = 0;
00431         while ( !terminationRequested() &&
00432                 ::readdir_r( dir, dirPosition, &dirEntry ) == 0 && dirEntry )
00433 #endif
00434 
00435         {
00436             // Skip hidden files if m_noHidden is true
00437 
00438             if ( dirEntry->d_name[0] == '.' && m_noHidden )
00439                 continue;
00440 
00441             // Skip "."
00442 
00443             if ( dirEntry->d_name[0] == '.' && dirEntry->d_name[1] == '\0' )
00444                 continue;
00445 
00446             // Skip ".."
00447 
00448             if ( dirEntry->d_name[0] == '.' && dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0' )
00449                 continue;
00450 
00451             QString file = QFile::decodeName( dirEntry->d_name );
00452 
00453             if ( m_filter.isEmpty() || file.startsWith( m_filter ) ) {
00454 
00455                 QString toAppend = m_complete_url ? QUrl::toPercentEncoding(file) : file;
00456 
00457                 if ( m_onlyExe || m_onlyDir || m_appendSlashToDir ) {
00458                     KDE_struct_stat sbuff;
00459 
00460                     if ( KDE_stat( dirEntry->d_name, &sbuff ) == 0 ) {
00461 
00462                         // Verify executable
00463 
00464                         if ( m_onlyExe && ( sbuff.st_mode & MODE_EXE ) == 0 )
00465                             continue;
00466 
00467                         // Verify directory
00468 
00469                         if ( m_onlyDir && !S_ISDIR( sbuff.st_mode ) )
00470                             continue;
00471 
00472                         // Add '/' to directories
00473 
00474                         if ( m_appendSlashToDir && S_ISDIR( sbuff.st_mode ) )
00475                             toAppend.append( QLatin1Char( '/' ) );
00476 
00477                     }
00478                     else {
00479                         kDebug() << "Could not stat file" << file;
00480                         continue;
00481                     }
00482                 }
00483 
00484                 addMatch( m_prepend + toAppend );
00485             }
00486         }
00487 
00488         // chdir to the original directory
00489 
00490         QDir::setCurrent( path );
00491 
00492         ::closedir( dir );
00493         dir = 0;
00494 #ifdef HAVE_READDIR_R
00495         free( dirPosition );
00496 #endif
00497     }
00498 
00499     done();
00500 }
00501 #endif  // !Q_WS_WIN
00502 
00503 KUrlCompletionPrivate::~KUrlCompletionPrivate()
00504 {
00505     if ( userListThread )
00506         userListThread->requestTermination();
00507     if ( dirListThread )
00508         dirListThread->requestTermination();
00509 }
00510 
00513 // MyURL - wrapper for KUrl with some different functionality
00514 //
00515 
00516 class KUrlCompletionPrivate::MyURL
00517 {
00518 public:
00519     MyURL(const QString &url, const QString &cwd);
00520     MyURL(const MyURL &url);
00521     ~MyURL();
00522 
00523     KUrl *kurl() const { return m_kurl; }
00524 
00525     QString protocol() const { return m_kurl->protocol(); }
00526     // The directory with a trailing '/'
00527         QString dir() const { return m_kurl->directory(KUrl::AppendTrailingSlash|KUrl::ObeyTrailingSlash); }
00528     QString file() const { return m_kurl->fileName(KUrl::ObeyTrailingSlash); }
00529 
00530     // The initial, unparsed, url, as a string.
00531     QString url() const { return m_url; }
00532 
00533     // Is the initial string a URL, or just a path (whether absolute or relative)
00534     bool isURL() const { return m_isURL; }
00535 
00536     void filter( bool replace_user_dir, bool replace_env );
00537 
00538 private:
00539     void init(const QString &url, const QString &cwd);
00540 
00541     KUrl *m_kurl;
00542     QString m_url;
00543     bool m_isURL;
00544 };
00545 
00546 KUrlCompletionPrivate::MyURL::MyURL(const QString &_url, const QString &cwd)
00547 {
00548     init(_url, cwd);
00549 }
00550 
00551 KUrlCompletionPrivate::MyURL::MyURL(const MyURL &_url)
00552 {
00553     m_kurl = new KUrl( *(_url.m_kurl) );
00554     m_url = _url.m_url;
00555     m_isURL = _url.m_isURL;
00556 }
00557 
00558 void KUrlCompletionPrivate::MyURL::init(const QString &_url, const QString &cwd)
00559 {
00560     // Save the original text
00561     m_url = _url;
00562 
00563     // Non-const copy
00564     QString url_copy = _url;
00565 
00566     // Special shortcuts for "man:" and "info:"
00567     if ( url_copy.startsWith( QLatin1Char('#') ) ) {
00568         if ( url_copy.length() > 1 && url_copy.at(1) == QLatin1Char('#') )
00569             url_copy.replace( 0, 2, QLatin1String("info:") );
00570         else
00571             url_copy.replace( 0, 1, QLatin1String("man:") );
00572     }
00573 
00574     // Look for a protocol in 'url'
00575     QRegExp protocol_regex = QRegExp( "^(?![A-Za-z]:)[^/\\s\\\\]*:" );
00576 
00577     // Assume "file:" or whatever is given by 'cwd' if there is
00578     // no protocol.  (KUrl does this only for absolute paths)
00579     if ( protocol_regex.indexIn( url_copy ) == 0 )
00580     {
00581         m_kurl = new KUrl( url_copy );
00582         m_isURL = true;
00583     }
00584     else // relative path or ~ or $something
00585     {
00586         m_isURL = false;
00587         if ( !QDir::isRelativePath(url_copy) ||
00588              url_copy.startsWith( QLatin1Char('~') ) ||
00589              url_copy.startsWith( QLatin1Char('$') ))
00590         {
00591             m_kurl = new KUrl;
00592             m_kurl->setPath( url_copy );
00593         }
00594         else
00595         {
00596             if ( cwd.isEmpty() ) {
00597                 m_kurl = new KUrl( url_copy );
00598             } else {
00599                 m_kurl = new KUrl( cwd );
00600                 m_kurl->addPath( url_copy );
00601             }
00602         }
00603     }
00604 }
00605 
00606 KUrlCompletionPrivate::MyURL::~MyURL()
00607 {
00608     delete m_kurl;
00609 }
00610 
00611 void KUrlCompletionPrivate::MyURL::filter( bool replace_user_dir, bool replace_env )
00612 {
00613     QString d = dir() + file();
00614     if ( replace_user_dir ) expandTilde( d );
00615     if ( replace_env ) expandEnv( d );
00616     m_kurl->setPath( d );
00617 }
00618 
00621 // KUrlCompletion
00622 //
00623 
00624 KUrlCompletion::KUrlCompletion() : KCompletion(),d(new KUrlCompletionPrivate(this))
00625 {
00626     d->init();
00627 }
00628 
00629 
00630 KUrlCompletion::KUrlCompletion( Mode _mode ) : KCompletion(),d(new KUrlCompletionPrivate(this))
00631 {
00632     d->init();
00633     setMode ( _mode );
00634 }
00635 
00636 KUrlCompletion::~KUrlCompletion()
00637 {
00638     stop();
00639     delete d;
00640 }
00641 
00642 
00643 void KUrlCompletionPrivate::init()
00644 {
00645 
00646     cwd = QDir::homePath();
00647 
00648     replace_home = true;
00649     replace_env = true;
00650     last_no_hidden = false;
00651     last_compl_type = 0;
00652     list_job = 0L;
00653     mode = KUrlCompletion::FileCompletion;
00654 
00655     // Read settings
00656     KConfigGroup cg( KGlobal::config(), "URLCompletion" );
00657 
00658     url_auto_completion = cg.readEntry("alwaysAutoComplete", true);
00659     popup_append_slash = cg.readEntry("popupAppendSlash", true);
00660     onlyLocalProto = cg.readEntry("LocalProtocolsOnly", false);
00661 }
00662 
00663 void KUrlCompletion::setDir(const QString &_dir)
00664 {
00665     d->cwd = _dir;
00666 }
00667 
00668 QString KUrlCompletion::dir() const
00669 {
00670     return d->cwd;
00671 }
00672 
00673 KUrlCompletion::Mode KUrlCompletion::mode() const
00674 {
00675     return d->mode;
00676 }
00677 
00678 void KUrlCompletion::setMode( Mode _mode )
00679 {
00680     d->mode = _mode;
00681 }
00682 
00683 bool KUrlCompletion::replaceEnv() const
00684 {
00685     return d->replace_env;
00686 }
00687 
00688 void KUrlCompletion::setReplaceEnv( bool replace )
00689 {
00690     d->replace_env = replace;
00691 }
00692 
00693 bool KUrlCompletion::replaceHome() const
00694 {
00695     return d->replace_home;
00696 }
00697 
00698 void KUrlCompletion::setReplaceHome( bool replace )
00699 {
00700     d->replace_home = replace;
00701 }
00702 
00703 /*
00704  * makeCompletion()
00705  *
00706  * Entry point for file name completion
00707  */
00708 QString KUrlCompletion::makeCompletion(const QString &text)
00709 {
00710     //kDebug() << text << "d->cwd=" << d->cwd;
00711 
00712   KUrlCompletionPrivate::MyURL url(text, d->cwd);
00713 
00714     d->compl_text = text;
00715 
00716     // Set d->prepend to the original URL, with the filename [and ref/query] stripped.
00717     // This is what gets prepended to the directory-listing matches.
00718     int toRemove = url.file().length() - url.kurl()->query().length();
00719     if ( url.kurl()->hasRef() )
00720         toRemove += url.kurl()->ref().length() + 1;
00721     d->prepend = text.left( text.length() - toRemove );
00722     d->complete_url = url.isURL();
00723 
00724     QString aMatch;
00725 
00726     // Environment variables
00727     //
00728     if ( d->replace_env && d->envCompletion( url, &aMatch ) )
00729         return aMatch;
00730 
00731     // User directories
00732     //
00733     if ( d->replace_home && d->userCompletion( url, &aMatch ) )
00734         return aMatch;
00735 
00736     // Replace user directories and variables
00737     url.filter( d->replace_home, d->replace_env );
00738 
00739     //kDebug() << "Filtered: proto=" << url.protocol()
00740     //          << ", dir=" << url.dir()
00741     //          << ", file=" << url.file()
00742     //          << ", kurl url=" << *url.kurl();
00743 
00744     if ( d->mode == ExeCompletion ) {
00745         // Executables
00746         //
00747         if ( d->exeCompletion( url, &aMatch ) )
00748             return aMatch;
00749 
00750         // KRun can run "man:" and "info:" etc. so why not treat them
00751         // as executables...
00752 
00753         if ( d->urlCompletion( url, &aMatch ) )
00754             return aMatch;
00755     }
00756     else {
00757         // Local files, directories
00758         //
00759         if ( d->fileCompletion( url, &aMatch ) )
00760             return aMatch;
00761 
00762         // All other...
00763         //
00764         if ( d->urlCompletion( url, &aMatch ) )
00765             return aMatch;
00766     }
00767 
00768     d->setListedUrl( CTNone );
00769     stop();
00770 
00771     return QString();
00772 }
00773 
00774 /*
00775  * finished
00776  *
00777  * Go on and call KCompletion.
00778  * Called when all matches have been added
00779  */
00780 QString KUrlCompletionPrivate::finished()
00781 {
00782     if ( last_compl_type == CTInfo )
00783         return q->KCompletion::makeCompletion( compl_text.toLower() );
00784     else
00785         return q->KCompletion::makeCompletion( compl_text );
00786 }
00787 
00788 /*
00789  * isRunning
00790  *
00791  * Return true if either a KIO job or the DirLister
00792  * is running
00793  */
00794 bool KUrlCompletion::isRunning() const
00795 {
00796     return d->list_job || (d->dirListThread && !d->dirListThread->isFinished());
00797 }
00798 
00799 /*
00800  * stop
00801  *
00802  * Stop and delete a running KIO job or the DirLister
00803  */
00804 void KUrlCompletion::stop()
00805 {
00806     if ( d->list_job ) {
00807         d->list_job->kill();
00808         d->list_job = 0L;
00809     }
00810 
00811     while ( !d->list_urls.isEmpty() ) {
00812         delete d->list_urls.takeFirst();
00813     }
00814 
00815     if ( d->dirListThread ) {
00816         d->dirListThread->requestTermination();
00817         d->dirListThread = 0;
00818     }
00819 }
00820 
00821 /*
00822  * Keep track of the last listed directory
00823  */
00824 void KUrlCompletionPrivate::setListedUrl( int complType,
00825                                    const QString& directory,
00826                                    const QString& filter,
00827                                    bool no_hidden )
00828 {
00829     last_compl_type = complType;
00830     last_path_listed = directory;
00831     last_file_listed = filter;
00832     last_no_hidden = (int)no_hidden;
00833     last_prepend = prepend;
00834 }
00835 
00836 bool KUrlCompletionPrivate::isListedUrl( int complType,
00837                                   const QString& directory,
00838                                   const QString& filter,
00839                                   bool no_hidden )
00840 {
00841     return  last_compl_type == complType
00842             && ( last_path_listed == directory
00843                     || (directory.isEmpty() && last_path_listed.isEmpty()) )
00844             && ( filter.startsWith(last_file_listed)
00845                     || (filter.isEmpty() && last_file_listed.isEmpty()) )
00846             && last_no_hidden == (int)no_hidden
00847             && last_prepend == prepend; // e.g. relative path vs absolute
00848 }
00849 
00850 /*
00851  * isAutoCompletion
00852  *
00853  * Returns true if completion mode is Auto or Popup
00854  */
00855 bool KUrlCompletionPrivate::isAutoCompletion()
00856 {
00857     return q->completionMode() == KGlobalSettings::CompletionAuto
00858            || q->completionMode() == KGlobalSettings::CompletionPopup
00859            || q->completionMode() == KGlobalSettings::CompletionMan
00860            || q->completionMode() == KGlobalSettings::CompletionPopupAuto;
00861 }
00864 // User directories
00865 //
00866 
00867 bool KUrlCompletionPrivate::userCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
00868 {
00869     if ( url.protocol() != QLatin1String("file")
00870           || !url.dir().isEmpty()
00871           || !url.file().startsWith( QLatin1Char('~') ) )
00872         return false;
00873 
00874     if ( !isListedUrl( CTUser ) ) {
00875         q->stop();
00876         q->clear();
00877 
00878         if ( !userListThread ) {
00879             userListThread = new UserListThread( this );
00880             userListThread->start();
00881 
00882             // If the thread finishes quickly make sure that the results
00883             // are added to the first matching case.
00884 
00885             userListThread->wait( 200 );
00886             const QStringList l = userListThread->matches();
00887             addMatches( l );
00888         }
00889     }
00890     *pMatch = finished();
00891     return true;
00892 }
00893 
00896 // Environment variables
00897 //
00898 
00899 #ifndef Q_OS_WIN
00900 extern char **environ; // Array of environment variables
00901 #endif
00902 
00903 bool KUrlCompletionPrivate::envCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
00904 {
00905     if ( url.file().isEmpty() || url.file().at(0) != QLatin1Char('$') )
00906         return false;
00907 
00908     if ( !isListedUrl( CTEnv ) ) {
00909         q->stop();
00910         q->clear();
00911 
00912         char **env = environ;
00913 
00914         QString dollar = QLatin1String("$");
00915 
00916         QStringList l;
00917 
00918         while ( *env ) {
00919             QString s = QString::fromLocal8Bit( *env );
00920 
00921             int pos = s.indexOf(QLatin1Char('='));
00922 
00923             if ( pos == -1 )
00924                 pos = s.length();
00925 
00926             if ( pos > 0 )
00927                 l.append( prepend + dollar + s.left(pos) );
00928 
00929             env++;
00930         }
00931 
00932         addMatches( l );
00933     }
00934 
00935     setListedUrl( CTEnv );
00936 
00937     *pMatch = finished();
00938     return true;
00939 }
00940 
00943 // Executables
00944 //
00945 
00946 bool KUrlCompletionPrivate::exeCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
00947 {
00948     if ( url.protocol() != QLatin1String("file") )
00949         return false;
00950 
00951     QString directory = unescape( url.dir() ); // remove escapes
00952 
00953     // Find directories to search for completions, either
00954     //
00955     // 1. complete path given in url
00956     // 2. current directory (d->cwd)
00957     // 3. $PATH
00958     // 4. no directory at all
00959 
00960     QStringList dirList;
00961 
00962     if ( !QDir::isRelativePath(directory) ) {
00963         // complete path in url
00964         dirList.append( directory );
00965     }
00966     else if ( !directory.isEmpty() && !cwd.isEmpty() ) {
00967         // current directory
00968         dirList.append( cwd + QLatin1Char('/') + directory );
00969     }
00970     else if ( !url.file().isEmpty() ) {
00971         // $PATH
00972         dirList = QString::fromLocal8Bit(qgetenv("PATH")).split(
00973                 KPATH_SEPARATOR,QString::SkipEmptyParts);
00974 
00975         QStringList::Iterator it = dirList.begin();
00976 
00977         for ( ; it != dirList.end(); ++it )
00978             it->append(QLatin1Char('/'));
00979     }
00980 
00981     // No hidden files unless the user types "."
00982     bool no_hidden_files = url.file().isEmpty() || url.file().at(0) != QLatin1Char('.');
00983 
00984     // List files if needed
00985     //
00986     if ( !isListedUrl( CTExe, directory, url.file(), no_hidden_files ) )
00987     {
00988         q->stop();
00989         q->clear();
00990 
00991         setListedUrl( CTExe, directory, url.file(), no_hidden_files );
00992 
00993         *pMatch = listDirectories( dirList, url.file(), true, false, no_hidden_files );
00994     }
00995     else if ( !q->isRunning() ) {
00996         *pMatch = finished();
00997     }
00998     else {
00999         if ( dirListThread )
01000             setListedUrl( CTExe, directory, url.file(), no_hidden_files );
01001         pMatch->clear();
01002     }
01003 
01004     return true;
01005 }
01006 
01009 // Local files
01010 //
01011 
01012 bool KUrlCompletionPrivate::fileCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
01013 {
01014     if ( url.protocol() != QLatin1String("file") )
01015         return false;
01016 
01017     QString directory = unescape( url.dir() );
01018 
01019     if (url.url().length() && url.url().at(0) == QLatin1Char('.'))
01020     {
01021         if (url.url().length() == 1)
01022         {
01023             *pMatch = ( q->completionMode() == KGlobalSettings::CompletionMan )?
01024                     QLatin1String(".") :
01025                     QLatin1String("..");
01026             return true;
01027         }
01028         else if (url.url().length() == 2 && url.url().at(1) == QLatin1Char('.'))
01029         {
01030             *pMatch = QLatin1String("..");
01031             return true;
01032         }
01033     }
01034 
01035     //kDebug() << "fileCompletion" << url << "dir=" << dir;
01036 
01037     // Find directories to search for completions, either
01038     //
01039     // 1. complete path given in url
01040     // 2. current directory (d->cwd)
01041     // 3. no directory at all
01042 
01043     QStringList dirList;
01044 
01045     if ( !QDir::isRelativePath(directory) ) {
01046         // complete path in url
01047         dirList.append( directory );
01048     }
01049     else if ( !cwd.isEmpty() ) {
01050         // current directory
01051         QString dirToAdd = cwd;
01052         if ( !directory.isEmpty() ) {
01053             if ( !cwd.endsWith('/') )
01054                 dirToAdd.append( QLatin1Char('/') );
01055             dirToAdd.append( directory );
01056         }
01057         dirList.append( dirToAdd );
01058     }
01059 
01060     // No hidden files unless the user types "."
01061     bool no_hidden_files = !url.file().startsWith( QLatin1Char('.') );
01062 
01063     // List files if needed
01064     //
01065     if ( !isListedUrl( CTFile, directory, QString(), no_hidden_files ) )
01066     {
01067         q->stop();
01068         q->clear();
01069 
01070         setListedUrl( CTFile, directory, QString(), no_hidden_files );
01071 
01072         // Append '/' to directories in Popup mode?
01073         bool append_slash = ( popup_append_slash
01074             && (q->completionMode() == KGlobalSettings::CompletionPopup ||
01075             q->completionMode() == KGlobalSettings::CompletionPopupAuto ) );
01076 
01077         bool only_dir = ( mode == KUrlCompletion::DirCompletion );
01078 
01079         *pMatch = listDirectories( dirList, QString(), false, only_dir, no_hidden_files,
01080                                   append_slash );
01081     }
01082     else if ( !q->isRunning() ) {
01083         *pMatch = finished();
01084     }
01085     else {
01086         pMatch->clear();
01087     }
01088 
01089     return true;
01090 }
01091 
01094 // URLs not handled elsewhere...
01095 //
01096 
01097 bool KUrlCompletionPrivate::urlCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
01098 {
01099     //kDebug() << *url.kurl();
01100     if (onlyLocalProto && KProtocolInfo::protocolClass(url.protocol()) != QLatin1String(":local"))
01101         return false;
01102 
01103     // Use d->cwd as base url in case url is not absolute
01104     KUrl url_cwd( cwd );
01105 
01106     // Create an URL with the directory to be listed
01107     KUrl url_dir( url_cwd, url.kurl()->url() );
01108 
01109     // Don't try url completion if
01110     // 1. malformed url
01111     // 2. protocol that doesn't have listDir()
01112     // 3. there is no directory (e.g. "ftp://ftp.kd" shouldn't do anything)
01113     // 4. auto or popup completion mode depending on settings
01114 
01115     bool man_or_info = ( url_dir.protocol() == QLatin1String("man")
01116                          || url_dir.protocol() == QLatin1String("info") );
01117 
01118     if ( !url_dir.isValid()
01119          || !KProtocolManager::supportsListing( url_dir )
01120          || ( !man_or_info
01121                      && ( url_dir.directory(KUrl::AppendTrailingSlash|KUrl::ObeyTrailingSlash).isEmpty()
01122                    || ( isAutoCompletion()
01123                         && !url_auto_completion ) ) ) ) {
01124                 return false;
01125         }
01126 
01127     url_dir.setFileName(QString()); // not really nesseccary, but clear the filename anyway...
01128 
01129     // Remove escapes
01130     QString directory = unescape( url_dir.directory(KUrl::AppendTrailingSlash|KUrl::ObeyTrailingSlash) );
01131 
01132     url_dir.setPath( directory );
01133 
01134     // List files if needed
01135     //
01136     if ( !isListedUrl( CTUrl, url_dir.prettyUrl(), url.file() ) )
01137     {
01138         q->stop();
01139         q->clear();
01140 
01141         setListedUrl( CTUrl, url_dir.prettyUrl(), QString() );
01142 
01143         QList<KUrl*> url_list;
01144         url_list.append( new KUrl( url_dir ) );
01145 
01146         listUrls( url_list, QString(), false );
01147 
01148         pMatch->clear();
01149     }
01150     else if ( !q->isRunning() ) {
01151         *pMatch = finished();
01152     }
01153     else {
01154         pMatch->clear();
01155     }
01156 
01157     return true;
01158 }
01159 
01162 // Directory and URL listing
01163 //
01164 
01165 /*
01166  * addMatches
01167  *
01168  * Called to add matches to KCompletion
01169  */
01170 void KUrlCompletionPrivate::addMatches( const QStringList &matchList )
01171 {
01172     q->insertItems(matchList);
01173 }
01174 
01175 /*
01176  * listDirectories
01177  *
01178  * List files starting with 'filter' in the given directories,
01179  * either using DirLister or listURLs()
01180  *
01181  * In either case, addMatches() is called with the listed
01182  * files, and eventually finished() when the listing is done
01183  *
01184  * Returns the match if available, or QString() if
01185  * DirLister timed out or using kio
01186  */
01187 QString KUrlCompletionPrivate::listDirectories(
01188         const QStringList &dirList,
01189         const QString &filter,
01190         bool only_exe,
01191         bool only_dir,
01192         bool no_hidden,
01193         bool append_slash_to_dir)
01194 {
01195     assert( !q->isRunning() );
01196 
01197     if ( qgetenv("KURLCOMPLETION_LOCAL_KIO").isEmpty() ) {
01198 
01199         //kDebug() << "Listing (listDirectories):" << dirList << "filter=" << filter << "without KIO";
01200 
01201         // Don't use KIO
01202 
01203         if ( dirListThread )
01204             dirListThread->requestTermination();
01205 
01206         QStringList dirs;
01207 
01208         for ( QStringList::ConstIterator it = dirList.begin();
01209               it != dirList.end();
01210               ++it )
01211         {
01212             KUrl url;
01213             url.setPath(*it);
01214             if ( KAuthorized::authorizeUrlAction( QLatin1String("list"), KUrl(), url ) )
01215                 dirs.append( *it );
01216         }
01217 
01218         dirListThread = new DirectoryListThread( this, dirs, filter, only_exe, only_dir,
01219                                                  no_hidden, append_slash_to_dir );
01220         dirListThread->start();
01221         dirListThread->wait( 200 );
01222         addMatches( dirListThread->matches() );
01223 
01224         return finished();
01225     }
01226 
01227     // Use KIO
01228     //kDebug() << "Listing (listDirectories):" << dirList << "with KIO";
01229 
01230     QList<KUrl*> url_list;
01231 
01232     QStringList::ConstIterator it = dirList.begin();
01233 
01234     for ( ; it != dirList.end(); ++it ) {
01235         url_list.append( new KUrl( *it ) );
01236     }
01237 
01238     listUrls( url_list, filter, only_exe, no_hidden );
01239     // Will call addMatches() and finished()
01240 
01241     return QString();
01242 }
01243 
01244 /*
01245  * listURLs
01246  *
01247  * Use KIO to list the given urls
01248  *
01249  * addMatches() is called with the listed files
01250  * finished() is called when the listing is done
01251  */
01252 void KUrlCompletionPrivate::listUrls(
01253         const QList<KUrl *> &urls,
01254         const QString &filter,
01255         bool only_exe,
01256         bool no_hidden )
01257 {
01258     assert( list_urls.isEmpty() );
01259     assert( list_job == 0L );
01260 
01261     list_urls = urls;
01262     list_urls_filter = filter;
01263     list_urls_only_exe = only_exe;
01264     list_urls_no_hidden = no_hidden;
01265 
01266     //kDebug() << "Listing URLs:" << *urls[0] << ",...";
01267 
01268     // Start it off by calling _k_slotIOFinished
01269     //
01270     // This will start a new list job as long as there
01271     // are urls in d->list_urls
01272     //
01273     _k_slotIOFinished(0);
01274 }
01275 
01276 /*
01277  * _k_slotEntries
01278  *
01279  * Receive files listed by KIO and call addMatches()
01280  */
01281 void KUrlCompletionPrivate::_k_slotEntries(KIO::Job*, const KIO::UDSEntryList& entries)
01282 {
01283     QStringList matchList;
01284 
01285     KIO::UDSEntryList::ConstIterator it = entries.begin();
01286     const KIO::UDSEntryList::ConstIterator end = entries.end();
01287 
01288     QString filter = list_urls_filter;
01289 
01290     int filter_len = filter.length();
01291 
01292     // Iterate over all files
01293     //
01294     for (; it != end; ++it) {
01295         const KIO::UDSEntry& entry = *it;
01296         const QString url = entry.stringValue( KIO::UDSEntry::UDS_URL );
01297 
01298         QString entry_name;
01299         if (!url.isEmpty()) {
01300             // kDebug() << "url:" << url;
01301             entry_name = KUrl(url).fileName();
01302         } else {
01303             entry_name = entry.stringValue( KIO::UDSEntry::UDS_NAME );
01304         }
01305 
01306         // kDebug() << "name:" << name;
01307 
01308         if ( (!entry_name.isEmpty() && entry_name.at(0) == QLatin1Char('.')) &&
01309              ( list_urls_no_hidden ||
01310                 entry_name.length() == 1 ||
01311                   ( entry_name.length() == 2 && entry_name.at(1) == QLatin1Char('.') ) ) )
01312             continue;
01313 
01314         const bool isDir = entry.isDir();
01315 
01316         if ( mode == KUrlCompletion::DirCompletion && !isDir )
01317             continue;
01318 
01319         if ( filter_len == 0 || entry_name.left(filter_len) == filter ) {
01320 
01321             QString toAppend = complete_url ? QUrl::toPercentEncoding(entry_name) : entry_name;
01322 
01323             if (isDir)
01324                 toAppend.append( QLatin1Char( '/' ) );
01325 
01326             if ( !list_urls_only_exe ||
01327                  (entry.numberValue( KIO::UDSEntry::UDS_ACCESS ) & MODE_EXE) // true if executable
01328                 ) {
01329                 matchList.append( prepend + toAppend );
01330             }
01331         }
01332     }
01333 
01334     addMatches( matchList );
01335 }
01336 
01337 /*
01338  * _k_slotIOFinished
01339  *
01340  * Called when a KIO job is finished.
01341  *
01342  * Start a new list job if there are still urls in
01343  * list_urls, otherwise call finished()
01344  */
01345 void KUrlCompletionPrivate::_k_slotIOFinished( KJob * job )
01346 {
01347     assert( job == list_job );
01348 
01349     if ( list_urls.isEmpty() ) {
01350 
01351         list_job = 0L;
01352 
01353         finished(); // will call KCompletion::makeCompletion()
01354 
01355     }
01356     else {
01357 
01358         KUrl *kurl = list_urls.takeFirst();
01359 
01360 //      list_urls.removeAll( kurl );
01361 
01362 //      kDebug() << "Start KIO::listDir" << *kurl;
01363 
01364         list_job = KIO::listDir( *kurl, KIO::HideProgressInfo );
01365         list_job->addMetaData("no-auth-prompt", "true");
01366 
01367         assert( list_job );
01368 
01369         q->connect( list_job,
01370                 SIGNAL(result(KJob*)),
01371                 SLOT(_k_slotIOFinished(KJob*)) );
01372 
01373         q->connect( list_job,
01374                 SIGNAL( entries( KIO::Job*, const KIO::UDSEntryList&)),
01375                 SLOT( _k_slotEntries( KIO::Job*, const KIO::UDSEntryList&)) );
01376 
01377         delete kurl;
01378     }
01379 }
01380 
01383 
01384 /*
01385  * postProcessMatch, postProcessMatches
01386  *
01387  * Called by KCompletion before emitting match() and matches()
01388  *
01389  * Append '/' to directories for file completion. This is
01390  * done here to avoid stat()'ing a lot of files
01391  */
01392 void KUrlCompletion::postProcessMatch( QString *pMatch ) const
01393 {
01394 //  kDebug() << *pMatch;
01395 
01396     if ( !pMatch->isEmpty() ) {
01397 
01398         // Add '/' to directories in file completion mode
01399         // unless it has already been done
01400         if ( d->last_compl_type == CTFile
01401                && pMatch->at( pMatch->length()-1 ) != QLatin1Char('/') )
01402         {
01403             QString copy;
01404 
01405             if ( pMatch->startsWith( QLatin1String("file:") ) )
01406                 copy = KUrl(*pMatch).path();
01407             else
01408                 copy = *pMatch;
01409 
01410             expandTilde( copy );
01411             expandEnv( copy );
01412 #ifdef Q_WS_WIN
01413             DWORD dwAttr = GetFileAttributesW( (LPCWSTR) copy.utf16() );
01414             if ( dwAttr == INVALID_FILE_ATTRIBUTES ) {
01415                 kDebug() << "Could not get file attribs ( "
01416                     << GetLastError()
01417                     << " ) for "
01418                     << copy;
01419                         } else
01420             if ( ( dwAttr & FILE_ATTRIBUTE_DIRECTORY ) == FILE_ATTRIBUTE_DIRECTORY )
01421                 pMatch->append( QLatin1Char( '/' ) );
01422 #else
01423             if ( QDir::isRelativePath(copy) )
01424                 copy.prepend( d->cwd + QLatin1Char('/') );
01425 
01426 //          kDebug() << "stat'ing" << copy;
01427 
01428             KDE_struct_stat sbuff;
01429 
01430             QByteArray file = QFile::encodeName( copy );
01431 
01432             if ( KDE_stat( file.data(), &sbuff ) == 0 ) {
01433                 if ( S_ISDIR ( sbuff.st_mode ) )
01434                     pMatch->append( QLatin1Char( '/' ) );
01435             }
01436             else {
01437                 kDebug() << "Could not stat file" << copy;
01438             }
01439 #endif
01440         }
01441     }
01442 }
01443 
01444 void KUrlCompletion::postProcessMatches( QStringList * /*matches*/ ) const
01445 {
01446     // Maybe '/' should be added to directories here as in
01447     // postProcessMatch() but it would slow things down
01448     // when there are a lot of matches...
01449 }
01450 
01451 void KUrlCompletion::postProcessMatches( KCompletionMatches * /*matches*/ ) const
01452 {
01453     // Maybe '/' should be added to directories here as in
01454     // postProcessMatch() but it would slow things down
01455     // when there are a lot of matches...
01456 }
01457 
01458 void KUrlCompletion::customEvent(QEvent *e)
01459 {
01460     if ( e->type() == CompletionMatchEvent::uniqueType() ) {
01461 
01462         CompletionMatchEvent *matchEvent = static_cast<CompletionMatchEvent *>( e );
01463 
01464         matchEvent->completionThread()->wait();
01465 
01466         if ( !d->isListedUrl( CTUser ) ) {
01467             stop();
01468             clear();
01469             d->addMatches( matchEvent->completionThread()->matches() );
01470         }
01471 
01472         d->setListedUrl( CTUser );
01473 
01474         if ( d->userListThread == matchEvent->completionThread() )
01475             d->userListThread = 0;
01476 
01477         if ( d->dirListThread == matchEvent->completionThread() )
01478             d->dirListThread = 0;
01479 
01480         delete matchEvent->completionThread();
01481     }
01482 }
01483 
01484 // static
01485 QString KUrlCompletion::replacedPath( const QString& text, bool replaceHome, bool replaceEnv )
01486 {
01487     if ( text.isEmpty() )
01488         return text;
01489 
01490     KUrlCompletionPrivate::MyURL url( text, QString() ); // no need to replace something of our current cwd
01491     if ( !url.kurl()->isLocalFile() )
01492         return text;
01493 
01494     url.filter( replaceHome, replaceEnv );
01495     return url.dir() + url.file();
01496 }
01497 
01498 
01499 QString KUrlCompletion::replacedPath( const QString& text ) const
01500 {
01501     return replacedPath( text, d->replace_home, d->replace_env );
01502 }
01503 
01506 // Static functions
01507 
01508 /*
01509  * expandEnv
01510  *
01511  * Expand environment variables in text. Escaped '$' are ignored.
01512  * Return true if expansion was made.
01513  */
01514 static bool expandEnv( QString &text )
01515 {
01516     // Find all environment variables beginning with '$'
01517     //
01518     int pos = 0;
01519 
01520     bool expanded = false;
01521 
01522     while ( (pos = text.indexOf(QLatin1Char('$'), pos)) != -1 ) {
01523 
01524         // Skip escaped '$'
01525         //
01526         if ( pos > 0 && text.at(pos-1) == QLatin1Char('\\') ) {
01527             pos++;
01528         }
01529         // Variable found => expand
01530         //
01531         else {
01532             // Find the end of the variable = next '/' or ' '
01533             //
01534             int pos2 = text.indexOf( QLatin1Char(' '), pos+1 );
01535             int pos_tmp = text.indexOf( QLatin1Char('/'), pos+1 );
01536 
01537             if ( pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2) )
01538                 pos2 = pos_tmp;
01539 
01540             if ( pos2 == -1 )
01541                 pos2 = text.length();
01542 
01543             // Replace if the variable is terminated by '/' or ' '
01544             // and defined
01545             //
01546             if ( pos2 >= 0 ) {
01547                 int len = pos2 - pos;
01548                 QString key = text.mid( pos+1, len-1);
01549                 QString value =
01550                     QString::fromLocal8Bit( qgetenv(key.toLocal8Bit()) );
01551 
01552                 if ( !value.isEmpty() ) {
01553                     expanded = true;
01554                     text.replace( pos, len, value );
01555                     pos = pos + value.length();
01556                 }
01557                 else {
01558                     pos = pos2;
01559                 }
01560             }
01561         }
01562     }
01563 
01564     return expanded;
01565 }
01566 
01567 /*
01568  * expandTilde
01569  *
01570  * Replace "~user" with the users home directory
01571  * Return true if expansion was made.
01572  */
01573 static bool expandTilde(QString &text)
01574 {
01575     if ( text.isEmpty() || ( text.at(0) != QLatin1Char('~') ))
01576         return false;
01577 
01578     bool expanded = false;
01579 
01580     // Find the end of the user name = next '/' or ' '
01581     //
01582     int pos2 = text.indexOf( QLatin1Char(' '), 1 );
01583     int pos_tmp = text.indexOf( QLatin1Char('/'), 1 );
01584 
01585     if ( pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2) )
01586         pos2 = pos_tmp;
01587 
01588     if ( pos2 == -1 )
01589         pos2 = text.length();
01590 
01591     // Replace ~user if the user name is terminated by '/' or ' '
01592     //
01593     if ( pos2 >= 0 ) {
01594 
01595         QString user = text.mid( 1, pos2-1 );
01596         QString dir;
01597 
01598         // A single ~ is replaced with $HOME
01599         //
01600         if ( user.isEmpty() ) {
01601             dir = QDir::homePath();
01602         }
01603         // ~user is replaced with the dir from passwd
01604         //
01605         else {
01606             struct passwd *pw = ::getpwnam( user.toLocal8Bit() );
01607 
01608             if ( pw )
01609                 dir = QFile::decodeName( pw->pw_dir );
01610 
01611             ::endpwent();
01612         }
01613 
01614         if ( !dir.isEmpty() ) {
01615             expanded = true;
01616             text.replace(0, pos2, dir);
01617         }
01618     }
01619 
01620     return expanded;
01621 }
01622 
01623 /*
01624  * unescape
01625  *
01626  * Remove escapes and return the result in a new string
01627  *
01628  */
01629 static QString unescape(const QString &text)
01630 {
01631     QString result;
01632 
01633     for (int pos = 0; pos < text.length(); pos++)
01634         if ( text.at(pos) != QLatin1Char('\\') )
01635             result.insert( result.length(), text.at(pos) );
01636 
01637     return result;
01638 }
01639 
01640 #include "kurlcompletion.moc"

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal