You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1611 lines
37 KiB
C++
1611 lines
37 KiB
C++
/* -*- indent-tabs-mode: t; tab-width: 4; c-basic-offset:4 -*-
|
|
|
|
This file is part of the KDE libraries
|
|
Copyright (C) 2000 David Smith <dsmith@algonet.se>
|
|
Copyright (C) 2004 Scott Wheeler <wheeler@kde.org>
|
|
|
|
This class was inspired by a previous KURLCompletion by
|
|
Henner Zeller <zeller@think.de>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <limits.h>
|
|
|
|
#include <tqstring.h>
|
|
#include <tqstringlist.h>
|
|
#include <tqvaluelist.h>
|
|
#include <tqregexp.h>
|
|
#include <tqtimer.h>
|
|
#include <tqdir.h>
|
|
#include <tqfile.h>
|
|
#include <tqtextstream.h>
|
|
#include <tqdeepcopy.h>
|
|
#include <tqthread.h>
|
|
|
|
#include <tdeapplication.h>
|
|
#include <kdebug.h>
|
|
#include <kcompletion.h>
|
|
#include <kurl.h>
|
|
#include <tdeio/jobclasses.h>
|
|
#include <tdeio/job.h>
|
|
#include <kprotocolinfo.h>
|
|
#include <tdeconfig.h>
|
|
#include <tdeglobal.h>
|
|
#include <tdelocale.h>
|
|
#include <kde_file.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <pwd.h>
|
|
#include <time.h>
|
|
#include <sys/param.h>
|
|
|
|
#include "kurlcompletion.h"
|
|
|
|
static bool expandTilde(TQString &);
|
|
static bool expandEnv(TQString &);
|
|
|
|
static TQString unescape(const TQString &text);
|
|
|
|
// Permission mask for files that are executable by
|
|
// user, group or other
|
|
#define MODE_EXE (S_IXUSR | S_IXGRP | S_IXOTH)
|
|
|
|
// Constants for types of completion
|
|
enum ComplType {CTNone=0, CTEnv, CTUser, CTMan, CTExe, CTFile, CTUrl, CTInfo};
|
|
|
|
class CompletionThread;
|
|
|
|
/**
|
|
* A custom event type that is used to return a list of completion
|
|
* matches from an asyncrynous lookup.
|
|
*/
|
|
|
|
class CompletionMatchEvent : public TQCustomEvent
|
|
{
|
|
public:
|
|
CompletionMatchEvent( CompletionThread *thread ) :
|
|
TQCustomEvent( uniqueType() ),
|
|
m_completionThread( thread )
|
|
{}
|
|
|
|
CompletionThread *completionThread() const { return m_completionThread; }
|
|
static int uniqueType() { return User + 61080; }
|
|
|
|
private:
|
|
CompletionThread *m_completionThread;
|
|
};
|
|
|
|
class CompletionThread : public TQThread
|
|
{
|
|
protected:
|
|
CompletionThread( KURLCompletion *receiver ) :
|
|
TQThread(),
|
|
m_receiver( receiver ),
|
|
m_terminationRequested( false )
|
|
{}
|
|
|
|
public:
|
|
void requestTermination() { m_terminationRequested = true; }
|
|
TQDeepCopy<TQStringList> matches() const { return m_matches; }
|
|
|
|
protected:
|
|
void addMatch( const TQString &match ) { m_matches.append( match ); }
|
|
bool terminationRequested() const { return m_terminationRequested; }
|
|
void done()
|
|
{
|
|
if ( !m_terminationRequested )
|
|
kapp->postEvent( m_receiver, new CompletionMatchEvent( this ) );
|
|
else
|
|
delete this;
|
|
}
|
|
|
|
private:
|
|
KURLCompletion *m_receiver;
|
|
TQStringList m_matches;
|
|
bool m_terminationRequested;
|
|
};
|
|
|
|
/**
|
|
* A simple thread that fetches a list of tilde-completions and returns this
|
|
* to the caller via a CompletionMatchEvent.
|
|
*/
|
|
|
|
class UserListThread : public CompletionThread
|
|
{
|
|
public:
|
|
UserListThread( KURLCompletion *receiver ) :
|
|
CompletionThread( receiver )
|
|
{}
|
|
|
|
protected:
|
|
virtual void run()
|
|
{
|
|
static const TQChar tilde = '~';
|
|
|
|
struct passwd *pw;
|
|
while ( ( pw = ::getpwent() ) && !terminationRequested() )
|
|
addMatch( tilde + TQString::fromLocal8Bit( pw->pw_name ) );
|
|
|
|
::endpwent();
|
|
|
|
addMatch( tilde );
|
|
|
|
done();
|
|
}
|
|
};
|
|
|
|
class DirectoryListThread : public CompletionThread
|
|
{
|
|
public:
|
|
DirectoryListThread( KURLCompletion *receiver,
|
|
const TQStringList &dirList,
|
|
const TQString &filter,
|
|
bool onlyExe,
|
|
bool onlyDir,
|
|
bool noHidden,
|
|
bool appendSlashToDir ) :
|
|
CompletionThread( receiver ),
|
|
m_dirList( TQDeepCopy<TQStringList>( dirList ) ),
|
|
m_filter( TQDeepCopy<TQString>( filter ) ),
|
|
m_onlyExe( onlyExe ),
|
|
m_onlyDir( onlyDir ),
|
|
m_noHidden( noHidden ),
|
|
m_appendSlashToDir( appendSlashToDir )
|
|
{}
|
|
|
|
virtual void run();
|
|
|
|
private:
|
|
TQStringList m_dirList;
|
|
TQString m_filter;
|
|
bool m_onlyExe;
|
|
bool m_onlyDir;
|
|
bool m_noHidden;
|
|
bool m_appendSlashToDir;
|
|
};
|
|
|
|
void DirectoryListThread::run()
|
|
{
|
|
// Thread safety notes:
|
|
//
|
|
// There very possibly may be thread safety issues here, but I've done a check
|
|
// of all of the things that would seem to be problematic. Here are a few
|
|
// things that I have checked to be safe here (some used indirectly):
|
|
//
|
|
// TQDir::currentDirPath(), TQDir::setCurrent(), TQFile::decodeName(), TQFile::encodeName()
|
|
// TQString::fromLocal8Bit(), TQString::local8Bit(), TQTextCodec::codecForLocale()
|
|
//
|
|
// Also see (for POSIX functions):
|
|
// http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_09.html
|
|
|
|
DIR *dir = 0;
|
|
|
|
for ( TQStringList::ConstIterator it = m_dirList.begin();
|
|
it != m_dirList.end() && !terminationRequested();
|
|
++it )
|
|
{
|
|
// Open the next directory
|
|
|
|
if ( !dir ) {
|
|
dir = ::opendir( TQFile::encodeName( *it ) );
|
|
if ( ! dir ) {
|
|
kdDebug() << "Failed to open dir: " << *it << endl;
|
|
done();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// A trick from TDEIO that helps performance by a little bit:
|
|
// chdir to the directroy so we won't have to deal with full paths
|
|
// with stat()
|
|
|
|
TQString path = TQDir::currentDirPath();
|
|
TQDir::setCurrent( *it );
|
|
|
|
// Loop through all directory entries
|
|
// Solaris and IRIX dirent structures do not allocate space for d_name. On
|
|
// systems that do (HP-UX, Linux, Tru64 UNIX), we overallocate space but
|
|
// that's ok.
|
|
#ifndef HAVE_READDIR_R
|
|
struct dirent *dirEntry = 0;
|
|
while ( !terminationRequested() &&
|
|
(dirEntry = ::readdir( dir)))
|
|
#else
|
|
#if !defined(MAXPATHLEN) && defined(__GNU__)
|
|
#define MAXPATHLEN UCHAR_MAX
|
|
#endif
|
|
struct dirent *dirPosition = (struct dirent *) malloc( sizeof( struct dirent ) + MAXPATHLEN + 1 );
|
|
struct dirent *dirEntry = 0;
|
|
while ( !terminationRequested() &&
|
|
::readdir_r( dir, dirPosition, &dirEntry ) == 0 && dirEntry )
|
|
#endif
|
|
|
|
{
|
|
// Skip hidden files if m_noHidden is true
|
|
|
|
if ( dirEntry->d_name[0] == '.' && m_noHidden )
|
|
continue;
|
|
|
|
// Skip "."
|
|
|
|
if ( dirEntry->d_name[0] == '.' && dirEntry->d_name[1] == '\0' )
|
|
continue;
|
|
|
|
// Skip ".."
|
|
|
|
if ( dirEntry->d_name[0] == '.' && dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0' )
|
|
continue;
|
|
|
|
TQString file = TQFile::decodeName( dirEntry->d_name );
|
|
|
|
if ( m_filter.isEmpty() || file.startsWith( m_filter ) ) {
|
|
|
|
if ( m_onlyExe || m_onlyDir || m_appendSlashToDir ) {
|
|
KDE_struct_stat sbuff;
|
|
|
|
if ( KDE_stat( dirEntry->d_name, &sbuff ) == 0 ) {
|
|
|
|
// Verify executable
|
|
|
|
if ( m_onlyExe && ( sbuff.st_mode & MODE_EXE ) == 0 )
|
|
continue;
|
|
|
|
// Verify directory
|
|
|
|
if ( m_onlyDir && !S_ISDIR( sbuff.st_mode ) )
|
|
continue;
|
|
|
|
// Add '/' to directories
|
|
|
|
if ( m_appendSlashToDir && S_ISDIR( sbuff.st_mode ) )
|
|
file.append( '/' );
|
|
|
|
}
|
|
else {
|
|
kdDebug() << "Could not stat file " << file << endl;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
addMatch( file );
|
|
}
|
|
}
|
|
|
|
// chdir to the original directory
|
|
|
|
TQDir::setCurrent( path );
|
|
|
|
::closedir( dir );
|
|
dir = 0;
|
|
#ifdef HAVE_READDIR_R
|
|
free( dirPosition );
|
|
#endif
|
|
}
|
|
|
|
done();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////
|
|
// MyURL - wrapper for KURL with some different functionality
|
|
//
|
|
|
|
class KURLCompletion::MyURL
|
|
{
|
|
public:
|
|
MyURL(const TQString &url, const TQString &cwd);
|
|
MyURL(const MyURL &url);
|
|
~MyURL();
|
|
|
|
KURL *kurl() const { return m_kurl; }
|
|
|
|
TQString protocol() const { return m_kurl->protocol(); }
|
|
// The directory with a trailing '/'
|
|
TQString dir() const { return m_kurl->directory(false, false); }
|
|
TQString file() const { return m_kurl->fileName(false); }
|
|
|
|
// The initial, unparsed, url, as a string.
|
|
TQString url() const { return m_url; }
|
|
|
|
// Is the initial string a URL, or just a path (whether absolute or relative)
|
|
bool isURL() const { return m_isURL; }
|
|
|
|
void filter( bool replace_user_dir, bool replace_env );
|
|
|
|
private:
|
|
void init(const TQString &url, const TQString &cwd);
|
|
|
|
KURL *m_kurl;
|
|
TQString m_url;
|
|
bool m_isURL;
|
|
};
|
|
|
|
KURLCompletion::MyURL::MyURL(const TQString &url, const TQString &cwd)
|
|
{
|
|
init(url, cwd);
|
|
}
|
|
|
|
KURLCompletion::MyURL::MyURL(const MyURL &url)
|
|
{
|
|
m_kurl = new KURL( *(url.m_kurl) );
|
|
m_url = url.m_url;
|
|
m_isURL = url.m_isURL;
|
|
}
|
|
|
|
void KURLCompletion::MyURL::init(const TQString &url, const TQString &cwd)
|
|
{
|
|
// Save the original text
|
|
m_url = url;
|
|
|
|
// Non-const copy
|
|
TQString url_copy = url;
|
|
|
|
// Special shortcuts for "man:" and "info:"
|
|
if ( url_copy[0] == '#' ) {
|
|
if ( url_copy[1] == '#' )
|
|
url_copy.replace( 0, 2, TQString("info:") );
|
|
else
|
|
url_copy.replace( 0, 1, TQString("man:") );
|
|
}
|
|
|
|
// Look for a protocol in 'url'
|
|
TQRegExp protocol_regex = TQRegExp( "^[^/\\s\\\\]*:" );
|
|
|
|
// Assume "file:" or whatever is given by 'cwd' if there is
|
|
// no protocol. (KURL does this only for absoute paths)
|
|
if ( protocol_regex.search( url_copy ) == 0 )
|
|
{
|
|
m_kurl = new KURL( url_copy );
|
|
m_isURL = true;
|
|
}
|
|
else // relative path or ~ or $something
|
|
{
|
|
m_isURL = false;
|
|
if ( cwd.isEmpty() )
|
|
{
|
|
m_kurl = new KURL();
|
|
if ( !TQDir::isRelativePath(url_copy) || url_copy[0] == '$' || url_copy[0] == '~' )
|
|
m_kurl->setPath( url_copy );
|
|
else
|
|
*m_kurl = url_copy;
|
|
}
|
|
else
|
|
{
|
|
KURL base = KURL::fromPathOrURL( cwd );
|
|
base.adjustPath(+1);
|
|
|
|
if ( !TQDir::isRelativePath(url_copy) || url_copy[0] == '~' || url_copy[0] == '$' )
|
|
{
|
|
m_kurl = new KURL();
|
|
m_kurl->setPath( url_copy );
|
|
}
|
|
else // relative path
|
|
{
|
|
//m_kurl = new KURL( base, url_copy );
|
|
m_kurl = new KURL( base );
|
|
m_kurl->addPath( url_copy );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
KURLCompletion::MyURL::~MyURL()
|
|
{
|
|
delete m_kurl;
|
|
}
|
|
|
|
void KURLCompletion::MyURL::filter( bool replace_user_dir, bool replace_env )
|
|
{
|
|
TQString d = dir() + file();
|
|
if ( replace_user_dir ) expandTilde( d );
|
|
if ( replace_env ) expandEnv( d );
|
|
m_kurl->setPath( d );
|
|
}
|
|
|
|
///////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////
|
|
// KURLCompletionPrivate
|
|
//
|
|
class KURLCompletionPrivate
|
|
{
|
|
public:
|
|
KURLCompletionPrivate() : url_auto_completion(true),
|
|
userListThread(0),
|
|
dirListThread(0) {}
|
|
~KURLCompletionPrivate();
|
|
|
|
TQValueList<KURL*> list_urls;
|
|
|
|
bool onlyLocalProto;
|
|
|
|
// urlCompletion() in Auto/Popup mode?
|
|
bool url_auto_completion;
|
|
|
|
// Append '/' to directories in Popup mode?
|
|
// Doing that stat's all files and is slower
|
|
bool popup_append_slash;
|
|
|
|
// Keep track of currently listed files to avoid reading them again
|
|
TQString last_path_listed;
|
|
TQString last_file_listed;
|
|
TQString last_prepend;
|
|
int last_compl_type;
|
|
int last_no_hidden;
|
|
|
|
TQString cwd; // "current directory" = base dir for completion
|
|
|
|
KURLCompletion::Mode mode; // ExeCompletion, FileCompletion, DirCompletion
|
|
bool replace_env;
|
|
bool replace_home;
|
|
bool complete_url; // if true completing a URL (i.e. 'prepend' is a URL), otherwise a path
|
|
|
|
TDEIO::ListJob *list_job; // tdeio job to list directories
|
|
|
|
TQString prepend; // text to prepend to listed items
|
|
TQString compl_text; // text to pass on to TDECompletion
|
|
|
|
// Filters for files read with tdeio
|
|
bool list_urls_only_exe; // true = only list executables
|
|
bool list_urls_no_hidden;
|
|
TQString list_urls_filter; // filter for listed files
|
|
|
|
CompletionThread *userListThread;
|
|
CompletionThread *dirListThread;
|
|
};
|
|
|
|
KURLCompletionPrivate::~KURLCompletionPrivate()
|
|
{
|
|
if ( userListThread )
|
|
userListThread->requestTermination();
|
|
if ( dirListThread )
|
|
dirListThread->requestTermination();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////
|
|
// KURLCompletion
|
|
//
|
|
|
|
KURLCompletion::KURLCompletion() : TDECompletion()
|
|
{
|
|
init();
|
|
}
|
|
|
|
|
|
KURLCompletion::KURLCompletion( Mode mode ) : TDECompletion()
|
|
{
|
|
init();
|
|
setMode ( mode );
|
|
}
|
|
|
|
KURLCompletion::~KURLCompletion()
|
|
{
|
|
stop();
|
|
delete d;
|
|
}
|
|
|
|
|
|
void KURLCompletion::init()
|
|
{
|
|
d = new KURLCompletionPrivate;
|
|
|
|
d->cwd = TQDir::homeDirPath();
|
|
|
|
d->replace_home = true;
|
|
d->replace_env = true;
|
|
d->last_no_hidden = false;
|
|
d->last_compl_type = 0;
|
|
d->list_job = 0L;
|
|
d->mode = KURLCompletion::FileCompletion;
|
|
|
|
// Read settings
|
|
TDEConfig *c = TDEGlobal::config();
|
|
TDEConfigGroupSaver cgs( c, "URLCompletion" );
|
|
|
|
d->url_auto_completion = c->readBoolEntry("alwaysAutoComplete", true);
|
|
d->popup_append_slash = c->readBoolEntry("popupAppendSlash", true);
|
|
d->onlyLocalProto = c->readBoolEntry("LocalProtocolsOnly", false);
|
|
}
|
|
|
|
void KURLCompletion::setDir(const TQString &dir)
|
|
{
|
|
d->cwd = dir;
|
|
}
|
|
|
|
TQString KURLCompletion::dir() const
|
|
{
|
|
return d->cwd;
|
|
}
|
|
|
|
KURLCompletion::Mode KURLCompletion::mode() const
|
|
{
|
|
return d->mode;
|
|
}
|
|
|
|
void KURLCompletion::setMode( Mode mode )
|
|
{
|
|
d->mode = mode;
|
|
}
|
|
|
|
bool KURLCompletion::replaceEnv() const
|
|
{
|
|
return d->replace_env;
|
|
}
|
|
|
|
void KURLCompletion::setReplaceEnv( bool replace )
|
|
{
|
|
d->replace_env = replace;
|
|
}
|
|
|
|
bool KURLCompletion::replaceHome() const
|
|
{
|
|
return d->replace_home;
|
|
}
|
|
|
|
void KURLCompletion::setReplaceHome( bool replace )
|
|
{
|
|
d->replace_home = replace;
|
|
}
|
|
|
|
/*
|
|
* makeCompletion()
|
|
*
|
|
* Entry point for file name completion
|
|
*/
|
|
TQString KURLCompletion::makeCompletion(const TQString &text)
|
|
{
|
|
//kdDebug() << "KURLCompletion::makeCompletion: " << text << " d->cwd=" << d->cwd << endl;
|
|
|
|
MyURL url(text, d->cwd);
|
|
|
|
d->compl_text = text;
|
|
|
|
// Set d->prepend to the original URL, with the filename [and ref/query] stripped.
|
|
// This is what gets prepended to the directory-listing matches.
|
|
int toRemove = url.file().length() - url.kurl()->query().length();
|
|
if ( url.kurl()->hasRef() )
|
|
toRemove += url.kurl()->ref().length() + 1;
|
|
d->prepend = text.left( text.length() - toRemove );
|
|
d->complete_url = url.isURL();
|
|
|
|
TQString match;
|
|
|
|
// Environment variables
|
|
//
|
|
if ( d->replace_env && envCompletion( url, &match ) )
|
|
return match;
|
|
|
|
// User directories
|
|
//
|
|
if ( d->replace_home && userCompletion( url, &match ) )
|
|
return match;
|
|
|
|
// Replace user directories and variables
|
|
url.filter( d->replace_home, d->replace_env );
|
|
|
|
//kdDebug() << "Filtered: proto=" << url.protocol()
|
|
// << ", dir=" << url.dir()
|
|
// << ", file=" << url.file()
|
|
// << ", kurl url=" << *url.kurl() << endl;
|
|
|
|
if ( d->mode == ExeCompletion ) {
|
|
// Executables
|
|
//
|
|
if ( exeCompletion( url, &match ) )
|
|
return match;
|
|
|
|
// KRun can run "man:" and "info:" etc. so why not treat them
|
|
// as executables...
|
|
|
|
if ( urlCompletion( url, &match ) )
|
|
return match;
|
|
}
|
|
else if ( d->mode == SystemExeCompletion ) {
|
|
// Executables
|
|
//
|
|
if ( systemexeCompletion( url, &match ) )
|
|
return match;
|
|
|
|
// KRun can run "man:" and "info:" etc. so why not treat them
|
|
// as executables...
|
|
|
|
if ( urlCompletion( url, &match ) )
|
|
return match;
|
|
}
|
|
else {
|
|
// Local files, directories
|
|
//
|
|
if ( fileCompletion( url, &match ) )
|
|
return match;
|
|
|
|
// All other...
|
|
//
|
|
if ( urlCompletion( url, &match ) )
|
|
return match;
|
|
}
|
|
|
|
setListedURL( CTNone );
|
|
stop();
|
|
|
|
return TQString::null;
|
|
}
|
|
|
|
/*
|
|
* finished
|
|
*
|
|
* Go on and call TDECompletion.
|
|
* Called when all matches have been added
|
|
*/
|
|
TQString KURLCompletion::finished()
|
|
{
|
|
if ( d->last_compl_type == CTInfo )
|
|
return TDECompletion::makeCompletion( d->compl_text.lower() );
|
|
else
|
|
return TDECompletion::makeCompletion( d->compl_text );
|
|
}
|
|
|
|
/*
|
|
* isRunning
|
|
*
|
|
* Return true if either a TDEIO job or the DirLister
|
|
* is running
|
|
*/
|
|
bool KURLCompletion::isRunning() const
|
|
{
|
|
return d->list_job || (d->dirListThread && !d->dirListThread->finished());
|
|
}
|
|
|
|
/*
|
|
* stop
|
|
*
|
|
* Stop and delete a running TDEIO job or the DirLister
|
|
*/
|
|
void KURLCompletion::stop()
|
|
{
|
|
if ( d->list_job ) {
|
|
d->list_job->kill();
|
|
d->list_job = 0L;
|
|
}
|
|
|
|
if ( !d->list_urls.isEmpty() ) {
|
|
TQValueList<KURL*>::Iterator it = d->list_urls.begin();
|
|
for ( ; it != d->list_urls.end(); it++ )
|
|
delete (*it);
|
|
d->list_urls.clear();
|
|
}
|
|
|
|
if ( d->dirListThread ) {
|
|
d->dirListThread->requestTermination();
|
|
d->dirListThread = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Keep track of the last listed directory
|
|
*/
|
|
void KURLCompletion::setListedURL( int complType,
|
|
const TQString& dir,
|
|
const TQString& filter,
|
|
bool no_hidden )
|
|
{
|
|
d->last_compl_type = complType;
|
|
d->last_path_listed = dir;
|
|
d->last_file_listed = filter;
|
|
d->last_no_hidden = (int)no_hidden;
|
|
d->last_prepend = d->prepend;
|
|
}
|
|
|
|
bool KURLCompletion::isListedURL( int complType,
|
|
const TQString& dir,
|
|
const TQString& filter,
|
|
bool no_hidden )
|
|
{
|
|
return d->last_compl_type == complType
|
|
&& ( d->last_path_listed == dir
|
|
|| (dir.isEmpty() && d->last_path_listed.isEmpty()) )
|
|
&& ( filter.startsWith(d->last_file_listed)
|
|
|| (filter.isEmpty() && d->last_file_listed.isEmpty()) )
|
|
&& d->last_no_hidden == (int)no_hidden
|
|
&& d->last_prepend == d->prepend; // e.g. relative path vs absolute
|
|
}
|
|
|
|
/*
|
|
* isAutoCompletion
|
|
*
|
|
* Returns true if completion mode is Auto or Popup
|
|
*/
|
|
bool KURLCompletion::isAutoCompletion()
|
|
{
|
|
return completionMode() == TDEGlobalSettings::CompletionAuto
|
|
|| completionMode() == TDEGlobalSettings::CompletionPopup
|
|
|| completionMode() == TDEGlobalSettings::CompletionMan
|
|
|| completionMode() == TDEGlobalSettings::CompletionPopupAuto;
|
|
}
|
|
//////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////
|
|
// User directories
|
|
//
|
|
|
|
bool KURLCompletion::userCompletion(const MyURL &url, TQString *match)
|
|
{
|
|
if ( url.protocol() != "file"
|
|
|| !url.dir().isEmpty()
|
|
|| url.file().at(0) != '~' )
|
|
return false;
|
|
|
|
if ( !isListedURL( CTUser ) ) {
|
|
stop();
|
|
clear();
|
|
|
|
if ( !d->userListThread ) {
|
|
d->userListThread = new UserListThread( this );
|
|
d->userListThread->start();
|
|
|
|
// If the thread finishes quickly make sure that the results
|
|
// are added to the first matching case.
|
|
|
|
d->userListThread->wait( 200 );
|
|
TQStringList l = d->userListThread->matches();
|
|
addMatches( l );
|
|
}
|
|
}
|
|
*match = finished();
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////
|
|
// Environment variables
|
|
//
|
|
|
|
#if !defined(__OpenBSD__) && !defined(__FreeBSD__)
|
|
extern char **environ; // Array of environment variables
|
|
#endif
|
|
|
|
bool KURLCompletion::envCompletion(const MyURL &url, TQString *match)
|
|
{
|
|
#if defined(__OpenBSD__) || defined(__FreeBSD__)
|
|
return false;
|
|
#else
|
|
if ( url.file().at(0) != '$' )
|
|
return false;
|
|
|
|
if ( !isListedURL( CTEnv ) ) {
|
|
stop();
|
|
clear();
|
|
|
|
char **env = environ;
|
|
|
|
TQString dollar = TQString("$");
|
|
|
|
TQStringList l;
|
|
|
|
while ( *env ) {
|
|
TQString s = TQString::fromLocal8Bit( *env );
|
|
|
|
int pos = s.find('=');
|
|
|
|
if ( pos == -1 )
|
|
pos = s.length();
|
|
|
|
if ( pos > 0 )
|
|
l.append( dollar + s.left(pos) );
|
|
|
|
env++;
|
|
}
|
|
|
|
addMatches( l );
|
|
}
|
|
|
|
setListedURL( CTEnv );
|
|
|
|
*match = finished();
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////
|
|
// Executables
|
|
//
|
|
|
|
bool KURLCompletion::exeCompletion(const MyURL &url, TQString *match)
|
|
{
|
|
if ( url.protocol() != "file" )
|
|
return false;
|
|
|
|
TQString dir = url.dir();
|
|
|
|
dir = unescape( dir ); // remove escapes
|
|
|
|
// Find directories to search for completions, either
|
|
//
|
|
// 1. complete path given in url
|
|
// 2. current directory (d->cwd)
|
|
// 3. $PATH
|
|
// 4. no directory at all
|
|
|
|
TQStringList dirList;
|
|
|
|
if ( !TQDir::isRelativePath(dir) ) {
|
|
// complete path in url
|
|
dirList.append( dir );
|
|
}
|
|
else if ( !dir.isEmpty() && !d->cwd.isEmpty() ) {
|
|
// current directory
|
|
dirList.append( d->cwd + '/' + dir );
|
|
}
|
|
else if ( !url.file().isEmpty() ) {
|
|
// $PATH
|
|
dirList = TQStringList::split(KPATH_SEPARATOR,
|
|
TQString::fromLocal8Bit(::getenv("PATH")));
|
|
|
|
TQStringList::Iterator it = dirList.begin();
|
|
|
|
for ( ; it != dirList.end(); it++ )
|
|
(*it).append('/');
|
|
}
|
|
|
|
// No hidden files unless the user types "."
|
|
bool no_hidden_files = url.file().at(0) != '.';
|
|
|
|
// List files if needed
|
|
//
|
|
if ( !isListedURL( CTExe, dir, url.file(), no_hidden_files ) )
|
|
{
|
|
stop();
|
|
clear();
|
|
|
|
setListedURL( CTExe, dir, url.file(), no_hidden_files );
|
|
|
|
*match = listDirectories( dirList, url.file(), true, false, no_hidden_files );
|
|
}
|
|
else if ( !isRunning() ) {
|
|
*match = finished();
|
|
}
|
|
else {
|
|
if ( d->dirListThread )
|
|
setListedURL( CTExe, dir, url.file(), no_hidden_files );
|
|
*match = TQString::null;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////
|
|
// System Executables
|
|
//
|
|
|
|
bool KURLCompletion::systemexeCompletion(const MyURL &url, TQString *match)
|
|
{
|
|
if ( url.protocol() != "file" )
|
|
return false;
|
|
|
|
TQString dir = url.dir();
|
|
|
|
dir = unescape( dir ); // remove escapes
|
|
|
|
// Find directories to search for completions, either
|
|
//
|
|
// 1. complete path given in url
|
|
// 2. current directory (d->cwd)
|
|
// 3. $PATH
|
|
// 4. no directory at all
|
|
|
|
TQStringList dirList;
|
|
|
|
if ( !url.file().isEmpty() ) {
|
|
// $PATH
|
|
dirList = TQStringList::split(KPATH_SEPARATOR,
|
|
TQString::fromLocal8Bit(::getenv("PATH")));
|
|
|
|
TQStringList::Iterator it = dirList.begin();
|
|
|
|
for ( ; it != dirList.end(); it++ )
|
|
(*it).append('/');
|
|
}
|
|
|
|
// No hidden files unless the user types "."
|
|
bool no_hidden_files = url.file().at(0) != '.';
|
|
|
|
// List files if needed
|
|
//
|
|
if ( !isListedURL( CTExe, dir, url.file(), no_hidden_files ) )
|
|
{
|
|
stop();
|
|
clear();
|
|
|
|
setListedURL( CTExe, dir, url.file(), no_hidden_files );
|
|
|
|
*match = listDirectories( dirList, url.file(), true, false, no_hidden_files );
|
|
}
|
|
else if ( !isRunning() ) {
|
|
*match = finished();
|
|
}
|
|
else {
|
|
if ( d->dirListThread )
|
|
setListedURL( CTExe, dir, url.file(), no_hidden_files );
|
|
*match = TQString::null;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////
|
|
// Local files
|
|
//
|
|
|
|
bool KURLCompletion::fileCompletion(const MyURL &url, TQString *match)
|
|
{
|
|
if ( url.protocol() != "file" )
|
|
return false;
|
|
|
|
TQString dir = url.dir();
|
|
|
|
if (url.url()[0] == '.')
|
|
{
|
|
if (url.url().length() == 1)
|
|
{
|
|
*match =
|
|
( completionMode() == TDEGlobalSettings::CompletionMan )? "." : "..";
|
|
return true;
|
|
}
|
|
if (url.url().length() == 2 && url.url()[1]=='.')
|
|
{
|
|
*match="..";
|
|
return true;
|
|
}
|
|
}
|
|
|
|
//kdDebug() << "fileCompletion " << url.url() << " dir=" << dir << endl;
|
|
|
|
dir = unescape( dir ); // remove escapes
|
|
|
|
// Find directories to search for completions, either
|
|
//
|
|
// 1. complete path given in url
|
|
// 2. current directory (d->cwd)
|
|
// 3. no directory at all
|
|
|
|
TQStringList dirList;
|
|
|
|
if ( !TQDir::isRelativePath(dir) ) {
|
|
// complete path in url
|
|
dirList.append( dir );
|
|
}
|
|
else if ( !d->cwd.isEmpty() ) {
|
|
// current directory
|
|
dirList.append( d->cwd + '/' + dir );
|
|
}
|
|
|
|
// No hidden files unless the user types "."
|
|
bool no_hidden_files = ( url.file().at(0) != '.' );
|
|
|
|
// List files if needed
|
|
//
|
|
if ( !isListedURL( CTFile, dir, "", no_hidden_files ) )
|
|
{
|
|
stop();
|
|
clear();
|
|
|
|
setListedURL( CTFile, dir, "", no_hidden_files );
|
|
|
|
// Append '/' to directories in Popup mode?
|
|
bool append_slash = ( d->popup_append_slash
|
|
&& (completionMode() == TDEGlobalSettings::CompletionPopup ||
|
|
completionMode() == TDEGlobalSettings::CompletionPopupAuto ) );
|
|
|
|
bool only_dir = ( d->mode == DirCompletion );
|
|
|
|
*match = listDirectories( dirList, "", false, only_dir, no_hidden_files,
|
|
append_slash );
|
|
}
|
|
else if ( !isRunning() ) {
|
|
*match = finished();
|
|
}
|
|
else {
|
|
*match = TQString::null;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////
|
|
// URLs not handled elsewhere...
|
|
//
|
|
|
|
bool KURLCompletion::urlCompletion(const MyURL &url, TQString *match)
|
|
{
|
|
//kdDebug() << "urlCompletion: url = " << *url.kurl() << endl;
|
|
if (d->onlyLocalProto && KProtocolInfo::protocolClass(url.protocol()) != ":local")
|
|
return false;
|
|
|
|
// Use d->cwd as base url in case url is not absolute
|
|
KURL url_cwd = KURL::fromPathOrURL( d->cwd );
|
|
|
|
// Create an URL with the directory to be listed
|
|
KURL url_dir( url_cwd, url.kurl()->url() );
|
|
|
|
// Don't try url completion if
|
|
// 1. malformed url
|
|
// 2. protocol that doesn't have listDir()
|
|
// 3. there is no directory (e.g. "ftp://ftp.kd" shouldn't do anything)
|
|
// 4. auto or popup completion mode depending on settings
|
|
|
|
bool man_or_info = ( url_dir.protocol() == TQString("man")
|
|
|| url_dir.protocol() == TQString("info") );
|
|
|
|
if ( !url_dir.isValid()
|
|
|| !KProtocolInfo::supportsListing( url_dir )
|
|
|| ( !man_or_info
|
|
&& ( url_dir.directory(false,false).isEmpty()
|
|
|| ( isAutoCompletion()
|
|
&& !d->url_auto_completion ) ) ) ) {
|
|
return false;
|
|
}
|
|
|
|
url_dir.setFileName(""); // not really nesseccary, but clear the filename anyway...
|
|
|
|
// Remove escapes
|
|
TQString dir = url_dir.directory( false, false );
|
|
|
|
dir = unescape( dir );
|
|
|
|
url_dir.setPath( dir );
|
|
|
|
// List files if needed
|
|
//
|
|
if ( !isListedURL( CTUrl, url_dir.prettyURL(), url.file() ) )
|
|
{
|
|
stop();
|
|
clear();
|
|
|
|
setListedURL( CTUrl, url_dir.prettyURL(), "" );
|
|
|
|
TQValueList<KURL*> url_list;
|
|
url_list.append( new KURL( url_dir ) );
|
|
|
|
listURLs( url_list, "", false );
|
|
|
|
*match = TQString::null;
|
|
}
|
|
else if ( !isRunning() ) {
|
|
*match = finished();
|
|
}
|
|
else {
|
|
*match = TQString::null;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////
|
|
// Directory and URL listing
|
|
//
|
|
|
|
/*
|
|
* addMatches
|
|
*
|
|
* Called to add matches to TDECompletion
|
|
*/
|
|
void KURLCompletion::addMatches( const TQStringList &matches )
|
|
{
|
|
TQStringList::ConstIterator it = matches.begin();
|
|
TQStringList::ConstIterator end = matches.end();
|
|
|
|
if ( d->complete_url )
|
|
for ( ; it != end; it++ )
|
|
addItem( d->prepend + KURL::encode_string(*it));
|
|
else
|
|
for ( ; it != end; it++ )
|
|
addItem( d->prepend + (*it));
|
|
}
|
|
|
|
/*
|
|
* listDirectories
|
|
*
|
|
* List files starting with 'filter' in the given directories,
|
|
* either using DirLister or listURLs()
|
|
*
|
|
* In either case, addMatches() is called with the listed
|
|
* files, and eventually finished() when the listing is done
|
|
*
|
|
* Returns the match if available, or TQString::null if
|
|
* DirLister timed out or using tdeio
|
|
*/
|
|
TQString KURLCompletion::listDirectories(
|
|
const TQStringList &dirList,
|
|
const TQString &filter,
|
|
bool only_exe,
|
|
bool only_dir,
|
|
bool no_hidden,
|
|
bool append_slash_to_dir)
|
|
{
|
|
assert( !isRunning() );
|
|
|
|
if ( !::getenv("KURLCOMPLETION_LOCAL_TDEIO") ) {
|
|
|
|
//kdDebug() << "Listing (listDirectories): " << dirList << " filter=" << filter << " without TDEIO" << endl;
|
|
|
|
// Don't use TDEIO
|
|
|
|
if ( d->dirListThread )
|
|
d->dirListThread->requestTermination();
|
|
|
|
TQStringList dirs;
|
|
|
|
for ( TQStringList::ConstIterator it = dirList.begin();
|
|
it != dirList.end();
|
|
++it )
|
|
{
|
|
KURL url;
|
|
url.setPath(*it);
|
|
if ( kapp->authorizeURLAction( "list", KURL(), url ) )
|
|
dirs.append( *it );
|
|
}
|
|
|
|
d->dirListThread = new DirectoryListThread( this, dirs, filter, only_exe, only_dir,
|
|
no_hidden, append_slash_to_dir );
|
|
d->dirListThread->start();
|
|
d->dirListThread->wait( 200 );
|
|
addMatches( d->dirListThread->matches() );
|
|
|
|
return finished();
|
|
}
|
|
else {
|
|
|
|
// Use TDEIO
|
|
//kdDebug() << "Listing (listDirectories): " << dirList << " with TDEIO" << endl;
|
|
|
|
TQValueList<KURL*> url_list;
|
|
|
|
TQStringList::ConstIterator it = dirList.begin();
|
|
|
|
for ( ; it != dirList.end(); it++ )
|
|
url_list.append( new KURL(*it) );
|
|
|
|
listURLs( url_list, filter, only_exe, no_hidden );
|
|
// Will call addMatches() and finished()
|
|
|
|
return TQString::null;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* listURLs
|
|
*
|
|
* Use TDEIO to list the given urls
|
|
*
|
|
* addMatches() is called with the listed files
|
|
* finished() is called when the listing is done
|
|
*/
|
|
void KURLCompletion::listURLs(
|
|
const TQValueList<KURL *> &urls,
|
|
const TQString &filter,
|
|
bool only_exe,
|
|
bool no_hidden )
|
|
{
|
|
assert( d->list_urls.isEmpty() );
|
|
assert( d->list_job == 0L );
|
|
|
|
d->list_urls = urls;
|
|
d->list_urls_filter = filter;
|
|
d->list_urls_only_exe = only_exe;
|
|
d->list_urls_no_hidden = no_hidden;
|
|
|
|
// kdDebug() << "Listing URLs: " << urls[0]->prettyURL() << ",..." << endl;
|
|
|
|
// Start it off by calling slotIOFinished
|
|
//
|
|
// This will start a new list job as long as there
|
|
// are urls in d->list_urls
|
|
//
|
|
slotIOFinished(0L);
|
|
}
|
|
|
|
/*
|
|
* slotEntries
|
|
*
|
|
* Receive files listed by TDEIO and call addMatches()
|
|
*/
|
|
void KURLCompletion::slotEntries(TDEIO::Job*, const TDEIO::UDSEntryList& entries)
|
|
{
|
|
TQStringList matches;
|
|
|
|
TDEIO::UDSEntryListConstIterator it = entries.begin();
|
|
TDEIO::UDSEntryListConstIterator end = entries.end();
|
|
|
|
TQString filter = d->list_urls_filter;
|
|
|
|
int filter_len = filter.length();
|
|
|
|
// Iterate over all files
|
|
//
|
|
for (; it != end; ++it) {
|
|
TQString name;
|
|
TQString url;
|
|
bool is_exe = false;
|
|
bool is_dir = false;
|
|
|
|
TDEIO::UDSEntry e = *it;
|
|
TDEIO::UDSEntry::ConstIterator it_2 = e.begin();
|
|
|
|
for( ; it_2 != e.end(); it_2++ ) {
|
|
switch ( (*it_2).m_uds ) {
|
|
case TDEIO::UDS_NAME:
|
|
name = (*it_2).m_str;
|
|
break;
|
|
case TDEIO::UDS_ACCESS:
|
|
is_exe = ((*it_2).m_long & MODE_EXE) != 0;
|
|
break;
|
|
case TDEIO::UDS_FILE_TYPE:
|
|
is_dir = ((*it_2).m_long & S_IFDIR) != 0;
|
|
break;
|
|
case TDEIO::UDS_URL:
|
|
url = (*it_2).m_str;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!url.isEmpty()) {
|
|
// kdDebug() << "KURLCompletion::slotEntries url: " << url << endl;
|
|
name = KURL(url).fileName();
|
|
}
|
|
|
|
// kdDebug() << "KURLCompletion::slotEntries name: " << name << endl;
|
|
|
|
if ( name[0] == '.' &&
|
|
( d->list_urls_no_hidden ||
|
|
name.length() == 1 ||
|
|
( name.length() == 2 && name[1] == '.' ) ) )
|
|
continue;
|
|
|
|
if ( d->mode == DirCompletion && !is_dir )
|
|
continue;
|
|
|
|
if ( filter_len == 0 || name.left(filter_len) == filter ) {
|
|
if ( is_dir )
|
|
name.append( '/' );
|
|
|
|
if ( is_exe || !d->list_urls_only_exe )
|
|
matches.append( name );
|
|
}
|
|
}
|
|
|
|
addMatches( matches );
|
|
}
|
|
|
|
/*
|
|
* slotIOFinished
|
|
*
|
|
* Called when a TDEIO job is finished.
|
|
*
|
|
* Start a new list job if there are still urls in
|
|
* d->list_urls, otherwise call finished()
|
|
*/
|
|
void KURLCompletion::slotIOFinished( TDEIO::Job * job )
|
|
{
|
|
// kdDebug() << "slotIOFinished() " << endl;
|
|
|
|
assert( job == d->list_job );
|
|
|
|
if ( d->list_urls.isEmpty() ) {
|
|
|
|
d->list_job = 0L;
|
|
|
|
finished(); // will call TDECompletion::makeCompletion()
|
|
|
|
}
|
|
else {
|
|
|
|
KURL *kurl = d->list_urls.first();
|
|
|
|
d->list_urls.remove( kurl );
|
|
|
|
// kdDebug() << "Start TDEIO: " << kurl->prettyURL() << endl;
|
|
|
|
d->list_job = TDEIO::listDir( *kurl, false );
|
|
d->list_job->addMetaData("no-auth-prompt", "true");
|
|
|
|
assert( d->list_job );
|
|
|
|
connect( d->list_job,
|
|
TQT_SIGNAL(result(TDEIO::Job*)),
|
|
TQT_SLOT(slotIOFinished(TDEIO::Job*)) );
|
|
|
|
connect( d->list_job,
|
|
TQT_SIGNAL( entries( TDEIO::Job*, const TDEIO::UDSEntryList&)),
|
|
TQT_SLOT( slotEntries( TDEIO::Job*, const TDEIO::UDSEntryList&)) );
|
|
|
|
delete kurl;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////
|
|
|
|
/*
|
|
* postProcessMatch, postProcessMatches
|
|
*
|
|
* Called by TDECompletion before emitting match() and matches()
|
|
*
|
|
* Append '/' to directories for file completion. This is
|
|
* done here to avoid stat()'ing a lot of files
|
|
*/
|
|
void KURLCompletion::postProcessMatch( TQString *match ) const
|
|
{
|
|
// kdDebug() << "KURLCompletion::postProcess: " << *match << endl;
|
|
|
|
if ( !match->isEmpty() ) {
|
|
|
|
// Add '/' to directories in file completion mode
|
|
// unless it has already been done
|
|
if ( d->last_compl_type == CTFile )
|
|
adjustMatch( *match );
|
|
}
|
|
}
|
|
|
|
void KURLCompletion::adjustMatch( TQString& match ) const
|
|
{
|
|
if ( match.at( match.length()-1 ) != '/' )
|
|
{
|
|
TQString copy;
|
|
|
|
if ( match.startsWith( TQString("file:") ) )
|
|
copy = KURL(match).path();
|
|
else
|
|
copy = match;
|
|
|
|
expandTilde( copy );
|
|
expandEnv( copy );
|
|
if ( TQDir::isRelativePath(copy) )
|
|
copy.prepend( d->cwd + '/' );
|
|
|
|
// kdDebug() << "postProcess: stating " << copy << endl;
|
|
|
|
KDE_struct_stat sbuff;
|
|
|
|
TQCString file = TQFile::encodeName( copy );
|
|
|
|
if ( KDE_stat( (const char*)file, &sbuff ) == 0 ) {
|
|
if ( S_ISDIR ( sbuff.st_mode ) )
|
|
match.append( '/' );
|
|
}
|
|
else {
|
|
kdDebug() << "Could not stat file " << copy << endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
void KURLCompletion::postProcessMatches( TQStringList * matches ) const
|
|
{
|
|
if ( !matches->isEmpty() && d->last_compl_type == CTFile ) {
|
|
TQStringList::Iterator it = matches->begin();
|
|
for (; it != matches->end(); ++it ) {
|
|
adjustMatch( (*it) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void KURLCompletion::postProcessMatches( TDECompletionMatches * matches ) const
|
|
{
|
|
if ( !matches->isEmpty() && d->last_compl_type == CTFile ) {
|
|
TDECompletionMatches::Iterator it = matches->begin();
|
|
for (; it != matches->end(); ++it ) {
|
|
adjustMatch( (*it).value() );
|
|
}
|
|
}
|
|
}
|
|
|
|
void KURLCompletion::customEvent(TQCustomEvent *e)
|
|
{
|
|
if ( e->type() == CompletionMatchEvent::uniqueType() ) {
|
|
|
|
CompletionMatchEvent *event = static_cast<CompletionMatchEvent *>( e );
|
|
|
|
event->completionThread()->wait();
|
|
|
|
if ( !isListedURL( CTUser ) ) {
|
|
stop();
|
|
clear();
|
|
addMatches( event->completionThread()->matches() );
|
|
}
|
|
|
|
setListedURL( CTUser );
|
|
|
|
if ( d->userListThread == event->completionThread() )
|
|
d->userListThread = 0;
|
|
|
|
if ( d->dirListThread == event->completionThread() )
|
|
d->dirListThread = 0;
|
|
|
|
delete event->completionThread();
|
|
}
|
|
}
|
|
|
|
// static
|
|
TQString KURLCompletion::replacedPath( const TQString& text, bool replaceHome, bool replaceEnv )
|
|
{
|
|
if ( text.isEmpty() )
|
|
return text;
|
|
|
|
MyURL url( text, TQString::null ); // no need to replace something of our current cwd
|
|
if ( !url.kurl()->isLocalFile() )
|
|
return text;
|
|
|
|
url.filter( replaceHome, replaceEnv );
|
|
return url.dir() + url.file();
|
|
}
|
|
|
|
|
|
TQString KURLCompletion::replacedPath( const TQString& text )
|
|
{
|
|
return replacedPath( text, d->replace_home, d->replace_env );
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////
|
|
// Static functions
|
|
|
|
/*
|
|
* expandEnv
|
|
*
|
|
* Expand environment variables in text. Escaped '$' are ignored.
|
|
* Return true if expansion was made.
|
|
*/
|
|
static bool expandEnv( TQString &text )
|
|
{
|
|
// Find all environment variables beginning with '$'
|
|
//
|
|
int pos = 0;
|
|
|
|
bool expanded = false;
|
|
|
|
while ( (pos = text.find('$', pos)) != -1 ) {
|
|
|
|
// Skip escaped '$'
|
|
//
|
|
if ( text[pos-1] == '\\' ) {
|
|
pos++;
|
|
}
|
|
// Variable found => expand
|
|
//
|
|
else {
|
|
// Find the end of the variable = next '/' or ' '
|
|
//
|
|
int pos2 = text.find( ' ', pos+1 );
|
|
int pos_tmp = text.find( '/', pos+1 );
|
|
|
|
if ( pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2) )
|
|
pos2 = pos_tmp;
|
|
|
|
if ( pos2 == -1 )
|
|
pos2 = text.length();
|
|
|
|
// Replace if the variable is terminated by '/' or ' '
|
|
// and defined
|
|
//
|
|
if ( pos2 >= 0 ) {
|
|
int len = pos2 - pos;
|
|
TQString key = text.mid( pos+1, len-1);
|
|
TQString value =
|
|
TQString::fromLocal8Bit( ::getenv(key.local8Bit()) );
|
|
|
|
if ( !value.isEmpty() ) {
|
|
expanded = true;
|
|
text.replace( pos, len, value );
|
|
pos = pos + value.length();
|
|
}
|
|
else {
|
|
pos = pos2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return expanded;
|
|
}
|
|
|
|
/*
|
|
* expandTilde
|
|
*
|
|
* Replace "~user" with the users home directory
|
|
* Return true if expansion was made.
|
|
*/
|
|
static bool expandTilde(TQString &text)
|
|
{
|
|
if ( text[0] != '~' )
|
|
return false;
|
|
|
|
bool expanded = false;
|
|
|
|
// Find the end of the user name = next '/' or ' '
|
|
//
|
|
int pos2 = text.find( ' ', 1 );
|
|
int pos_tmp = text.find( '/', 1 );
|
|
|
|
if ( pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2) )
|
|
pos2 = pos_tmp;
|
|
|
|
if ( pos2 == -1 )
|
|
pos2 = text.length();
|
|
|
|
// Replace ~user if the user name is terminated by '/' or ' '
|
|
//
|
|
if ( pos2 >= 0 ) {
|
|
|
|
TQString user = text.mid( 1, pos2-1 );
|
|
TQString dir;
|
|
|
|
// A single ~ is replaced with $HOME
|
|
//
|
|
if ( user.isEmpty() ) {
|
|
dir = TQDir::homeDirPath();
|
|
}
|
|
// ~user is replaced with the dir from passwd
|
|
//
|
|
else {
|
|
struct passwd *pw = ::getpwnam( user.local8Bit() );
|
|
|
|
if ( pw )
|
|
dir = TQFile::decodeName( pw->pw_dir );
|
|
|
|
::endpwent();
|
|
}
|
|
|
|
if ( !dir.isEmpty() ) {
|
|
expanded = true;
|
|
text.replace(0, pos2, dir);
|
|
}
|
|
}
|
|
|
|
return expanded;
|
|
}
|
|
|
|
/*
|
|
* unescape
|
|
*
|
|
* Remove escapes and return the result in a new string
|
|
*
|
|
*/
|
|
static TQString unescape(const TQString &text)
|
|
{
|
|
TQString result;
|
|
|
|
for (uint pos = 0; pos < text.length(); pos++)
|
|
if ( text[pos] != '\\' )
|
|
result.insert( result.length(), text[pos] );
|
|
|
|
return result;
|
|
}
|
|
|
|
void KURLCompletion::virtual_hook( int id, void* data )
|
|
{ TDECompletion::virtual_hook( id, data ); }
|
|
|
|
#include "kurlcompletion.moc"
|
|
|