2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 * Implements gmx::CommandLineHelpModule.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_commandline
42 #include "gromacs/commandline/cmdlinehelpmodule.h"
47 #include <boost/scoped_ptr.hpp>
49 #include "gromacs/legacyheaders/copyrite.h"
50 #include "gromacs/legacyheaders/smalloc.h"
52 #include "gromacs/commandline/cmdlinehelpcontext.h"
53 #include "gromacs/commandline/cmdlineparser.h"
54 #include "gromacs/fileio/futil.h"
55 #include "gromacs/onlinehelp/helpformat.h"
56 #include "gromacs/onlinehelp/helpmanager.h"
57 #include "gromacs/onlinehelp/helptopic.h"
58 #include "gromacs/onlinehelp/helpwritercontext.h"
59 #include "gromacs/options/basicoptions.h"
60 #include "gromacs/options/options.h"
61 #include "gromacs/utility/exceptions.h"
62 #include "gromacs/utility/file.h"
63 #include "gromacs/utility/gmxassert.h"
64 #include "gromacs/utility/programinfo.h"
65 #include "gromacs/utility/stringutil.h"
72 class HelpExportInterface;
76 /********************************************************************
77 * CommandLineHelpModuleImpl declaration
80 class CommandLineHelpModuleImpl
83 CommandLineHelpModuleImpl(const ProgramInfo &programInfo,
84 const CommandLineModuleMap &modules,
85 const CommandLineModuleGroupList &groups);
87 void exportHelp(HelpExportInterface *exporter) const;
89 boost::scoped_ptr<RootHelpTopic> rootTopic_;
90 const ProgramInfo &programInfo_;
91 const CommandLineModuleMap &modules_;
92 const CommandLineModuleGroupList &groups_;
94 CommandLineHelpContext *context_;
95 const CommandLineModuleInterface *moduleOverride_;
98 GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModuleImpl);
104 /********************************************************************
110 static const char name[];
111 static const char title[];
112 static const char *const text[];
115 // The first two are not used.
116 const char RootHelpText::name[] = "";
117 const char RootHelpText::title[] = "";
118 const char *const RootHelpText::text[] = {
119 "Usage: [PROGRAM] <command> [<args>]",
123 * Help topic that forms the root of the help tree for the help subcommand.
125 * \ingroup module_commandline
127 class RootHelpTopic : public CompositeHelpTopic<RootHelpText>
131 * Creates a root help topic.
133 * \param[in] modules List of modules for to use for module listings.
137 explicit RootHelpTopic(const CommandLineModuleMap &modules)
142 virtual void writeHelp(const HelpWriterContext &context) const;
145 void printModuleList(const HelpWriterContext &context) const;
147 const CommandLineModuleMap &modules_;
149 GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
152 void RootHelpTopic::writeHelp(const HelpWriterContext &context) const
154 if (context.outputFormat() != eHelpOutputFormat_Console)
156 // TODO: Implement once the situation with Redmine issue #969 is more
158 GMX_THROW(NotImplementedError(
159 "Root help is not implemented for this output format"));
161 writeBasicHelpTopic(context, *this, helpText());
162 // TODO: If/when this list becomes long, it may be better to only print
163 // "common" commands here, and have a separate topic (e.g.,
164 // "help commands") that prints the full list.
165 printModuleList(context);
166 context.writeTextBlock(
167 "For additional help on a command, use '[PROGRAM] help <command>'");
168 writeSubTopicList(context,
169 "\nAdditional help is available on the following topics:");
170 context.writeTextBlock(
171 "To access the help, use '[PROGRAM] help <topic>'.");
174 void RootHelpTopic::printModuleList(const HelpWriterContext &context) const
176 if (context.outputFormat() != eHelpOutputFormat_Console)
178 // TODO: Implement once the situation with Redmine issue #969 is more
180 GMX_THROW(NotImplementedError(
181 "Module list is not implemented for this output format"));
183 int maxNameLength = 0;
184 CommandLineModuleMap::const_iterator module;
185 for (module = modules_.begin(); module != modules_.end(); ++module)
187 int nameLength = static_cast<int>(module->first.length());
188 if (module->second->shortDescription() != NULL
189 && nameLength > maxNameLength)
191 maxNameLength = nameLength;
194 File &file = context.outputFile();
195 TextTableFormatter formatter;
196 formatter.addColumn(NULL, maxNameLength + 1, false);
197 formatter.addColumn(NULL, 72 - maxNameLength, true);
198 formatter.setFirstColumnIndent(4);
200 file.writeLine("Available commands:");
201 for (module = modules_.begin(); module != modules_.end(); ++module)
203 const char *name = module->first.c_str();
204 const char *description = module->second->shortDescription();
205 if (description != NULL)
208 formatter.addColumnLine(0, name);
209 formatter.addColumnLine(1, description);
210 file.writeString(formatter.formatRow());
215 /********************************************************************
220 * Help topic wrapper for a command-line module.
222 * This class implements HelpTopicInterface such that it wraps a
223 * CommandLineModuleInterface, allowing subcommand "help <command>"
224 * to produce the help for "<command>".
226 * \ingroup module_commandline
228 class ModuleHelpTopic : public HelpTopicInterface
231 //! Constructs a help topic for a specific module.
232 ModuleHelpTopic(const CommandLineModuleInterface &module,
233 const CommandLineHelpModule &helpModule)
234 : module_(module), helpModule_(helpModule)
238 virtual const char *name() const { return module_.name(); }
239 virtual const char *title() const { return NULL; }
240 virtual bool hasSubTopics() const { return false; }
241 virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
245 virtual void writeHelp(const HelpWriterContext &context) const;
248 const CommandLineModuleInterface &module_;
249 const CommandLineHelpModule &helpModule_;
251 GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
254 void ModuleHelpTopic::writeHelp(const HelpWriterContext & /*context*/) const
256 CommandLineHelpContext context(helpModule_.context());
257 const char *const program =
258 helpModule_.programInfo().realBinaryName().c_str();
259 context.setModuleDisplayName(formatString("%s %s", program, module_.name()));
260 module_.writeHelp(context);
263 /********************************************************************
264 * HelpExportInterface
268 * Callbacks for exporting help information for command-line modules.
270 * \ingroup module_commandline
272 class HelpExportInterface
275 //! Shorthand for a list of modules contained in a group.
276 typedef CommandLineModuleGroupData::ModuleList ModuleGroupContents;
278 virtual ~HelpExportInterface() {};
281 * Called once before exporting individual modules.
283 * Can, e.g., open shared output files (e.g., if the output is written
284 * into a single file, or if a separate index is required) and write
287 virtual void startModuleExport() = 0;
289 * Called to export the help for each module.
291 * \param[in] module Module for which the help should be exported.
292 * \param[in] tag Unique tag for the module (gmx-something).
293 * \param[in] displayName Display name for the module (gmx something).
295 virtual void exportModuleHelp(
296 const CommandLineModuleInterface &module,
297 const std::string &tag,
298 const std::string &displayName) = 0;
300 * Called after all modules have been exported.
302 * Can close files opened in startModuleExport(), write footers to them
305 virtual void finishModuleExport() = 0;
308 * Called once before exporting module groups.
310 * Can, e.g., open a single output file for listing all the groups.
312 virtual void startModuleGroupExport() = 0;
314 * Called to export the help for each module group.
316 * \param[in] title Title for the group.
317 * \param[in] modules List of modules in the group.
319 virtual void exportModuleGroup(const char *title,
320 const ModuleGroupContents &modules) = 0;
322 * Called after all module groups have been exported.
324 * Can close files opened in startModuleGroupExport(), write footers to them
327 virtual void finishModuleGroupExport() = 0;
331 * Adds hyperlinks to modules within this binary.
333 * \param[in,out] links Links are added here.
334 * \param[in] modules Modules in the current binary.
335 * \throws std::bad_alloc if out of memory.
337 * Initializes a HelpLinks object with links to modules defined in \p modules.
339 * \ingroup module_commandline
341 void initProgramLinks(HelpLinks *links, const CommandLineModuleMap &modules)
343 const char *const program =
344 ProgramInfo::getInstance().realBinaryName().c_str();
345 CommandLineModuleMap::const_iterator module;
346 for (module = modules.begin(); module != modules.end(); ++module)
348 if (module->second->shortDescription() != NULL)
350 std::string linkName("[gmx-" + module->first + "]");
351 std::string targetName(
352 formatString("%s-%s", program, module->first.c_str()));
353 std::string displayName(
354 formatString("[TT]%s %s[tt]", program, module->first.c_str()));
355 links->addLink(linkName, targetName, displayName);
360 /********************************************************************
365 * Implements export for man pages.
367 * \ingroup module_commandline
369 class HelpExportMan : public HelpExportInterface
372 //! Initializes man page exporter.
373 explicit HelpExportMan(const CommandLineModuleMap &modules)
374 : links_(eHelpOutputFormat_Man)
376 initProgramLinks(&links_, modules);
379 virtual void startModuleExport() {}
380 virtual void exportModuleHelp(
381 const CommandLineModuleInterface &module,
382 const std::string &tag,
383 const std::string &displayName);
384 virtual void finishModuleExport() {}
386 virtual void startModuleGroupExport();
387 virtual void exportModuleGroup(const char *title,
388 const ModuleGroupContents &modules);
389 virtual void finishModuleGroupExport();
393 boost::scoped_ptr<File> man7File_;
394 std::string man7Footer_;
397 void HelpExportMan::exportModuleHelp(
398 const CommandLineModuleInterface &module,
399 const std::string &tag,
400 const std::string &displayName)
402 File file("man1/" + tag + ".1", "w");
404 // TODO: It would be nice to remove the VERSION prefix from the version
405 // string to make it shorter.
406 file.writeLine(formatString(".TH %s 1 \"\" \"%s\" \"GROMACS Manual\"\n",
409 file.writeLine(".SH NAME");
410 file.writeLine(formatString("%s - %s", tag.c_str(),
411 module.shortDescription()));
414 CommandLineHelpContext context(&file, eHelpOutputFormat_Man, &links_);
415 context.setModuleDisplayName(displayName);
416 module.writeHelp(context);
418 file.writeLine(".SH SEE ALSO");
419 file.writeLine(".BR gromacs(7)");
421 file.writeLine("More information about \\fBGROMACS\\fR is available at <\\fIhttp://www.gromacs.org/\\fR>.");
424 void HelpExportMan::startModuleGroupExport()
426 const char *const programListPlaceholder = "@PROGMANPAGES@";
428 const std::string man7Template = gmx::File::readToString("man7/gromacs.7.in");
429 const size_t index = man7Template.find(programListPlaceholder);
430 GMX_RELEASE_ASSERT(index != std::string::npos,
431 "gromacs.7.in must contain a @PROGMANPAGES@ line");
432 std::string header = man7Template.substr(0, index);
433 man7Footer_ = man7Template.substr(index + std::strlen(programListPlaceholder));
434 header = replaceAll(header, "@VERSION@", GromacsVersion());
435 man7File_.reset(new File("man7/gromacs.7", "w"));
436 man7File_->writeLine(header);
439 void HelpExportMan::exportModuleGroup(const char *title,
440 const ModuleGroupContents &modules)
442 man7File_->writeLine(formatString(".Sh \"%s\"", title));
443 man7File_->writeLine(formatString(".IX Subsection \"%s\"", title));
444 man7File_->writeLine(".Vb");
445 man7File_->writeLine(".ta 16n");
447 ModuleGroupContents::const_iterator module;
448 for (module = modules.begin(); module != modules.end(); ++module)
450 const std::string &tag(module->first);
451 man7File_->writeLine(formatString("\\& %s\t%s",
452 tag.c_str(), module->second));
455 man7File_->writeLine(".Ve");
458 void HelpExportMan::finishModuleGroupExport()
460 man7File_->writeLine(man7Footer_);
464 /********************************************************************
469 * Implements export for HTML help.
471 * \ingroup module_commandline
473 class HelpExportHtml : public HelpExportInterface
476 //! Initializes HTML exporter.
477 explicit HelpExportHtml(const CommandLineModuleMap &modules);
479 virtual void startModuleExport();
480 virtual void exportModuleHelp(
481 const CommandLineModuleInterface &module,
482 const std::string &tag,
483 const std::string &displayName);
484 virtual void finishModuleExport();
486 virtual void startModuleGroupExport();
487 virtual void exportModuleGroup(const char *title,
488 const ModuleGroupContents &modules);
489 virtual void finishModuleGroupExport();
492 void setupHeaderAndFooter();
494 void writeHtmlHeader(File *file, const std::string &title) const;
495 void writeHtmlFooter(File *file) const;
498 boost::scoped_ptr<File> indexFile_;
503 HelpExportHtml::HelpExportHtml(const CommandLineModuleMap &modules)
504 : links_(eHelpOutputFormat_Html)
506 initProgramLinks(&links_, modules);
507 char *linksFilename = low_gmxlibfn("links.dat", FALSE, FALSE);
508 if (linksFilename != NULL)
510 scoped_ptr_sfree guard(linksFilename);
511 File linksFile(linksFilename, "r");
513 while (linksFile.readLine(&line))
515 links_.addLink(line, "../online/" + line, line);
518 setupHeaderAndFooter();
521 void HelpExportHtml::setupHeaderAndFooter()
523 header_ = gmx::File::readToString("header.html.in");
524 header_ = replaceAll(header_, "@VERSION@", GromacsVersion());
525 gmx::File::writeFileFromString("header.html", header_);
526 header_ = replaceAll(header_, "@ROOTPATH@", "../");
527 footer_ = gmx::File::readToString("footer.html");
530 void HelpExportHtml::startModuleExport()
532 indexFile_.reset(new File("final/programs/byname.html", "w"));
533 writeHtmlHeader(indexFile_.get(), "GROMACS Programs by Name");
534 indexFile_->writeLine("<H3>GROMACS Programs Alphabetically</H3>");
537 void HelpExportHtml::exportModuleHelp(
538 const CommandLineModuleInterface &module,
539 const std::string &tag,
540 const std::string &displayName)
542 File file("final/programs/" + tag + ".html", "w");
543 writeHtmlHeader(&file, displayName);
545 CommandLineHelpContext context(&file, eHelpOutputFormat_Html, &links_);
546 context.setModuleDisplayName(displayName);
547 module.writeHelp(context);
549 writeHtmlFooter(&file);
552 indexFile_->writeLine(formatString("<a href=\"%s.html\">%s</a> - %s<br>",
553 tag.c_str(), displayName.c_str(),
554 module.shortDescription()));
557 void HelpExportHtml::finishModuleExport()
559 writeHtmlFooter(indexFile_.get());
563 void HelpExportHtml::startModuleGroupExport()
565 indexFile_.reset(new File("final/programs/bytopic.html", "w"));
566 writeHtmlHeader(indexFile_.get(), "GROMACS Programs by Topic");
567 indexFile_->writeLine("<H3>GROMACS Programs by Topic</H3>");
570 void HelpExportHtml::exportModuleGroup(const char *title,
571 const ModuleGroupContents &modules)
573 indexFile_->writeLine(formatString("<H4>%s</H4>", title));
575 ModuleGroupContents::const_iterator module;
576 for (module = modules.begin(); module != modules.end(); ++module)
578 const std::string &tag(module->first);
579 std::string displayName(tag);
580 std::replace(displayName.begin(), displayName.end(), '-', ' ');
581 indexFile_->writeLine(formatString("<a href=\"%s.html\">%s</a> - %s<br>",
582 tag.c_str(), displayName.c_str(),
587 void HelpExportHtml::finishModuleGroupExport()
589 writeHtmlFooter(indexFile_.get());
593 void HelpExportHtml::writeHtmlHeader(File *file, const std::string &title) const
595 file->writeLine(replaceAll(header_, "@TITLE@", title));
598 void HelpExportHtml::writeHtmlFooter(File *file) const
600 file->writeLine(footer_);
605 /********************************************************************
606 * CommandLineHelpModuleImpl implementation
608 CommandLineHelpModuleImpl::CommandLineHelpModuleImpl(
609 const ProgramInfo &programInfo,
610 const CommandLineModuleMap &modules,
611 const CommandLineModuleGroupList &groups)
612 : rootTopic_(new RootHelpTopic(modules)), programInfo_(programInfo),
613 modules_(modules), groups_(groups),
614 context_(NULL), moduleOverride_(NULL), bHidden_(false)
618 void CommandLineHelpModuleImpl::exportHelp(HelpExportInterface *exporter) const
620 // TODO: Would be nicer to have the file names supplied by the build system
621 // and/or export a list of files from here.
622 const char *const program = programInfo_.realBinaryName().c_str();
624 exporter->startModuleExport();
625 CommandLineModuleMap::const_iterator module;
626 for (module = modules_.begin(); module != modules_.end(); ++module)
628 if (module->second->shortDescription() != NULL)
630 const char *const moduleName = module->first.c_str();
631 std::string tag(formatString("%s-%s", program, moduleName));
632 std::string displayName(tag);
633 std::replace(displayName.begin(), displayName.end(), '-', ' ');
634 exporter->exportModuleHelp(*module->second, tag, displayName);
637 exporter->finishModuleExport();
639 exporter->startModuleGroupExport();
640 CommandLineModuleGroupList::const_iterator group;
641 for (group = groups_.begin(); group != groups_.end(); ++group)
643 exporter->exportModuleGroup((*group)->title(), (*group)->modules());
645 exporter->finishModuleGroupExport();
648 /********************************************************************
649 * CommandLineHelpModule
652 CommandLineHelpModule::CommandLineHelpModule(
653 const ProgramInfo &programInfo,
654 const CommandLineModuleMap &modules,
655 const CommandLineModuleGroupList &groups)
656 : impl_(new Impl(programInfo, modules, groups))
660 CommandLineHelpModule::~CommandLineHelpModule()
664 HelpTopicPointer CommandLineHelpModule::createModuleHelpTopic(
665 const CommandLineModuleInterface &module) const
667 return HelpTopicPointer(new ModuleHelpTopic(module, *this));
670 void CommandLineHelpModule::addTopic(HelpTopicPointer topic)
672 impl_->rootTopic_->addSubTopic(move(topic));
675 void CommandLineHelpModule::setShowHidden(bool bHidden)
677 impl_->bHidden_ = bHidden;
680 void CommandLineHelpModule::setModuleOverride(
681 const CommandLineModuleInterface &module)
683 impl_->moduleOverride_ = &module;
686 const CommandLineHelpContext &CommandLineHelpModule::context() const
688 return *impl_->context_;
691 const ProgramInfo &CommandLineHelpModule::programInfo() const
693 return impl_->programInfo_;
696 int CommandLineHelpModule::run(int argc, char *argv[])
698 const char *const exportFormats[] = { "man", "html", "completion" };
699 std::string exportFormat;
700 Options options(NULL, NULL);
701 options.addOption(StringOption("export").store(&exportFormat)
702 .enumValue(exportFormats));
703 CommandLineParser(&options).parse(&argc, argv);
704 if (!exportFormat.empty())
706 boost::scoped_ptr<HelpExportInterface> exporter;
707 if (exportFormat == "man")
709 exporter.reset(new HelpExportMan(impl_->modules_));
711 else if (exportFormat == "html")
713 exporter.reset(new HelpExportHtml(impl_->modules_));
717 GMX_THROW(NotImplementedError("This help format is not implemented"));
719 impl_->exportHelp(exporter.get());
723 HelpLinks links(eHelpOutputFormat_Console);
724 initProgramLinks(&links, impl_->modules_);
725 boost::scoped_ptr<CommandLineHelpContext> context(
726 new CommandLineHelpContext(&File::standardOutput(),
727 eHelpOutputFormat_Console, &links));
728 context->setShowHidden(impl_->bHidden_);
729 if (impl_->moduleOverride_ != NULL)
731 context->setModuleDisplayName(impl_->programInfo_.displayName());
732 impl_->moduleOverride_->writeHelp(*context);
735 impl_->context_ = context.get();
737 HelpManager helpManager(*impl_->rootTopic_, context->writerContext());
740 for (int i = 1; i < argc; ++i)
742 helpManager.enterTopic(argv[i]);
745 catch (const InvalidInputError &ex)
747 fprintf(stderr, "%s\n", ex.what());
750 helpManager.writeCurrentTopic();
754 void CommandLineHelpModule::writeHelp(const CommandLineHelpContext &context) const
756 const HelpWriterContext &writerContext = context.writerContext();
758 if (writerContext.outputFormat() != eHelpOutputFormat_Console)
762 writerContext.writeTextBlock(
763 "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
764 // TODO: More information.