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"
48 #include <boost/scoped_ptr.hpp>
50 #include "gromacs/legacyheaders/copyrite.h"
52 #include "gromacs/commandline/cmdlinehelpcontext.h"
53 #include "gromacs/commandline/cmdlinehelpwriter.h"
54 #include "gromacs/commandline/cmdlineparser.h"
55 #include "gromacs/commandline/shellcompletions.h"
56 #include "gromacs/onlinehelp/helpformat.h"
57 #include "gromacs/onlinehelp/helpmanager.h"
58 #include "gromacs/onlinehelp/helptopic.h"
59 #include "gromacs/onlinehelp/helpwritercontext.h"
60 #include "gromacs/options/basicoptions.h"
61 #include "gromacs/options/options.h"
62 #include "gromacs/utility/exceptions.h"
63 #include "gromacs/utility/file.h"
64 #include "gromacs/utility/gmxassert.h"
65 #include "gromacs/utility/programinfo.h"
66 #include "gromacs/utility/stringutil.h"
73 class HelpExportInterface;
77 /********************************************************************
78 * CommandLineHelpModuleImpl declaration
81 class CommandLineHelpModuleImpl
84 CommandLineHelpModuleImpl(const ProgramInfo &programInfo,
85 const std::string &binaryName,
86 const CommandLineModuleMap &modules,
87 const CommandLineModuleGroupList &groups);
89 void exportHelp(HelpExportInterface *exporter) const;
91 boost::scoped_ptr<RootHelpTopic> rootTopic_;
92 const ProgramInfo &programInfo_;
93 std::string binaryName_;
94 const CommandLineModuleMap &modules_;
95 const CommandLineModuleGroupList &groups_;
97 CommandLineHelpContext *context_;
98 const CommandLineModuleInterface *moduleOverride_;
101 GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModuleImpl);
107 /********************************************************************
113 static const char name[];
114 static const char title[];
115 static const char *const text[];
118 // The first two are not used.
119 const char RootHelpText::name[] = "";
120 const char RootHelpText::title[] = "";
121 const char *const RootHelpText::text[] = {
122 "Usage: [PROGRAM] [<options>] <command> [<args>]",
126 * Help topic that forms the root of the help tree for the help subcommand.
128 * \ingroup module_commandline
130 class RootHelpTopic : public CompositeHelpTopic<RootHelpText>
134 * Creates a root help topic.
136 * \param[in] modules List of modules for to use for module listings.
140 explicit RootHelpTopic(const CommandLineModuleMap &modules)
141 : modules_(modules), commonOptions_(NULL)
145 //! Sets the common options for the wrapper binary.
146 void setCommonOptions(const Options *options)
148 commonOptions_ = options;
151 virtual void writeHelp(const HelpWriterContext &context) const;
154 void printModuleList(const HelpWriterContext &context) const;
156 const CommandLineModuleMap &modules_;
157 const Options *commonOptions_;
159 GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
162 void RootHelpTopic::writeHelp(const HelpWriterContext &context) const
164 if (context.outputFormat() != eHelpOutputFormat_Console)
166 // TODO: Implement once the situation with Redmine issue #969 is more
168 GMX_THROW(NotImplementedError(
169 "Root help is not implemented for this output format"));
171 writeBasicHelpTopic(context, *this, helpText());
172 context.outputFile().writeLine();
174 CommandLineHelpContext cmdlineContext(context);
175 // TODO: Propagate the -hidden option here.
176 CommandLineHelpWriter(*commonOptions_)
177 .writeHelp(cmdlineContext);
179 // TODO: If/when this list becomes long, it may be better to only print
180 // "common" commands here, and have a separate topic (e.g.,
181 // "help commands") that prints the full list.
182 printModuleList(context);
183 context.writeTextBlock(
184 "For additional help on a command, use '[PROGRAM] help <command>'");
185 writeSubTopicList(context,
186 "\nAdditional help is available on the following topics:");
187 context.writeTextBlock(
188 "To access the help, use '[PROGRAM] help <topic>'.");
191 void RootHelpTopic::printModuleList(const HelpWriterContext &context) const
193 if (context.outputFormat() != eHelpOutputFormat_Console)
195 // TODO: Implement once the situation with Redmine issue #969 is more
197 GMX_THROW(NotImplementedError(
198 "Module list is not implemented for this output format"));
200 int maxNameLength = 0;
201 CommandLineModuleMap::const_iterator module;
202 for (module = modules_.begin(); module != modules_.end(); ++module)
204 int nameLength = static_cast<int>(module->first.length());
205 if (module->second->shortDescription() != NULL
206 && nameLength > maxNameLength)
208 maxNameLength = nameLength;
211 File &file = context.outputFile();
212 TextTableFormatter formatter;
213 formatter.addColumn(NULL, maxNameLength + 1, false);
214 formatter.addColumn(NULL, 72 - maxNameLength, true);
215 formatter.setFirstColumnIndent(4);
217 file.writeLine("Available commands:");
218 for (module = modules_.begin(); module != modules_.end(); ++module)
220 const char *name = module->first.c_str();
221 const char *description = module->second->shortDescription();
222 if (description != NULL)
225 formatter.addColumnLine(0, name);
226 formatter.addColumnLine(1, description);
227 file.writeString(formatter.formatRow());
232 /********************************************************************
237 * Help topic wrapper for a command-line module.
239 * This class implements HelpTopicInterface such that it wraps a
240 * CommandLineModuleInterface, allowing subcommand "help <command>"
241 * to produce the help for "<command>".
243 * \ingroup module_commandline
245 class ModuleHelpTopic : public HelpTopicInterface
248 //! Constructs a help topic for a specific module.
249 ModuleHelpTopic(const CommandLineModuleInterface &module,
250 const CommandLineHelpModuleImpl &helpModule)
251 : module_(module), helpModule_(helpModule)
255 virtual const char *name() const { return module_.name(); }
256 virtual const char *title() const { return NULL; }
257 virtual bool hasSubTopics() const { return false; }
258 virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
262 virtual void writeHelp(const HelpWriterContext &context) const;
265 const CommandLineModuleInterface &module_;
266 const CommandLineHelpModuleImpl &helpModule_;
268 GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
271 void ModuleHelpTopic::writeHelp(const HelpWriterContext & /*context*/) const
273 CommandLineHelpContext context(*helpModule_.context_);
274 const char *const program = helpModule_.binaryName_.c_str();
275 context.setModuleDisplayName(formatString("%s %s", program, module_.name()));
276 module_.writeHelp(context);
279 /********************************************************************
280 * HelpExportInterface
284 * Callbacks for exporting help information for command-line modules.
286 * \ingroup module_commandline
288 class HelpExportInterface
291 //! Shorthand for a list of modules contained in a group.
292 typedef CommandLineModuleGroupData::ModuleList ModuleGroupContents;
294 virtual ~HelpExportInterface() {};
297 * Called once before exporting individual modules.
299 * Can, e.g., open shared output files (e.g., if the output is written
300 * into a single file, or if a separate index is required) and write
303 virtual void startModuleExport() = 0;
305 * Called to export the help for each module.
307 * \param[in] module Module for which the help should be exported.
308 * \param[in] tag Unique tag for the module (gmx-something).
309 * \param[in] displayName Display name for the module (gmx something).
311 virtual void exportModuleHelp(
312 const CommandLineModuleInterface &module,
313 const std::string &tag,
314 const std::string &displayName) = 0;
316 * Called after all modules have been exported.
318 * Can close files opened in startModuleExport(), write footers to them
321 virtual void finishModuleExport() = 0;
324 * Called once before exporting module groups.
326 * Can, e.g., open a single output file for listing all the groups.
328 virtual void startModuleGroupExport() = 0;
330 * Called to export the help for each module group.
332 * \param[in] title Title for the group.
333 * \param[in] modules List of modules in the group.
335 virtual void exportModuleGroup(const char *title,
336 const ModuleGroupContents &modules) = 0;
338 * Called after all module groups have been exported.
340 * Can close files opened in startModuleGroupExport(), write footers to them
343 virtual void finishModuleGroupExport() = 0;
347 * Adds hyperlinks to modules within this binary.
349 * \param[in,out] links Links are added here.
350 * \param[in] helpModule Help module to get module information from.
351 * \throws std::bad_alloc if out of memory.
353 * Initializes a HelpLinks object with links to modules defined in
356 * \ingroup module_commandline
358 void initProgramLinks(HelpLinks *links, const CommandLineHelpModuleImpl &helpModule)
360 const char *const program = helpModule.binaryName_.c_str();
361 CommandLineModuleMap::const_iterator module;
362 for (module = helpModule.modules_.begin();
363 module != helpModule.modules_.end();
366 if (module->second->shortDescription() != NULL)
368 std::string linkName("[gmx-" + module->first + "]");
369 std::string targetName(
370 formatString("%s-%s", program, module->first.c_str()));
371 std::string displayName(
372 formatString("[TT]%s %s[tt]", program, module->first.c_str()));
373 links->addLink(linkName, targetName, displayName);
378 /********************************************************************
383 * Implements export for man pages.
385 * \ingroup module_commandline
387 class HelpExportMan : public HelpExportInterface
390 //! Initializes man page exporter.
391 explicit HelpExportMan(const CommandLineHelpModuleImpl &helpModule)
392 : links_(eHelpOutputFormat_Man)
394 initProgramLinks(&links_, helpModule);
397 virtual void startModuleExport() {}
398 virtual void exportModuleHelp(
399 const CommandLineModuleInterface &module,
400 const std::string &tag,
401 const std::string &displayName);
402 virtual void finishModuleExport() {}
404 virtual void startModuleGroupExport();
405 virtual void exportModuleGroup(const char *title,
406 const ModuleGroupContents &modules);
407 virtual void finishModuleGroupExport();
411 boost::scoped_ptr<File> man7File_;
412 std::string man7Footer_;
415 void HelpExportMan::exportModuleHelp(
416 const CommandLineModuleInterface &module,
417 const std::string &tag,
418 const std::string &displayName)
420 File file("man1/" + tag + ".1", "w");
422 // TODO: It would be nice to remove the VERSION prefix from the version
423 // string to make it shorter.
424 file.writeLine(formatString(".TH %s 1 \"\" \"%s\" \"GROMACS Manual\"\n",
427 file.writeLine(".SH NAME");
428 file.writeLine(formatString("%s - %s", tag.c_str(),
429 module.shortDescription()));
432 CommandLineHelpContext context(&file, eHelpOutputFormat_Man, &links_);
433 context.setModuleDisplayName(displayName);
434 module.writeHelp(context);
436 file.writeLine(".SH SEE ALSO");
437 file.writeLine(".BR gromacs(7)");
439 file.writeLine("More information about \\fBGROMACS\\fR is available at <\\fIhttp://www.gromacs.org/\\fR>.");
442 void HelpExportMan::startModuleGroupExport()
444 const char *const programListPlaceholder = "@PROGMANPAGES@";
446 const std::string man7Template = gmx::File::readToString("man7/gromacs.7.in");
447 const size_t index = man7Template.find(programListPlaceholder);
448 GMX_RELEASE_ASSERT(index != std::string::npos,
449 "gromacs.7.in must contain a @PROGMANPAGES@ line");
450 std::string header = man7Template.substr(0, index);
451 man7Footer_ = man7Template.substr(index + std::strlen(programListPlaceholder));
452 header = replaceAll(header, "@VERSION@", GromacsVersion());
453 man7File_.reset(new File("man7/gromacs.7", "w"));
454 man7File_->writeLine(header);
457 void HelpExportMan::exportModuleGroup(const char *title,
458 const ModuleGroupContents &modules)
460 man7File_->writeLine(formatString(".Sh \"%s\"", title));
461 man7File_->writeLine(formatString(".IX Subsection \"%s\"", title));
462 man7File_->writeLine(".Vb");
463 man7File_->writeLine(".ta 16n");
465 ModuleGroupContents::const_iterator module;
466 for (module = modules.begin(); module != modules.end(); ++module)
468 const std::string &tag(module->first);
469 man7File_->writeLine(formatString("\\& %s\t%s",
470 tag.c_str(), module->second));
473 man7File_->writeLine(".Ve");
476 void HelpExportMan::finishModuleGroupExport()
478 man7File_->writeLine(man7Footer_);
482 /********************************************************************
487 * Implements export for HTML help.
489 * \ingroup module_commandline
491 class HelpExportHtml : public HelpExportInterface
494 //! Initializes HTML exporter.
495 explicit HelpExportHtml(const CommandLineHelpModuleImpl &helpModule);
497 virtual void startModuleExport();
498 virtual void exportModuleHelp(
499 const CommandLineModuleInterface &module,
500 const std::string &tag,
501 const std::string &displayName);
502 virtual void finishModuleExport();
504 virtual void startModuleGroupExport();
505 virtual void exportModuleGroup(const char *title,
506 const ModuleGroupContents &modules);
507 virtual void finishModuleGroupExport();
510 void setupHeaderAndFooter();
512 void writeHtmlHeader(File *file, const std::string &title) const;
513 void writeHtmlFooter(File *file) const;
516 boost::scoped_ptr<File> indexFile_;
521 HelpExportHtml::HelpExportHtml(const CommandLineHelpModuleImpl &helpModule)
522 : links_(eHelpOutputFormat_Html)
524 initProgramLinks(&links_, helpModule);
525 File linksFile("links.dat", "r");
527 while (linksFile.readLine(&line))
529 links_.addLink(line, "../online/" + line, line);
532 setupHeaderAndFooter();
535 void HelpExportHtml::setupHeaderAndFooter()
537 header_ = gmx::File::readToString("header.html.in");
538 header_ = replaceAll(header_, "@VERSION@", GromacsVersion());
539 gmx::File::writeFileFromString("header.html", header_);
540 header_ = replaceAll(header_, "@ROOTPATH@", "../");
541 footer_ = gmx::File::readToString("footer.html");
544 void HelpExportHtml::startModuleExport()
546 indexFile_.reset(new File("final/programs/byname.html", "w"));
547 writeHtmlHeader(indexFile_.get(), "GROMACS Programs by Name");
548 indexFile_->writeLine("<H3>GROMACS Programs Alphabetically</H3>");
551 void HelpExportHtml::exportModuleHelp(
552 const CommandLineModuleInterface &module,
553 const std::string &tag,
554 const std::string &displayName)
556 File file("final/programs/" + tag + ".html", "w");
557 writeHtmlHeader(&file, displayName);
559 CommandLineHelpContext context(&file, eHelpOutputFormat_Html, &links_);
560 context.setModuleDisplayName(displayName);
561 module.writeHelp(context);
563 writeHtmlFooter(&file);
566 indexFile_->writeLine(formatString("<a href=\"%s.html\">%s</a> - %s<br>",
567 tag.c_str(), displayName.c_str(),
568 module.shortDescription()));
571 void HelpExportHtml::finishModuleExport()
573 writeHtmlFooter(indexFile_.get());
577 void HelpExportHtml::startModuleGroupExport()
579 indexFile_.reset(new File("final/programs/bytopic.html", "w"));
580 writeHtmlHeader(indexFile_.get(), "GROMACS Programs by Topic");
581 indexFile_->writeLine("<H3>GROMACS Programs by Topic</H3>");
584 void HelpExportHtml::exportModuleGroup(const char *title,
585 const ModuleGroupContents &modules)
587 indexFile_->writeLine(formatString("<H4>%s</H4>", title));
589 ModuleGroupContents::const_iterator module;
590 for (module = modules.begin(); module != modules.end(); ++module)
592 const std::string &tag(module->first);
593 std::string displayName(tag);
594 std::replace(displayName.begin(), displayName.end(), '-', ' ');
595 indexFile_->writeLine(formatString("<a href=\"%s.html\">%s</a> - %s<br>",
596 tag.c_str(), displayName.c_str(),
601 void HelpExportHtml::finishModuleGroupExport()
603 writeHtmlFooter(indexFile_.get());
607 void HelpExportHtml::writeHtmlHeader(File *file, const std::string &title) const
609 file->writeLine(replaceAll(header_, "@TITLE@", title));
612 void HelpExportHtml::writeHtmlFooter(File *file) const
614 file->writeLine(footer_);
617 /********************************************************************
618 * HelpExportCompletion
622 * Implements export for command-line completion.
624 * \ingroup module_commandline
626 class HelpExportCompletion : public HelpExportInterface
629 //! Initializes completion exporter.
630 explicit HelpExportCompletion(const CommandLineHelpModuleImpl &helpModule);
632 virtual void startModuleExport();
633 virtual void exportModuleHelp(
634 const CommandLineModuleInterface &module,
635 const std::string &tag,
636 const std::string &displayName);
637 virtual void finishModuleExport();
639 virtual void startModuleGroupExport() {}
640 virtual void exportModuleGroup(const char * /*title*/,
641 const ModuleGroupContents & /*modules*/) {}
642 virtual void finishModuleGroupExport() {}
645 ShellCompletionWriter bashWriter_;
646 std::vector<std::string> modules_;
649 HelpExportCompletion::HelpExportCompletion(
650 const CommandLineHelpModuleImpl &helpModule)
651 : bashWriter_(helpModule.binaryName_, eShellCompletionFormat_Bash)
655 void HelpExportCompletion::startModuleExport()
657 bashWriter_.startCompletions();
660 void HelpExportCompletion::exportModuleHelp(
661 const CommandLineModuleInterface &module,
662 const std::string & /*tag*/,
663 const std::string & /*displayName*/)
665 modules_.push_back(module.name());
667 CommandLineHelpContext context(&bashWriter_);
668 // We use the display name to pass the name of the module to the
669 // completion writer.
670 context.setModuleDisplayName(module.name());
671 module.writeHelp(context);
675 void HelpExportCompletion::finishModuleExport()
677 bashWriter_.writeWrapperCompletions(modules_);
678 bashWriter_.finishCompletions();
683 /********************************************************************
684 * CommandLineHelpModuleImpl implementation
686 CommandLineHelpModuleImpl::CommandLineHelpModuleImpl(
687 const ProgramInfo &programInfo,
688 const std::string &binaryName,
689 const CommandLineModuleMap &modules,
690 const CommandLineModuleGroupList &groups)
691 : rootTopic_(new RootHelpTopic(modules)), programInfo_(programInfo),
692 binaryName_(binaryName), modules_(modules), groups_(groups),
693 context_(NULL), moduleOverride_(NULL), bHidden_(false)
697 void CommandLineHelpModuleImpl::exportHelp(HelpExportInterface *exporter) const
699 // TODO: Would be nicer to have the file names supplied by the build system
700 // and/or export a list of files from here.
701 const char *const program = binaryName_.c_str();
703 exporter->startModuleExport();
704 CommandLineModuleMap::const_iterator module;
705 for (module = modules_.begin(); module != modules_.end(); ++module)
707 if (module->second->shortDescription() != NULL)
709 const char *const moduleName = module->first.c_str();
710 std::string tag(formatString("%s-%s", program, moduleName));
711 std::string displayName(tag);
712 std::replace(displayName.begin(), displayName.end(), '-', ' ');
713 exporter->exportModuleHelp(*module->second, tag, displayName);
716 exporter->finishModuleExport();
718 exporter->startModuleGroupExport();
719 CommandLineModuleGroupList::const_iterator group;
720 for (group = groups_.begin(); group != groups_.end(); ++group)
722 exporter->exportModuleGroup((*group)->title(), (*group)->modules());
724 exporter->finishModuleGroupExport();
727 /********************************************************************
728 * CommandLineHelpModule
731 CommandLineHelpModule::CommandLineHelpModule(
732 const ProgramInfo &programInfo,
733 const std::string &binaryName,
734 const CommandLineModuleMap &modules,
735 const CommandLineModuleGroupList &groups)
736 : impl_(new Impl(programInfo, binaryName, modules, groups))
740 CommandLineHelpModule::~CommandLineHelpModule()
744 HelpTopicPointer CommandLineHelpModule::createModuleHelpTopic(
745 const CommandLineModuleInterface &module) const
747 return HelpTopicPointer(new ModuleHelpTopic(module, *impl_));
750 void CommandLineHelpModule::addTopic(HelpTopicPointer topic)
752 impl_->rootTopic_->addSubTopic(move(topic));
755 void CommandLineHelpModule::setShowHidden(bool bHidden)
757 impl_->bHidden_ = bHidden;
760 void CommandLineHelpModule::setCommonOptions(const Options *options)
762 impl_->rootTopic_->setCommonOptions(options);
765 void CommandLineHelpModule::setModuleOverride(
766 const CommandLineModuleInterface &module)
768 impl_->moduleOverride_ = &module;
771 int CommandLineHelpModule::run(int argc, char *argv[])
773 const char *const exportFormats[] = { "man", "html", "completion" };
774 std::string exportFormat;
775 Options options(NULL, NULL);
776 options.addOption(StringOption("export").store(&exportFormat)
777 .enumValue(exportFormats));
778 CommandLineParser(&options).parse(&argc, argv);
779 if (!exportFormat.empty())
781 boost::scoped_ptr<HelpExportInterface> exporter;
782 if (exportFormat == "man")
784 exporter.reset(new HelpExportMan(*impl_));
786 else if (exportFormat == "html")
788 exporter.reset(new HelpExportHtml(*impl_));
790 else if (exportFormat == "completion")
792 exporter.reset(new HelpExportCompletion(*impl_));
796 GMX_THROW(NotImplementedError("This help format is not implemented"));
798 impl_->exportHelp(exporter.get());
802 HelpLinks links(eHelpOutputFormat_Console);
803 initProgramLinks(&links, *impl_);
804 boost::scoped_ptr<CommandLineHelpContext> context(
805 new CommandLineHelpContext(&File::standardOutput(),
806 eHelpOutputFormat_Console, &links));
807 context->setShowHidden(impl_->bHidden_);
808 if (impl_->moduleOverride_ != NULL)
810 context->setModuleDisplayName(impl_->programInfo_.displayName());
811 impl_->moduleOverride_->writeHelp(*context);
814 impl_->context_ = context.get();
816 HelpManager helpManager(*impl_->rootTopic_, context->writerContext());
819 for (int i = 1; i < argc; ++i)
821 helpManager.enterTopic(argv[i]);
824 catch (const InvalidInputError &ex)
826 fprintf(stderr, "%s\n", ex.what());
829 helpManager.writeCurrentTopic();
833 void CommandLineHelpModule::writeHelp(const CommandLineHelpContext &context) const
835 const HelpWriterContext &writerContext = context.writerContext();
837 if (writerContext.outputFormat() != eHelpOutputFormat_Console)
841 writerContext.writeTextBlock(
842 "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
843 // TODO: More information.