You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1036 lines
32 KiB
1036 lines
32 KiB
/***************************************************************************
|
|
* kio-locate: KDE I/O Slave for the locate command *
|
|
* *
|
|
* Copyright (C) 2005 by Tobi Vollebregt *
|
|
* tobivollebregt@gmail.com *
|
|
* *
|
|
* Thanks to Google's Summer Of Code Program! *
|
|
* *
|
|
* Copyright (C) 2004 by Armin Straub *
|
|
* linux@arminstraub.de *
|
|
* *
|
|
* This program was initially written by Michael Schuerig. *
|
|
* Although I have completely rewritten it, most ideas are adopted *
|
|
* from his original work. *
|
|
* *
|
|
* Copyright (C) 2002 by Michael Schuerig *
|
|
* michael@schuerig.de *
|
|
* *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
***************************************************************************/
|
|
|
|
#include <algorithm>
|
|
#include <stdlib.h>
|
|
#include <sys/stat.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
|
|
#include <kapplication.h>
|
|
#include <kconfigdialog.h>
|
|
#include <kdebug.h>
|
|
#include <kiconloader.h>
|
|
#include <klocale.h>
|
|
#include <kurl.h>
|
|
#include <kuser.h>
|
|
#include <tqfile.h>
|
|
|
|
#include "kio_locate.h"
|
|
#include "klocateconfig.h"
|
|
#include "klocateconfigwidget.h"
|
|
#include "klocateconfigfilterwidget.h"
|
|
#include "klocateconfiglocatewidget.h"
|
|
|
|
using namespace KIO;
|
|
|
|
|
|
static const TQString queryQuery = "q";
|
|
static const TQString queryDirectory = "directory";
|
|
static const TQString queryCase = "case";
|
|
static const TQString queryRegExp = "regexp";
|
|
|
|
static const TQString iconToStringTable[] = {
|
|
"folder", "folder_green", "folder_grey", "folder_orange",
|
|
"folder_red", "folder_violet", "folder_yellow"
|
|
};
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// HELPERS
|
|
|
|
/**
|
|
* Determines if a string contains unescaped wildcard characters.
|
|
* Currently, wildcard characters are: * + ? [ ]
|
|
* For older versions of Konqueror: + behaves identical to *
|
|
* @param s the string to inspect
|
|
*/
|
|
static bool hasWildcards(const TQString& s)
|
|
{
|
|
for (unsigned int i = 0; i < s.length(); ++i) {
|
|
if ((s[i] == '*' || s[i] == '+' || s[i] == '?' || s[i] == '[' || s[i] == ']') && (i < 1 || s[i-1] != '\\'))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Converts a string containing shell wildcard characters (globbing characters)
|
|
* to a regular expression. This function takes care of escaped wildcards and
|
|
* any regexp special characters which happen to be in the string.
|
|
* @param s the string to convert
|
|
* @return the converted string
|
|
*/
|
|
static TQString convertWildcardsToRegExp(TQString s)
|
|
{
|
|
bool in_set = false;
|
|
|
|
// No support for regexp searches.
|
|
// (Konqueror makes passing chars like "/" almost impossible anyway.)
|
|
// Note that this converts actual wildcards to escaped wildcards (\wildcard),
|
|
// and escaped wildcards to 'triple'-escaped wildcards (\\\wildcard).
|
|
s = TQRegExp::escape(s);
|
|
|
|
// Walk through the string, converting \wildcard to regexp and
|
|
// \\\wildcard back to \wildcard.
|
|
for (unsigned i = 1; i < s.length(); ++i) {
|
|
//DEBUGSTR << s.left(i+1) << endl;
|
|
if (i < 3 || s[i-3] != '\\' || s[i-2] != '\\') {
|
|
// If it was an unescaped character (now possibly escaped once)
|
|
if (s[i-1] == '\\') {
|
|
// If it actually was escaped once
|
|
if (!in_set) {
|
|
// If it's NOT inside a character set, we need to convert *?[
|
|
if (s[i] == '*' || s[i] == '+') {
|
|
s = s.left(i-1) + "[^/]*" + s.mid(i+1);
|
|
i += 3; // 3 == strlen("[^/]*")-strlen("\\*")
|
|
} else if (s[i] == '?') {
|
|
s = s.left(i-1) + "[^/]" + s.mid(i+1);
|
|
i += 2; // 2 == strlen("[^/]")-strlen("\\*")
|
|
} else if (s[i] == '[') {
|
|
s = s.left(i-1) + s.mid(i);
|
|
--i;
|
|
in_set = true;
|
|
}
|
|
} else {
|
|
// If it's inside a character set, we need to convert ]^
|
|
// and to unescape everything else.
|
|
if (s[i] == ']') {
|
|
s = s.left(i-1) + s.mid(i);
|
|
--i;
|
|
in_set = false;
|
|
} else if (s[i] == '^' && i >= 2 && s[i-2] == '[') {
|
|
s = s.left(i-1) + s.mid(i);
|
|
--i;
|
|
} else {
|
|
s = s.left(i-1) + s.mid(i);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// If it's an escaped character, remove the extra escape characters.
|
|
s = s.left(i-3) + s.mid(i-1);
|
|
i -= 2; // 2 == strlen("\\\\")-strlen("")
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
|
|
/**
|
|
* Determines if path includes a trailing slash.
|
|
* @param path the path to inspect
|
|
*/
|
|
static bool hasTrailingSlash(const TQString& path)
|
|
{
|
|
int n = path.length();
|
|
return ((n > 0) && (path[n-1] == '/'));
|
|
}
|
|
|
|
|
|
/**
|
|
* Strips a trailing slash / from a path.
|
|
* @param path the path to strip the slash off
|
|
*/
|
|
static TQString stripTrailingSlash(const TQString& path)
|
|
{
|
|
int n = path.length();
|
|
if ((n > 0) && (path[n-1] == '/')) {
|
|
return path.left(n-1);
|
|
}
|
|
return path;
|
|
}
|
|
|
|
|
|
/**
|
|
* Add a trailing slash / to a path if there is none yet.
|
|
* @param path the path to append the slash to
|
|
*/
|
|
static TQString addTrailingSlash(const TQString& path)
|
|
{
|
|
int n = path.length();
|
|
if ((n > 0) && (path[n-1] == '/')) {
|
|
return path;
|
|
}
|
|
return path + '/';
|
|
}
|
|
|
|
|
|
static void addAtom(UDSEntry& entry, unsigned int uds, const TQString& s)
|
|
{
|
|
UDSAtom a;
|
|
a.m_uds = uds;
|
|
a.m_str = s;
|
|
entry.append(a);
|
|
}
|
|
|
|
|
|
static void addAtom(UDSEntry& entry, unsigned int uds, long l)
|
|
{
|
|
UDSAtom a;
|
|
a.m_uds = uds;
|
|
a.m_long = l;
|
|
entry.append(a);
|
|
}
|
|
|
|
|
|
static const UDSEntry pathToUDSEntry(const TQString& path, const TQString& display,
|
|
const TQString& url = TQString(), const TQString& icon = TQString())
|
|
{
|
|
UDSEntry entry;
|
|
addAtom(entry, KIO::UDS_NAME, display);
|
|
|
|
if (!path.isEmpty()) {
|
|
struct stat info;
|
|
lstat(path.local8Bit(), &info);
|
|
|
|
addAtom(entry, KIO::UDS_SIZE, info.st_size);
|
|
addAtom(entry, KIO::UDS_ACCESS, info.st_mode);
|
|
addAtom(entry, KIO::UDS_MODIFICATION_TIME, info.st_mtime);
|
|
addAtom(entry, KIO::UDS_ACCESS_TIME, info.st_atime);
|
|
addAtom(entry, KIO::UDS_CREATION_TIME, info.st_ctime);
|
|
|
|
struct passwd * user = getpwuid(info.st_uid);
|
|
struct group * group = getgrgid(info.st_gid);
|
|
addAtom(entry, KIO::UDS_USER, ((user != NULL) ? user->pw_name : "???" ));
|
|
addAtom(entry, KIO::UDS_GROUP, ((group != NULL) ? group->gr_name : "???" ));
|
|
|
|
if (url.isEmpty()) {
|
|
// List an existing file.
|
|
addAtom(entry, KIO::UDS_URL, "file:" + path);
|
|
|
|
mode_t type = info.st_mode;
|
|
if (S_ISLNK(type)) {
|
|
TQString slink = TQString();
|
|
char buff[1000];
|
|
int n = readlink(path.ascii(), buff, 1000);
|
|
if (n != -1) {
|
|
buff[n] = 0;
|
|
slink = buff;
|
|
}
|
|
addAtom(entry, KIO::UDS_LINK_DEST, slink);
|
|
} else {
|
|
type &= S_IFMT;
|
|
}
|
|
addAtom(entry, KIO::UDS_FILE_TYPE, type);
|
|
|
|
#ifdef HAVE_UDS_HIDDEN
|
|
if (path.contains("/.")) {
|
|
addAtom(entry, KIO::UDS_HIDDEN, 1);
|
|
}
|
|
#endif
|
|
} else {
|
|
// List a locate link.
|
|
addAtom(entry, KIO::UDS_URL, url);
|
|
addAtom(entry, KIO::UDS_FILE_TYPE, S_IFDIR);
|
|
}
|
|
} else {
|
|
addAtom(entry, KIO::UDS_URL, url);
|
|
}
|
|
|
|
if (!icon.isEmpty()) {
|
|
addAtom(entry, KIO::UDS_ICON_NAME, icon);
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// INITIALIZATION
|
|
|
|
LocateProtocol::LocateProtocol(const TQCString &pool_socket, const TQCString &app_socket)
|
|
: SlaveBase("kio_locate", pool_socket, app_socket)
|
|
{
|
|
DEBUGSTR << "LocateProtocol::LocateProtocol()" << endl;
|
|
|
|
connect(&m_locater, TQT_SIGNAL(found(const TQStringList&)),
|
|
this, TQT_SLOT(processLocateOutput(const TQStringList&)));
|
|
connect(&m_locater, TQT_SIGNAL(finished()),
|
|
this, TQT_SLOT(locateFinished()));
|
|
|
|
m_baseDir = NULL;
|
|
m_curDir = NULL;
|
|
}
|
|
|
|
|
|
LocateProtocol::~LocateProtocol()
|
|
{
|
|
DEBUGSTR << "LocateProtocol::~LocateProtocol()" << endl;
|
|
|
|
delete m_baseDir;
|
|
}
|
|
|
|
|
|
const LocateRegExp& LocateProtocol::getRegExp() const
|
|
{
|
|
return m_locateRegExp;
|
|
}
|
|
|
|
|
|
int LocateProtocol::getCollapseDirectoryThreshold() const
|
|
{
|
|
return m_config.m_collapseDirectoryThreshold;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// KIO STUFF
|
|
|
|
void LocateProtocol::setUrl(const KURL& url)
|
|
{
|
|
if (url.protocol() != "locater") {
|
|
TQString pattern = KURL::decode_string(url.url());
|
|
pattern = pattern.mid(url.protocol().length() + 1);
|
|
|
|
KURL newUrl;
|
|
newUrl.setProtocol("locater");
|
|
|
|
if (pattern.isEmpty() || pattern == "/") {
|
|
// Give help.
|
|
newUrl.setPath("help");
|
|
} else if (hasTrailingSlash(pattern)) {
|
|
// Detect auto-completion from within konqueror and "stop"
|
|
// this search.
|
|
newUrl.setPath("autosearch");
|
|
newUrl.addQueryItem(queryQuery, pattern);
|
|
} else if (url.protocol() == "rlocate") {
|
|
// Standard regexp search.
|
|
newUrl.setPath("search");
|
|
newUrl.addQueryItem(queryQuery, pattern);
|
|
newUrl.addQueryItem(queryRegExp, "1");
|
|
} else {
|
|
// Standard wildcard search.
|
|
newUrl.setPath("search");
|
|
newUrl.addQueryItem(queryQuery, pattern);
|
|
}
|
|
m_url = newUrl;
|
|
|
|
DEBUGSTR << "Redirect: " << m_url << endl;
|
|
} else {
|
|
m_url = url;
|
|
}
|
|
// Perhaps this will be unneccessary most times, but who knows...
|
|
updateConfig();
|
|
}
|
|
|
|
void LocateProtocol::get(const KURL& url)
|
|
{
|
|
DEBUGSTR << "LocateProtocol::get(" << url << ")" << endl;
|
|
|
|
setUrl(url);
|
|
|
|
if (isSearchRequest()) {
|
|
if (m_locater.binaryExists()) {
|
|
error(KIO::ERR_IS_DIRECTORY, TQString());
|
|
} else {
|
|
TQString html = i18n("<h1>\"%1\" could not be started.</h1><p>Please note that kio-locate can't be used on its own. You need an additional program for doing searches. Typically this is the command line tool <i>locate</i> that can be found in many distributions by default. You can check if the correct tool is used by looking at the <a href=\"locater:config\">setting</a> \"Locate Binary\".<p>Besides the mentioned tool <i>locate</i>, kio-locate can use any tool that uses the same syntax. In particular, it was reported to work with <i>slocate</i> and <i>rlocate</i>.").tqarg(m_locater.binary());
|
|
outputHtml(html);
|
|
}
|
|
} else if (isConfigRequest()) {
|
|
configRequest();
|
|
} else if (isHelpRequest()) {
|
|
helpRequest();
|
|
} else {
|
|
// What's this?
|
|
error(KIO::ERR_DOES_NOT_EXIST, TQString());
|
|
}
|
|
}
|
|
|
|
|
|
void LocateProtocol::stat(const KURL& url)
|
|
{
|
|
DEBUGSTR << "LocateProtocol::stat(" << url << ")" << endl ;
|
|
|
|
setUrl(url);
|
|
|
|
if (isSearchRequest() || isConfigRequest() || isHelpRequest()) {
|
|
bool isDir = isSearchRequest() && m_locater.binaryExists();
|
|
/// \todo Is UDS_NAME used for anything in stat? If so we should
|
|
/// at least strip of the protocol part.
|
|
UDSEntry entry;
|
|
addAtom(entry, KIO::UDS_NAME, url.decode_string(url.url()));
|
|
addAtom(entry, KIO::UDS_FILE_TYPE, isDir ? S_IFDIR : S_IFREG);
|
|
statEntry(entry);
|
|
finished();
|
|
/// \todo Somehow locate: and locate:/ is thought to be a directory
|
|
/// by konqueror anyway. How to change this?
|
|
} else {
|
|
// What's this?
|
|
error(KIO::ERR_DOES_NOT_EXIST, TQString());
|
|
}
|
|
}
|
|
|
|
|
|
void LocateProtocol::listDir(const KURL& url)
|
|
{
|
|
DEBUGSTR << "LocateProtocol::listDir(" << url << ")" << endl ;
|
|
|
|
setUrl(url);
|
|
|
|
if (isSearchRequest()) {
|
|
searchRequest();
|
|
} else if (isConfigRequest() || isHelpRequest()) {
|
|
error(KIO::ERR_IS_FILE, TQString());
|
|
} else {
|
|
// What's this?
|
|
error(KIO::ERR_DOES_NOT_EXIST, TQString());
|
|
}
|
|
}
|
|
|
|
|
|
void LocateProtocol::mimetype(const KURL& url)
|
|
{
|
|
DEBUGSTR << "LocateProtocol::mimetype(" << url << ")" << endl ;
|
|
|
|
setUrl(url);
|
|
|
|
if (isSearchRequest()) {
|
|
if (m_locater.binaryExists()) {
|
|
mimeType("inode/directory");
|
|
} else {
|
|
mimeType("text/html");
|
|
}
|
|
} else if (isConfigRequest() || isHelpRequest()) {
|
|
mimeType("text/html");
|
|
}
|
|
finished();
|
|
}
|
|
|
|
|
|
void LocateProtocol::outputHtml(const TQString& body)
|
|
{
|
|
mimeType("text/html");
|
|
|
|
TQString theData = "<html><body>" + body + "</body></html>";
|
|
data(theData.local8Bit());
|
|
finished();
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// SEARCHING
|
|
|
|
bool LocateProtocol::isSearchRequest()
|
|
{
|
|
return m_url.path() == "search";
|
|
}
|
|
|
|
|
|
void LocateProtocol::searchRequest()
|
|
{
|
|
// Reset old values.
|
|
m_caseSensitivity = caseAuto;
|
|
m_useRegExp = false;
|
|
m_locatePattern = TQString();
|
|
m_locateDirectory = TQString();
|
|
m_regExps.clear();
|
|
m_pendingPath = TQString();
|
|
|
|
delete m_baseDir;
|
|
m_baseDir = NULL;
|
|
m_curDir = NULL;
|
|
|
|
updateConfig();
|
|
|
|
TQString query = m_url.queryItem(queryQuery);
|
|
m_locateDirectory = addTrailingSlash(m_url.queryItem(queryDirectory));
|
|
|
|
TQString caseSensitivity = m_url.queryItem(queryCase);
|
|
if (caseSensitivity == "sensitive") {
|
|
m_caseSensitivity = caseSensitive;
|
|
} else if (caseSensitivity == "insensitive") {
|
|
m_caseSensitivity = caseInsensitive;
|
|
}
|
|
|
|
TQString useRegExp = m_url.queryItem(queryRegExp);
|
|
if (!useRegExp.isEmpty() && useRegExp != "0") {
|
|
m_useRegExp = true;
|
|
}
|
|
|
|
// Split query into components. The first component is the query
|
|
// for locate. The others are filtering regular expressions. They are
|
|
// delimited by (not escaped) whitespace.
|
|
// If the last component starts with two backslahes \\, then the search
|
|
// is only to be done within the directory following them.
|
|
query = query.simplifyWhiteSpace();
|
|
int s = 0;
|
|
int n = query.length();
|
|
bool regexp;
|
|
TQString display;
|
|
for (int i = 0; i <= n; i++) {
|
|
if ((i == n) || ((query[i] == ' ') && (i > 0)
|
|
&& (query[i-1] != '\\') && (i-s > 0))) {
|
|
TQString temp = query.mid(s, i-s);
|
|
TQString part = partToPattern(temp, s==0);
|
|
if (s == 0) {
|
|
// We don't want to show the escaped regexpified string to
|
|
// the user, so we store the string we get in for later display.
|
|
display = temp;
|
|
// This is the same check as in partToPattern.
|
|
// ie. regexp is used if locate pattern contains wildcards,
|
|
// or regexp searching was enabled.
|
|
regexp = hasWildcards(temp);
|
|
m_locatePattern = part;
|
|
} else {
|
|
// For each regular expression determine if it should be
|
|
// case sensitive.
|
|
m_regExps += LocateRegExp(part, !isCaseSensitive(part));
|
|
}
|
|
s = i+1;
|
|
}
|
|
}
|
|
|
|
DEBUGSTR << "Pattern: " << m_locatePattern << endl;
|
|
DEBUGSTR << "Directory: " << m_locateDirectory << endl;
|
|
|
|
// We set up the regexp used to see whether the match was in the
|
|
// directory part or the filename part of a path.
|
|
m_locateRegExp = LocateRegExp(convertWildcardsToRegExp(m_locatePattern), !isCaseSensitive(m_locatePattern));
|
|
|
|
// Now perform the search...
|
|
infoMessage(i18n("Locating %1 ...").tqarg(display));
|
|
|
|
bool started = m_locater.locate(m_locatePattern, !isCaseSensitive(m_locatePattern), regexp);
|
|
|
|
if (!started) {
|
|
DEBUGSTR << "Locate could not be found." << endl;
|
|
finished();
|
|
}
|
|
}
|
|
|
|
|
|
bool LocateProtocol::isCaseSensitive(const TQString& text)
|
|
{
|
|
if (m_caseSensitivity == caseSensitive) {
|
|
return true;
|
|
} else if (m_caseSensitivity == caseInsensitive) {
|
|
return false;
|
|
} else if (m_config.m_caseSensitivity == caseSensitive) {
|
|
return true;
|
|
} else if (m_config.m_caseSensitivity == caseInsensitive) {
|
|
return false;
|
|
} else {
|
|
return text != text.lower();
|
|
}
|
|
}
|
|
|
|
|
|
void LocateProtocol::addHit(const TQString& path, int subItems)
|
|
{
|
|
// DEBUGSTR << "LocateProtocol::addHit( " << path << ", " << subItems << " )" << endl;
|
|
if (TQFile::exists(path)) {
|
|
if (subItems > 0) {
|
|
m_entries += pathToUDSEntry(path, pathToDisplay(path, subItems), makeLocaterUrl(path), iconToStringTable[m_config.m_collapsedIcon]);
|
|
} else {
|
|
m_entries += pathToUDSEntry(path, pathToDisplay(path));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void LocateProtocol::addPreviousLocateOutput()
|
|
{
|
|
if (m_baseDir == NULL) {
|
|
return;
|
|
}
|
|
// m_baseDir->debugTrace();
|
|
if (m_locateDirectory == "/") {
|
|
// Allow toplevel directories to collapse (e.g. when locating "/usr/").
|
|
m_baseDir->prepareListing(this, 0);
|
|
} else {
|
|
m_baseDir->prepareListing(this, m_locateDirectory.length());
|
|
}
|
|
m_baseDir->listItems(this);
|
|
delete m_baseDir;
|
|
m_baseDir = NULL;
|
|
m_curDir = NULL;
|
|
|
|
listEntries(m_entries);
|
|
m_entries.clear();
|
|
}
|
|
|
|
|
|
void LocateProtocol::processPath(const TQString &path, const TQString &nextPath)
|
|
{
|
|
if (!nextPath) {
|
|
// We need to know the next path, so we remember this path for later processing.
|
|
m_pendingPath = path;
|
|
} else if (!nextPath.startsWith(path + '/')) {
|
|
if (isMatching(path)) {
|
|
if ((m_baseDir != NULL) && !path.startsWith(m_baseDir->m_path)) {
|
|
addPreviousLocateOutput();
|
|
}
|
|
// Add path to current directory.
|
|
if (m_baseDir == NULL) {
|
|
int p = path.find('/', 1);
|
|
TQString base = path;
|
|
if (p >= 0) {
|
|
base = path.left(p+1);
|
|
}
|
|
m_baseDir = new LocateDirectory(NULL, base);
|
|
m_curDir = m_baseDir;
|
|
}
|
|
m_curDir = m_curDir->addPath(path);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void LocateProtocol::processLocateOutput(const TQStringList& items)
|
|
{
|
|
// I don't know if this really necessary, but if we were signaled, we'll
|
|
// better stop.
|
|
if (wasKilled()) {
|
|
m_locater.stop();
|
|
return;
|
|
}
|
|
// Go through what we have found.
|
|
TQStringList::ConstIterator it = items.begin();
|
|
if (!m_pendingPath.isNull()) {
|
|
processPath(m_pendingPath, *it);
|
|
m_pendingPath = TQString();
|
|
}
|
|
for (; it != items.end();) {
|
|
TQString path = *it;
|
|
++it;
|
|
processPath(path, it != items.end() ? *it : TQString());
|
|
}
|
|
}
|
|
|
|
|
|
void LocateProtocol::locateFinished()
|
|
{
|
|
// Add any pending items.
|
|
if (!m_pendingPath.isNull()) {
|
|
processPath(m_pendingPath, "");
|
|
m_pendingPath = TQString();
|
|
}
|
|
addPreviousLocateOutput();
|
|
|
|
DEBUGSTR << "LocateProtocol::locateFinished" << endl;
|
|
infoMessage(i18n("Finished."));
|
|
finished();
|
|
}
|
|
|
|
|
|
TQString LocateProtocol::partToPattern(const TQString& part, bool forLocate)
|
|
{
|
|
DEBUGSTR << "BEG part: " << part << endl;
|
|
TQString pattern = part;
|
|
// Unescape whitespace.
|
|
pattern.replace("\\ ", " ");
|
|
// Unquote quoted pattern.
|
|
int n = pattern.length(), index;
|
|
if ((n > 1) && (pattern[0] == '"') && (pattern[n-1] == '"')) {
|
|
pattern = pattern.mid(1, n-2);
|
|
}
|
|
|
|
// We can't do regular expression matching on the locate pattern,
|
|
// the regular expression format used by locate is incompatible
|
|
// with the format used by TQRegExp.
|
|
if (!m_useRegExp || forLocate) {
|
|
// Escape regexp characters for filtering pattern, and for locate,
|
|
// but the latter only if it is actually necessary to pass a regexp to locate.
|
|
// (ie. the pattern contains wildcards.)
|
|
if (!forLocate || hasWildcards(pattern)) {
|
|
pattern = convertWildcardsToRegExp(pattern);
|
|
} else {
|
|
// Special case for locate pattern without wildcards:
|
|
// Unescape all escaped wildcards.
|
|
pattern.replace("\\*", "*");
|
|
pattern.replace("\\+", "+");
|
|
pattern.replace("\\?", "?");
|
|
pattern.replace("\\[", "[");
|
|
pattern.replace("\\]", "]");
|
|
}
|
|
}
|
|
|
|
// Special treatment for the pattern used for locate:
|
|
if (forLocate) {
|
|
// Replace ~/ and ~user/ at the beginning (as the shell does)
|
|
if ((pattern.length() > 0) && (pattern[0] == '~')) {
|
|
index = pattern.find('/');
|
|
if (index >= 0) {
|
|
TQString name = pattern.mid(1, index-1);
|
|
TQString homeDir;
|
|
if (name.isEmpty()) {
|
|
homeDir = KUser(KUser::UseRealUserID).homeDir();
|
|
} else {
|
|
homeDir = KUser(name).homeDir();
|
|
}
|
|
if (!homeDir.isEmpty()) {
|
|
pattern.replace(0, index, homeDir);
|
|
}
|
|
}
|
|
}
|
|
pattern.replace("\\~", "~");
|
|
}
|
|
DEBUGSTR << "END part: " << pattern << endl;
|
|
return pattern;
|
|
}
|
|
|
|
|
|
bool LocateProtocol::isMatching(const TQString& path)
|
|
{
|
|
// The file has to belong to our directory.
|
|
if (!path.startsWith(m_locateDirectory)) {
|
|
return false;
|
|
}
|
|
// And it has to match at least one regular expression in the whitelist.
|
|
if (!m_config.m_whiteList.isMatchingOne(path)) {
|
|
return false;
|
|
}
|
|
// And it may not match any regular expression in the blacklist.
|
|
if (m_config.m_blackList.isMatchingOne(path)) {
|
|
return false;
|
|
}
|
|
// And it has to match against all regular expressions specified in the URL.
|
|
if (!m_regExps.isMatchingAll(path)) {
|
|
return false;
|
|
}
|
|
// And it must not solely match m_locateDirectory.
|
|
return m_locateRegExp.isMatching(path.mid(m_locateDirectory.length()));
|
|
}
|
|
|
|
|
|
TQString LocateProtocol::pathToDisplay(const TQString& path, int subItems)
|
|
{
|
|
// Split off the directory part. If it is not just the minimal '/'.
|
|
TQString display = path;
|
|
if ((m_locateDirectory != "/") && display.startsWith(m_locateDirectory)) {
|
|
display = display.mid(m_locateDirectory.length());
|
|
}
|
|
if (subItems > 0) {
|
|
// Can't use m_collapsedDisplay.tqarg(subItems).tqarg(display); here
|
|
// because user might forget to type %1 or %2, or type it twice.
|
|
// In both cases the result of arg() is undefined.
|
|
TQString output = m_config.m_collapsedDisplay, temp;
|
|
temp.setNum(subItems);
|
|
output.replace("%1", temp);
|
|
output.replace("%2", display);
|
|
display = output;
|
|
}
|
|
return display;
|
|
}
|
|
|
|
|
|
TQString LocateProtocol::makeLocaterUrl(const TQString& directory)
|
|
{
|
|
KURL url(m_url);
|
|
url.removeQueryItem(queryDirectory);
|
|
url.addQueryItem(queryDirectory, directory);
|
|
return url.url();
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// CONFIG
|
|
|
|
bool LocateProtocol::isConfigRequest()
|
|
{
|
|
return m_url.path() == "config";
|
|
}
|
|
|
|
|
|
void LocateProtocol::configRequest()
|
|
{
|
|
// This flag is used to show either a "succesful" or an "unchanged" message
|
|
// in configFinished().
|
|
m_configUpdated = false;
|
|
|
|
// Don't show it twice. During my tests I never got there however.
|
|
if(KConfigDialog::showDialog("settings"))
|
|
return;
|
|
|
|
KConfigDialog *dialog = new KConfigDialog(0, "settings", KLocateConfig::self(),
|
|
KDialogBase::IconList,
|
|
KDialogBase::Default|KDialogBase::Ok|KDialogBase::Cancel|KDialogBase::Help,
|
|
KDialogBase::Ok, true);
|
|
dialog->setCaption(i18n("Configure - kio-locate"));
|
|
dialog->setIcon(SmallIcon("find"));
|
|
|
|
dialog->addPage(new KLocateConfigWidget(), i18n("General"), "package_settings");
|
|
dialog->addPage(new KLocateConfigFilterWidget(), i18n("Filters"), "filter");
|
|
dialog->addPage(new KLocateConfigLocateWidget(), i18n("Locate"), "find");
|
|
|
|
// React on user's actions.
|
|
connect(dialog, TQT_SIGNAL(settingsChanged()), this, TQT_SLOT(updateConfig()));
|
|
connect(dialog, TQT_SIGNAL(finished()), this, TQT_SLOT(configFinished()));
|
|
|
|
dialog->show();
|
|
tqApp->enter_loop();
|
|
delete dialog;
|
|
}
|
|
|
|
|
|
void LocateProtocol::configFinished()
|
|
{
|
|
DEBUGSTR << "LocateProtocol::configFinished" << endl;
|
|
|
|
tqApp->exit_loop();
|
|
|
|
TQString html;
|
|
if (m_configUpdated) {
|
|
html = i18n("Configuration succesfully updated.");
|
|
} else {
|
|
html = i18n("Configuration unchanged.");
|
|
}
|
|
outputHtml("<h1>" + html + "</h1>");
|
|
}
|
|
|
|
|
|
void LocateProtocol::updateConfig()
|
|
{
|
|
// It's not needed to update the config if it's still up to date.
|
|
DEBUGSTR << "LocateProtocol::updateConfig" << endl;
|
|
|
|
KLocateConfig::self()->readConfig();
|
|
m_config.m_caseSensitivity = (LocateCaseSensitivity) KLocateConfig::caseSensitivity();
|
|
m_config.m_collapseDirectoryThreshold = KLocateConfig::collapseDirectoryThreshold();
|
|
m_config.m_collapsedDisplay = KLocateConfig::collapsedDisplay();
|
|
m_config.m_collapsedIcon = (LocateCollapsedIcon) KLocateConfig::collapsedIcon();
|
|
m_config.m_whiteList = KLocateConfig::whiteList();
|
|
m_config.m_blackList = KLocateConfig::blackList();
|
|
|
|
m_locater.setupLocate(KLocateConfig::locateBinary(),
|
|
KLocateConfig::locateAdditionalArguments());
|
|
|
|
m_configUpdated = true;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// HELP
|
|
|
|
bool LocateProtocol::isHelpRequest()
|
|
{
|
|
return m_url.path() == "help";
|
|
}
|
|
|
|
|
|
void LocateProtocol::helpRequest()
|
|
{
|
|
// Redirect the user to our help documents.
|
|
redirection("help:/kio-locate/");
|
|
finished();
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// SEARCH STRUCTURES
|
|
|
|
LocateDirectory::LocateDirectory(LocateDirectory *parent, const TQString& path)
|
|
{
|
|
m_parent = parent;
|
|
m_path = path;
|
|
m_childs.setAutoDelete(true);
|
|
m_itemsCount = 0;
|
|
}
|
|
|
|
|
|
LocateDirectory *LocateDirectory::addPath(const TQString& path)
|
|
{
|
|
if (path.startsWith(m_path)) {
|
|
TQString relPath = path.mid(m_path.length());
|
|
int p = relPath.findRev('/');
|
|
if (p >= 0) {
|
|
LocateDirectory *child = getSubDirectory(relPath.left(p));
|
|
child->addItem(relPath.mid(p+1));
|
|
return child;
|
|
}
|
|
addItem(relPath);
|
|
return this;
|
|
}
|
|
if (m_parent != NULL) {
|
|
return m_parent->addPath(path);
|
|
}
|
|
// This should not happen
|
|
return this;
|
|
}
|
|
|
|
|
|
LocateDirectory *LocateDirectory::getSubDirectory(const TQString& relPath)
|
|
{
|
|
TQString base = relPath;
|
|
int p = relPath.find('/');
|
|
if (p >= 0) {
|
|
base = relPath.left(p);
|
|
}
|
|
LocateDirectory *child = m_childs.find(base);
|
|
if (child == NULL) {
|
|
child = new LocateDirectory(this, addTrailingSlash(m_path + base));
|
|
m_childs.insert(base, child);
|
|
}
|
|
if (p >= 0) {
|
|
return child->getSubDirectory(relPath.mid(p+1));
|
|
}
|
|
return child;
|
|
}
|
|
|
|
|
|
void LocateDirectory::addItem(const TQString& path)
|
|
{
|
|
m_items += LocateItem(m_path + path, 0);
|
|
m_itemsCount++;
|
|
}
|
|
|
|
|
|
int LocateDirectory::countMatchingItems(const LocateProtocol* protocol, int skip)
|
|
{
|
|
int count = 0;
|
|
LocateItems::ConstIterator item = m_items.begin();
|
|
for (; item != m_items.end(); ++item) {
|
|
if ((*item).m_subItems) {
|
|
count += (*item).m_subItems;
|
|
} else if (protocol->getRegExp().isMatching((*item).m_path.mid(skip))) {
|
|
++count;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
|
|
void LocateDirectory::prepareListing(const LocateProtocol* protocol, int skip)
|
|
{
|
|
int n = m_path.length(), newSkip = n;
|
|
if (skip > newSkip) newSkip = skip;
|
|
|
|
// Recursively walk through all childs.
|
|
LocateDirectoriesIterator child(m_childs);
|
|
for (; child.current(); ++child) {
|
|
child.current()->prepareListing(protocol, newSkip);
|
|
}
|
|
|
|
// Set m_fullCount to the total number of childs matching the pattern.
|
|
m_fullCount = countMatchingItems(protocol, newSkip);
|
|
|
|
// Collapse if directory part matches.
|
|
LocateDirectory* parent = m_parent;
|
|
if (parent == NULL) {
|
|
parent = this;
|
|
}
|
|
if (n > skip && protocol->getRegExp().isMatching(m_path.mid(skip))) {
|
|
// Directory part matches.
|
|
m_childs.clear();
|
|
m_items.clear();
|
|
m_itemsCount = 0;
|
|
parent->m_items += LocateItem(m_path, m_fullCount);
|
|
++parent->m_itemsCount;
|
|
if (m_fullCount != 0) {
|
|
parent->m_items += LocateItem(m_path, 0);
|
|
++parent->m_itemsCount;
|
|
}
|
|
}
|
|
|
|
// Collapse if we got too many hits.
|
|
int maxHits = protocol->getCollapseDirectoryThreshold();
|
|
if (n > skip && maxHits != 0 && m_itemsCount > maxHits) {
|
|
if (m_parent != NULL) {
|
|
m_parent->m_items += LocateItem(m_path, m_fullCount);
|
|
++m_parent->m_itemsCount;
|
|
} else {
|
|
m_items.clear();
|
|
m_items += LocateItem(m_path, m_fullCount);
|
|
++m_itemsCount;
|
|
}
|
|
} else {
|
|
// Propagate items to parent.
|
|
// (only root LocateDirectory runs listItems)
|
|
if (m_parent != NULL) {
|
|
m_parent->m_items += m_items;
|
|
m_parent->m_itemsCount += m_itemsCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void LocateDirectory::listItems(LocateProtocol *protocol)
|
|
{
|
|
LocateItems::ConstIterator item = m_items.begin();
|
|
for (; item != m_items.end(); ++item) {
|
|
protocol->addHit(stripTrailingSlash((*item).m_path), (*item).m_subItems);
|
|
}
|
|
}
|
|
|
|
|
|
void LocateDirectory::debugTrace(int level)
|
|
{
|
|
TQString ws;
|
|
ws.fill(' ', level);
|
|
DEBUGSTR << ws << m_path << endl;
|
|
LocateItems::ConstIterator item = m_items.begin();
|
|
for (; item != m_items.end(); ++item) {
|
|
DEBUGSTR << ws << "+ " << (*item).m_path << endl;
|
|
}
|
|
LocateDirectoriesIterator child(m_childs);
|
|
for (; child.current(); ++child) {
|
|
child.current()->debugTrace(level+2);
|
|
}
|
|
}
|
|
|
|
|
|
LocateItem::LocateItem()
|
|
{
|
|
}
|
|
|
|
|
|
LocateItem::LocateItem(const TQString& path, int subItems)
|
|
{
|
|
m_path = path;
|
|
m_subItems = subItems;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// INVOKATION
|
|
|
|
extern "C"
|
|
{
|
|
int kdemain(int argc, char **argv)
|
|
{
|
|
// We use KApplication instead of KInstance here, because we use a
|
|
// config dialog and such gui stuff.
|
|
KApplication app(argc, argv, "kio_locate", false, true);
|
|
|
|
DEBUGSTR << "*** Starting kio_locate " << endl;
|
|
|
|
if (argc != 4) {
|
|
DEBUGSTR << "Usage: kio_locate protocol domain-socket1 domain-socket2" << endl;
|
|
exit(-1);
|
|
}
|
|
|
|
LocateProtocol slave(argv[2], argv[3]);
|
|
slave.dispatchLoop();
|
|
|
|
DEBUGSTR << "*** kio_locate Done" << endl;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
#include "kio_locate.moc"
|