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.
tdeio-locate/src/tdeio_locate.cpp

1040 lines
32 KiB

/***************************************************************************
* tdeio-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 <tdeapplication.h>
#include <tdeconfigdialog.h>
#include <kdebug.h>
#include <kde_file.h>
#include <kiconloader.h>
#include <tdelocale.h>
#include <kurl.h>
#include <kuser.h>
#include <tqfile.h>
#include "tdeio_locate.h"
#include "klocateconfig.h"
#include "klocateconfigwidget.h"
#include "klocateconfigfilterwidget.h"
#include "klocateconfiglocatewidget.h"
using namespace TDEIO;
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) {
//kdDebug(7134) << 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 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, TDEIO::UDS_NAME, display);
if (!path.isEmpty()) {
KDE_struct_stat info;
KDE_lstat(path.local8Bit(), &info);
addAtom(entry, TDEIO::UDS_SIZE, info.st_size);
addAtom(entry, TDEIO::UDS_ACCESS, info.st_mode);
addAtom(entry, TDEIO::UDS_MODIFICATION_TIME, info.st_mtime);
addAtom(entry, TDEIO::UDS_ACCESS_TIME, info.st_atime);
addAtom(entry, TDEIO::UDS_CREATION_TIME, info.st_ctime);
struct passwd * user = getpwuid(info.st_uid);
struct group * group = getgrgid(info.st_gid);
addAtom(entry, TDEIO::UDS_USER, ((user != NULL) ? user->pw_name : "???" ));
addAtom(entry, TDEIO::UDS_GROUP, ((group != NULL) ? group->gr_name : "???" ));
if (url.isEmpty()) {
// List an existing file.
addAtom(entry, TDEIO::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, TDEIO::UDS_LINK_DEST, slink);
} else {
type &= S_IFMT;
}
addAtom(entry, TDEIO::UDS_FILE_TYPE, type);
#ifdef HAVE_UDS_HIDDEN
if (path.contains("/.")) {
addAtom(entry, TDEIO::UDS_HIDDEN, 1);
}
#endif
} else {
// List a locate link.
addAtom(entry, TDEIO::UDS_URL, url);
addAtom(entry, TDEIO::UDS_FILE_TYPE, S_IFDIR);
}
} else {
addAtom(entry, TDEIO::UDS_URL, url);
}
if (!icon.isEmpty()) {
addAtom(entry, TDEIO::UDS_ICON_NAME, icon);
}
return entry;
}
/////////////////////////////////////////////////////////////////////
// INITIALIZATION
LocateProtocol::LocateProtocol(const TQCString &pool_socket, const TQCString &app_socket)
: SlaveBase("tdeio_locate", pool_socket, app_socket)
{
kdDebug(7134) << "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()
{
kdDebug(7134) << "LocateProtocol::~LocateProtocol()" << endl;
delete m_baseDir;
}
const LocateRegExp& LocateProtocol::getRegExp() const
{
return m_locateRegExp;
}
int LocateProtocol::getCollapseDirectoryThreshold() const
{
return m_config.m_collapseDirectoryThreshold;
}
/////////////////////////////////////////////////////////////////////
// TDEIO 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;
kdDebug(7134) << "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)
{
kdDebug(7134) << "LocateProtocol::get(" << url << ")" << endl;
setUrl(url);
if (isSearchRequest()) {
if (m_locater.binaryExists()) {
error(TDEIO::ERR_IS_DIRECTORY, TQString());
} else {
TQString html = i18n("<h1>\"%1\" could not be started.</h1><p>Please note that tdeio-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>, tdeio-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>.").arg(m_locater.binary());
outputHtml(html);
}
} else if (isConfigRequest()) {
configRequest();
} else if (isHelpRequest()) {
helpRequest();
} else {
// What's this?
error(TDEIO::ERR_DOES_NOT_EXIST, TQString());
}
}
void LocateProtocol::stat(const KURL& url)
{
kdDebug(7134) << "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, TDEIO::UDS_NAME, url.decode_string(url.url()));
addAtom(entry, TDEIO::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(TDEIO::ERR_DOES_NOT_EXIST, TQString());
}
}
void LocateProtocol::listDir(const KURL& url)
{
kdDebug(7134) << "LocateProtocol::listDir(" << url << ")" << endl ;
setUrl(url);
if (isSearchRequest()) {
searchRequest();
} else if (isConfigRequest() || isHelpRequest()) {
error(TDEIO::ERR_IS_FILE, TQString());
} else {
// What's this?
error(TDEIO::ERR_DOES_NOT_EXIST, TQString());
}
}
void LocateProtocol::mimetype(const KURL& url)
{
kdDebug(7134) << "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;
}
}
kdDebug(7134) << "Pattern: " << m_locatePattern << endl;
kdDebug(7134) << "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 ...").arg(display));
bool started = m_locater.locate(m_locatePattern, !isCaseSensitive(m_locatePattern), regexp);
if (!started) {
kdDebug(7134) << "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)
{
// kdDebug(7134) << "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();
kdDebug(7134) << "LocateProtocol::locateFinished" << endl;
infoMessage(i18n("Finished."));
finished();
}
TQString LocateProtocol::partToPattern(const TQString& part, bool forLocate)
{
kdDebug(7134) << "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("\\~", "~");
}
kdDebug(7134) << "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.arg(subItems).arg(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(TDEConfigDialog::showDialog("settings"))
return;
TDEConfigDialog *dialog = new TDEConfigDialog(0, "settings", KLocateConfig::self(),
KDialogBase::IconList,
KDialogBase::Default|KDialogBase::Ok|KDialogBase::Cancel|KDialogBase::Help,
KDialogBase::Ok, true);
dialog->setCaption(i18n("Configure - tdeio-locate"));
dialog->setIcon(SmallIcon("edit-find"));
dialog->addPage(new KLocateConfigWidget(), i18n("General"), "package_settings");
dialog->addPage(new KLocateConfigFilterWidget(), i18n("Filters"), "filter");
dialog->addPage(new KLocateConfigLocateWidget(), i18n("Locate"), "edit-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()
{
kdDebug(7134) << "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.
kdDebug(7134) << "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:/tdeio-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);
kdDebug(7134) << ws << m_path << endl;
LocateItems::ConstIterator item = m_items.begin();
for (; item != m_items.end(); ++item) {
kdDebug(7134) << 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" { KDE_EXPORT int kdemain(int argc, char **argv); }
extern "C"
{
int kdemain(int argc, char **argv)
{
// We use TDEApplication instead of TDEInstance here, because we use a
// config dialog and such gui stuff.
TDEApplication::disableAutoDcopRegistration();
TDEApplication app(argc, argv, "tdeio_locate", false, true, false);
kdDebug(7134) << "*** Starting tdeio_locate " << endl;
if (argc != 4) {
kdDebug(7134) << "Usage: tdeio_locate protocol domain-socket1 domain-socket2" << endl;
exit(-1);
}
LocateProtocol slave(argv[2], argv[3]);
slave.dispatchLoop();
kdDebug(7134) << "*** tdeio_locate Done" << endl;
return 0;
}
}
#include "tdeio_locate.moc"