[Debtags-commits] [svn] r1641 - debtags/trunk/tools

Enrico Zini enrico at costa.debian.org
Sun Mar 5 18:01:37 UTC 2006


Author: enrico
Date: Sun Mar  5 18:01:35 2006
New Revision: 1641

Added:
   debtags/trunk/tools/DebtagsOptions.h
Removed:
   debtags/trunk/tools/CommandlineParser.cc
   debtags/trunk/tools/CommandlineParser.h
Modified:
   debtags/trunk/tools/Makefile.am
   debtags/trunk/tools/debtags.cc
Log:
Ported to the new commandline parser

Modified: debtags/trunk/tools/Makefile.am
==============================================================================
--- debtags/trunk/tools/Makefile.am	(original)
+++ debtags/trunk/tools/Makefile.am	Sun Mar  5 18:01:35 2006
@@ -5,7 +5,6 @@
 debtags_SOURCES = \
 	Exec.cc \
 	ChildProcess.cc \
-	CommandlineParser.cc \
 	Environment.cc \
 	acqprogress.cc \
 	instantiations.cc \
@@ -14,4 +13,4 @@
 
 INCLUDES = -I.. $(LIBTAGCOLL_CFLAGS) $(LIBABT_FRONT_CFLAGS)
 
-EXTRA_DIST = acqprogress.h ChildProcess.h CommandlineParser.h Environment.h Exec.h Printer.h
+EXTRA_DIST = acqprogress.h ChildProcess.h DebtagsOptions.h Environment.h Exec.h Printer.h

Modified: debtags/trunk/tools/debtags.cc
==============================================================================
--- debtags/trunk/tools/debtags.cc	(original)
+++ debtags/trunk/tools/debtags.cc	Sun Mar  5 18:01:35 2006
@@ -46,7 +46,7 @@
 #include <tagcoll/experiments.h>
 
 #include "Environment.h"
-#include "CommandlineParser.h"
+#include "DebtagsOptions.h"
 #include "acqprogress.h"
 #include "Printer.h"
 
@@ -1096,859 +1096,706 @@
 public:
 };
 
-class CommandlineParserWithCommand : public CommandlineParser
+int main(int argc, const char* argv[])
 {
-protected:
-	map<string, int> command_map;
+	commandline::DebtagsOptions opts;
 
-public:
-	CommandlineParserWithCommand(const std::string& argv0,
-									const std::string& cmdline_summary,
-									const std::string& description) throw ()
-		: CommandlineParser(argv0, cmdline_summary, description)
-	{
-		add("version", 'V', "version", "print the program version, then exit");
-	}
+	try {
+		// Install the handler for unexpected exceptions
+		InstallUnexpected installUnexpected;
 
-	void addCommand(const std::string name, int id) throw ()
-	{
-		command_map[name] = id;
-	}
+		opts.parse(argc, argv);
+		if (!opts.lastCommand())
+			throw commandline::BadOption("could not understand the command to execute");
 
-	int parse(int& argc, const char**& argv) throw ()
-	{
-		if (!CommandlineParser::parse(argc, argv))
+		if (opts.outputGroup.verbose->boolValue())
+			::Environment::get().verbose(true);
+
+		if (opts.outputGroup.debug->boolValue())
+			::Environment::get().debug(true);
+
+		// Perform the correct operation
+		if (opts.helpGroup.help->boolValue())
 		{
-			printHelp();
-			exit(1);
+			// Provide help as requested
+			commandline::Help help(APPNAME, VERSION);
+			commandline::OptionParser* o = opts.lastCommand();
+
+			if (o && !o->name().empty())
+				// Help on a specific command
+				help.outputHelp(cout, *o);
+			else
+				// General help
+				help.outputHelp(cout, opts);
 		}
-		if (get("help").defined())
+		else if (opts.helpGroup.version->boolValue())
 		{
-			printHelp();
-			exit(0);
+			// Print the program version
+			commandline::Help help(APPNAME, VERSION);
+			help.outputVersion(cout);
 		}
-		if (get("version").defined())
+		else if (opts.lastCommand() == &opts.generic)
 		{
-			printf("%s ver." PACKAGE_VERSION "\n", APPNAME);
-			exit(0);
+			commandline::Help help(APPNAME, VERSION);
+			help.outputHelp(cout, opts);
 		}
-		if (argc == 1)
+		else if (opts.lastCommand() == &opts.help)
 		{
-			printHelp();
-			exit(1);
+			commandline::Help help(APPNAME, VERSION);
+			commandline::OptionParser* o = 0;
+			if (opts.hasNext())
+				o = opts.command(opts.next());
+
+			if (o)
+				// Help on a specific command
+				help.outputHelp(cout, *o);
+			else
+				// General help
+				help.outputHelp(cout, opts);
 		}
-
-		string command_string = argv[1];
-		map<string, int>::const_iterator cmap_i = command_map.find(command_string);
-		if (cmap_i == command_map.end())
+		else if (opts.lastCommand() == &opts.selfcheck)
 		{
-			fprintf(stderr, "Invalid command: \"%.*s\"\n", PFSTR(command_string));
-			printHelp();
-			exit(1);
-		}
+			debtagsInit();
+			component::Tags& voc = Global::get().tags();
 
-		for (int i = 1; i < argc; i++)
-			argv[i] = argv[i + 1];
-		--argc;
-		
-		return cmap_i->second;
-	}
-};
+			// ensure that all facets are readable
+			FacetSet facets = voc.facets();
+			for (FacetSet::const_iterator i = facets.begin(); i != facets.end(); i++)
+			{
+				i->name(string("foo"));
+				i->shortDescription(string("foo"));
+				i->longDescription(string("foo"));
+				i->tags();
+			}
+
+			// ensure that all tags are readable
+			TagSet tags = voc.tags();
+			for (TagSet::const_iterator i = tags.begin(); i != tags.end(); i++)
+			{
+				i->name(string("foo"));
+				i->fullname(string("foo"));
+				i->shortDescription(string("foo"));
+				i->longDescription(string("foo"));
+			}
 
-class CommandlineArgs
-{
-protected:
-	int argc;
-	const char** argv;
-	int _next;
+			return 0;
+		}
+		// Output the full package tag database
+		else if (opts.lastCommand() == &opts.cat)
+		{
+			component::PackageTags& debtags = debtagsInit();
+			wantTagDatabase();
+			auto_ptr< Printer<Package, Tag> > printer;
 
-public:
-	CommandlineArgs(int argc, const char* argv[]) throw () : argc(argc), argv(argv), _next(1) {}
+			if (opts.outputGroup.group->boolValue())
+				printer = auto_ptr< Printer<Package, Tag> >(new GroupedTagcollPrinter);
+			else
+				printer = auto_ptr< Printer<Package, Tag> >(new TagcollPrinter);
 
-	// Return true if there is another argument left in the list
-	bool hasNext() const throw () { return argc >= _next + 1; }
+			Searcher searcher(debtags, printer.get());
+			searcher.output();
 
-	// Return the next argument in the list
-	string next() throw ()
-	{
-		if (hasNext())
-		{
-			return argv[_next++];
-		} else {
-			return "-";
+			return 0;
 		}
-	}
-};
+		// search [-v] <tag expression>\n"
+		// Output the names and description of the packages that match\n"
+		// the given tag expression\n"
+		else if (opts.lastCommand() == &opts.search)
+		{
+			component::PackageTags& debtags = debtagsInit();
+			wantTagDatabase();
+			APTPrinter printer;
+			Searcher searcher(debtags, &printer);
+			string expression = opts.next();
 
-enum valid_command { UPDATE, SELFCHECK, CHECK, TAGSHOW, TAGSEARCH, TAGCAT, SHOW, RELATED, CAT, SEARCH, GREP, INSTALL, MKPATCH, MAINTAINERS, TAG, SUBMIT, TODO, SCORE, FACETCOLL, STATS, TODOREPORT, SMARTSEARCH };
+			int matched =
+				searcher.output(expression, opts.matchGroup.invert->boolValue());
 
-int main(int argc, const char* argv[])
-{
-	try {
-		// Install the handler for unexpected exceptions
-		InstallUnexpected installUnexpected;
+			return matched > 0 ? 0 : 1;
+		}
+		// grep [-v] [-q] <tag expression>
+		// Output the lines of the full package tag database that match the
+		// given tag expression
+		else if (opts.lastCommand() == &opts.grep)
+		{
+			component::PackageTags& debtags = debtagsInit();
+			wantTagDatabase();
+			auto_ptr< Printer<Package, Tag> > printer;
+
+			if (opts.outputGroup.quiet->boolValue())
+				printer = auto_ptr< Printer<Package, Tag> >(new NullPrinter);
+			if (opts.outputGroup.group->boolValue())
+				printer = auto_ptr< Printer<Package, Tag> >(new GroupedTagcollPrinter);
+			else
+				printer = auto_ptr< Printer<Package, Tag> >(new TagcollPrinter);
 
-		CommandlineParserWithCommand opts(APPNAME, "[options] [command] [file1|-] [file2|-]",
-				"Commandline interface to access and manipulate Debian Package Tags.\n\n"
-				"Commands are:\n"
-				"  update        Updates the package tag database (requires root)\n"
-				"  selfcheck     Perform a series of internal self checks using the current tag data\n"
-				"  check <file>	 Check that all the tags in the given tagged collection are present\n"
-				"                in the tag vocabulary.  Checks the main database if no file is\n"
-				"                specified\n"
-				"  tagcat    	 Output the tag vocabulary\n"
-				"  tagshow <tag> Show the vocabulary informations about a tag\n"
-				"  tagsearch <string [string [string [...]]]>\n"
-				"                Show a summary of all tags whose data contains the given strings\n"
-				"  show <pkg>    Call apt-cache show <pkg>, but add tag informations to the output.\n"
-				"  related <pkg1[,pkg2[,pkg2,[...]]]>\n"
-				"                Show packages related to the specified ones.\n"
-				"  cat           Output the full package tag database\n"
-				"  search [-v] <tag expression>\n"
-				"                Output the names and description of the packages that match\n"
-				"                the given tag expression\n"
-				"  grep [-v] [-q] <tag expression>\n"
-				"                Output the lines of the full package tag database that match\n"
-				"                the given tag expression\n"
-				"  install [-v] [-q] <tag expression>\n"
-				"                apt-get install the packages that match the given tag expression\n"
-				"  mkpatch [filename]\n"
-				"                Create a tag patch between the current tag database and the tag\n"
-				"                collection [filename]\n"
-				"  maintainers   Create a tagged collection of maintainers and the tags of the\n"
-				"                packages they maintain\n"
-				"  tag [add <package> <tags...>\n"
-				"  tag [rm  <package> <tags...>\n"
-				"  tag [ls  <package>\n"
-				"                View and edit the tags for a package\n"
-				"  submit [patch]\n"
-				"                Mail the given patch file to the central tag repository.\n"
-				"                If [patch] is omitted, mail the local tag modifications.\n"
-				"  todo          Print a list of the installed packages that are not yet tagged\n"
-				"  score         Score uninstalled packages according to how often their tags\n"
-				"                appear in the packages that are installed already\n"
-				"  facetcoll     Print the tagged collection where each package is tagged with\n"
-				"                its facets only\n"
-				"  stats         Print statistics about Debtags\n"
-				"  todoreport    Print a report of packages needing work\n"
-				"  ssearch <word [word1 [+tag [-tag1 ...]]]>\n"
-				"                Perform a keyword search integrated with related packages.\n"
-				"                A + prefix indicates a wanted tag.  A - prefix indicates\n"
-				"                an unwanted tag.  Other words indicate keywords to search.\n"
-				"                Remember to use '--' before unwanted tags to avoid to have\n"
-				"                them interpreted as commandline switches.\n");
-
-
-		/*
-		opts.add("hierarchy", 's', "smart-hierarchy", "build a smart hierarchy");
-		opts.add("implications", 'm', "show-implications", "output a list of tag implications");
-		opts.add("copy", 'c', "copy", "output the collection");
-		opts.add("diff", 'd', "diff", "output a tag patch file with the differences between two files (requires two file arguments)");
-		*/
-
-		//opts.add("expanded", 'x', "expanded-output", "produce full (and redundant) output data instead of compact");
-		opts.add("groupitems", 'g', "group-items", "group items with the same tagset in the output collection");
-		opts.add("distance", 'd', "distance", "set the maximum distance to use for the \"related\" command (defaults to 0)", "num");
-
-		opts.add("invert-match", 'v', "invert-match", "invert the sense of matching, to select non-matching lines");
-		opts.add("quiet", 'q', "quiet", "do not write anything to standard output, but exit with 0 if any match is found");
-		opts.add("local", 0, "local", "do not download files when performing an update");
-
-		opts.add("verbose", 'V', "verbose", "enable verbose output");
-		opts.add("debug", 0, "debug", "enable debugging output (including verbose output)");
-
-		opts.addCommand("update", (int)UPDATE);
-		opts.addCommand("check", (int)CHECK);
-		opts.addCommand("selfcheck", (int)SELFCHECK);
-		opts.addCommand("tagcat", (int)TAGCAT);
-		opts.addCommand("tagshow", (int)TAGSHOW);
-		opts.addCommand("tagsearch", (int)TAGSEARCH);
-		opts.addCommand("show", (int)SHOW);
-		opts.addCommand("related", (int)RELATED);
-		opts.addCommand("cat", (int)CAT);
-		opts.addCommand("search", (int)SEARCH);
-		opts.addCommand("grep", (int)GREP);
-		opts.addCommand("install", (int)INSTALL);
-		opts.addCommand("mkpatch", (int)MKPATCH);
-		opts.addCommand("maintainers", (int)MAINTAINERS);
-		opts.addCommand("tag", (int)TAG);
-		opts.addCommand("submit", (int)SUBMIT);
-		opts.addCommand("todo", (int)TODO);
-		opts.addCommand("score", (int)SCORE);
-		opts.addCommand("facetcoll", (int)FACETCOLL);
-		opts.addCommand("stats", (int)STATS);
-		opts.addCommand("todoreport", (int)TODOREPORT);
-		opts.addCommand("ssearch", (int)SMARTSEARCH);
+			Searcher searcher(debtags, printer.get());
+			string expression = opts.next();
 
-		// Process the commandline
-		valid_command cmd = (valid_command)opts.parse(argc, argv);
+			int matched =
+				searcher.output(expression, opts.matchGroup.invert->boolValue());
 
-		CommandlineArgs args(argc, argv);
+			return matched > 0 ? 0 : 1;
+		}
+		// install [-v] [-q] <tag expression>
+		// apt-get install the packages that match the given tag expression
+		else if (opts.lastCommand() == &opts.install)
+		{
+			component::PackageTags& debtags = debtagsInit();
+			wantTagDatabase();
+			Installer installer;
 
-		if (opts.get("verbose").defined())
-			::Environment::get().verbose(true);
+			Searcher searcher(debtags, &installer);
+			string expression = opts.next();
 
-		if (opts.get("debug").defined())
-			::Environment::get().debug(true);
+			searcher.output(expression, opts.matchGroup.invert->boolValue());
 
-		/*
-		if (cmd != UPDATE && cmd != CAT && cmd != GREP && cmd != INSTALL && cmd != TAGSHOW)
-			Debtags::Environment::init(false);
-		*/
-		
-		// Perform the correct operation
-		switch (cmd)
+			return 1;
+		}
+		// tagcat
+		// Output the entire tag vocabulary
+		else if (opts.lastCommand() == &opts.tagcat)
 		{
-			case SELFCHECK:
-			{
-				debtagsInit();
-				component::Tags& voc = Global::get().tags();
-
-				// ensure that all facets are readable
-				FacetSet facets = voc.facets();
-				for (FacetSet::const_iterator i = facets.begin(); i != facets.end(); i++)
-				{
-					i->name(string("foo"));
-					i->shortDescription(string("foo"));
-					i->longDescription(string("foo"));
-					i->tags();
-				}
+			debtagsInit();
+			wantTagDatabase();
 
-				// ensure that all tags are readable
-				TagSet tags = voc.tags();
-				for (TagSet::const_iterator i = tags.begin(); i != tags.end(); i++)
-				{
-					i->name(string("foo"));
-					i->fullname(string("foo"));
-					i->shortDescription(string("foo"));
-					i->longDescription(string("foo"));
-				}
+			component::Tags& voc = Global::get().tags();
 
-				return 0;
-			}
-			// Output the full package tag database
-			case CAT:
+			OpSet<Facet> facets = voc.facets();
+			for (OpSet<Facet>::const_iterator i = facets.begin();
+					i != facets.end(); i++)
 			{
-				component::PackageTags& debtags = debtagsInit();
-				wantTagDatabase();
-				auto_ptr< Printer<Package, Tag> > printer;
-
-				if (opts.get("groupitems").defined())
-					printer = auto_ptr< Printer<Package, Tag> >(new GroupedTagcollPrinter);
-				else
-					printer = auto_ptr< Printer<Package, Tag> >(new TagcollPrinter);
+				printVocabularyItem(*i);
 
-				Searcher searcher(debtags, printer.get());
-				searcher.output();
-
-				return 0;
+				OpSet<Tag> tags = i->tags();
+				for (OpSet<Tag>::const_iterator j = tags.begin();
+						j != tags.end(); j++)
+					printVocabularyItem(*j);
 			}
+			return 0;
+		}
+		// tagshow <tag>
+		// Show the vocabulary informations about a tag
+		else if (opts.lastCommand() == &opts.tagshow)
+		{
+			debtagsInit();
+			wantTagDatabase();
+			string tag = opts.next();
 
-			// search [-v] <tag expression>\n"
-			// Output the names and description of the packages that match\n"
-			// the given tag expression\n"
-			case SEARCH:
+			Tag t = Global::get().tags().tagByName(tag);
+			if (!t)
 			{
-				component::PackageTags& debtags = debtagsInit();
-				wantTagDatabase();
-				APTPrinter printer;
-				Searcher searcher(debtags, &printer);
-				string expression = args.next();
-
-				int matched =
-					searcher.output(expression, opts.get("invert-match").defined());
-
-				return matched > 0 ? 0 : 1;
+				verbose("Tag `%.*s' was not found in tag vocabulary\n", PFSTR(tag));
+				return 1;
 			}
-			
-
-			// grep [-v] [-q] <tag expression>
-			// Output the lines of the full package tag database that match the
-			// given tag expression
-			case GREP:
+			else
 			{
-				component::PackageTags& debtags = debtagsInit();
-				wantTagDatabase();
-				auto_ptr< Printer<Package, Tag> > printer;
-
-				if (opts.get("quiet").defined())
-					printer = auto_ptr< Printer<Package, Tag> >(new NullPrinter);
-				if (opts.get("groupitems").defined())
-					printer = auto_ptr< Printer<Package, Tag> >(new GroupedTagcollPrinter);
-				else
-					printer = auto_ptr< Printer<Package, Tag> >(new TagcollPrinter);
-
-				Searcher searcher(debtags, printer.get());
-				string expression = args.next();
+				printVocabularyItem(t);
+				return 0;
+			}
+		}
+		// tagsearch <pattern [pattern [pattern [...]]]>
+		// Show a summary of all tags matching the given patterns
+		else if (opts.lastCommand() == &opts.tagsearch)
+		{
+			debtagsInit();
+			wantTagDatabase();
 
-				int matched =
-					searcher.output(expression, opts.get("invert-match").defined());
+			SubstringTagMatcher match;
 
-				return matched > 0 ? 0 : 1;
+			// Get the patterns to be matched
+			bool empty;
+			while (opts.hasNext())
+			{
+				string pattern = opts.next();
+				match.add(pattern);
+				empty = false;
 			}
 
-			// install [-v] [-q] <tag expression>
-			// apt-get install the packages that match the given tag expression
-			case INSTALL:
+			if (empty)
 			{
-				component::PackageTags& debtags = debtagsInit();
-				wantTagDatabase();
-				Installer installer;
-
-				Searcher searcher(debtags, &installer);
-				string expression = args.next();
-
-				searcher.output(expression, opts.get("invert-match").defined());
-
+				error("No patterns given in commandline\n");
 				return 1;
 			}
 
-			// tagcat
-			// Output the entire tag vocabulary
-			case TAGCAT:
-			{
-				debtagsInit();
-				wantTagDatabase();
+			component::Tags& voc = Global::get().tags();
 
-				component::Tags& voc = Global::get().tags();
+			int matched = 0;
 
-				OpSet<Facet> facets = voc.facets();
-				for (OpSet<Facet>::const_iterator i = facets.begin();
-						i != facets.end(); i++)
-				{
-					printVocabularyItem(*i);
-
-					OpSet<Tag> tags = i->tags();
-					for (OpSet<Tag>::const_iterator j = tags.begin();
-							j != tags.end(); j++)
-						printVocabularyItem(*j);
-				}
-				return 0;
-			}
-			// tagshow <tag>
-			// Show the vocabulary informations about a tag
-			case TAGSHOW:
+			OpSet<Facet> facets = voc.facets();
+			for (OpSet<Facet>::const_iterator i = facets.begin();
+					i != facets.end(); i++)
 			{
-				debtagsInit();
-				wantTagDatabase();
-				string tag = args.next();
-
-				Tag t = Global::get().tags().tagByName(tag);
-				if (!t)
+				if (match(*i))
 				{
-					verbose("Tag `%.*s' was not found in tag vocabulary\n", PFSTR(tag));
-					return 1;
-				}
-				else
-				{
-					printVocabularyItem(t);
-					return 0;
+					matched++;
+					printShortVocabularyItem(*i);
 				}
-			}
-			// tagsearch <pattern [pattern [pattern [...]]]>
-			// Show a summary of all tags matching the given patterns
-			case TAGSEARCH:
-			{
-				debtagsInit();
-				wantTagDatabase();
 
-				SubstringTagMatcher match;
-				
-				// Get the patterns to be matched
-				bool empty;
-				while (args.hasNext())
-				{
-					string pattern = args.next();
-					match.add(pattern);
-					empty = false;
-				}
-
-				if (empty)
-				{
-					error("No patterns given in commandline\n");
-					return 1;
-				}
-
-				component::Tags& voc = Global::get().tags();
-
-				int matched = 0;
-
-				OpSet<Facet> facets = voc.facets();
-				for (OpSet<Facet>::const_iterator i = facets.begin();
-						i != facets.end(); i++)
-				{
-					if (match(*i))
+				OpSet<Tag> tags = i->tags();
+				for (OpSet<Tag>::const_iterator j = tags.begin();
+						j != tags.end(); j++)
+					if (match(*j))
 					{
 						matched++;
-						printShortVocabularyItem(*i);
+						printShortVocabularyItem(*j);
 					}
+			}
 
-					OpSet<Tag> tags = i->tags();
-					for (OpSet<Tag>::const_iterator j = tags.begin();
-							j != tags.end(); j++)
-						if (match(*j))
-						{
-							matched++;
-							printShortVocabularyItem(*j);
-						}
-				}
+			return matched > 0 ? 0 : 1;
+		}
+		// show <pkg>
+		// Call apt-cache show <pkg>, but add tag informations to the output.\n"
+		else if (opts.lastCommand() == &opts.show)
+		{
+			component::PackageTags& debtags = debtagsInit();
+			wantTagDatabase();
 
-				return matched > 0 ? 0 : 1;
-			}
-			// show <pkg>
-			// Call apt-cache show <pkg>, but add tag informations to the output.\n"
-			case SHOW:
+			while (opts.hasNext())
 			{
-				component::PackageTags& debtags = debtagsInit();
-				wantTagDatabase();
+				string name = opts.next();
 
-				while (args.hasNext())
+				entity::Package pkg = packageByName(name);
+				if (pkg.valid())
 				{
-					string name = args.next();
-					
-					entity::Package pkg = packageByName(name);
-					if (pkg.valid())
+					cout << pkg.candidateVersion().completeRecord() << endl;
+					cout << "Tags: ";
+					OpSet<Tag> ts = debtags.tagdb().getTags(pkg);
+					for (OpSet<Tag>::const_iterator i = ts.begin();
+							i != ts.end(); i++)
+						if (i == ts.begin())
+							cout << i->fullname();
+						else
+							cout << ", " + i->fullname();
+					cout << endl;
+					return 0;
+				} else {
+					verbose("Package %.*s not found", PFSTR(name));
+					return 1;
+				}
+			}
+		}
+		// related <pkg1[,pkg2[,pkg2,[...]]]>
+		// Show packages related to the specified ones
+		else if (opts.lastCommand() == &opts.related)
+		{
+			component::PackageTags& debtags = debtagsInit();
+			wantTagDatabase();
+
+			int maxdist = 0;
+			if (opts.related.distance->boolValue())
+				maxdist = opts.related.distance->intValue();
+			string pkg = opts.next();
+
+			// Split the items on commas
+			string splititem;
+			OpSet<entity::Package> splititems;
+			for (string::const_iterator c = pkg.begin(); c != pkg.end(); c++)
+				if (*c == ',')
+				{
+					entity::Package p = packageByName(splititem);
+					if (p.valid())
 					{
-						cout << pkg.candidateVersion().completeRecord() << endl;
-						cout << "Tags: ";
-						OpSet<Tag> ts = debtags.tagdb().getTags(pkg);
-						for (OpSet<Tag>::const_iterator i = ts.begin();
-								i != ts.end(); i++)
-							if (i == ts.begin())
-								cout << i->fullname();
-							else
-								cout << ", " + i->fullname();
-						cout << endl;
-						return 0;
+						splititems.insert(p);
 					} else {
-						verbose("Package %.*s not found", PFSTR(name));
+						error("Item \"%.*s\" does not exist in the collection\n", PFSTR(splititem));
 						return 1;
 					}
-				}
+					splititem = string();
+				} else
+					splititem += *c;
+			entity::Package p = packageByName(splititem);
+			if (p.valid())
+			{
+				splititems.insert(p);
+			} else {
+				error("Item \"%.*s\" does not exist in the collection\n", PFSTR(splititem));
+				return 1;
 			}
-			// related <pkg1[,pkg2[,pkg2,[...]]]>
-			// Show packages related to the specified ones
-			case RELATED:
-			{
-				component::PackageTags& debtags = debtagsInit();
-				wantTagDatabase();
 
-				int maxdist = 0;
-				if (opts.get("distance").defined())
-					maxdist = opts.get("distance").intVal();
-				string pkg = args.next();
-
-				// Split the items on commas
-				string splititem;
-				OpSet<entity::Package> splititems;
-				for (string::const_iterator c = pkg.begin(); c != pkg.end(); c++)
-					if (*c == ',')
-					{
-						entity::Package p = packageByName(splititem);
-						if (p.valid())
-						{
-							splititems.insert(p);
-						} else {
-							error("Item \"%.*s\" does not exist in the collection\n", PFSTR(splititem));
-							return 1;
-						}
-						splititem = string();
-					} else
-						splititem += *c;
-				entity::Package p = packageByName(splititem);
-				if (p.valid())
-				{
-					splititems.insert(p);
-				} else {
-					error("Item \"%.*s\" does not exist in the collection\n", PFSTR(splititem));
-					return 1;
-				}
+			// Get the tagset as the intersection of the tagsets of all input items
+			OpSet<entity::Package>::const_iterator i = splititems.begin();
+			OpSet<Tag> ts = debtags.tagdb().getTags(*i);
+			for (++i; i != splititems.end(); i++)
+				ts = ts ^ debtags.tagdb().getTags(*i);
 
-				// Get the tagset as the intersection of the tagsets of all input items
-				OpSet<entity::Package>::const_iterator i = splititems.begin();
-				OpSet<Tag> ts = debtags.tagdb().getTags(*i);
-				for (++i; i != splititems.end(); i++)
-					ts = ts ^ debtags.tagdb().getTags(*i);
+			if (ts.empty())
+			{
+				if (splititems.size() > 1)
+					fprintf(stderr, "The packages %.*s are unrelated: cannot find a barycenter to start computing relationships from.\n", PFSTR(pkg));
+				else
+					fprintf(stderr, "The package %.*s has no tags attached.\n", PFSTR(pkg));
+				return 1;
+			}
 
-				if (ts.empty())
-				{
-					if (splititems.size() > 1)
-						fprintf(stderr, "The packages %.*s are unrelated: cannot find a barycenter to start computing relationships from.\n", PFSTR(pkg));
+			APTPrinter printer(splititems);
+			OpSet<entity::Package> related(debtags.tagdb().getRelatedItems(ts, maxdist));
+			printer.print(related);
+			printer.flush();
+
+			if (printer.count() > 50 && maxdist == 0 && isatty(1))
+			{
+				string tags;
+				for (OpSet<Tag>::const_iterator i = ts.begin();
+						i != ts.end(); i++)
+					if (i == ts.begin())
+						tags += i->fullname();
 					else
-						fprintf(stderr, "The package %.*s has no tags attached.\n", PFSTR(pkg));
-					return 1;
-				}
-
-				APTPrinter printer(splititems);
-				OpSet<entity::Package> related(debtags.tagdb().getRelatedItems(ts, maxdist));
-				printer.print(related);
-				printer.flush();
-
-				if (printer.count() > 50 && maxdist == 0 && isatty(1))
-				{
-					string tags;
-					for (OpSet<Tag>::const_iterator i = ts.begin();
-							i != ts.end(); i++)
-						if (i == ts.begin())
-							tags += i->fullname();
-						else
-							tags += "%2C" + i->fullname();
-					feedback("\nIt seems that this set of packages lacks tag information that could help to better distinguish package similarities.\nYou can help providing better tagging: just point your browser to http://debian.vitavonni.de/packagebrowser/?tags=%.*s\n", PFSTR(tags));
-				}
-				return 0;
+						tags += "%2C" + i->fullname();
+				feedback("\nIt seems that this set of packages lacks tag information that could help to better distinguish package similarities.\nYou can help providing better tagging: just point your browser to http://debian.vitavonni.de/packagebrowser/?tags=%.*s\n", PFSTR(tags));
 			}
-			// check <file>
-			// Check that all the tags in the given tagged collection are
-			// present in the tag vocabulary.  Checks the main database if no
-			// file is specified
-			case CHECK:
-			{
-				debtagsInit();
-				wantTagDatabase();
+			return 0;
+		}
+		// check <file>
+		// Check that all the tags in the given tagged collection are
+		// present in the tag vocabulary.  Checks the main database if no
+		// file is specified
+		else if (opts.lastCommand() == &opts.check)
+		{
+			debtagsInit();
+			wantTagDatabase();
 
-				string file;
-				if (args.hasNext())
-					file = args.next();
-				else
-					file = utils::Path::tagdb();
+			string file;
+			if (opts.hasNext())
+				file = opts.next();
+			else
+				file = utils::Path::tagdb();
 
-				TagcollChecker checker;
-				readCollection(file, checker);
+			TagcollChecker checker;
+			readCollection(file, checker);
 
-				if (checker.missingCount() > 0)
-				{
-					checker.report();
-					return 1;
-				}
-				else
-					return 0;
+			if (checker.missingCount() > 0)
+			{
+				checker.report();
+				return 1;
 			}
-			// update
-			// Updates the package tag database (requires root)
-			case UPDATE:
+			else
+				return 0;
+		}
+		// update
+		// Updates the package tag database (requires root)
+		else if (opts.lastCommand() == &opts.update)
+		{
+			if (geteuid() != 0)
 			{
-				if (geteuid() != 0)
-				{
-					throw ConsistencyCheckException("You must be root to update the system debtags database");
-				}
-				struct winsize ws;
-				unsigned int ScreenWidth;
+				throw ConsistencyCheckException("You must be root to update the system debtags database");
+			}
+			struct winsize ws;
+			unsigned int ScreenWidth;
 
-				if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col >= 5)
-					ScreenWidth = ws.ws_col - 1;
+			if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col >= 5)
+				ScreenWidth = ws.ws_col - 1;
+
+			AcqTextStatus Stat(ScreenWidth,_config->FindI("quiet",0));
+			//Debtags::Environment::get().updateDebtagsDatabase(&Stat);
+			mode_t prev_umask = umask(022);
+			//debtags::updateDatabase(&Stat);
+			if (!opts.update.local->boolValue())
+				debtags::fetchNewData(&Stat);
+			debtagsInit();
+			umask(prev_umask);
+		}
+		// mkpatch [filename]
+		// Create a tag patch between the current tag database and the tag
+		// collection [filename]
+		else if (opts.lastCommand() == &opts.diff)
+		{
+			component::PackageTags& debtags = debtagsInit();
+			wantTagDatabase();
 
-				AcqTextStatus Stat(ScreenWidth,_config->FindI("quiet",0));
-				//Debtags::Environment::get().updateDebtagsDatabase(&Stat);
-				mode_t prev_umask = umask(022);
-				//debtags::updateDatabase(&Stat);
-				if (!opts.get("local").defined())
-					debtags::fetchNewData(&Stat);
-				debtagsInit();
-				umask(prev_umask);
-				break;
-			}
-			// mkpatch [filename]
-			// Create a tag patch between the current tag database and the tag
-			// collection [filename]
-			case MKPATCH:
-			{
-				component::PackageTags& debtags = debtagsInit();
-				wantTagDatabase();
+			string file = opts.next();
 
-				string file = args.next();
+			InputMerger<entity::Package, Tag> coll;
+			readCollection(
+					Global::get().packagestringconverter(),
+					Global::get().tagstringconverter(),
+					file, coll);
+
+			PatchList<entity::Package, Tag> newpatches;
+			newpatches.addPatch(debtags.tagdb(), coll);
+
+			TextFormat<entity::Package, Tag>::outputPatch(
+					Global::get().packagestringconverter(),
+					Global::get().tagstringconverter(),
+					newpatches, stdout);
+		}
+		// maintainers
+		// Create a tagged collection of maintainers and the tags of the
+		// packages they maintain
+		else if (opts.lastCommand() == &opts.maintainers)
+		{
+			debtagsInit();
+			wantTagDatabase();
+
+			// Gather maintainer informations
+			InputMerger<string, Tag> maints;
+			for (component::Packages::iterator i = Global::get().packages().packagesBegin();
+					i != Global::get().packages().packagesEnd(); ++i)
+			{
+				if (!i->valid())
+					continue;
+				entity::Version v = i->candidateVersion();
+				if (!v.valid())
+					continue;
+				maints.consume(v.maintainer(), i->tags());
+			}
+			MaintPrinter printer;
+			maints.output(printer);
+		}
+		// tag
+		//   tag [add <package> <tags...>\n"
+		//   tag [rm  <package> <tags...>\n"
+		//   tag [ls  <package>\n"
+		//                View and edit the tags for a package\n");
+		else if (opts.lastCommand() == &opts.tag)
+		{
+			std::string cmd = opts.next();
 
-				InputMerger<entity::Package, Tag> coll;
-				readCollection(
-						Global::get().packagestringconverter(),
-						Global::get().tagstringconverter(),
-						file, coll);
-
-				PatchList<entity::Package, Tag> newpatches;
-				newpatches.addPatch(debtags.tagdb(), coll);
-
-				TextFormat<entity::Package, Tag>::outputPatch(
-						Global::get().packagestringconverter(),
-						Global::get().tagstringconverter(),
-						newpatches, stdout);
-				break;
-			}
-			// maintainers
-			// Create a tagged collection of maintainers and the tags of the
-			// packages they maintain
-			case MAINTAINERS:
+			if (cmd == "add" || cmd == "rm")
 			{
-				debtagsInit();
+				component::PackageTags& debtags = debtagsInit(true);
 				wantTagDatabase();
 
-				// Gather maintainer informations
-				InputMerger<string, Tag> maints;
-				for (component::Packages::iterator i = Global::get().packages().packagesBegin();
-						i != Global::get().packages().packagesEnd(); ++i)
-				{
-					if (!i->valid())
-						continue;
-					entity::Version v = i->candidateVersion();
-					if (!v.valid())
-						continue;
-					maints.consume(v.maintainer(), i->tags());
-				}
-				MaintPrinter printer;
-				maints.output(printer);
-				break;
-			}
-			// tag
-			//   tag [add <package> <tags...>\n"
-			//   tag [rm  <package> <tags...>\n"
-			//   tag [ls  <package>\n"
-			//                View and edit the tags for a package\n");
-			case TAG:
-			{
-				std::string cmd = args.next();
+				string name = opts.next();
 
-				if (cmd == "add" || cmd == "rm")
+				entity::Package pkg = packageByName(name);
+				if (!pkg.valid())
 				{
-					component::PackageTags& debtags = debtagsInit(true);
-					wantTagDatabase();
-
-					string name = args.next();
-
-					entity::Package pkg = packageByName(name);
-					if (!pkg.valid())
-					{
-						error("Package %.*s not found\n", PFSTR(name));
-						return 1;
-					}
+					error("Package %.*s not found\n", PFSTR(name));
+					return 1;
+				}
 
-					OpSet<Tag> tagset;
+				OpSet<Tag> tagset;
 
-					while (args.hasNext())
-					{
-						string tag = args.next();
-						Tag t = Global::get().tags().tagByName(tag);
-						if (t)
-							tagset += t;
-						else
-							error("Tag `%.*s' not found: ignored\n", PFSTR(tag));
-					}
-					
-					if (!tagset.empty())
-					{
-						PatchList<entity::Package, Tag> change;
-						if (cmd == "add")
-							change.addPatch(Patch<entity::Package, Tag>(pkg, tagset, OpSet<Tag>()));
-						else
-							change.addPatch(Patch<entity::Package, Tag>(pkg, OpSet<Tag>(), tagset));
-						debtags.tagdb().applyChange(change);
-						debtags.savePatch();
-					} else
-						verbose("No tags to add\n");
-				}
-				else if (cmd == "ls")
+				while (opts.hasNext())
 				{
-					component::PackageTags& debtags = debtagsInit();
-					wantTagDatabase();
-
-					string name = args.next();
-					entity::Package pkg = packageByName(name);
-					if (pkg.valid())
-					{
-						OpSet<Tag> ts = debtags.tagdb().getTags(pkg);
-						for (OpSet<Tag>::const_iterator i = ts.begin();
-								i != ts.end(); i++)
-							printf("%.*s\n", PFSTR(i->fullname()));
-						return 0;
-					} else {
-						verbose("Package %.*s not found", PFSTR(name));
-						return 1;
-					}
+					string tag = opts.next();
+					Tag t = Global::get().tags().tagByName(tag);
+					if (t)
+						tagset += t;
+					else
+						error("Tag `%.*s' not found: ignored\n", PFSTR(tag));
 				}
-				else
-					throw ConsistencyCheckException("command " + cmd + " is not valid working with tags");
-				break;
+
+				if (!tagset.empty())
+				{
+					PatchList<entity::Package, Tag> change;
+					if (cmd == "add")
+						change.addPatch(Patch<entity::Package, Tag>(pkg, tagset, OpSet<Tag>()));
+					else
+						change.addPatch(Patch<entity::Package, Tag>(pkg, OpSet<Tag>(), tagset));
+					debtags.tagdb().applyChange(change);
+					debtags.savePatch();
+				} else
+					verbose("No tags to add\n");
 			}
-			// submit
-			// Mail the local updates to the tag database to the central tag
-			// repository
-			case SUBMIT:
+			else if (cmd == "ls")
 			{
 				component::PackageTags& debtags = debtagsInit();
 				wantTagDatabase();
 
-				if (args.hasNext())
+				string name = opts.next();
+				entity::Package pkg = packageByName(name);
+				if (pkg.valid())
 				{
-					StdioParserInput in(args.next());
-					PatchList<entity::Package, Tag> patch =
-							TextFormat<entity::Package, Tag>::parsePatch(
-								Global::get().packagestringconverter(),
-								Global::get().tagstringconverter(),
-								in);
-					debtags.sendPatch(patch);
+					OpSet<Tag> ts = debtags.tagdb().getTags(pkg);
+					for (OpSet<Tag>::const_iterator i = ts.begin();
+							i != ts.end(); i++)
+						printf("%.*s\n", PFSTR(i->fullname()));
+					return 0;
+				} else {
+					verbose("Package %.*s not found", PFSTR(name));
+					return 1;
 				}
-				else
-					debtags.sendPatch();
-
-				break;
 			}
-			// todo
-			// Print a list of the installed packages that are not yet tagged
-			case TODO:
+			else
+				throw ConsistencyCheckException("command " + cmd + " is not valid working with tags");
+		}
+		// submit
+		// Mail the local updates to the tag database to the central tag
+		// repository
+		else if (opts.lastCommand() == &opts.submit)
+		{
+			component::PackageTags& debtags = debtagsInit();
+			wantTagDatabase();
+
+			if (opts.hasNext())
 			{
-				component::PackageTags& debtags = debtagsInit();
-				wantTagDatabase();
+				StdioParserInput in(opts.next());
+				PatchList<entity::Package, Tag> patch =
+					TextFormat<entity::Package, Tag>::parsePatch(
+							Global::get().packagestringconverter(),
+							Global::get().tagstringconverter(),
+							in);
+				debtags.sendPatch(patch);
+			}
+			else
+				debtags.sendPatch();
 
-				// Write the package names to stdout
-				APTPrinter printer;
+		}
+		// todo
+		// Print a list of the installed packages that are not yet tagged
+		else if (opts.lastCommand() == &opts.todo)
+		{
+			component::PackageTags& debtags = debtagsInit();
+			wantTagDatabase();
 
-				// Filter to select the right packages
-				TODOFilter filter(printer);
+			// Write the package names to stdout
+			APTPrinter printer;
 
-				debtags.outputPatched(filter);
+			// Filter to select the right packages
+			TODOFilter filter(printer);
 
-				printer.flush();
+			debtags.outputPatched(filter);
 
-				break;
-			}
-			// score
-			// Score uninstalled packages according to how often their tags
-			// appear in the packages that are installed already
-			case SCORE:
-			{
-				component::PackageTags& debtags = debtagsInit();
-				wantTagDatabase();
+			printer.flush();
 
-				// Compute tag scores
-				TagsScorer tscorer;
-				debtags.outputPatched(tscorer);
-
-				// Compute package scores
-				PackageScorer pscorer(tscorer);
-				debtags.outputPatched(pscorer);
-
-				// Print the results
-				for (PackageScorer::const_iterator i = pscorer.begin();
-						i != pscorer.end(); i++)
-					printf("%d %.*s - %.*s\n", i->first,
-							PFSTR(i->second.name()), PFSTR(i->second.shortDescription(string("(short description not available)"))));
-				break;
-			}
-			// facetcoll
-			// Print the tagged collection where each package is tagged with
-			// its facets only
-			case FACETCOLL:
-			{
-				component::PackageTags& debtags = debtagsInit();
-				wantTagDatabase();
+		}
+		// score
+		// Score uninstalled packages according to how often their tags
+		// appear in the packages that are installed already
+		else if (opts.lastCommand() == &opts.score)
+		{
+			component::PackageTags& debtags = debtagsInit();
+			wantTagDatabase();
 
-				FacetcollPrinter<entity::Package> printer;
-				TagToFacet<entity::Package> tagToFacet(printer);
-				debtags.outputPatched(tagToFacet);
-				break;
-			}
-			// stats
-			// Print statistics about Debtags
-			case STATS:
-			{
-				component::PackageTags& debtags = debtagsInit();
-				wantTagDatabase();
+			// Compute tag scores
+			TagsScorer tscorer;
+			debtags.outputPatched(tscorer);
+
+			// Compute package scores
+			PackageScorer pscorer(tscorer);
+			debtags.outputPatched(pscorer);
+
+			// Print the results
+			for (PackageScorer::const_iterator i = pscorer.begin();
+					i != pscorer.end(); i++)
+				printf("%d %.*s - %.*s\n", i->first,
+						PFSTR(i->second.name()), PFSTR(i->second.shortDescription(string("(short description not available)"))));
+		}
+		// facetcoll
+		// Print the tagged collection where each package is tagged with
+		// its facets only
+		else if (opts.lastCommand() == &opts.facetcoll)
+		{
+			component::PackageTags& debtags = debtagsInit();
+			wantTagDatabase();
 
-				int pkgCount = Global::get().packages().packageCount();
-				printf("Total count of packages: %d\n", pkgCount);
+			FacetcollPrinter<entity::Package> printer;
+			TagToFacet<entity::Package> tagToFacet(printer);
+			debtags.outputPatched(tagToFacet);
+		}
+		// stats
+		// Print statistics about Debtags
+		else if (opts.lastCommand() == &opts.stats)
+		{
+			component::PackageTags& debtags = debtagsInit();
+			wantTagDatabase();
 
-				StatsCollector stats;
-				debtags.outputPatched(stats);
+			int pkgCount = Global::get().packages().packageCount();
+			printf("Total count of packages: %d\n", pkgCount);
 
-				printf("Total count of packages (according to APT): %d\n", pkgCount);
-				printf("Total count of packages (according to Debtags): %d\n", stats.get_seen());
+			StatsCollector stats;
+			debtags.outputPatched(stats);
 
-				const component::Tags& voc = Global::get().tags();
-				printf("Number of facets: %d\n", voc.facets().size());
-				printf("Number of tags: %d\n", voc.tags().size());
-
-				// Copied from Debtags class: compute the toplevel facets
-				// TODO: use Debtags instead of Environment throughout all Debtags
-				CardinalityStore<entity::Package, Facet> coll;
-				TagToFacet<entity::Package> tagStripper(coll);
-				debtags.outputPatched(tagStripper);
-				Facet f;
-				SmartHierarchyNode<entity::Package, Facet> node(f, coll, 0);
-				printf("Number of automatically computed toplevel facets: %d\n", node.size());
-
-				printf("Number of packages with special::completely-tagged tags: %d (%.1f%%)\n",
-						stats.get_complete(), (float)stats.get_complete()*100/stats.get_seen());
-				printf("Number of packages with tags, but no special::not-yet-tagged tags: %d (%.1f%%)\n",
-						stats.get_tagged(), (float)stats.get_tagged()*100/stats.get_seen());
-				printf("Number of packages with special::not-yet-tagged tags: %d (%.1f%%)\n",
-						stats.get_nyt(), (float)stats.get_nyt()*100/stats.get_seen());
-				printf("Number of packages with only special::not-yet-tagged tags: %d (%.1f%%)\n",
-						stats.get_onlynyt(), (float)stats.get_onlynyt()*100/stats.get_seen());
-				printf("Number of packages with no tags: %d (%.1f%%)\n",
-						stats.get_notags(), (float)stats.get_notags()*100/stats.get_seen());
-
-				break;
-			}
-			// todoreport
-			// Print a report of packages needing work
-			case TODOREPORT:
-			{
-				debtagsInit();
-				wantTagDatabase();
+			printf("Total count of packages (according to APT): %d\n", pkgCount);
+			printf("Total count of packages (according to Debtags): %d\n", stats.get_seen());
+
+			const component::Tags& voc = Global::get().tags();
+			printf("Number of facets: %d\n", voc.facets().size());
+			printf("Number of tags: %d\n", voc.tags().size());
+
+			// Copied from Debtags class: compute the toplevel facets
+			// TODO: use Debtags instead of Environment throughout all Debtags
+			CardinalityStore<entity::Package, Facet> coll;
+			TagToFacet<entity::Package> tagStripper(coll);
+			debtags.outputPatched(tagStripper);
+			Facet f;
+			SmartHierarchyNode<entity::Package, Facet> node(f, coll, 0);
+			printf("Number of automatically computed toplevel facets: %d\n", node.size());
+
+			printf("Number of packages with special::completely-tagged tags: %d (%.1f%%)\n",
+					stats.get_complete(), (float)stats.get_complete()*100/stats.get_seen());
+			printf("Number of packages with tags, but no special::not-yet-tagged tags: %d (%.1f%%)\n",
+					stats.get_tagged(), (float)stats.get_tagged()*100/stats.get_seen());
+			printf("Number of packages with special::not-yet-tagged tags: %d (%.1f%%)\n",
+					stats.get_nyt(), (float)stats.get_nyt()*100/stats.get_seen());
+			printf("Number of packages with only special::not-yet-tagged tags: %d (%.1f%%)\n",
+					stats.get_onlynyt(), (float)stats.get_onlynyt()*100/stats.get_seen());
+			printf("Number of packages with no tags: %d (%.1f%%)\n",
+					stats.get_notags(), (float)stats.get_notags()*100/stats.get_seen());
 
-				unsigned int itemsPerGroup = 0;
-				if (args.hasNext())
-					itemsPerGroup = atoi(args.next().c_str());
-
-				ReportMaker rm(itemsPerGroup);
-				rm.printReport();
-
-				break;
-			}
-			// ssearch <word [word1 [word2 ...]]>
-			// Perform a keyword search integrated with related packages
-			case SMARTSEARCH:
-			{
-				component::PackageTags& debtags = debtagsInit();
-				wantTagDatabase();
-				APTPrinter printer;
-				SmartSearcher smart(debtags, &printer);
-				Searcher searcher(debtags, &smart);
-				predicate::Predicate<Package> p = predicate::True<Package>();
-					//predicate::Factory<Package>::description(args.next());
-				while (args.hasNext())
-				{
-					string arg = args.next();
-					switch (arg[0])
-					{
-						case '+':
-							p = p and predicate::Factory<Package>::tag(
-									Global::get().tags().tagByName(arg.substr(1)));
-							break;
-						case '-':
-							p = p and not predicate::Factory<Package>::tag(
-									Global::get().tags().tagByName(arg.substr(1)));
-							break;
-						default:
-							p = p and predicate::Factory<Package>::description(arg);
-							break;
-					}
-				}
+		}
+		// todoreport
+		// Print a report of packages needing work
+		else if (opts.lastCommand() == &opts.todoreport)
+		{
+			debtagsInit();
+			wantTagDatabase();
 
-				int step1 = searcher.output(p);
-				int step2 = smart.outputRelated();
+			unsigned int itemsPerGroup = 0;
+			if (opts.hasNext())
+				itemsPerGroup = atoi(opts.next().c_str());
 
-				cout << step1 << " normal matches plus " << step2 << " related packages." << endl;
-				OpSet<Tag> topTags = smart.topTags();
-				cout << "Top tags were: " << topTags << endl;
+			ReportMaker rm(itemsPerGroup);
+			rm.printReport();
 
-				return step1 + step2 > 0 ? 0 : 1;
+		}
+		// ssearch <word [word1 [word2 ...]]>
+		// Perform a keyword search integrated with related packages
+		else if (opts.lastCommand() == &opts.smartsearch)
+		{
+			component::PackageTags& debtags = debtagsInit();
+			wantTagDatabase();
+			APTPrinter printer;
+			SmartSearcher smart(debtags, &printer);
+			Searcher searcher(debtags, &smart);
+			predicate::Predicate<Package> p = predicate::True<Package>();
+			//predicate::Factory<Package>::description(opts.next());
+			while (opts.hasNext())
+			{
+				string arg = opts.next();
+				switch (arg[0])
+				{
+					case '+':
+						p = p and predicate::Factory<Package>::tag(
+								Global::get().tags().tagByName(arg.substr(1)));
+						break;
+					case '-':
+						p = p and not predicate::Factory<Package>::tag(
+								Global::get().tags().tagByName(arg.substr(1)));
+						break;
+					default:
+						p = p and predicate::Factory<Package>::description(arg);
+						break;
+				}
 			}
-			
 
+			int step1 = searcher.output(p);
+			int step2 = smart.outputRelated();
+
+			cout << step1 << " normal matches plus " << step2 << " related packages." << endl;
+			OpSet<Tag> topTags = smart.topTags();
+			cout << "Top tags were: " << topTags << endl;
+
+			return step1 + step2 > 0 ? 0 : 1;
 		}
+		else
+			throw commandline::BadOption(string("unhandled command ") +
+						(opts.lastCommand() ? opts.lastCommand()->name() : "(null)"));
 
 		return 0;
-	}
-	catch (Exception& e)
-	{
+	} catch (commandline::BadOption& e) {
+		cerr << e.desc() << endl;
+		commandline::Help help(APPNAME, VERSION);
+		if (opts.lastCommand())
+		{
+			help.outputHelp(cerr, *opts.lastCommand());
+		} else {
+			help.outputHelp(cerr, opts);
+		}
+		exit(1);
+	} catch (Exception& e) {
 		fatal_error("%s: %.*s\n", e.type(), PFSTR(e.desc()));
 		return 1;
 	}



More information about the Debtags-commits mailing list