[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