summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am25
-rw-r--r--src/apt+http.protocol11
-rw-r--r--src/apt-file.desktop8
-rw-r--r--src/apt-files.desktop8
-rw-r--r--src/apt-search.desktop8
-rw-r--r--src/apt.cpp946
-rw-r--r--src/apt.h137
-rw-r--r--src/apt.protocol11
-rw-r--r--src/aptcache.cpp292
-rw-r--r--src/aptcache.h81
-rw-r--r--src/debug.h17
-rw-r--r--src/dpkg.cpp210
-rw-r--r--src/dpkg.h50
-rw-r--r--src/headerbg.pngbin0 -> 492 bytes
-rw-r--r--src/kdedeb_logo.pngbin0 -> 10351 bytes
-rw-r--r--src/kio_apt.css250
-rw-r--r--src/packagemanager.cpp31
-rw-r--r--src/packagemanager.h76
-rw-r--r--src/parsers/Makefile.am7
-rw-r--r--src/parsers/filesearch.cpp52
-rw-r--r--src/parsers/list.cpp69
-rw-r--r--src/parsers/parsers.cpp64
-rw-r--r--src/parsers/parsers.h91
-rw-r--r--src/parsers/policy.cpp202
-rw-r--r--src/parsers/qhtmlstream.h286
-rw-r--r--src/parsers/search.cpp76
-rw-r--r--src/parsers/show.cpp194
-rw-r--r--src/regexps.cpp45
-rw-r--r--src/regexps.h23
29 files changed, 3270 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..ebd9d1f
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,25 @@
+INCLUDES = $(all_includes)
+
+kde_module_LTLIBRARIES = kio_apt.la
+
+METASOURCES = AUTO
+
+kio_apt_la_SOURCES = aptcache.cpp apt.cpp regexps.cpp packagemanager.cpp dpkg.cpp
+
+kio_apt_la_LIBADD = $(top_builddir)/src/parsers/libparsers.la -lkio
+kio_apt_la_LDFLAGS = -avoid-version -module $(all_libraries) $(KDE_PLUGIN)
+
+protocoldir = $(kde_servicesdir)
+protocol_DATA = apt.protocol apt+http.protocol
+
+appdatadir = $(kde_datadir)/kio_apt/
+appdata_DATA = kio_apt.css kdedeb_logo.png
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/kio_apt.pot
+
+noinst_HEADERS = debug.h aptcache.h apt.h regexps.h dpkg.h packagemanager.h
+
+searchproviderdir = $(kde_servicesdir)/searchproviders
+searchprovider_DATA = apt-search.desktop apt-file.desktop apt-files.desktop
+SUBDIRS = parsers
diff --git a/src/apt+http.protocol b/src/apt+http.protocol
new file mode 100644
index 0000000..bd3c25c
--- /dev/null
+++ b/src/apt+http.protocol
@@ -0,0 +1,11 @@
+[Protocol]
+exec=kio_apt
+protocol=apt+http
+input=none
+output=stream
+reading=true
+defaultMimetype=text/html
+Icon=remote
+Description=A kioslave for using apt-cache functionalities inside konqueror
+determineMimetypeFromExtension=false
+DocPath=kio_apt/index.html
diff --git a/src/apt-file.desktop b/src/apt-file.desktop
new file mode 100644
index 0000000..52891e0
--- /dev/null
+++ b/src/apt-file.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Charset=
+Hidden=false
+Keys=apt-file
+Name=APT - Find File
+Query=apt:/fsearch?\\{@}
+ServiceTypes=SearchProvider
+Type=Service
diff --git a/src/apt-files.desktop b/src/apt-files.desktop
new file mode 100644
index 0000000..7089b96
--- /dev/null
+++ b/src/apt-files.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Charset=
+Hidden=false
+Keys=apt-files
+Name=APT - Filelist
+Query=apt:/list?\\{@}
+ServiceTypes=SearchProvider
+Type=Service
diff --git a/src/apt-search.desktop b/src/apt-search.desktop
new file mode 100644
index 0000000..876337f
--- /dev/null
+++ b/src/apt-search.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Charset=
+Hidden=false
+Keys=apt-search
+Name=APT - Search Package
+Query=apt:/search?\\{@}
+ServiceTypes=SearchProvider
+Type=Service
diff --git a/src/apt.cpp b/src/apt.cpp
new file mode 100644
index 0000000..9a68991
--- /dev/null
+++ b/src/apt.cpp
@@ -0,0 +1,946 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Sylvain Joyeux *
+ * sylvain.joyeux@m4x.org *
+ * The forms are by Willy De la Court <wdl@linux-lovers.be> *
+ * *
+ * 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 "apt.h"
+#include "regexps.h"
+
+#include "dpkg.h"
+
+#include "parsers/parsers.h"
+
+#include <qcstring.h>
+
+#include <kapplication.h>
+#include <kinstance.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kurl.h>
+#include <kio/slavebase.h>
+#include <kmessagebox.h>
+
+#include <kdebug.h>
+
+#include <qregexp.h>
+
+#include <stdlib.h>
+
+#include <config.h>
+
+using namespace KIO;
+
+/*************************************************************************
+* Common definitions of HTML fragments
+*/
+
+static const QString
+ html_preamble("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Strict//EN\"\n"
+ "\t\"http://www.w3.org/TR/html4/strict.dtd\">\n"
+ "<html>\n");
+static const QString
+ html_redirect(html_preamble +
+ QString("<head>\n"
+ "\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n"
+ "\t<meta http-equiv=\"Refresh\" content=\"0 ; URL=%1\">\n"
+ "</head>\n"
+ "\n<body></body>\n"
+ "</html>"));
+
+static const QString
+ html_head(html_preamble +
+ QString("<head>\n"
+ "\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n"
+ "\t<link rel=\"stylesheet\" href=\"file:%1\">\n"
+ "\t<title>%2</title>\n"
+ "</head>\n\n"
+ "<body>\n"));
+
+static QString close_html_head();
+static QString open_html_head(const QString& title, bool links, AptProtocol const& config)
+{
+
+ static const QString
+ html_head_table(
+ "<table class=\"header\" style=\"background-image: url(file:%1);\"\n"
+ "\t\tcellspacing=\"0\" cellpadding=\"0\">\n"
+ "<tr>\n"
+ "\t<td class=\"logo\" %2><img src=\"file:%3\" alt=\"%4\" style=\"border: 0px\" /></td>\n"
+ "\t<td class=\"header-title\">%4</td>\n");
+
+ QString rowspan;
+ if (links) rowspan = "rowspan=\"2\"";
+ QString ret =
+ html_head
+ .arg(config.stylesheet())
+ .arg(title)
+ + html_head_table
+ .arg(config.header_background())
+ .arg(rowspan)
+ .arg(config.logo())
+ .arg(config.logo_alt())
+ .arg(title);
+
+ if (links)
+ {
+ return ret +
+ "</tr>\n"
+ "<tr>\n"
+ "\t<td class=\"links\">\n"
+ "\t<table class=\"links\" cellspacing=\"0\" cellpadding=\"0\">\n"
+ "\t<tr>\n";
+ }
+ else
+ {
+ return ret + "</tr>\n</table>\n\n";
+ }
+}
+static QString add_html_head_link(const QString& url, const QString& name, const QString& long_desc)
+{
+ static const QString format("\t\t<td><a href=\"%1\" title=\"%2\">%3</a></td>\n");
+ return format.arg(url).arg(long_desc).arg(name);
+}
+static QString close_html_head()
+{
+ return "\t</tr>\n"
+ "\t</table>\n"
+ "\t</td>\n"
+ "</tr>"
+ "</table>";
+}
+
+static const QString
+ html_tail("<div class=\"footer\">%1</div>\n"
+ "</body>\n"
+ "</html>");
+
+
+QString AptProtocol::make_html_tail(const QString& note, bool with_form)
+{
+ with_form = m_search && with_form;
+
+ QString ret;
+ if (with_form)
+ ret = "<hr>\n" + make_html_form();
+
+ if (!note.isEmpty())
+ ret += html_tail.arg(note + ". " + i18n("Page generated by kio_apt."));
+ else ret += html_tail.arg(i18n("Page generated by kio_apt."));
+
+ return ret;
+}
+
+/**********************************************************************************
+ * Search form
+ */
+
+static const QString
+ html_form_begin("\n<form action=\"apt:/\" method=\"GET\">\n"
+ "<table class=\"query\">\n");
+static const QString
+ html_form_end("<tr>\n"
+ "\t<td class=\"button\" colspan=\"2\"><input type=\"submit\" value=\"%1\"></td>\n"
+ "</tr>\n"
+ "</table>\n"
+ "</form>\n");
+
+static const QString
+ html_form_line("<tr>\n"
+ "\t<td><label for=\"%1\">%2</label></td>\n"
+ "\t<td><input type=\"text\" name=\"%3\" id=\"%4\"></td>\n"
+ "</tr>\n");
+
+static QString make_html_form_line(const QString& type, const QString& label)
+{ return html_form_line.arg(type).arg(label).arg(type).arg(type); }
+
+
+static QString make_extform_cmd(bool ext_form, const KURL& query)
+{
+ QString cmd = ext_form ? "0" : "1";
+ QString msg = ext_form ? i18n("Hide extended form") : i18n("Show extended form");
+
+ KURL url(query);
+ url.addQueryItem("extended_form", cmd);
+ url.setRef("extformcmd");
+
+ return
+ "<div class=\"command\" id=\"extformcmd\">\n"
+ "\t<a href=\"" + url.htmlURL() + "\">[" + msg + "]</a>\n"
+ "</div>\n";
+}
+
+/** Prints the HTML code for the query form */
+QString AptProtocol::make_html_form() const
+{
+ bool can_fsearch = false;
+ bool ext_form = KGlobal::config() -> readBoolEntry("extended_form", true);
+ // Only in config-file. Needed for some dpkg-based distros that are not Debian
+ can_fsearch = can_searchfile(true);
+
+ bool online = false;
+ bool online_form = KGlobal::config() -> readBoolEntry("online_form", true);
+ if (m_adept_batch)
+ online = online_form && (!m_internal) && ext_form && m_adept_batch -> capabilities(PackageManager::ONLINE);
+
+ QString ret;
+ QTextOStream stream(&ret);
+ stream << make_extform_cmd(ext_form, m_query);
+
+ if (online)
+ stream << "<table class=\"queryform\"><tr><td>\n";
+
+ stream << html_form_begin;
+ stream << "<tr><td colspan=\"2\" class=\"title\">" + i18n("Offline search") + "</td></tr>" << endl;
+ stream << make_html_form_line("search", i18n("Package search"));
+ if (ext_form)
+ {
+ if (can_fsearch)
+ stream << make_html_form_line("fsearch", i18n("File search"));
+ stream << make_html_form_line("show", i18n("Package info"));
+ }
+ stream << html_form_end.arg( i18n("Search") );
+
+ if (online)
+ {
+ stream << "\n</td><td>\n";
+ stream << m_adept_batch -> getOnlineForm();
+ stream << "\n</td></tr>\n</table>";
+ }
+
+ return ret;
+}
+
+/****************************************************************************************/
+
+AptProtocol::AptProtocol( const QCString &pool_socket, const QCString &app_socket )
+ : SlaveBase( "kio_apt", pool_socket, app_socket ),
+ m_adept_batch(0), m_parser(0)
+
+{
+ KStandardDirs* dirs = KGlobal::dirs();
+ m_stylesheet = dirs->findResource( "data", "kio_apt/kio_apt.css" );
+
+ m_logo = dirs->findResource( "data", "kio_apt/"
+ + KGlobal::config() -> readEntryUntranslated("logo", "kdedeb_logo.png" ) );
+
+ m_header_background = dirs->findResource( "data", "kio_apt/"
+ + KGlobal::config() -> readEntryUntranslated("background", "headerbg.png" ) );
+
+ m_logo_alt = KGlobal::config() -> readEntryUntranslated("alt_tag", i18n("KDE on Debian") );
+
+ connect(&m_process, SIGNAL(token(const QString&, const QString&)),
+ this, SLOT(token_dispatch(const QString&, const QString&)));
+
+ m_adept_batch = new Dpkg(this);
+
+ if (m_adept_batch)
+ {
+ connect(m_adept_batch, SIGNAL(token(const QString&, const QString&)),
+ this, SLOT(token_dispatch(const QString&, const QString&)));
+ }
+}
+
+AptProtocol::~AptProtocol() {}
+
+QString AptProtocol::stylesheet() const { return m_stylesheet; }
+QString AptProtocol::logo() const { return m_logo; }
+QString AptProtocol::logo_alt() const { return m_logo_alt; }
+QString AptProtocol::header_background() const { return m_header_background; }
+
+void AptProtocol::token_dispatch(const QString& name, const QString& val)
+{
+ if (m_parser.get())
+ (*m_parser)(this, name, val);
+}
+
+void AptProtocol::data(const QCString& string)
+{
+ using namespace Parsers;
+ (*this) << string;
+}
+
+void AptProtocol::data(const QString& string)
+{
+ using namespace Parsers;
+ (*this) << string;
+}
+
+void AptProtocol::data(const char* string)
+{
+ using namespace Parsers;
+ (*this) << string;
+}
+
+void AptProtocol::data(const QByteArray& array)
+{ SlaveBase::data(array); }
+
+void AptProtocol::mimetype( const KURL & /*url*/ )
+{
+ mimeType( "text/html" );
+ finished();
+}
+
+bool AptProtocol::check_validpackage(const QString& query)
+{
+ static QRegExp rx_pkgname(rxs_pkgname);
+ if (!rx_pkgname.exactMatch(query))
+ {
+ error( ERR_SLAVE_DEFINED, i18n("\"%1\" is not a valid package name").arg(query) );
+ return false;
+ }
+ return true;
+}
+
+
+/********************************************************************
+ * Main entry point
+ */
+
+static QString read_option(QMap<QString, QString>& map, const QString& name, const QString& def)
+{
+ if (!map.contains(name)) return def;
+ QString ret = map[name];
+ map.remove(name);
+ return ret;
+}
+
+void AptProtocol::get ( const KURL& url )
+{
+ /* The queries have two possible formats :
+
+ - clean way to call a command
+ apt:/command?query&option=value&option=value&...
+ - needed to simplify forms
+ apt:/?command=query&command2=&command3=&option=value&option=value&...
+ - calls only the query form page
+ apt:/
+ */
+
+ typedef void (AptProtocol::*Command)(const QString&, const QueryOptions&);
+ static const QString commands[] =
+ { "search", "show", "policy",
+ "fsearch", "list", "online",
+ "get", QString::null };
+ static const Command methods[] =
+ { &AptProtocol::search, &AptProtocol::show, &AptProtocol::policy,
+ &AptProtocol::searchfile, &AptProtocol::listfiles, &AptProtocol::online,
+ &AptProtocol::adept_batch };
+
+ QString command, query;
+ Command method = 0;
+ QueryOptions options = url.queryItems(KURL::CaseInsensitiveKeys);
+
+ // canonize the part before ? : remove the first /
+ QString path = url.path();
+ QString host = url.host();
+
+ if ( path.isEmpty() && !host.isEmpty() )
+ {
+ path = host;
+ }
+
+ if (path [0] == '/')
+ path = path.right(path.length() - 1);
+
+ for (int cmd_idx = 0; !commands[cmd_idx].isNull(); ++cmd_idx)
+ {
+ const QString cmd_it = commands[cmd_idx];
+
+ // Look if the command is in the path part
+ if (command.isEmpty() && cmd_it == path)
+ {
+ command = cmd_it;
+ method = methods[cmd_idx];
+ }
+ if (options.contains(cmd_it))
+ {
+
+ if (options[cmd_it].isEmpty() && !options[cmd_it].isNull())
+ { // we have a &command=& format, we remove it
+ options.remove(cmd_it);
+ }
+ else if (command.isEmpty() && !options[cmd_it].isEmpty())
+ { // the command is set in the options map
+ command = cmd_it;
+ method = methods[cmd_idx];
+ query = options[cmd_it];
+ options.remove(cmd_it);
+ }
+ }
+ }
+
+ // Well, we have no query for now, let's find it
+ if (query.isEmpty())
+ {
+ for (QueryOptions::Iterator i = options.begin(); i != options.end(); ++i)
+ {
+ if ((*i).isNull())
+ {
+ query = KURL::decode_string(i.key());
+ options.remove(i);
+ break;
+ }
+ }
+ }
+
+ // Interpret the ioslave config options
+ // and remove them from the options map
+ QString opt = read_option(options, "extended_form", QString::null);
+ if (!opt.isNull())
+ {
+ bool ext_form = (opt != "0");
+ KGlobal::config() -> writeEntry("extended_form", ext_form);
+ }
+
+ // Enable install/remove/upgrade/...
+ opt = read_option(options, "enable_actions", "1");
+ m_act = (opt != "0");
+
+ opt = read_option(options, "enable_search", "1");
+ m_search = (opt != "0");
+
+ // Allow links outside of apt:/ hierarchy
+ opt = read_option(options, "stay_internal", "0");
+ m_internal = (opt == "1");
+
+ // Sync the config (must use kcfg sometime :p)
+ KGlobal::config() -> sync();
+
+ if (command.isEmpty() || query.isEmpty())
+ {
+ //If path isn't empty, then a package is to be installed via an apt:/ link
+ if ( !path.isEmpty())
+ {
+ query = "install";
+ options["package"] = path;
+ options["weblinkinstall"] = 1;
+ adept_batch(query, options);
+ return;
+ }
+
+ // No query or no command, we go in help mode
+ m_query = buildURL(KURL("apt:/"));
+ help();
+ }
+ else
+ {
+ m_query = buildURL(command, query);
+ m_query.setHTMLRef(url.htmlRef());
+ for (QueryOptions::ConstIterator i = options.begin(); i != options.end(); ++i)
+ m_query.addQueryItem(i.key(), i.data());
+
+ kdDebug() << "Old url " << url << ", new url " << m_query << endl;
+
+ if (m_query != url)
+ {
+ redirection(m_query);
+ data(QByteArray());
+ finished();
+ return;
+ }
+
+ (this->*method)(query, options);
+ }
+}
+
+
+/***********************************************************************************
+*
+* form
+*
+*/
+
+void AptProtocol::help()
+{
+ mimeType("text/html");
+
+ QString buffer;
+ QTextOStream stream(&buffer);
+ stream
+ << open_html_head(i18n("Search Form"), false, *this)
+ << make_html_form()
+ << make_html_tail(QString::null, false);
+ data(buffer);
+ data(QByteArray());
+ finished();
+}
+
+
+
+
+
+
+
+/***********************************************************************************
+ * apt-cache search
+ */
+
+void AptProtocol::search( const QString& query, const QueryOptions& /*options*/ )
+{
+ mimeType("text/html");
+
+ data(open_html_head(i18n("Package search result for \"%1\"").arg(query), false, *this));
+
+ m_parser.reset(new Parsers::Search);
+ (*m_parser)(this, "begin", query);
+ if (!m_process.search( query ))
+ {
+ error(ERR_SLAVE_DEFINED, i18n("Error launching the search").arg(query));
+ return;
+ }
+ (*m_parser)(this, "end", QString::null);
+
+ data(make_html_tail( i18n("%1 results").arg(m_parser -> result_count())) );
+ data(QByteArray());
+ finished();
+}
+
+
+
+
+
+
+/***********************************************************************************
+ * apt-cache show
+ */
+
+static QString filelist_cmd(bool show_filelist, const KURL& query)
+{
+ QString value = show_filelist ? "0" : "1";
+ QString msg = show_filelist ? i18n("Hide file list") : i18n("Show file list");
+
+ KURL url(query);
+ url.addQueryItem("show_filelist", value);
+ url.setRef("filelistcmd");
+
+ return
+ "<div class=\"command\" id=\"filelistcmd\">\n"
+ "\t<a href=\"" + url.htmlURL() + "\">"
+ "[" + msg + "]"
+ "</a>\n"
+ "</div>";
+}
+
+void AptProtocol::show(const QString& package, const QueryOptions& options)
+{
+ if (!check_validpackage(package)) return;
+
+ if (options.contains("show_filelist"))
+ {
+ KGlobal::config() -> writeEntry("show_filelist", options["show_filelist"] != "0");
+ KGlobal::config() -> sync();
+ }
+
+ mimeType("text/html");
+
+ QString installed_version;
+
+ /** First, we parse policy
+ * We use here the fact that HTML is generated
+ * during the call of (*policy)(...,"end",...),
+ * since the header changes when the package
+ * is installed or not */
+ Parsers::Policy* policy = new Parsers::Policy(package, m_act);
+ m_parser.reset(policy);
+ (*m_parser)(this, "begin", QString::null);
+ {
+ if (!m_process.policy( package ))
+ {
+ error(ERR_SLAVE_DEFINED, i18n("Can't launch \"apt-cache policy %1\"").arg(package));
+ return;
+ }
+
+ installed_version = policy->getInstalled();
+ bool can_list = can_listfiles(!installed_version.isEmpty());
+ QString buffer;
+ QTextOStream s(&buffer);
+ if (can_list)
+ {
+ KURL url = buildURL("list", package);
+ s << open_html_head(i18n("Package description for \"%1\"").arg(package), true, *this)
+ << add_html_head_link(url.htmlURL(), i18n("List package files"), "")
+ << close_html_head();
+ }
+ else
+ {
+ s << open_html_head(i18n("Package description for \"%1\"").arg(package), false, *this);
+ }
+ data(buffer);
+ }
+ (*m_parser)(this, "end", QString::null);
+
+
+ /** Add package description section */
+ m_parser.reset(new Parsers::Show(package, installed_version, m_act));
+ (*m_parser)(this, "begin", QString::null);
+ {
+ if (!m_process.show(package))
+ {
+ error(ERR_SLAVE_DEFINED, i18n("Can't launch \"apt-cache show %1\"").arg(package));
+ return;
+ }
+ if (!m_parser -> result_count())
+ {
+ data("<div class=\"error\">" + i18n("No package found named \"%1\"").arg(package) + "</div>\n");
+ data(make_html_tail());
+ data(QByteArray());
+ finished();
+ return;
+ }
+ }
+ (*m_parser)(this, "end", QString::null);
+
+
+
+ /** Add file list (if enabled) */
+ bool show_filelist = KGlobal::config() -> readBoolEntry("show_filelist", false);
+ if ( show_filelist )
+ {
+ if (can_listfiles(!installed_version.isEmpty()))
+ {
+ data(
+ "<hr>\n"
+ + filelist_cmd(show_filelist, m_query)
+ + "<div class=\"filelist\">\n");
+
+ m_parser.reset(new Parsers::List(!m_internal));
+ (*m_parser)(this, "begin", QString::null);
+ if (!m_adept_batch -> list(package))
+ {
+ error(ERR_SLAVE_DEFINED, i18n("Error listing files of %1").arg(package));
+ return;
+ }
+ (*m_parser)(this, "end", QString::null);
+
+ data("\n</div>\n");
+ }
+ else // cannot list files
+ {
+ data(
+ "<hr>\n"
+ + filelist_cmd(show_filelist, m_query)
+ + "<div class=\"error\">" + i18n("Cannot list files for non-installed packages") + "</div>\n");
+ }
+ }
+ else
+ {
+ data("<hr>\n" + filelist_cmd(show_filelist, m_query));
+ }
+
+
+ data(make_html_tail());
+ data(QByteArray());
+ finished();
+}
+
+
+
+
+/***********************************************************************************
+ * apt-cache policy
+ */
+
+void AptProtocol::policy( const QString& query, const QueryOptions& /*options*/ )
+{
+ if (!check_validpackage(query)) return;
+
+ mimeType("text/html");
+
+ data( open_html_head(i18n("Apt policy for \"%1\"").arg(query), false, *this) );
+
+ m_parser.reset(new Parsers::Policy(query, m_act));
+ (*m_parser)(this, "begin", QString::null);
+ if (!m_process.policy( query ))
+ {
+ error(ERR_SLAVE_DEFINED, i18n("Can't launch the policy for %1").arg(query));
+ return;
+ }
+ (*m_parser)(this, "end", QString::null);
+
+ data(make_html_tail());
+ data(QByteArray());
+ finished();
+}
+
+
+
+/***********************************************************************************
+* Search the package which contains a specific file
+*/
+
+static const QString
+ html_dpkgs_begin("\n\n<table>\n"),
+ html_dpkgs_end("\n\n</table>\n");
+
+
+bool AptProtocol::can_searchfile(bool is_installed) const
+{
+ if (!m_adept_batch) return false;
+ int caps = m_adept_batch -> capabilities(PackageManager::SEARCH_FILE | PackageManager::OFFLINE);
+ if (!caps) return false;
+ return is_installed || !(caps & PackageManager::INSTALLED_ONLY);
+}
+void AptProtocol::searchfile(const QString& query, const QueryOptions& /*options*/)
+{
+ if (!can_searchfile(true)) return;
+
+ mimeType("text/html");
+ data( open_html_head(i18n("File search for \"%1\"").arg(query), false, *this) + html_dpkgs_begin );
+
+ m_parser.reset(new Parsers::FileSearch);
+ (*m_parser)(this, "begin", QString::null);
+ if (!m_adept_batch -> search( query ))
+ {
+ error(ERR_SLAVE_DEFINED, i18n("Can't launch the package manager").arg(query));
+ return;
+ }
+ (*m_parser)(this, "end", QString::null);
+
+ data( html_dpkgs_end + make_html_tail(i18n("%1 files found").arg(m_parser -> result_count())) );
+ data(QByteArray());
+ finished();
+}
+
+
+
+
+/***********************************************************************************
+* List the files of a package
+*/
+
+bool AptProtocol::can_listfiles(bool is_installed) const
+{
+ if (!m_adept_batch) return false;
+ int caps = m_adept_batch -> capabilities(PackageManager::LIST_FILES | PackageManager::OFFLINE);
+ if (!caps) return false;
+ return is_installed || !(caps & PackageManager::INSTALLED_ONLY);
+}
+
+void AptProtocol::listfiles(const QString& query, const QueryOptions& /*options*/)
+{
+ if (!can_listfiles(true)) return;
+ if (!check_validpackage(query)) return;
+
+ mimeType("text/html");
+
+ KURL ret_url = buildURL("show", query);
+
+ QString buffer;
+ QTextOStream stream(&buffer);
+ stream
+ << open_html_head(i18n("Files in \"%1\"").arg(query), true, *this)
+ << add_html_head_link(ret_url.htmlURL(), i18n("Show package info"), "")
+ << close_html_head()
+ << endl;
+ data(buffer);
+
+ m_parser.reset(new Parsers::List(!m_internal));
+ (*m_parser)(this, "begin", QString::null);
+ if (!m_adept_batch -> list( query ))
+ {
+ error(ERR_SLAVE_DEFINED, i18n("Can't launch the package manager").arg(query));
+ return;
+ }
+ (*m_parser)(this, "end", QString::null);
+
+ data(make_html_tail());
+ data(QByteArray());
+ finished();
+}
+
+
+
+
+/***********************************************************************************
+ * Go use online search services (like packages.debian.org for instance)
+ */
+
+//bool AptProtocol::can_online(int mode) const
+//{
+//// if (!m_adept_batch) return false;
+//// return m_adept_batch -> capabilities(PackageManager::ONLINE | mode);
+// return false;
+//}
+
+void AptProtocol::online(const QString& query, const QueryOptions& options)
+{
+ QString url = m_adept_batch -> getOnlineURL(query, options);
+ redirection(url);
+ finished();
+ return;
+}
+
+/***********************************************************************************
+ * Send commands for adept_batch
+ */
+void AptProtocol::adept_batch(const QString& query, const QueryOptions& options)
+{
+ p=NULL;
+
+ QString command;
+ QString url;
+ QStringList plist;
+ QStringList puninst;
+ QStringList pinst;
+ int pcount;
+ int ip;
+
+ if (query == "install" || query.isEmpty()) {
+ command = "kdesu adept_batch install ";
+ } else if (query == "remove") {
+ command = "kdesu adept_batch remove ";
+ }
+
+ if (command.isEmpty())
+ {
+ error(ERR_SLAVE_DEFINED, i18n("No package manager command specified"));
+ return;
+ }
+
+ if (!options.contains("package"))
+ {
+ error(ERR_SLAVE_DEFINED, i18n("No package specified"));
+ return;
+ }
+
+ plist = QStringList::split(", ", options["package"], false);
+ pcount = plist.count();
+ command += plist.join(" ");
+
+ if (pcount == 1)
+ {
+ if (query == "install")
+ ip = SlaveBase::messageBox(QuestionYesNo, i18n("Do you want to install %1 ?").arg(plist[0]), i18n("Package Installation"));
+ else
+ ip = SlaveBase::messageBox(QuestionYesNo, i18n("Do you want to remove %1 ?").arg(plist[0]), i18n("Package Removal"));
+ }
+ else
+ {
+ if (query == "install")
+ ip = SlaveBase::messageBox(QuestionYesNo,i18n("Do you want to install the following %1 packages ?\n%2").arg(pcount).arg(options["package"]));
+ else
+ ip = SlaveBase::messageBox(QuestionYesNo,i18n("Do you want to remove the following %1 packages ?\n").arg(pcount).arg(options["package"]));
+ }
+
+ kdDebug(DEBUG_ZONE) << command << endl;
+
+ if (ip == KMessageBox::Yes)
+ {
+ p = new KShellProcess;
+ p->clearArguments();
+ *p << command;
+ p->start( KProcess::Block, KProcess::All );
+
+ for(int i = 0; i != pcount; ++i)
+ {
+ QString installed_version;
+
+ Parsers::Policy* policy = new Parsers::Policy(plist[i], m_act);
+ m_parser.reset(policy);
+ (*m_parser)(this, "begin", QString::null);
+ {
+ if (!m_process.policy( plist[i] ))
+ {
+ error(ERR_SLAVE_DEFINED, i18n("Can't launch \"apt-cache policy %1\"").arg(plist[i]));
+ return;
+ }
+
+ installed_version = policy->getInstalled();
+ if (installed_version.isEmpty())
+ {
+ puninst += plist[i];
+ }
+ else
+ {
+ pinst += plist[i];
+ }
+ }
+ }
+
+ if (options.contains("weblinkinstall"))
+ {
+ if (puninst.count() == 0)
+ {
+ messageBox(Information,i18n("Installation successfull."));
+ }
+ else
+ {
+ QString toto = puninst.join(" ");
+ messageBox(Information,i18n("There was a problem installing %1.").arg(toto));
+ }
+ return;
+ }
+ else
+ {
+ url = "apt:/show?";
+ // Outside of a weblink, only one package can be installed at time
+ url += plist[0];
+ redirection(url);
+ data(QByteArray());
+ finished();
+ return;
+ }
+ }
+ else
+ {
+ return;
+ }
+}
+
+KURL AptProtocol::buildURL( const QString & command, const QString & query ) const
+{
+ KURL url;
+ url.setProtocol("apt");
+ if (!command.startsWith("/"))
+ url.setPath("/" + command);
+ else
+ url.setPath(command);
+ url.setQuery(query);
+ return buildURL(url);
+}
+
+KURL AptProtocol::buildURL( const KURL& query ) const
+{
+ KURL url(query);
+
+ if (!m_act)
+ url.addQueryItem("enable_actions", "0");
+ if (!m_search)
+ url.addQueryItem("enable_search", "0");
+ if (m_internal)
+ url.addQueryItem("stay_internal", "1");
+
+ return url;
+}
+
+/***********************************************************************************
+*
+* kdemain
+*
+*/
+
+extern "C" {
+ int kdemain( int argc, char **argv ) {
+ KInstance instance( "kio_apt" );
+
+ if ( argc != 4 ) {
+ kdDebug( DEBUG_ZONE ) << "Usage: kio_apt protocol domain-socket1 domain-socket2" << endl;
+ exit ( -1 );
+ }
+
+ AptProtocol slave( argv[ 2 ], argv[ 3 ] );
+ slave.dispatchLoop();
+
+ return 0;
+ }
+}
+
+#include "apt.moc"
diff --git a/src/apt.h b/src/apt.h
new file mode 100644
index 0000000..d18ffa8
--- /dev/null
+++ b/src/apt.h
@@ -0,0 +1,137 @@
+#ifndef KIOAPT_APTPROTOCOL_H
+#define KIOAPT_APTPROTOCOL_H
+
+#include <qstring.h>
+#include <qcstring.h>
+
+#include <kurl.h>
+#include <kio/global.h>
+#include <kio/slavebase.h>
+
+#include "debug.h"
+#include "aptcache.h"
+#include "packagemanager.h"
+
+#include <memory>
+
+class QCString;
+
+namespace Parsers
+{
+ class Parser;
+}
+
+class AptProtocol : public QObject, public KIO::SlaveBase {
+ Q_OBJECT
+
+ friend class AptCache;
+
+ AptCache m_process;
+ PackageManager* m_adept_batch;
+
+ typedef QMap<QString, QString> QueryOptions;
+
+ /** This this the URL that should be used if we want
+ * to get the same output again. It is especially
+ * used in the make_html_form for the command link.
+ *
+ * The get() method sets it to apt:/command?query
+ * the various methods should add the relevant options,
+ * that is options which modify the query itself.
+ * The GUI-related options (like show_filelist in the
+ * show command) should NOT be added
+ */
+ KURL m_query;
+
+ /** if m_act is false, the ioslave is in browse-only
+ * mode (for forbidding install, for instance) */
+ bool m_act, m_search, m_internal;
+
+ QString m_stylesheet;
+ QString m_header_background;
+ QString m_logo;
+ QString m_logo_alt;
+
+
+public:
+ AptProtocol( const QCString &pool_socket, const QCString &app_socket );
+ virtual ~AptProtocol();
+ virtual void mimetype( const KURL& url );
+ virtual void get ( const KURL& url );
+
+ /** Sends the string to the ioslave's master
+ * SlaveBase::data() sends a byte array as is. Since we
+ * definitely don't want to send \0 to the master,
+ * we redefine data() for strings */
+ void data(const QCString& string);
+
+ /** @overload */
+ void data(const QString& string);
+
+ /** @overload */
+ void data(const char* string);
+
+ void data(const QByteArray& array);
+
+ KURL buildURL(const QString& command, const QString& query) const;
+ KURL buildURL(const KURL& query) const;
+
+ QString stylesheet() const;
+ QString header_background() const;
+ QString logo() const;
+ QString logo_alt() const;
+
+private slots:
+ void token_dispatch(const QString& tag, const QString& value);
+
+private:
+ std::auto_ptr<Parsers::Parser> m_parser;
+
+ /** apt-cache search
+ * Performs apt-cache search, with the query encoded in url.query()
+ * and sends the result as an HTML file */
+ void search( const QString& url, const QueryOptions& options );
+
+ /** apt-cache show
+ * Performs apt-cache search, with the package name encoded in url.query()
+ * and sends the result as an HTML file.
+ * It checks that the query contains a valid package name */
+ void show( const QString& url, const QueryOptions& options );
+
+ /** apt-cache policy
+ * Performs apt-cache policy, with the package name encoded in url.query()
+ * and sends the result as an HTML file.
+ * It checks that the query contains a valid package name */
+ void policy( const QString& url, const QueryOptions& options );
+
+ /**
+ * Sends an application/x-adept_batch file with commands
+ * understandable by adept_batch */
+ void adept_batch( const QString& url, const QueryOptions& options );
+
+ /**
+ * Shows a form where one can enter parameters for some queries
+ **/
+ void help();
+
+ /** Offline listing of the file of a package */
+ bool can_listfiles(bool is_installed) const;
+ void listfiles( const QString& query, const QueryOptions& options);
+
+ /** Offline file search.
+ * Searches the package which contains the specified file */
+ bool can_searchfile(bool is_installed) const;
+ void searchfile( const QString& query, const QueryOptions& options);
+
+ bool can_online(int mode) const;
+ void online( const QString& query, const QueryOptions& options);
+
+ bool check_validpackage(const QString& query);
+ QString make_html_form() const;
+ QString make_html_tail(const QString& note = QString::null, bool with_form = true);
+
+ KShellProcess * p;
+
+};
+
+#endif
diff --git a/src/apt.protocol b/src/apt.protocol
new file mode 100644
index 0000000..24a197e
--- /dev/null
+++ b/src/apt.protocol
@@ -0,0 +1,11 @@
+[Protocol]
+exec=kio_apt
+protocol=apt
+input=none
+output=stream
+reading=true
+defaultMimetype=text/html
+Icon=kioapt
+Description=A kioslave for using apt-cache functionalities inside konqueror
+determineMimetypeFromExtension=false
+DocPath=kio_apt/index.html
diff --git a/src/aptcache.cpp b/src/aptcache.cpp
new file mode 100644
index 0000000..9511760
--- /dev/null
+++ b/src/aptcache.cpp
@@ -0,0 +1,292 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Sylvain Joyeux *
+ * sylvain.joyeux@m4x.org *
+ * *
+ * 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 "aptcache.h"
+#include "apt.h"
+#include "debug.h"
+
+#include "regexps.h"
+
+#include <qstringlist.h>
+#include <qregexp.h>
+
+#include <kdebug.h>
+
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+
+
+AptCache::AptCache()
+{
+ connect(&m_process, SIGNAL(receivedStderr(KProcess*, char*, int)),
+ this, SLOT(receivedStdErr(KProcess*, char*, int )));
+ connect(&m_process, SIGNAL(receivedStdout(KProcess*, char*, int)),
+ this, SLOT(receivedStdOut(KProcess*, char*, int )));
+}
+AptCache::~AptCache() {}
+
+
+
+
+static QStringList received(QString& buffer, char* input, int input_len)
+{
+ buffer += QString::fromLatin1(input, input_len);
+ QStringList ret = QStringList::split('\n', buffer, true);
+ if (!buffer.endsWith("\n"))
+ {
+ buffer = ret.last();
+ ret.pop_back();
+ }
+ else
+ buffer = "";
+
+ return ret;
+}
+void AptCache::receivedStdErr( KProcess * /*process*/, char * buffer, int len )
+{
+ static QRegExp rx_we("(W|E):\\s+(.*)");
+
+ QStringList lines = received(m_received_err, buffer, len);
+ for (QStringList::ConstIterator i = lines.begin(); i != lines.end(); ++i)
+ {
+ if (rx_we.exactMatch(*i))
+ {
+ if (rx_we.cap(1) == "E") emit token("error", rx_we.cap(2));
+ else emit token("warning", rx_we.cap(2));
+ }
+ else
+ {
+ kdDebug() << "Unmatched error : " << *i << endl;
+ }
+ }
+}
+void AptCache::receivedStdOut( KProcess * /*process*/, char * buffer, int len )
+{
+ QStringList lines = received(m_received_out, buffer, len);
+ (this->*m_receive)(lines);
+}
+
+
+
+
+void AptCache::clear()
+{
+ m_process.clearArguments();
+ m_attribute = "";
+ m_received_err = "";
+ m_received_out = "";
+}
+
+bool AptCache::search(const QString& expression)
+{
+ clear();
+
+ m_process.setEnvironment("LANGUAGE", "C");
+ m_process << "apt-cache" << "search";
+ m_process << QStringList::split(" ", expression);
+ m_receive = &AptCache::receiveSearch;
+ return m_process.start(KProcess::Block, KProcess::Stdout );
+}
+
+void AptCache::receiveSearch(const QStringList& lines)
+{
+ static QRegExp rx_parse("([^ ]+) - (.*)");
+
+ QStringList::ConstIterator i;
+ for (i = lines.begin(); i != lines.end(); ++i)
+ {
+ if ((*i).isEmpty()) continue;
+
+ if (!rx_parse.exactMatch(*i))
+ {
+ kdDebug(DEBUG_ZONE) << "Parsing error. Line is " << *i << endl;
+ continue;
+ }
+
+ emit token("package", rx_parse.cap(1));
+ emit token("short_desc", rx_parse.cap(2));
+
+ kdDebug(DEBUG_ZONE) << "Found package : " << rx_parse.cap(1) << " - " << rx_parse.cap(2) << endl;
+ }
+}
+
+bool AptCache::show(const QString& package)
+{
+ clear();
+
+ m_process.setEnvironment("LANGUAGE", "C");
+ m_process << "apt-cache" << "show" << package;
+ m_receive = &AptCache::receiveShow;
+ return m_process.start(KProcess::Block, KProcess::Stdout );
+}
+
+void AptCache::receiveShow(const QStringList& lines)
+{
+ static bool pkgfield = false, insert_newline = false;
+ static int indent = 0;
+
+ static QRegExp rx_attribute("([\\w-]+): (.*)");
+ static const QString pkg_fields[] =
+ { "Suggests", "Replaces", "Depends", "Conflicts", QString::null };
+
+ QStringList::ConstIterator i;
+ for (i = lines.begin(); i != lines.end(); ++i)
+ {
+ QString data(*i);
+ if (data.isEmpty()) continue;
+
+ if (rx_attribute.exactMatch(*i))
+ {
+ m_attribute = rx_attribute.cap(1);
+ data = rx_attribute.cap(2);
+
+ if (m_attribute != "Package")
+ emit token("field", m_attribute);
+
+ insert_newline = pkgfield = false;
+ indent = 0;
+
+ const QString * test_field;
+ for (test_field = pkg_fields; !test_field -> isNull(); ++test_field)
+ if (*test_field == m_attribute)
+ {
+ pkgfield = true;
+ break;
+ }
+ }
+
+ if (m_attribute == "Package")
+ emit token("package", data);
+ else if (pkgfield)
+ parse_pkgfield(data);
+ else
+ {
+ int new_indent = data.find( QRegExp("[^\\s]") );
+
+ // new_indent > 0 means that we are in a multi-line
+ // field. Those lines always begin with " ", so we want
+ // to drop it.
+ if (new_indent > 0) --new_indent;
+
+ if (new_indent != indent)
+ {
+ emit token("indent", QString::number(new_indent) );
+ indent = new_indent;
+ insert_newline = false;
+ }
+
+ if (data == " .")
+ {
+ if (insert_newline)
+ emit token("data", "\n");
+ }
+ else
+ {
+ if (insert_newline)
+ emit token("data", "\n" + data);
+ else
+ emit token("data", data);
+ }
+
+ insert_newline = true;
+ }
+ }
+}
+
+void AptCache::parse_pkgfield(const QString& data)
+{
+ QStringList split(QStringList::split(",", data));
+ for (QStringList::ConstIterator i = split.begin(); i != split.end(); ++i)
+ {
+ if (i != split.begin()) emit token("data", ", ");
+
+ QStringList bar(QStringList::split("|", *i));
+ for (QStringList::ConstIterator j = bar.begin(); j != bar.end(); ++j)
+ {
+ if (j != bar.begin()) emit token("data", " | ");
+ QString pkg, remaining;
+
+ int paren = (*j).find('(');
+ if (paren != -1)
+ {
+ pkg = (*j).left(paren - 1);
+ remaining = (*j).right((*j).length() - paren + 1);
+ }
+ else
+ {
+ pkg = (*j);
+ }
+
+ pkg = pkg.stripWhiteSpace();
+ remaining = remaining.stripWhiteSpace();
+
+ emit token("package_link", pkg);
+ if (!remaining.isEmpty()) emit token("data", " " + remaining);
+ }
+ }
+}
+
+bool AptCache::policy( const QString & package )
+{
+ clear();
+
+ m_process.setEnvironment("LANGUAGE", "C");
+ m_process << "apt-cache" << "policy" << package;
+ m_receive = &AptCache::receivePolicy;
+ return m_process.start(KProcess::Block, KProcess::Stdout );
+}
+
+void AptCache::receivePolicy(const QStringList& lines)
+{
+ static QRegExp rx_pkgname("(\\w[\\w+-.]+):");
+ static QRegExp rx_location("^\\s*\\d+\\s[^\\d]");
+
+ for(QStringList::ConstIterator l = lines.begin(); l != lines.end(); ++l)
+ {
+ if ((*l).isEmpty()) continue;
+
+ QString data( (*l).stripWhiteSpace() );
+ if (rx_pkgname.exactMatch(*l))
+ emit token("package", rx_pkgname.cap(1));
+ else if (data.startsWith("Installed:", false))
+ {
+ data = data.right(data.length() - 11);
+ emit token("installed", data);
+ m_installed = data;
+ }
+ else if (data.startsWith("Candidate:", false))
+ {
+ data = data.right(data.length() - 11);
+ emit token("candidate", data);
+ m_candidate = data;
+ }
+ else if (data.startsWith("Version table:", false))
+ emit token("version_table", QString::null);
+ else if (rx_location.search(data) > -1)
+ emit token("location", data);
+ else
+ {
+ if (data.startsWith("*** "))
+ data = data.right( data.length() - 4 );
+
+ if (match_dversion(data.section(' ', 0, 0)))
+ emit token("version", data);
+ }
+ }
+}
+
+QString AptCache::policy_installed() const
+{ return m_installed; }
+QString AptCache::policy_candidate() const
+{ return m_candidate; }
+
+
+#include "aptcache.moc"
+
diff --git a/src/aptcache.h b/src/aptcache.h
new file mode 100644
index 0000000..c209507
--- /dev/null
+++ b/src/aptcache.h
@@ -0,0 +1,81 @@
+/***************************************************************************
+* Copyright (C) 2003 by Sylvain Joyeux *
+* sylvain.joyeux@m4x.org *
+* *
+* 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. *
+***************************************************************************/
+#ifndef KIOAPT_APTCACHE_H
+#define KIOAPT_APTCACHE_H
+
+#include <qvaluelist.h>
+#include <qmap.h>
+#include <qstring.h>
+#include <kprocess.h>
+
+class AptProtocol;
+
+/**
+@author Sylvain Joyeux
+*/
+class AptCache : public QObject {
+ Q_OBJECT
+
+ typedef void (AptCache::*ReceiveMethod) (const QStringList& lines);
+ ReceiveMethod m_receive;
+
+ // KProcIO messes the stderr and the stdout lines :(
+ KProcess m_process;
+ QString m_received_out, m_received_err;
+
+ QString m_attribute;
+
+ QString m_installed, m_candidate;
+
+private slots:
+ void receivedStdErr(KProcess* process, char* buffer, int len);
+ void receivedStdOut(KProcess* process, char* buffer, int len);
+
+private:
+ void clear();
+ void receiveSearch(const QStringList& lines);
+ void receiveShow(const QStringList& lines);
+ void receivePolicy(const QStringList& lines);
+ void parse_pkgfield(const QString& data);
+
+public:
+ AptCache();
+ ~AptCache();
+
+ bool search(const QString& expression);
+ bool show(const QString& package);
+ bool policy(const QString& package);
+
+ QString policy_installed() const;
+ QString policy_candidate() const;
+
+signals:
+ /** Tags:
+ * warning (warning text)
+ * error (error text)
+ * package (package_name)
+ * short_desc (description)
+ * field (field_name)
+ * package_link (package_name)
+ * data (plain_data)
+ * indent (value)
+ * policy
+ * installed (version)
+ * candidate (version)
+ * version_table
+ * version (version_desc)
+ * location (location_desc)
+ * file (file_name) [for dpkg]
+ * end
+ */
+ void token(const QString& tag, const QString& value);
+};
+
+#endif
diff --git a/src/debug.h b/src/debug.h
new file mode 100644
index 0000000..ad8151b
--- /dev/null
+++ b/src/debug.h
@@ -0,0 +1,17 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Sylvain Joyeux *
+ * sylvain.joyeux@m4x.org *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#ifndef KIOAPT_DEBUG
+#define KIOAPT_DEBUG
+
+#define DEBUG_ZONE 7000
+
+#endif
+
diff --git a/src/dpkg.cpp b/src/dpkg.cpp
new file mode 100644
index 0000000..0a95851
--- /dev/null
+++ b/src/dpkg.cpp
@@ -0,0 +1,210 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Sylvain Joyeux *
+ * sylvain.joyeux@m4x.org *
+ * *
+ * 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 <config.h>
+
+#include "dpkg.h"
+#include "debug.h"
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <qstringlist.h>
+#include <qregexp.h>
+
+
+
+Dpkg::Dpkg(QObject *parent, const char *name)
+ : PackageManager(parent, name)
+{
+ connect(&m_process, SIGNAL(readReady (KProcIO *)), this, SLOT(readReady(KProcIO*)));
+}
+
+Dpkg::~Dpkg()
+{
+}
+
+int Dpkg::capabilities( int query ) const
+{
+ if ( (query & SEARCH_FILE) && (query & OFFLINE) )
+ return query | INSTALLED_ONLY;
+ if ( (query & LIST_FILES) && (query & OFFLINE) )
+ return query | INSTALLED_ONLY;
+ if ( query & ONLINE )
+ return query;
+ return NOT_SUPPORTED;
+}
+
+void Dpkg::readReady(KProcIO*)
+{
+ bool partial;
+
+ QString newline;
+ QStringList lines;
+ while(m_process.readln(newline, true, &partial) != -1)
+ {
+ if (partial) m_buffer += newline;
+ else
+ {
+ newline.truncate(newline.length());
+ QString line(m_buffer + newline);
+ lines << line;
+ m_buffer = "";
+ }
+
+ }
+
+ (this->*m_receive)(lines);
+}
+
+bool Dpkg::search( const QString & file )
+{
+ m_process.resetAll();
+ m_buffer = QString::null;
+
+ m_process.clearArguments();
+ m_process << "dpkg" << "-S" << file;
+ m_receive = &Dpkg::receiveSearch;
+ return m_process.start(KProcess::Block, KProcess::Stdout );
+}
+
+void Dpkg::receiveSearch( const QStringList & line )
+{
+ static QRegExp rx_notfound("dpkg: (.*) not found");
+ // the format of the dpkg -S answer is
+ // package1[, package2[, package3...]]: file
+ for (QStringList::ConstIterator i = line.begin(); i != line.end(); ++i)
+ {
+ //kdDebug(DEBUG_ZONE) << *i << endl;
+ if ((*i).isEmpty()) continue;
+ if (rx_notfound.exactMatch(*i))
+ {
+ emit token("error", i18n("%1 not found").arg(rx_notfound.cap(1)));
+ continue;
+ }
+
+ int semicolon = (*i).find(':');
+ if (semicolon == -1)
+ {
+ kdDebug(DEBUG_ZONE) << "receiveSearch unmatched line : " << *i << endl;
+ continue;
+ }
+ QStringList packages = QStringList::split(',', (*i).left(semicolon));
+ QString file = (*i).right( (*i).length() - semicolon - 1 );
+
+ emit token("file", file.stripWhiteSpace());
+
+ for (QStringList::ConstIterator j = packages.begin(); j != packages.end(); ++j)
+ emit token("package", (*j).stripWhiteSpace());
+ }
+}
+
+bool Dpkg::list( const QString & package )
+{
+ m_process.resetAll();
+ m_buffer = QString::null;
+
+ m_process.clearArguments();
+ m_process << "dpkg" << "-L" << package;
+ m_receive = &Dpkg::receiveList;
+ return m_process.start(KProcess::Block, KProcess::Stdout );
+}
+
+void Dpkg::receiveList( const QStringList & line )
+{
+ static QRegExp rx_notfound("Package (.*) is not installed");
+ for (QStringList::ConstIterator i = line.begin(); i != line.end(); ++i)
+ {
+ if (rx_notfound.search(*i) > -1)
+ emit token("error", i18n("Package %1 is not installed").arg(rx_notfound.cap(1)));
+ else if ((*i).startsWith("/"))
+ emit token("file", *i);
+ }
+}
+
+static const QString
+ html_form_begin("\n<form action=\"http://packages.ubuntu.com/cgi-bin/search_contents.pl\" method=\"GET\">\n"
+ "<table class=\"query\">\n");
+
+static QString make_title(const QString& title)
+{ return "\t<tr><td class=\"title\" colspan=\"2\">" + title + "</td></tr>\n"; }
+static const QString
+ html_form_end("<tr>\n"
+ "\t<td class=\"button\" colspan=\"2\">\n"
+ "\t\t<input type=\"submit\" value=\"%1\">\n"
+ "\t\t<input type=\"hidden\" name=\"searchmode\" value=\"searchfilesanddirs\">\n"
+ "\t\t<input type=\"hidden\" name=\"case\" value=\"insensitive\">\n"
+ "\t</td>\n"
+ "</tr>\n"
+ "</table>\n"
+ "</form>\n");
+
+static const QString
+ html_form_line_begin("<tr>\n"
+ "\t<td><label for=\"%1\">%2</label></td>\n"
+ "\t<td>\n");
+static const QString
+ html_form_line_end("</td>\n</tr>\n");
+
+static const QString html_form_combo("<select name=\"%1\" id=\"%2\">");
+
+static QString make_form_text(const QString& type, const QString& label)
+{
+ return
+ html_form_line_begin.arg(type).arg(label)
+ + QString("<input type=\"text\" name=\"%1\" id=\"%2\">").arg(type).arg(type)
+ + html_form_line_end;
+}
+
+static QString begin_form_combo(const QString& type, const QString& label)
+{
+ return
+ html_form_line_begin.arg(type).arg(label)
+ + QString("\t<select name=\"%1\" id=\"%2\">\n").arg(type).arg(type);
+}
+static QString make_form_option(const QString& name, const QString& text)
+{ return "\t\t<option value=" + name + ">" + text + "</option>\n"; }
+static QString end_form_combo()
+{ return "\t</select>\n\t</td>\n</tr>\n"; }
+
+QString Dpkg::getOnlineForm()
+{
+ QString buffer;
+ QTextOStream stream(&buffer);
+ stream
+ << html_form_begin
+ << make_title( i18n("packages.ubuntu.com"))
+
+ << make_form_text("word", i18n("File to search"))
+ << begin_form_combo("arch", i18n("Architecture"))
+ << make_form_option("i386", i18n("Intel x86"))
+ << make_form_option("amd64", i18n("AMD64"))
+ << make_form_option("sparc", i18n("SPARC"))
+ << make_form_option("powerpc", i18n("PowerPC"))
+ << make_form_option("hppa", i18n("HP PA/RISC"))
+ << make_form_option("ia64", i18n("Intel IA-64"))
+ << end_form_combo()
+ << begin_form_combo("version", i18n("Version"))
+ << make_form_option("gutsy", "gutsy")
+ << make_form_option("feisty", "feisty")
+ << make_form_option("edgy", "edgy")
+ << make_form_option("dapper", "dapper")
+ << make_form_option("breezy", "breezy")
+ << make_form_option("hoary", "hoary")
+ << make_form_option("warty", "warty")
+ << end_form_combo()
+
+ << html_form_end.arg(i18n("Go online!"));
+
+ return buffer;
+}
+
+#include "dpkg.moc"
+
diff --git a/src/dpkg.h b/src/dpkg.h
new file mode 100644
index 0000000..1092386
--- /dev/null
+++ b/src/dpkg.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Sylvain Joyeux *
+ * sylvain.joyeux@m4x.org *
+ * *
+ * 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. *
+ ***************************************************************************/
+#ifndef DPKG_H
+#define DPKG_H
+
+#include <config.h>
+
+#include "packagemanager.h"
+#include <kprocio.h>
+
+/**
+@author Sylvain Joyeux
+*/
+class Dpkg : public PackageManager
+{
+ Q_OBJECT
+
+ typedef void (Dpkg::*ReceiveMethod) (const QStringList& lines);
+ ReceiveMethod m_receive;
+
+ KProcIO m_process;
+ QString m_buffer;
+
+private slots:
+ void readReady(KProcIO* io);
+
+private:
+ void receiveSearch(const QStringList& line);
+ void receiveList(const QStringList& line);
+
+public:
+ Dpkg(QObject *parent = 0, const char *name = 0);
+ ~Dpkg();
+
+ virtual bool list(const QString& package);
+ virtual bool search(const QString& file);
+
+ virtual int capabilities(int query) const;
+
+ virtual QString getOnlineForm();
+};
+
+#endif
diff --git a/src/headerbg.png b/src/headerbg.png
new file mode 100644
index 0000000..b72738a
--- /dev/null
+++ b/src/headerbg.png
Binary files differ
diff --git a/src/kdedeb_logo.png b/src/kdedeb_logo.png
new file mode 100644
index 0000000..2489def
--- /dev/null
+++ b/src/kdedeb_logo.png
Binary files differ
diff --git a/src/kio_apt.css b/src/kio_apt.css
new file mode 100644
index 0000000..4b13e26
--- /dev/null
+++ b/src/kio_apt.css
@@ -0,0 +1,250 @@
+/** This is the stylesheet for the
+ * HTML code produced by the apt:/ ioslave
+ * (c) 2003 Sylvain Joyeux <sylvain.joyeux@m4x.org>
+ * Provided under the GPL licenses */
+
+BODY {
+ background-color: white;
+ color: black;
+}
+
+table { margin-top: 1em; }
+table.header {
+ margin-top: 0;
+ margin-bottom: 1em;
+ width: 90%;
+ font-weight: bold;
+/* font-variant: small-caps; */
+ font-size: x-large;
+
+ color: #0855C5;
+
+ /*vertical-align: middle;*/
+
+ height: 85px;
+ background-color: #FFFFFF;
+ background-image: url("headerbg.png");
+}
+
+table.header td.logo {
+ width: 92px;
+}
+
+H2 { text-align: justify; }
+table.version TR { padding-top: 1em; }
+table.version TD { padding-top: 1em; vertical-align: top; }
+table.version TD.attname { font-weight: bold; }
+
+.footer {
+ margin-top: 3em;
+ font-size: small;
+ font-weight: bold;
+ text-align: center;
+}
+
+td.header-title {
+ vertical-align: top;
+ text-align: center;
+ padding-top: 1.5em;
+ font-size: 15pt;
+}
+td.links {
+ vertical-align: bottom;
+}
+
+/* global lind buttons at bottom of decorative header */
+table.links {
+ float: right;
+ border:0;
+ padding: 1px 0 0 0;
+ margin: 0;
+}
+table.links td a {
+ color: white
+}
+table.links td:hover {
+ background-color:#0C4293;
+}
+table.links td a:hover {
+ color: white;
+ background-color:#0C4293
+}
+table.links td {
+ border-left: 1px solid #4A81D5;
+ padding: 0px 12px 0px 12px;
+ background-color:#0E4EAF;
+ font-size:9pt;
+ margin: 0;
+ /*font-weight: bold;*/
+}
+/******************************************
+* Forms
+*/
+
+table.queryform {
+ margin: auto;
+ width: 80%;
+}
+table.query {
+ margin: auto;
+ margin-top: 1em;
+}
+table.queryform form {
+ margin: 2em;
+}
+table.query td.button {
+ text-align: center;
+ padding-top: .7em;
+}
+
+table.query td.title {
+ text-align: center;
+ font-weight: bold;
+ padding-bottom: .7em;
+}
+
+table.query td.button input[type="submit"] {
+ width: 40%;
+}
+
+/******************************************
+* Show
+*/
+
+.error {
+ text-align: center;
+ font-weight: bold;
+ font-size: larger;
+ margin-top: 1em;
+}
+
+/*h3.version, h3.installed-version {
+ padding-left: 1em;
+ margin-top: 4em;
+ text-align: left;
+ background-color: #0855C5;
+ color: white;
+}
+h3.installed-version {
+ background-color: #FF3333;
+}*/
+
+.version-header, .version-header-installed {
+ font-size: larger;
+ font-weight: bold;
+ /*padding-left: 1em;*/
+ margin-top: 4em;
+ text-align: left;
+ color: white;
+ background-color: #0855C5;
+}
+
+.version-header-installed {
+ background-color: #F03333;
+}
+
+.version-header .links, .version-header-installed .links {
+ float: left;
+
+ color: white;
+
+ font-size:9pt;
+ background-color:#0C4293;
+
+ padding: 0px 12px 0px 12px;
+ margin: 0 1em 0 0;
+
+ border-left: 1px solid #4A81D5;
+}
+
+.version-header-installed .links {
+ background-color: #CC2222;
+ border-left: 1px solid #FF7D7F;
+}
+
+table.version {
+ margin-left: 3%;
+ width: 90%;
+ border-width: thin;
+ border-style: solid;
+}
+
+.command
+{
+ margin-top: 1em;
+ margin-bottom: 1em;
+ text-align: center;
+ font-size: medium;
+}
+.filelist
+{
+ margin-left: 5%;
+}
+
+
+/***************************
+* Policy classes
+*/
+
+div.policy
+{ margin-top: 1em; }
+table.policy
+{ margin: auto; }
+table.policy TD
+{ vertical-align: middle; }
+
+table.curver {
+ border: solid thin;
+ margin-left: auto;
+ margin-right: 2em;
+}
+.curver form p {
+ margin: 0;
+ padding: 0;
+}
+
+.vtable {
+ border: solid thin;
+ margin-right: auto;
+ margin-left: 2em;
+ padding: .5em;
+}
+.vtable .header {
+ border-bottom: solid thin black;
+ text-align: center;
+ font-weight: bold;
+}
+
+
+.vtable-version {
+ font-weight: bold;
+ margin-right: 1em;
+}
+.vtable .version-pin {
+ font-weight: bold;
+}
+.vtable .location-pin {
+ font-weight: bold;
+ margin-left: 1em;
+ margin-right: 1em;
+}
+
+table.curver TD, table.vtable TD
+{ width: auto;
+ vertical-align: top; }
+
+
+table.vtable thead td {
+ font-weight: bold;
+ text-align: center;
+ border-bottom: solid thin;
+}
+
+table.curver form {
+ text-align: center;
+}
+
+.pin
+{ font-weight: bold; }
+.file
+{ font-weight: bold; }
diff --git a/src/packagemanager.cpp b/src/packagemanager.cpp
new file mode 100644
index 0000000..68dc35e
--- /dev/null
+++ b/src/packagemanager.cpp
@@ -0,0 +1,31 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Sylvain Joyeux *
+ * sylvain.joyeux@m4x.org *
+ * *
+ * 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 "packagemanager.h"
+
+PackageManager::PackageManager(QObject *parent, const char *name)
+ : QObject(parent, name) {}
+
+
+PackageManager::~PackageManager() {}
+
+bool PackageManager::list( const QString & /*package*/ ) { return false; }
+bool PackageManager::search( const QString & /*file*/ ) { return false; }
+
+int PackageManager::capabilities( int /*query*/ ) const { return 0; }
+
+QString PackageManager::getOnlineForm()
+{ return QString::null; }
+QString PackageManager::getOnlineURL
+ ( const QString& /* query */
+ , const QMap<QString, QString>& /*options*/ )
+{ return QString::null; }
+
+#include "packagemanager.moc"
+
diff --git a/src/packagemanager.h b/src/packagemanager.h
new file mode 100644
index 0000000..6d8d771
--- /dev/null
+++ b/src/packagemanager.h
@@ -0,0 +1,76 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Sylvain Joyeux *
+ * sylvain.joyeux@m4x.org *
+ * *
+ * 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. *
+ ***************************************************************************/
+#ifndef PACKAGEMANAGER_H
+#define PACKAGEMANAGER_H
+
+#include <qobject.h>
+
+/** Base class for accessing package-manager specific
+ * functionalities. ATM, it supports listing files in a
+ * package and searching the package which owns a file.
+ *
+ * Support for online-search support is also included.
+ *
+ * @author Sylvain Joyeux
+ */
+
+class PackageManager : public QObject
+{
+ Q_OBJECT
+public:
+ PackageManager(QObject *parent = 0, const char *name = 0);
+ virtual ~PackageManager();
+
+ virtual bool search(const QString& file);
+ virtual bool list(const QString& package);
+
+ /** Checks what the package manager is capable of.
+ * The \c cap parameter should be a or of exactly
+ * one function (SEARCH, LIST, ...) and one access
+ * (ONLINE, OFFLINE).
+ * The function returns NOT_SUPPORTED if the specified query is not
+ * supported. Otherwise, it returns cap with some
+ * restrictions (EXHAUSTIVE, INSTALLED_ONLY, ...) if
+ * they apply
+ *
+ * INSTALLED_ONLY meaning changes with the function considered.
+ * With SEARCH_FILE, it means that SEARCH_FILE only finds
+ * installed files. With LIST_FILES, it means that listing the
+ * files of a package is possible only if the package is already
+ * installed */
+ virtual int capabilities(int query) const;
+
+ virtual QString getOnlineForm();
+ virtual QString getOnlineURL(const QString& query, const QMap<QString, QString>& options);
+
+ enum Capabilities
+ {
+ NOT_SUPPORTED = 0,
+
+ SEARCH_FILE = 0x01,
+ LIST_FILES = 0x02,
+ SHOW = 0x04,
+ OFFLINE = 0x10,
+ ONLINE = 0x20,
+
+ INSTALLED_ONLY = 0x200
+ };
+
+signals:
+ /** Tags:
+ * warning (warning text)
+ * error (error text)
+ * file (file_name) [for dpkg]
+ * end
+ */
+ void token(const QString& tag, const QString& value);
+};
+
+#endif
diff --git a/src/parsers/Makefile.am b/src/parsers/Makefile.am
new file mode 100644
index 0000000..9893dd4
--- /dev/null
+++ b/src/parsers/Makefile.am
@@ -0,0 +1,7 @@
+INCLUDES = $(all_includes)
+METASOURCES = AUTO
+libparsers_la_LDFLAGS = -avoid-version $(all_libraries)
+noinst_LTLIBRARIES = libparsers.la
+noinst_HEADERS = parsers.h qhtmlstream.h
+libparsers_la_SOURCES = list.cpp search.cpp policy.cpp show.cpp parsers.cpp filesearch.cpp
+libparsers_la_LIBADD = $(LIB_KIO)
diff --git a/src/parsers/filesearch.cpp b/src/parsers/filesearch.cpp
new file mode 100644
index 0000000..c0a7a36
--- /dev/null
+++ b/src/parsers/filesearch.cpp
@@ -0,0 +1,52 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Sylvain Joyeux *
+ * sylvain.joyeux@m4x.org *
+ * *
+ * 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 "parsers.h"
+#include "../apt.h"
+
+static QString
+ html_dpkgs_line_begin("<tr><td class=\"file\">%1</td><td>\n\t"),
+ html_dpkgs_line_end("\n</td></tr>\n");
+
+namespace Parsers
+{
+
+void FileSearch::operator() (AptProtocol* slave, const QString & tag, const QString & value )
+{
+ static QString buffer;
+ if (tag == "begin")
+ {
+ m_result_count = 0;
+ }
+ else if (tag == "error")
+ {
+ *slave << "<div class=\"error\">" + value + "</div>";
+ }
+ else if (tag == "file")
+ {
+ if (m_result_count)
+ *slave << buffer + html_dpkgs_line_end;
+
+ *slave << html_dpkgs_line_begin.arg(value);
+ ++m_result_count;
+ buffer = "";
+ }
+ else if (tag == "package")
+ {
+ if (!buffer.isEmpty()) buffer = buffer + ", ";
+ buffer += "<a href=\"apt:/show?" + value + "\">" + value + "</a>";
+ }
+ else if (tag == "end")
+ {
+ *slave << buffer + html_dpkgs_line_end;
+ buffer="";
+ }
+}
+
+}
diff --git a/src/parsers/list.cpp b/src/parsers/list.cpp
new file mode 100644
index 0000000..afaac7f
--- /dev/null
+++ b/src/parsers/list.cpp
@@ -0,0 +1,69 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Sylvain Joyeux *
+ * sylvain.joyeux@m4x.org *
+ * *
+ * 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 "parsers.h"
+#include "../apt.h"
+
+#include <kio/slavebase.h>
+#include <klocale.h>
+#include <qregexp.h>
+#include <kurl.h>
+
+namespace Parsers
+{
+ List::List(bool links)
+ : m_links(links) {}
+
+ /** Parses the tokens sent by PackageManager::list */
+ void List::operator() (AptProtocol* slave, const QString& tag, const QString& value )
+ {
+ static QRegExp rx_manpage("/man/.*\\.\\d[^/]*$");
+
+ static QStringList files;
+
+ if (tag == "begin")
+ {
+ m_result_count = 0;
+ }
+ else if (tag == "error")
+ {
+ *slave << "<div class=\"error\">" + value + "</div>";
+ }
+ else if (tag == "file" && value != "/.")
+ {
+ if (m_links)
+ {
+ KURL url;
+ if (rx_manpage.search(value) >= 0)
+ url.setProtocol("man");
+ else
+ url.setProtocol("file");
+
+ url.setPath(value);
+
+ files += "<a href=\"" + url.htmlURL() + "\">" + value + "</a>";
+ }
+ else
+ {
+ files += value;
+ }
+
+ ++m_result_count;
+ }
+ else if (tag == "end")
+ {
+ files.sort();
+ *slave <<
+ "<div class=\"filelist\">\n" + files.join("\n<br>") + "\n</div>\n"
+ "<div class=\"footer\">" + i18n("%1 files in the package").arg(result_count()) + "</div>\n";
+ files.clear();
+ }
+ }
+}
diff --git a/src/parsers/parsers.cpp b/src/parsers/parsers.cpp
new file mode 100644
index 0000000..2f959ce
--- /dev/null
+++ b/src/parsers/parsers.cpp
@@ -0,0 +1,64 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Sylvain Joyeux *
+ * sylvain.joyeux@m4x.org *
+ * *
+ * 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 "parsers.h"
+
+#include <kuserprofile.h>
+
+#include <kio/slavebase.h>
+#include <qhtmlstream.h>
+#include <qregexp.h>
+
+#include <stdlib.h>
+
+namespace Parsers
+{
+ static void print_czstring(KIO::SlaveBase& slave, const char* data, int len)
+ {
+ QByteArray nonull;
+ nonull.setRawData(data, len);
+ slave.data(nonull);
+ nonull.resetRawData(data, len);
+ }
+ void operator << (KIO::SlaveBase& slave, const QCString& string)
+ { print_czstring(slave, string.data(), string.size() - 1); }
+ void operator << (KIO::SlaveBase& slave, const QString& string)
+ { slave << string.utf8(); }
+ void operator << (KIO::SlaveBase& slave, const char* string)
+ { print_czstring(slave, string, strlen(string)); }
+
+
+
+ Parser::Parser( ) {}
+ Parser::~Parser( ) {}
+
+ void Parser::attribute_begin(QHtmlStream& stream, const QString& text)
+ {
+ stream
+ << block("tr") << endl
+ << block("td") << param("class") << "attname" << data()
+ << text
+ << close() << endl
+ << block("td");
+ }
+ void Parser::attribute_end(QHtmlStream& stream)
+ { stream << close() << endl << close(); }
+
+// void Parser::operator ( )( KIO::SlaveBase * /*slave*/,
+// const QString & /*tag*/, const QString & /*value*/ )
+// {}
+
+ QString mangle_version(QString version)
+ { return "version_" + version.replace(QRegExp("[-:\\.\\+]"), QString("_")); }
+
+}
+
+
+
diff --git a/src/parsers/parsers.h b/src/parsers/parsers.h
new file mode 100644
index 0000000..26e0113
--- /dev/null
+++ b/src/parsers/parsers.h
@@ -0,0 +1,91 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Sylvain Joyeux *
+ * sylvain.joyeux@m4x.org *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#ifndef PARSERS_H
+#define PARSERS_H
+
+#include <qstring.h>
+
+namespace KIO
+{
+ class SlaveBase;
+}
+class QHtmlStream;
+class AptProtocol;
+
+/** Here are defined the functional objects that takes the tokens
+ * sent by AptCache and PackageManager objects, and produces HTML */
+namespace Parsers
+{
+ class Parser
+ {
+ protected:
+ int m_result_count;
+
+ static void attribute_begin(QHtmlStream& stream, const QString& text);
+ static void attribute_end(QHtmlStream& stream);
+
+ public:
+ Parser();
+ virtual ~Parser();
+ int result_count() const { return m_result_count; }
+ virtual void operator () (AptProtocol* slave, const QString& tag, const QString& value) = 0;
+ };
+
+ class Search : public Parser
+ {
+ public:
+ void operator () (AptProtocol* slave, const QString& tag, const QString& value);
+ };
+
+ class List : public Parser
+ {
+ bool m_links;
+ public:
+ List(bool show_links);
+ void operator () (AptProtocol* slave, const QString& tag, const QString& value);
+ };
+
+ class FileSearch : public Parser
+ {
+ public:
+ void operator () (AptProtocol* slave, const QString& tag, const QString& value);
+ };
+
+ class Show : public Parser
+ {
+ QString m_package, m_installed;
+ bool m_act;
+
+ public:
+ Show(const QString& package, const QString& installed, bool act);
+ void operator () (AptProtocol* slave, const QString& tag, const QString& value);
+ };
+
+ class Policy : public Parser
+ {
+ QString m_package, m_installed;
+ bool m_has_adept_batch;
+ bool m_act;
+
+ public:
+ Policy(const QString& package, bool act);
+ QString getInstalled() const { return m_installed; }
+ void operator () (AptProtocol* slave, const QString& tag, const QString& value);
+ };
+
+ void operator << (KIO::SlaveBase& slave, const QCString& string);
+ void operator << (KIO::SlaveBase& slave, const QString& string);
+ void operator << (KIO::SlaveBase& slave, const char* string);
+
+ QString mangle_version(QString version);
+}
+
+#endif
diff --git a/src/parsers/policy.cpp b/src/parsers/policy.cpp
new file mode 100644
index 0000000..ad31b7f
--- /dev/null
+++ b/src/parsers/policy.cpp
@@ -0,0 +1,202 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Sylvain Joyeux *
+ * sylvain.joyeux@m4x.org *
+ * *
+ * 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 "parsers.h"
+#include "../apt.h"
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <qhtmlstream.h>
+#include <qregexp.h>
+
+static void policy_begin(QHtmlStream& stream)
+{
+ stream
+ << block("div") << endl
+ << block("table", "policy") << endl
+ << block("tr") << block("td") << endl
+ << block("table", "curver") << endl
+ << block("tbody");
+}
+
+static void add_button(QHtmlStream& stream, const QString& mode, const QString& text, const QString& package)
+{
+ stream
+ << block("form")
+ << param("action") << "apt:/"
+ << param("method") << "get" << endl
+ << block("p") << endl
+ << tag("input")
+ << param("type") << "hidden"
+ << param("name") << "get"
+ << param("value") << mode << endl
+ << tag("input")
+ << param("type") << "hidden"
+ << param("name") << "package"
+ << param("value") << package << endl
+ << tag("input")
+ << param("type") << "submit"
+ << param("value") << text << endl
+ << close()
+ << close() << endl;
+}
+
+static void add_version_link(QHtmlStream& stream, AptProtocol* slave, const QString& package, const QString& version)
+{
+ KURL url(slave->buildURL("show", package));
+ url.setHTMLRef(Parsers::mangle_version(version));
+
+ stream
+ << block("a", "vtable-version")
+ << param("href") << url.htmlURL()
+ << data() << version
+ << close();
+}
+
+namespace Parsers
+{
+
+Policy::Policy(const QString& package, bool act)
+ : m_package(package), m_act(false)
+{
+ m_act = act;
+}
+
+void Policy::operator() (AptProtocol* slave, const QString& type, const QString& value)
+{
+ static bool first_version = false, received_sth = false;
+ static QString buffer;
+ static QHtmlStream* stream;
+
+ static QRegExp rx_notinstalled("(none)");
+
+ kdDebug() << "policy : " << type << " " << value << endl;
+
+ if (type == "begin")
+ {
+ stream = new QHtmlStream(&buffer);
+ policy_begin(*stream);
+ }
+ else if (type == "installed")
+ {
+ received_sth = true;
+
+ attribute_begin(*stream, i18n("Installed"));
+ if (rx_notinstalled.match(value) >= 0)
+ {
+ m_installed = QString::null;
+ *stream << i18n("no");
+ }
+ else
+ {
+ m_installed = value.stripWhiteSpace();
+ add_version_link(*stream, slave, m_package, m_installed);
+ }
+ attribute_end(*stream);
+ }
+ else if (type == "candidate")
+ {
+ received_sth = true;
+
+ bool has_candidate = (rx_notinstalled.match(value) == -1);
+
+ if (m_act && has_candidate)
+ {
+ *stream
+ << block("tr") << endl
+ << block("td") << param("colspan") << 2 << endl;
+
+ if (m_installed.isEmpty())
+ add_button(*stream, "install", i18n("Install"), m_package);
+ else
+ add_button(*stream, "remove", i18n("Remove"), m_package);
+ *stream << close() << close() << endl;
+ }
+
+ attribute_begin(*stream, i18n("Candidate"));
+ if (has_candidate)
+ add_version_link(*stream, slave, m_package, value);
+ else
+ *stream << i18n("none");
+ attribute_end(*stream);
+
+ if (m_act && has_candidate
+ && !m_installed.isEmpty() && m_installed != value)
+ {
+ *stream
+ << block("tr") << endl
+ << block("td") << param("colspan") << 2 << endl;
+ add_button(*stream, "install", i18n("Upgrade"), m_package);
+ *stream << close() << close() << endl;
+ }
+ }
+ else if (type == "version_table")
+ {
+ received_sth = true;
+
+ *stream
+ << close() << close() << endl // tbody, table, form
+ << close() << endl; // td
+
+ first_version = true;
+ }
+ else if (type == "version")
+ {
+ QString version = value.section(' ', 0, 0);
+ QString pin = value.section(' ', 1, 1);
+
+ if (first_version)
+ {
+ *stream
+ << block("td") << endl
+ << block("div", "vtable") << endl
+ << block("div", "header") << i18n("Version Table") << close() << endl
+ << block("div", "versions") << endl;
+ } /*else {
+ *stream << close() << close();
+ }*/
+
+ QString version_link;
+ version_link = "<a href=\"apt:/show?" + m_package + "#" + mangle_version(version) + "\">"
+ + version + "</a>";
+
+ *stream << tag("br") << endl;
+ add_version_link(*stream, slave, m_package, version);
+ *stream << "[Pin " << block("span", "version-pin") << pin << close() << "]";
+
+ first_version = false;
+ }
+ else if (type == "location")
+ {
+ QStringList sections = QStringList::split(' ', value);
+ QString pin = sections.first();
+ sections.pop_front();
+ // remove the "Packages" field if it is here
+ if (sections.last() == "Packages")
+ sections.pop_back();
+
+ *stream << tag("br") << endl;
+ *stream << block("span", "location-pin") << pin << close() << sections.join(" ") << endl;
+ }
+ else if (type == "end")
+ {
+ if (received_sth)
+ {
+ *stream << close_all() << endl;
+ *slave << buffer;
+ }
+
+ buffer = QString::null;
+ received_sth = false;
+ delete stream;
+ }
+}
+
+}
diff --git a/src/parsers/qhtmlstream.h b/src/parsers/qhtmlstream.h
new file mode 100644
index 0000000..330f9d5
--- /dev/null
+++ b/src/parsers/qhtmlstream.h
@@ -0,0 +1,286 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Sylvain Joyeux *
+ * sylvain.joyeux@m4x.org *
+ * *
+ * 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. *
+ ***************************************************************************/
+#ifndef QHtmlStream_H
+#define QHtmlStream_H
+
+#include <qtextstream.h>
+#include <qstringlist.h>
+
+class QHtmlStream;
+class QHtmlStreamManip;
+
+/**
+@author Sylvain Joyeux
+*/
+
+class QHtmlStreamManip
+{
+protected:
+ virtual void apply(QHtmlStream& stream) const = 0;
+
+public:
+ virtual ~QHtmlStreamManip() {};
+ void operator () (QHtmlStream& stream) const
+ { apply(stream); }
+};
+
+class QHtmlStream
+{
+ QTextOStream m_stream;
+
+ enum States
+ {
+ NORMAL_FLOW,
+ TAG,
+ BLOCK,
+ PARAM
+ };
+ int m_state, m_enclosing_state;
+
+ bool m_newline;
+ QString m_indent;
+ QStringList m_blockstack;
+
+ void finalize_open()
+ {
+ if (m_state == PARAM)
+ m_state = m_enclosing_state;
+
+ if (m_state == BLOCK)
+ m_stream << ">";
+ else if (m_state == TAG)
+ m_stream << " />";
+
+ m_state = NORMAL_FLOW;
+ }
+
+ void indent()
+ {
+ if (m_newline)
+ {
+ m_stream << m_indent;
+ m_newline = false;
+ }
+ }
+
+ template<class T>
+ QHtmlStream& output(const T& o)
+ {
+ indent();
+
+ if (m_state == PARAM)
+ {
+ m_stream << "=\"" << o << "\"";
+ m_state = m_enclosing_state;
+ return *this;
+ }
+
+ if (m_state == BLOCK)
+ {
+ m_stream << ">";
+ m_state = NORMAL_FLOW;
+ }
+ else if (m_state == TAG)
+ {
+ m_stream << "/>";
+ m_state = NORMAL_FLOW;
+ }
+ m_stream << o;
+
+ return *this;
+ }
+
+public:
+ QHtmlStream(QString* buffer)
+ : m_stream(buffer), m_state(NORMAL_FLOW), m_newline(true) {}
+ ~QHtmlStream() {}
+
+ void tag(const QString& name, const QString& cl, const QString& id)
+ {
+ finalize_open();
+ indent();
+
+ m_stream << '<' << name;
+ m_state = TAG;
+
+ if (!cl.isEmpty())
+ m_stream << " class=\"" << cl << "\"";
+ if (!id.isEmpty())
+ m_stream << " id=\"" << id << "\"";
+ }
+
+ void block(const QString& name, const QString& cl, const QString& id)
+ {
+ finalize_open();
+ indent();
+
+ m_stream << '<' << name;
+ m_indent += '\t';
+ m_blockstack.push_front(name);
+ m_state = BLOCK;
+
+ if (!cl.isEmpty())
+ m_stream << " class=\"" << cl << "\"";
+ if (!id.isEmpty())
+ m_stream << " id=\"" << id << "\"";
+ }
+
+ void parameter(const QString& param_name)
+ {
+ if (m_state == NORMAL_FLOW) return;
+
+ m_stream << " " << param_name;
+ m_enclosing_state = m_state;
+ m_state = PARAM;
+ }
+
+ void close()
+ {
+ finalize_open();
+
+ m_indent.truncate(m_indent.length() - 1);
+ indent();
+ m_stream << "</" << m_blockstack.first() << ">";
+ m_blockstack.pop_front();
+ }
+ void close_all(bool indent)
+ {
+ while( ! m_blockstack.empty() )
+ {
+ if (indent)
+ (*this) << endl;
+ close();
+ }
+ }
+
+ void data()
+ {
+ finalize_open();
+ }
+
+ QHtmlStream & operator<< ( QChar c ) { return output(c); }
+ QHtmlStream & operator<< ( char c ) { return output(c); }
+ QHtmlStream & operator<< ( signed short i ) { return output(i); }
+ QHtmlStream & operator<< ( unsigned short i ) { return output(i); }
+ QHtmlStream & operator<< ( signed int i ) { return output(i); }
+ QHtmlStream & operator<< ( unsigned int i ) { return output(i); }
+ QHtmlStream & operator<< ( signed long i ) { return output(i); }
+ QHtmlStream & operator<< ( unsigned long i ) { return output(i); }
+ QHtmlStream & operator<< ( float f ) { return output(f); }
+ QHtmlStream & operator<< ( double f ) { return output(f); }
+ QHtmlStream & operator<< ( const char * s ) { return output(s); }
+ QHtmlStream & operator<< ( const QString & s ) { return output(s); }
+ QHtmlStream & operator<< ( const QCString & s ) { return output(s); }
+
+ QHtmlStream & operator<< ( const QHtmlStreamManip& op )
+ {
+ op(*this);
+ return *this;
+ }
+
+ QHtmlStream & operator<< (QTSManip m)
+ {
+ finalize_open();
+ m_stream << m;
+ return (*this);
+ }
+
+ QHtmlStream & operator<< (QTSFUNC f)
+ {
+ finalize_open();
+ int old_flags = m_stream.flags();
+ m_stream << f;
+ if (old_flags == m_stream.flags())
+ m_newline = true;
+ return (*this);
+ }
+};
+
+/***************************************************************************************
+* Stream manipulators
+*/
+
+class QHtmlStreamManip0 : public QHtmlStreamManip
+{
+public:
+ typedef void (QHtmlStream::*Method)();
+
+private:
+ Method m_method;
+
+ void apply (QHtmlStream& stream) const
+ { (stream.*m_method)(); }
+
+public:
+ QHtmlStreamManip0(Method m)
+ : m_method(m) {}
+};
+
+class QHtmlStreamManip1 : public QHtmlStreamManip
+{
+public:
+ typedef void (QHtmlStream::*Method)(const QString& param);
+
+private:
+ Method m_method;
+ QString m_param;
+
+ void apply(QHtmlStream& stream) const
+ { (stream.*m_method)(m_param); }
+
+public:
+ QHtmlStreamManip1(Method m, const QString& param)
+ : m_method(m), m_param(param) {}
+};
+
+class QHtmlStreamManip3 : public QHtmlStreamManip
+{
+public:
+ typedef void (QHtmlStream::*Method)(const QString& param0, const QString& param1, const QString& param2);
+
+private:
+ Method m_method;
+ QString m_param0, m_param1, m_param2;
+
+ void apply(QHtmlStream& stream) const
+ { (stream.*m_method)(m_param0, m_param1, m_param2); }
+
+public:
+ QHtmlStreamManip3(Method m, const QString& param0, const QString& param1, const QString& param2)
+ : m_method(m),
+ m_param0(param0), m_param1(param1), m_param2(param2) {}
+};
+
+class CloseAll : public QHtmlStreamManip
+{
+private:
+ bool m_indent;
+ void apply(QHtmlStream& stream) const
+ { stream.close_all(m_indent); }
+public:
+ CloseAll(bool indent) : m_indent(indent) {}
+};
+
+inline QHtmlStreamManip3 tag(const QString& name, const QString& cl = QString::null, const QString& id = QString::null)
+{ return QHtmlStreamManip3(&QHtmlStream::tag, name, cl, id); }
+inline QHtmlStreamManip3 block(const QString& name, const QString& cl = QString::null, const QString& id = QString::null)
+{ return QHtmlStreamManip3(&QHtmlStream::block, name, cl, id); }
+
+inline QHtmlStreamManip1 param(const QString& name)
+{ return QHtmlStreamManip1(&QHtmlStream::parameter, name); }
+
+inline QHtmlStreamManip0 close()
+{ return QHtmlStreamManip0(&QHtmlStream::close); }
+inline QHtmlStreamManip0 data()
+{ return QHtmlStreamManip0(&QHtmlStream::data); }
+inline CloseAll close_all(bool indent = true)
+{ return CloseAll(indent); }
+
+#endif
diff --git a/src/parsers/search.cpp b/src/parsers/search.cpp
new file mode 100644
index 0000000..0d0a952
--- /dev/null
+++ b/src/parsers/search.cpp
@@ -0,0 +1,76 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Sylvain Joyeux *
+ * sylvain.joyeux@m4x.org *
+ * *
+ * 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 "parsers.h"
+#include "../apt.h"
+
+#include "qhtmlstream.h"
+
+#include <kio/slavebase.h>
+
+namespace Parsers
+{
+/** Parses the output of apt-cache search */
+void Search::operator() (AptProtocol* slave, const QString& tag, const QString& value)
+{
+ static QMap<QString, QString> results;
+ static QString cur_package;
+ static QString query;
+
+ if (tag == "begin")
+ {
+ query = value;
+ m_result_count = 0;
+ }
+ else if (tag == "package")
+ {
+ ++m_result_count;
+ cur_package = value;
+ }
+ else if (tag == "short_desc")
+ {
+ results[cur_package] = value;
+ }
+ else if (tag == "end")
+ {
+ // We separate results whose package name matches the query
+ // and those who matches only with the description
+ QString normal, special;
+ QHtmlStream sstream(&special), nstream(&normal);
+
+ // QMap iteration sorts wrt the key < operator
+ // with QStrings, it means case insensitive sort
+ QMap<QString, QString>::ConstIterator i;
+ for (i = results.begin(); i != results.end(); ++i)
+ {
+ const QString key = i.key();
+ QHtmlStream* stream = &nstream;
+ if (key == query)
+ stream = &sstream;
+
+ (*stream)
+ << block("tr")
+ << block("td")
+ << block("a") << param("href") << "apt:/show?" + key
+ << key
+ << close()
+ << close() << block("td") << *i << close() << endl
+ << close() << endl;
+ }
+
+ if (!special.isEmpty())
+ *slave << QString("<table>") + special + QString("</table>\n<hr>\n");
+ *slave << QString("<table>") + normal + QString("</table>");
+
+ results.clear();
+ }
+}
+}
+
diff --git a/src/parsers/show.cpp b/src/parsers/show.cpp
new file mode 100644
index 0000000..0a1c503
--- /dev/null
+++ b/src/parsers/show.cpp
@@ -0,0 +1,194 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Sylvain Joyeux *
+ * sylvain.joyeux@m4x.org *
+ * *
+ * 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 "parsers.h"
+#include "../apt.h"
+
+#include <klocale.h>
+#include <qregexp.h>
+
+static const QString
+ html_attribute_begin("<tr><td class=\"attname\">%1</td>\n\t<td>\n\t\t"),
+ html_attribute_classed("<tr class=\"%1\"><td class=\"attname\">%2</td>\n\t<td>\n\t\t"),
+ html_attribute_end("\n\t</td>\n</tr>\n");
+
+// Converts the special chars in orig into its HTML equivalents
+static QString text2html(const QString& orig)
+{ QString ret(orig);
+ ret = ret.replace("<(?!a href)", "&lt;");
+ //ret = ret.replace( QRegExp("\n"), "<br />\n");
+ return ret;
+}
+
+static void close_indent(int indent, QString& buffer)
+{
+ if (buffer.isEmpty()) return;
+ if (indent)
+ buffer += "\n\t\t</div>";
+}
+static void close_item(int indent, QString& buffer)
+{
+ if (buffer.isEmpty()) return;
+ close_indent(indent, buffer);
+ buffer += html_attribute_end;
+}
+
+static void close_table(const QString& version, int indent, QString& buffer)
+{
+ if (buffer.isEmpty()) return;
+ close_item(indent, buffer);
+ buffer = version + "<table class=\"version\">\n" + buffer + "</table>\n";
+}
+
+static QString version_header_link(const KURL& url, const QString& name)
+{ return QString("\t<a class=\"links\" href=\"" + url.htmlURL() + "\">" + name + "</a>\n"); }
+
+namespace Parsers
+{
+
+Show::Show(const QString& package, const QString& installed_version, bool act)
+ : m_package(package), m_installed(installed_version), m_act(false)
+{
+ m_act = act;
+}
+
+void Show::operator() (AptProtocol* slave, const QString& tag, const QString& value)
+{
+ // Since we want to show the version first, we should wait for it
+ // before sending anything
+ static QString version;
+ static QString buffer;
+ static QString attribute;
+ static int indent;
+ static bool multiline = false;
+ static bool first_line = false, new_paragraph = true;
+
+ if (tag == "begin" || tag == "package" || tag == "end")
+ {
+ if (multiline && !new_paragraph)
+ buffer += "</p>";
+
+ if (tag == "begin")
+ m_result_count = 0;
+ else
+ {
+ m_result_count += (tag == "package");
+
+ close_table(version, indent, buffer);
+ if (!buffer.isEmpty()) *slave << buffer;
+ }
+
+ // init the state variables for the next package
+ buffer = "";
+ indent = 0;
+ first_line = false;
+ new_paragraph = true;
+ }
+ else if (tag == "field")
+ {
+ if (multiline && !new_paragraph)
+ buffer += "</p>";
+
+ attribute = value;
+ if (value == "Depends" || value == "Description")
+ {
+ close_item(indent, buffer);
+ buffer +=
+ html_attribute_begin.arg(value)
+ + "&nbsp;";
+
+ close_item(indent, buffer);
+ buffer += html_attribute_begin.arg("&nbsp;");
+ }
+ else if (value != "Version" && value != "Package")
+ {
+ close_item(indent, buffer);
+ buffer += html_attribute_begin.arg(value);
+ }
+
+ if (value == "Description")
+ {
+ multiline = true;
+ new_paragraph = true;
+ first_line = true;
+ }
+ else
+ {
+ multiline = false;
+ }
+ }
+ else if (tag == "indent")
+ {
+ close_indent(indent, buffer);
+
+ int new_indent = value.toInt();
+ if (new_indent)
+ buffer += "\n\t<div style=\"margin-left: " + value + "em\">";
+ indent = new_indent;
+ }
+ else if (tag == "data" && attribute == "Version")
+ {
+ KURL action ("apt:/get");
+
+ QString item_id = mangle_version(value);
+ if (value == m_installed)
+ {
+ action.setQuery("remove");
+ version = QString("<div class=\"version-header-installed\" id=\"%1\">").arg(item_id)
+ + i18n("Installed version %1").arg(value);
+ }
+ else
+ {
+ action.setQuery("install");
+ version = QString("<div class=\"version-header\" id=\"%1\">").arg(item_id)
+ + i18n("Version %1").arg(value);
+ }
+
+ action.addQueryItem("package", m_package);
+ action.addQueryItem("version", value);
+
+ if (m_act)
+ {
+ if (value == m_installed)
+ version += version_header_link(action, i18n("Remove"));
+ else
+ version += version_header_link(action, i18n("Install"));
+ }
+ version += "</div>\n";
+
+ }
+ else if (tag == "data")
+ {
+ if (multiline)
+ {
+ static const QRegExp rx_empty("^\\s*$");
+ if (rx_empty.exactMatch(value))
+ {
+ buffer += "</p>";
+ new_paragraph = true;
+ }
+ else if (first_line)
+ {
+ new_paragraph = true;
+ first_line = false;
+ }
+ else if (new_paragraph)
+ {
+ buffer += "<p>";
+ new_paragraph = false;
+ }
+ }
+ buffer += text2html(QString(value).replace(QRegExp("(http://\\S+)"),QString("<a href=\"\\1\">\\1</a>")));
+ }
+ else if (tag == "package_link")
+ buffer += "<a href=\"" + slave->buildURL("show", value).htmlURL() + "\">" + value + "</a>";
+}
+
+}
diff --git a/src/regexps.cpp b/src/regexps.cpp
new file mode 100644
index 0000000..d49121d
--- /dev/null
+++ b/src/regexps.cpp
@@ -0,0 +1,45 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Sylvain Joyeux *
+ * sylvain.joyeux@m4x.org *
+ * *
+ * 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 "regexps.h"
+#include "debug.h"
+
+#include <kdebug.h>
+
+bool match_dversion(QString version)
+{
+ static QRegExp rx_revision(rxs_revision);
+ QString allowed_vchars = ".+\\w";
+
+ kdDebug(DEBUG_ZONE) << version << endl;
+ if (version[1] == ':')
+ {
+ allowed_vchars += ":";
+ if (! version[0].isDigit()) return false;
+ kdDebug(DEBUG_ZONE) << "Matched epoch" << endl;
+ version = version.right( version.length() - 2 );
+ }
+
+ kdDebug(DEBUG_ZONE) << version << endl;
+ int rev_pos = version.findRev('-');
+ if (rev_pos > -1)
+ {
+ allowed_vchars += "-";
+ QString revision = version.right( version.length() - rev_pos - 1);
+ if (! rx_revision.exactMatch(revision))
+ return false;
+
+ kdDebug(DEBUG_ZONE) << "Matched revision" << endl;
+ version.truncate( version.length() - rev_pos - 1 );
+ }
+
+ QRegExp rx_version("\\d[" + allowed_vchars + "]*");
+ return rx_version.exactMatch(version);
+}
diff --git a/src/regexps.h b/src/regexps.h
new file mode 100644
index 0000000..16f0e55
--- /dev/null
+++ b/src/regexps.h
@@ -0,0 +1,23 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Sylvain Joyeux *
+ * sylvain.joyeux@m4x.org *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#ifndef REGEXPS_H
+#define REGEXPS_H
+
+#include <qstring.h>
+#include <qregexp.h>
+
+static const QString rxs_pkgname("\\w[\\w+-.]+");
+static const QString rxs_revision("\\w[.+\\w]*");
+
+bool match_dversion(QString version);
+
+#endif
+