diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-12 18:09:55 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-12 18:09:55 +0000 |
commit | 630b7196d93786595546565329160ff26332704b (patch) | |
tree | 16f570c8f65cf789945d96ab7c4c7c9a46c10e2f /src/kio_locate.cpp | |
download | tdeio-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.cpp | 1035 |
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" |