[Debtags-commits] [svn] r1633 - in tagcoll/trunk: . doc tagcoll tools

Enrico Zini enrico at costa.debian.org
Sat Mar 4 17:05:35 UTC 2006


Author: enrico
Date: Sat Mar  4 17:05:34 2006
New Revision: 1633

Added:
   tagcoll/trunk/doc/tagcoll-man-hooks
Removed:
   tagcoll/trunk/tagcoll.1
Modified:
   tagcoll/trunk/Makefile.am
   tagcoll/trunk/doc/Makefile.am
   tagcoll/trunk/tagcoll/Commandline.cc
   tagcoll/trunk/tagcoll/Commandline.h
   tagcoll/trunk/tools/TagcollOptions.h
   tagcoll/trunk/tools/manpage.cc
Log:
Autogenerate the manpage for tagcoll


Modified: tagcoll/trunk/Makefile.am
==============================================================================
--- tagcoll/trunk/Makefile.am	(original)
+++ tagcoll/trunk/Makefile.am	Sat Mar  4 17:05:34 2006
@@ -1,6 +1,6 @@
 ## Process this file with automake to produce Makefile.in
 
-SUBDIRS = . tagcoll tests bench tools doc
+SUBDIRS = tagcoll tests bench tools doc .
 
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA=libtagcoll.pc
@@ -10,4 +10,7 @@
 
 man_MANS = tagcoll.1 taggrep.1
 
-EXTRA_DIST = libtagcoll.m4 libtagcoll.pc.in COPYING.libtagcoll DONE tagcoll.1 taggrep.1
+tagcoll.1: tools/manpage doc/tagcoll-man-hooks
+	tools/manpage tagcoll doc/tagcoll-man-hooks > $@ || rm $@
+
+EXTRA_DIST = libtagcoll.m4 libtagcoll.pc.in COPYING.libtagcoll DONE 

Modified: tagcoll/trunk/doc/Makefile.am
==============================================================================
--- tagcoll/trunk/doc/Makefile.am	(original)
+++ tagcoll/trunk/doc/Makefile.am	Sat Mar  4 17:05:34 2006
@@ -36,7 +36,7 @@
 	touch $@
 endif
 
-EXTRA_DIST = libtagcoll.dox tagbk-draft.tex local.bib
+EXTRA_DIST = libtagcoll.dox tagbk-draft.tex local.bib tagcoll-man-hooks
 
 CLEANFILES = libtagcoll.doxytags $(PAPER).aux $(PAPER).bbl $(PAPER).blg $(PAPER).ps $(PAPER).pdf $(PAPER).dvi $(PAPER).idx $(PAPER).log 
 

Modified: tagcoll/trunk/tagcoll/Commandline.cc
==============================================================================
--- tagcoll/trunk/tagcoll/Commandline.cc	(original)
+++ tagcoll/trunk/tagcoll/Commandline.cc	Sat Mar  4 17:05:34 2006
@@ -2,6 +2,8 @@
 #include <config.h>
 #include <locale.h>
 #include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
 #include <set>
 
 using namespace std;
@@ -326,6 +328,24 @@
 	return a->second->parse(list, begin);
 }
 
+std::map<std::string, OptionParser*> CommandParser::getCommandInfo() const
+{
+	std::map<std::string, OptionParser*> info;
+	
+	// Dig informations from cp
+	for(map<string, OptionParser*>::const_iterator i = m_aliases.begin();
+			i != m_aliases.end(); i++)
+	{
+		if (i->first == string())
+			continue;
+		map<string, OptionParser*>::iterator j = info.find(i->second->name());
+		if (j == info.end())
+			info[i->second->name()] = i->second;
+	}
+
+	return info;
+}
+
 
 
 class WordWrapper
@@ -440,19 +460,9 @@
 
 void Help::outputHelp(std::ostream& out, const CommandParser& cp)
 {
-	std::map<std::string, OptionParser*> m_info;
-	
 	// Dig informations from cp
-	for(map<string, OptionParser*>::const_iterator i = cp.m_aliases.begin();
-			i != cp.m_aliases.end(); i++)
-	{
-		if (i->first == string())
-			continue;
-		map<string, OptionParser*>::iterator j = m_info.find(i->second->name());
-		if (j == m_info.end())
-			m_info[i->second->name()] = i->second;
-	}
-
+	std::map<std::string, OptionParser*> m_info = cp.getCommandInfo();
+	
 	HelpWriter writer(out);
 
 	// Compute the maximum length of alias names
@@ -518,8 +528,8 @@
 
 	// Compute size of option display
 	size_t maxLeftCol = 0;
-	for (vector<OptionGroup*>::const_iterator i = o.m_groups.begin();
-			i != o.m_groups.end(); i++)
+	for (vector<OptionGroup*>::const_iterator i = o.groups().begin();
+			i != o.groups().end(); i++)
 		for (vector<Option*>::const_iterator j = (*i)->options.begin();
 				j != (*i)->options.end(); j++)
 		{
@@ -527,8 +537,8 @@
 			if (w > maxLeftCol)
 				maxLeftCol = w;
 		}
-	for (vector<Option*>::const_iterator j = o.m_options.begin();
-			j != o.m_options.end(); j++)
+	for (vector<Option*>::const_iterator j = o.options().begin();
+			j != o.options().end(); j++)
 	{
 		size_t w = (*j)->fullUsage().size();
 		if (w > maxLeftCol)
@@ -540,8 +550,8 @@
 		// Output the options
 		out << endl;
 		out << "Options are:" << endl;
-		for (vector<OptionGroup*>::const_iterator i = o.m_groups.begin();
-				i != o.m_groups.end(); i++)
+		for (vector<OptionGroup*>::const_iterator i = o.groups().begin();
+				i != o.groups().end(); i++)
 		{
 			if (!(*i)->description.empty())
 				writer.outstring((*i)->description + ":");
@@ -549,12 +559,12 @@
 					j != (*i)->options.end(); j++)
 				writer.outlist(" " + (*j)->fullUsage(), maxLeftCol + 3, (*j)->description);
 		}
-		if (!o.m_options.empty())
+		if (!o.options().empty())
 		{
 			out << endl;
 			writer.outstring("Other options:");
-			for (vector<Option*>::const_iterator j = o.m_options.begin();
-					j != o.m_options.end(); j++)
+			for (vector<Option*>::const_iterator j = o.options().begin();
+					j != o.options().end(); j++)
 				writer.outlist(" " + (*j)->fullUsage(), maxLeftCol + 3, (*j)->description);
 		}
 	}
@@ -580,41 +590,73 @@
 	return res;
 }
 
-void Help::outputManOption(std::ostream& out, const Option* o)
+void Manpage::outputParagraph(std::ostream& out, const std::string& str)
+{
+	for (size_t i = 0; i < str.size(); i++)
+		switch (str[i])
+		{
+			case '-':
+				out << "\\-";
+				break;
+			case '\n':
+				out << "\n.br\n";
+				break;
+			default:
+				out << str[i];
+		}		
+	out << '\n';
+}
+
+void Manpage::outputOption(std::ostream& out, const Option* o)
 {
 	out << ".TP" << endl;
 	out << ".B " << o->fullUsageForMan() << endl;
 	out << o->description << "." << endl;
 }
 
-void Help::outputMan(std::ostream& out, const CommandParser& cp, int section)
+void Manpage::runHooks(std::ostream& out, const std::string& section, where where)
 {
-	std::map<std::string, OptionParser*> m_info;
-	
-	// Dig informations from cp
-	for(map<string, OptionParser*>::const_iterator i = cp.m_aliases.begin();
-			i != cp.m_aliases.end(); i++)
-	{
-		if (i->first == string())
-			continue;
-		map<string, OptionParser*>::iterator j = m_info.find(i->second->name());
-		if (j == m_info.end())
-			m_info[i->second->name()] = i->second;
-	}
+	for (std::vector<Hook>::const_iterator i = hooks.begin();
+			i != hooks.end(); i++)
+		if (i->section == section && i->placement == where)
+			out << i->text;
+}
+
+void Manpage::startSection(std::ostream& out, const std::string& name)
+{
+	runHooks(out, name, BEFORE);
+	out << ".SH " << name << endl;
+	runHooks(out, name, BEGINNING);
+	lastSection = name;
+}
+
+void Manpage::endSection(std::ostream& out)
+{
+	runHooks(out, lastSection, END);
+	lastSection.clear();
+}
 
+void Manpage::output(std::ostream& out, const CommandParser& cp, int section)
+{
+	std::map<std::string, OptionParser*> m_info = cp.getCommandInfo();
 
 	// Manpage header
 	out << ".TH " << toupper(m_app) << " " << section << " \"" << man_date() << "\" \"" << m_ver << "\"" << endl;
 
-	out << ".SH NAME" << endl;
+	startSection(out, "NAME");
 	out << cp.name() << " \\- " << cp.description << endl;
+	endSection(out);
 
-	out << ".SH SYNOPSIS" << endl;
+	startSection(out, "SYNOPSIS");
 	out << m_app << " [options] " << cp.usage << endl;
+	endSection(out);
 
-	out << ".SH DESCRIPTION" << endl;
+	startSection(out, "DESCRIPTION");
+	if (!cp.longDescription.empty())
+		outputParagraph(out, cp.longDescription);
+	endSection(out);
 
-	out << ".SH COMMANDS" << endl;
+	startSection(out, "COMMANDS");
 	out << "\\fB" << cp.name() << "\\fP always requires a non-switch argument, that indicates what is the operation that should be performed:" << endl;
 	for (map<string, OptionParser*>::const_iterator i = m_info.begin();
 			i != m_info.end(); i++)
@@ -628,29 +670,32 @@
 		
 		out << " " << i->second->usage << endl;
 		out << ".br" << endl;
-		out << i->second->description << endl;
+		if (i->second->longDescription.empty())
+			outputParagraph(out, i->second->description);
+		else
+			outputParagraph(out, i->second->longDescription);
 	}
+	endSection(out);
 
-	out << ".SH OPTIONS" << endl;
-
+	startSection(out, "OPTIONS");
 	out << "This program follows the usual GNU command line syntax, with long options starting with two dashes (`\\-')." << endl << endl;
 	out << "Every one of the commands listed above has its own set of options.  To keep this manpage readable, all the options are presented together.  Please refer to \"\\fB" << cp.name() << "\\fP help \\fIcommand\\fP\" to see which options are accepted by a given command." << endl;
 
 	// Harvest merged option informations
 	set<OptionGroup*> groups;
 	vector<Option*> options;
-	for(map<string, OptionParser*>::const_iterator i = cp.m_aliases.begin();
-			i != cp.m_aliases.end(); i++)
+	for(map<string, OptionParser*>::const_iterator i = m_info.begin();
+			i != m_info.end(); i++)
 	{
 		if (i->first == string())
 			continue;
 		OptionParser& o = *i->second;
 
-		for (vector<OptionGroup*>::const_iterator i = o.m_groups.begin();
-				i != o.m_groups.end(); i++)
+		for (vector<OptionGroup*>::const_iterator i = o.groups().begin();
+				i != o.groups().end(); i++)
 			groups.insert(*i);
-		for (vector<Option*>::const_iterator j = o.m_options.begin();
-				j != o.m_options.end(); j++)
+		for (vector<Option*>::const_iterator j = o.options().begin();
+				j != o.options().end(); j++)
 			options.push_back(*j);
 	}
 
@@ -661,7 +706,7 @@
 			out << endl << (*i)->description << ":" << endl;
 		for (vector<Option*>::const_iterator j = (*i)->options.begin();
 				j != (*i)->options.end(); ++j)
-			outputManOption(out, *j);
+			outputOption(out, *j);
 		out << ".PP" << endl;
 	}
 
@@ -671,14 +716,73 @@
 		out << "Other options:" << endl;
 		for (vector<Option*>::const_iterator j = options.begin();
 				j != options.end(); ++j)
-			outputManOption(out, *j);
+			outputOption(out, *j);
 	}
+	endSection(out);
 
-	out << ".SH AUTHOR" << endl;
+	startSection(out, "AUTHOR");
 	out << "\\fB" << cp.name() << "\\fP is maintained by " << PACKAGE_BUGREPORT << "." << endl << endl;
 	out << "This manpage has been automatically generated by the " << m_app << " program." << endl;
+	endSection(out);
+}
+
+static string readline(FILE* in)
+{
+	string res;
+	int c;
+	while ((c = getc(in)) != EOF && c != '\n')
+		res += c;
+	return res;
 }
 
+void Manpage::readHooks(const std::string& file)
+{
+	FILE* in = fopen(file.c_str(), "r");
+	if (!in) throw SystemException(errno, "opening file " + file);
+	string section;
+	commandline::Manpage::where placement;
+	string text;
+	while (!feof(in))
+	{
+		string line(readline(in));
+		if (line.empty())
+			continue;
+		if (line[0] == '|')
+		{
+			text += line.substr(1) + "\n";
+		}
+		else if (isalpha(line[0]))
+		{
+			if (!section.empty())
+			{
+				addHook(section, placement, text);
+				text.clear();
+			}
+			size_t sep = line.find(' ');
+			if (sep == string::npos)
+			{
+				fclose(in);
+				throw ConsistencyCheckException("expected two words in line: " + line);
+			}
+			section = line.substr(0, sep);
+			string w(line, sep+1);
+			if (w == "before")
+			{
+				placement = commandline::Manpage::BEFORE;
+			} else if (w == "beginning") {
+				placement = commandline::Manpage::BEGINNING;
+			} else if (w == "end") {
+				placement = commandline::Manpage::END;
+			} else {
+				fclose(in);
+				throw ConsistencyCheckException("expected 'before', 'beginning' or 'end' in line: " + line);
+			}
+		}
+	}
+	if (!section.empty())
+		addHook(section, placement, text);
+	fclose(in);
+}
 #if 0
 void Help::outputMan(std::ostream& out, const OptionParser& o, int section)
 {

Modified: tagcoll/trunk/tagcoll/Commandline.h
==============================================================================
--- tagcoll/trunk/tagcoll/Commandline.h	(original)
+++ tagcoll/trunk/tagcoll/Commandline.h	Sat Mar  4 17:05:34 2006
@@ -202,16 +202,14 @@
 {
 	std::map<char, Option*> m_short;
 	std::map<std::string, Option*> m_long;
+	std::vector<OptionGroup*> m_groups;
+	std::vector<Option*> m_options;
 
 	/// Parse a consecutive sequence of switches
 	iter parseConsecutiveSwitches(arglist& list, iter begin);
 
 	void addWithoutAna(Option* o);
 
-protected:
-	std::vector<OptionGroup*> m_groups;
-	std::vector<Option*> m_options;
-
 public:
 	OptionParser(const std::string& name)
 		: Parser(name), primaryAlias(name) {}
@@ -219,6 +217,9 @@
 	void add(Option* o);
 	void add(OptionGroup* group);
 
+	const std::vector<OptionGroup*>& groups() const { return m_groups; }
+	const std::vector<Option*>& options() const { return m_options; }
+
 	/**
 	 * Parse all the switches in list, leaving only the non-switch arguments or
 	 * the arguments following "--"
@@ -229,19 +230,17 @@
 	std::vector<std::string> aliases;
 	std::string usage;
 	std::string description;
-
-	friend class Help;
+	std::string longDescription;
+	std::string examples;
 };
 
 class CommandParser : public Parser
 {
 	OptionParser* m_last_command;
+	std::map<std::string, OptionParser*> m_aliases;
 
 	void add(const std::string& alias, OptionParser* o);
 
-protected:
-	std::map<std::string, OptionParser*> m_aliases;
-
 public:
 	CommandParser(const std::string& name)
 		: Parser(name), m_last_command(0) {}
@@ -261,27 +260,72 @@
 	 */
 	virtual iter parse(arglist& list, iter begin);
 
+	std::map<std::string, OptionParser*> getCommandInfo() const;
+
 	std::string usage;
 	std::string description;
-
-	friend class Help;
+	std::string longDescription;
 };
 
-class Help
+class DocMaker
 {
+protected:
 	std::string m_app;
 	std::string m_ver;
 
 public:
-	Help(const std::string& app, const std::string& ver)
+	DocMaker(const std::string& app, const std::string& ver)
 		: m_app(app), m_ver(ver) {}
+};
+
+class Help : public DocMaker
+{
+public:
+	Help(const std::string& app, const std::string& ver)
+		: DocMaker(app, ver) {}
 	
 	void outputVersion(std::ostream& out);
 	void outputHelp(std::ostream& out, const CommandParser& cp);
 	void outputHelp(std::ostream& out, const OptionParser& cp);
+};
+
+class Manpage : public DocMaker
+{
+public:
+	enum where { BEFORE, BEGINNING, END };
+
+private:
+	struct Hook
+	{
+		std::string section;
+		where placement;
+		std::string text;
+
+		Hook(const std::string& section, where placement, const std::string& text)
+			: section(section), placement(placement), text(text) {}
+	};
+
+	std::vector<Hook> hooks;
+	std::string lastSection;
+	
+	void outputParagraph(std::ostream& out, const std::string& str);
+	void outputOption(std::ostream& out, const Option* o);
+	void runHooks(std::ostream& out, const std::string& section, where where);
+	void startSection(std::ostream& out, const std::string& name);
+	void endSection(std::ostream& out);
+
+
+public:
+	Manpage(const std::string& app, const std::string& ver)
+		: DocMaker(app, ver) {}
+
+	void addHook(const std::string& section, where placement, const std::string& text)
+	{
+		hooks.push_back(Hook(section, placement, text));
+	}
+	void readHooks(const std::string& file);
 
-	void outputManOption(std::ostream& out, const Option* o);
-	void outputMan(std::ostream& out, const CommandParser& cp, int section);
+	void output(std::ostream& out, const CommandParser& cp, int section);
 	//void outputMan(std::ostream& out, const OptionParser& cp, int section);
 };
 

Modified: tagcoll/trunk/tools/TagcollOptions.h
==============================================================================
--- tagcoll/trunk/tools/TagcollOptions.h	(original)
+++ tagcoll/trunk/tools/TagcollOptions.h	Sat Mar  4 17:05:34 2006
@@ -38,8 +38,8 @@
 			add(help = new BoolOption("help", 'h', "help"));
 			add(version = new BoolOption("version", 'V', "version"));
 			help->shortNames.push_back('?');
-			help->description = "show commandline help";
-			version->description = "show program version";
+			help->description = "print an help message and exit";
+			version->description = "print the program version and exit";
 			description = "Help options";
 		}
 		~HelpGroup()
@@ -119,7 +119,6 @@
 
 			description = "Options controlling generation of tag hierarchies";
 		}
-
 		~HierarchyGroup()
 		{
 			delete flatten;
@@ -148,6 +147,11 @@
 		{
 			usage = "[files...]";
 			description = "output the collection";
+			longDescription = 
+				"Output the normalized collection on standard output, applying transformations "
+				"if requested.  This is the default action if no other switches are provided.  "
+				"A normalized collection is a collection in which an item appears in just one "
+				"line.";
 			add(&cp->inputGroup);
 			add(&cp->outputGroup);
 			add(&cp->helpGroup);
@@ -166,6 +170,11 @@
 				
 			usage = "[files...]";
 			description = "\"reverse\" the collection, outputting one with items associated to tags";
+			longDescription =
+				"Output the inbound collection \"reversed\" from the tags point of view, that is, "
+				"associating to each tag the list of items associated to it in the input.\n"
+				"The --untagged-tag switch can be used to provide a name to which untagged "
+				"items will be associated in the output.";
 			add(&cp->helpGroup);
 		}
 		~Reverse()
@@ -179,6 +188,11 @@
 		{
 			usage = "<file1> <file2>";
 			description = "output a tag patch file with the differences between two files";
+			longDescription =
+				"Output a tag patch file with the differences between two files (requires two "
+				"file arguments).\n"
+				"The output tag patch file can then be applied when reading a collection with "
+				"the --patch-with option.";
 			add(&cp->helpGroup);
 		}
 	} diff;
@@ -193,6 +207,13 @@
 			
 			usage = "<item> [files...]";
 			description = "print a list of items related to the given one";
+			longDescription =
+				"Output a list of the items that are related to the given item or list of items.  "
+				"If more than one items are to be specified, separate them with commas.\n"
+				"The --distance option can be used to control how closely related the output "
+				"items shold be from the item(s) specified.";
+			examples = "tagcoll related mutt,mozilla-browser -";
+
 			add(&cp->helpGroup);
 		}
 		~Related()
@@ -206,6 +227,19 @@
 		{
 			usage = "[files...]";
 			description = "compute a list of tag implications";
+			longDescription =
+				"Output a list of all implicit implications between tags contained in the "
+				"hierarchy.  Implication is defined such that tag A implies tag B if every item "
+				"tagged with A is also tagged with B.\n"
+				"Implications can be used to discover implicit hierarchical relationships "
+				"between tags.\n"
+				"The output is one line per tag, with just tags that have implications, with the "
+				"name of the package, a colon and a comma-separated list of all implied tags.";
+			examples =
+				"C:devel,languages\n"
+				"ada:devel,languages\n"
+				"apachemodules:net,servers,web\n"
+				"browsers:net,web\n";
 			add(&cp->helpGroup);
 		}
 	} implications;
@@ -215,16 +249,31 @@
 		{
 			usage = "[files...]";
 			description = "build a smart hierarchy with the collection data";
+			longDescription =
+				"Organize the collection in an intuitively navigable hierarchy.  The "
+				"output is one line per package, with the package name prepended by the "
+				"path to the item in the hierarchy.\n"
+				"A detailed description of the hierarchy generation algorithm is found in the "
+				"tagbk-draft.pdf draft paper available in this package; if you want to "
+				"understand what are the goals of the algorithm and how it works, please give it "
+				"a read.";
+			examples =
+				"/net/clients/mail: mutt\n"
+				"/net/filters/mail: procmail\n";
 			add(&cp->hierarchyGroup);
 			add(&cp->helpGroup);
 		}
 	} hierarchy;
+
 	struct CleanHierarchy : public OptionParser
 	{
 		CleanHierarchy(TagcollOptions* cp) : OptionParser("cleanhierarchy")
 		{
 			usage = "[files...]";
 			description = "build a cleaned smart hierarchy with the collection data";
+			longDescription =
+				"Like hiearchy, but in every node it merges tags which are attached to the "
+				"same set of items.";
 			add(&cp->hierarchyGroup);
 			add(&cp->helpGroup);
 		}

Modified: tagcoll/trunk/tools/manpage.cc
==============================================================================
--- tagcoll/trunk/tools/manpage.cc	(original)
+++ tagcoll/trunk/tools/manpage.cc	Sat Mar  4 17:05:34 2006
@@ -32,12 +32,16 @@
 			throw commandline::BadOption("no arguments provided");
 
 		string cmd(argv[1]);
+		string hooks(argc > 2 ? argv[2] : "");
 
 		if (cmd == "tagcoll")
 		{
 			commandline::TagcollOptions opts;
-			commandline::Help help("tagcoll", VERSION);
-			help.outputMan(cout, opts, 1);
+			commandline::Manpage help("tagcoll", VERSION);
+			if (!hooks.empty())
+				help.readHooks(hooks);
+
+			help.output(cout, opts, 1);
 		}
 		else
 			throw commandline::BadOption("unknown command " + cmd);



More information about the Debtags-commits mailing list