summaryrefslogtreecommitdiffstats
path: root/src/kio_locate.cpp
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-02-12 18:09:55 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-02-12 18:09:55 +0000
commit630b7196d93786595546565329160ff26332704b (patch)
tree16f570c8f65cf789945d96ab7c4c7c9a46c10e2f /src/kio_locate.cpp
downloadtdeio-locate-630b7196d93786595546565329160ff26332704b.tar.gz
tdeio-locate-630b7196d93786595546565329160ff26332704b.zip
Added KDE3 version of kio-locate
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kio-locate@1089224 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/kio_locate.cpp')
-rw-r--r--src/kio_locate.cpp1035
1 files changed, 1035 insertions, 0 deletions
diff --git a/src/kio_locate.cpp b/src/kio_locate.cpp
new file mode 100644
index 0000000..9beee51
--- /dev/null
+++ b/src/kio_locate.cpp
@@ -0,0 +1,1035 @@
+/***************************************************************************
+ * 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 <qfile.h>
+
+#include "kio_locate.h"
+#include "klocateconfig.h"
+#include "klocateconfigwidget.h"
+#include "klocateconfigfilterwidget.h"
+#include "klocateconfiglocatewidget.h"
+
+using namespace KIO;
+
+
+static const QString queryQuery = "q";
+static const QString queryDirectory = "directory";
+static const QString queryCase = "case";
+static const QString queryRegExp = "regexp";
+
+static const QString 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 QString& 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 QString convertWildcardsToRegExp(QString 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 = QRegExp::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 QString& 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 QString stripTrailingSlash(const QString& 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 QString addTrailingSlash(const QString& path)
+{
+ int n = path.length();
+ if ((n > 0) && (path[n-1] == '/')) {
+ return path;
+ }
+ return path + '/';
+}
+
+
+static void addAtom(UDSEntry& entry, unsigned int uds, const QString& 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 QString& path, const QString& display,
+ const QString& url = QString::null, const QString& icon = QString::null)
+{
+ 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)) {
+ QString slink = QString::null;
+ char buff[1000];
+ int n = readlink(path, 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 QCString &pool_socket, const QCString &app_socket)
+ : SlaveBase("kio_locate", pool_socket, app_socket)
+{
+ DEBUGSTR << "LocateProtocol::LocateProtocol()" << endl;
+
+ connect(&m_locater, SIGNAL(found(const QStringList&)),
+ this, SLOT(processLocateOutput(const QStringList&)));
+ connect(&m_locater, SIGNAL(finished()),
+ this, 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") {
+ QString 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, QString::null);
+ } else {
+ QString 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>.").arg(m_locater.binary());
+ outputHtml(html);
+ }
+ } else if (isConfigRequest()) {
+ configRequest();
+ } else if (isHelpRequest()) {
+ helpRequest();
+ } else {
+ // What's this?
+ error(KIO::ERR_DOES_NOT_EXIST, QString::null);
+ }
+}
+
+
+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, QString::null);
+ }
+}
+
+
+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, QString::null);
+ } else {
+ // What's this?
+ error(KIO::ERR_DOES_NOT_EXIST, QString::null);
+ }
+}
+
+
+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 QString& body)
+{
+ mimeType("text/html");
+
+ QString 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 = QString::null;
+ m_locateDirectory = QString::null;
+ m_regExps.clear();
+ m_pendingPath = QString::null;
+
+ delete m_baseDir;
+ m_baseDir = NULL;
+ m_curDir = NULL;
+
+ updateConfig();
+
+ QString query = m_url.queryItem(queryQuery);
+ m_locateDirectory = addTrailingSlash(m_url.queryItem(queryDirectory));
+
+ QString caseSensitivity = m_url.queryItem(queryCase);
+ if (caseSensitivity == "sensitive") {
+ m_caseSensitivity = caseSensitive;
+ } else if (caseSensitivity == "insensitive") {
+ m_caseSensitivity = caseInsensitive;
+ }
+
+ QString 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;
+ QString display;
+ for (int i = 0; i <= n; i++) {
+ if ((i == n) || ((query[i] == ' ') && (i > 0)
+ && (query[i-1] != '\\') && (i-s > 0))) {
+ QString temp = query.mid(s, i-s);
+ QString 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 ...").arg(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 QString& 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 QString& path, int subItems)
+{
+ // DEBUGSTR << "LocateProtocol::addHit( " << path << ", " << subItems << " )" << endl;
+ if (QFile::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 QString &path, const QString &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);
+ QString 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 QStringList& 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.
+ QStringList::ConstIterator it = items.begin();
+ if (m_pendingPath) {
+ processPath(m_pendingPath, *it);
+ m_pendingPath = QString::null;
+ }
+ for (; it != items.end();) {
+ QString path = *it;
+ ++it;
+ processPath(path, it != items.end() ? *it : QString::null);
+ }
+}
+
+
+void LocateProtocol::locateFinished()
+{
+ // Add any pending items.
+ if (m_pendingPath) {
+ processPath(m_pendingPath, "");
+ m_pendingPath = QString::null;
+ }
+ addPreviousLocateOutput();
+
+ DEBUGSTR << "LocateProtocol::locateFinished" << endl;
+ infoMessage(i18n("Finished."));
+ finished();
+}
+
+
+QString LocateProtocol::partToPattern(const QString& part, bool forLocate)
+{
+ DEBUGSTR << "BEG part: " << part << endl;
+ QString 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 QRegExp.
+ 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) {
+ QString name = pattern.mid(1, index-1);
+ QString 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 QString& 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()));
+}
+
+
+QString LocateProtocol::pathToDisplay(const QString& path, int subItems)
+{
+ // Split off the directory part. If it is not just the minimal '/'.
+ QString 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.
+ QString output = m_config.m_collapsedDisplay, temp;
+ temp.setNum(subItems);
+ output.replace("%1", temp);
+ output.replace("%2", display);
+ display = output;
+ }
+ return display;
+}
+
+
+QString LocateProtocol::makeLocaterUrl(const QString& 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, SIGNAL(settingsChanged()), this, SLOT(updateConfig()));
+ connect(dialog, SIGNAL(finished()), this, SLOT(configFinished()));
+
+ dialog->show();
+ qApp->enter_loop();
+ delete dialog;
+}
+
+
+void LocateProtocol::configFinished()
+{
+ DEBUGSTR << "LocateProtocol::configFinished" << endl;
+
+ qApp->exit_loop();
+
+ QString 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 QString& path)
+{
+ m_parent = parent;
+ m_path = path;
+ m_childs.setAutoDelete(true);
+ m_itemsCount = 0;
+}
+
+
+LocateDirectory *LocateDirectory::addPath(const QString& path)
+{
+ if (path.startsWith(m_path)) {
+ QString 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 QString& relPath)
+{
+ QString 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 QString& 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)
+{
+ QString 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 QString& 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"