diff --git a/doc/doc.pl b/doc/doc.pl index 35e261771..cb6cbb49b 100755 --- a/doc/doc.pl +++ b/doc/doc.pl @@ -291,13 +291,11 @@ eval # Render output for my $strOutput (@stryOutput) { - if (!($strOutput eq 'man' && $oManifest->isBackRest())) - { - $oManifest->renderGet($strOutput); - } - &log(INFO, "render ${strOutput} output"); + # Man output has already been generated in C so do not remove it + next if ($strOutput eq 'man'); + # Clean contents of out directory if (!$bOutPreserve) { @@ -316,6 +314,8 @@ eval } } + $oManifest->renderGet($strOutput); + if ($strOutput eq 'markdown') { my $oMarkdown = @@ -329,18 +329,6 @@ eval $oMarkdown->process(); } - elsif ($strOutput eq 'man' && $oManifest->isBackRest()) - { - # Generate the command-line help - my $oRender = new pgBackRestDoc::Common::DocRender('text', $oManifest, !$bNoExe); - my $oDocConfig = - new pgBackRestDoc::Common::DocConfig( - new pgBackRestDoc::Common::Doc("${strBasePath}/../src/build/help/help.xml"), $oRender); - - $oStorageDoc->pathCreate( - "${strBasePath}/output/man", {strMode => '0770', bIgnoreExists => true, bCreateParent => true}); - $oStorageDoc->put("${strBasePath}/output/man/" . lc(PROJECT_NAME) . '.1.txt', $oDocConfig->manGet($oManifest)); - } elsif ($strOutput eq 'html') { my $oHtmlSite = diff --git a/doc/lib/pgBackRestDoc/Common/DocConfig.pm b/doc/lib/pgBackRestDoc/Common/DocConfig.pm index 480813136..b4666a6a5 100644 --- a/doc/lib/pgBackRestDoc/Common/DocConfig.pm +++ b/doc/lib/pgBackRestDoc/Common/DocConfig.pm @@ -298,223 +298,4 @@ sub process logDebugReturn($strOperation); } -#################################################################################################################################### -# manGet -# -# Generate the man page. -#################################################################################################################################### -sub manGet -{ - my $self = shift; - - # Assign function parameters, defaults, and log debug info - my - ( - $strOperation, - $oManifest - ) = - logDebugParam - ( - __PACKAGE__ . '->manGet', \@_, - {name => 'oManifest'} - ); - - # Get index.xml to pull various text from - my $oIndexDoc = ${$oManifest->sourceGet('index')}{doc}; - - # Write the header - my $strManPage = - "NAME\n" . - ' ' . PROJECT_NAME . ' - ' . $oManifest->variableReplace($oIndexDoc->paramGet('subtitle')) . "\n\n" . - "SYNOPSIS\n" . - ' ' . PROJECT_EXE . ' [options] [command]'; - - # Output the description (first two paragraphs of index.xml introduction) - my $iParaTotal = 0; - - $strManPage .= "\n\n" . - "DESCRIPTION"; - - foreach my $oPara ($oIndexDoc->nodeGetById('section', 'introduction')->nodeList('p')) - { - $strManPage .= ($iParaTotal == 0 ? "\n" : "\n\n") . ' ' . - manGetFormatText($oManifest->variableReplace($self->{oDocRender}->processText($oPara->textGet())), 80, 2); - - last; - } - - # Build command and config hashes - my $hConfigDefine = cfgDefine(); - my $hConfig = $self->{oConfigHash}; - my $hCommandList = {}; - my $iCommandMaxLen = 0; - my $hOptionList = {}; - my $iOptionMaxLen = 0; - - foreach my $strCommand (sort(keys(%{$$hConfig{&CONFIG_HELP_COMMAND}}))) - { - # Skip internal commands - next if $hConfig->{&CONFIG_HELP_COMMAND}{$strCommand}{&CONFIG_HELP_INTERNAL}; - - my $hCommand = $$hConfig{&CONFIG_HELP_COMMAND}{$strCommand}; - $iCommandMaxLen = length($strCommand) > $iCommandMaxLen ? length($strCommand) : $iCommandMaxLen; - - $$hCommandList{$strCommand}{summary} = $$hCommand{&CONFIG_HELP_SUMMARY}; - - if (defined($$hCommand{&CONFIG_HELP_OPTION})) - { - foreach my $strOption (sort(keys(%{$$hCommand{&CONFIG_HELP_OPTION}}))) - { - my $hOption = $$hCommand{&CONFIG_HELP_OPTION}{$strOption}; - - if ($$hOption{&CONFIG_HELP_SOURCE} eq CONFIG_HELP_SOURCE_COMMAND) - { - # Skip internal options - next if $hOption->{&CONFIG_HELP_INTERNAL}; - - $iOptionMaxLen = length($strOption) > $iOptionMaxLen ? length($strOption) : $iOptionMaxLen; - - $$hOptionList{$strCommand}{$strOption}{&CONFIG_HELP_SUMMARY} = $$hOption{&CONFIG_HELP_SUMMARY}; - } - } - } - } - - foreach my $strOption (sort(keys(%{$$hConfig{&CONFIG_HELP_OPTION}}))) - { - # Skip internal options - next if $hConfig->{&CONFIG_HELP_OPTION}{$strOption}{&CONFIG_HELP_INTERNAL}; - - my $hOption = $$hConfig{&CONFIG_HELP_OPTION}{$strOption}; - $iOptionMaxLen = length($strOption) > $iOptionMaxLen ? length($strOption) : $iOptionMaxLen; - my $strSection = defined($$hOption{&CONFIG_HELP_SECTION}) ? $$hOption{&CONFIG_HELP_SECTION} : CFGDEF_GENERAL; - - $$hOptionList{$strSection}{$strOption}{&CONFIG_HELP_SUMMARY} = $$hOption{&CONFIG_HELP_SUMMARY}; - } - - # Output Commands - $strManPage .= "\n\n" . - 'COMMANDS'; - - foreach my $strCommand (sort(keys(%{$hCommandList}))) - { - # Construct the summary - my $strSummary = $oManifest->variableReplace($self->{oDocRender}->processText($$hCommandList{$strCommand}{summary})); - # $strSummary = lcfirst(substr($strSummary, 0, length($strSummary) - 1)); - - # Output the summary - $strManPage .= - "\n " . "${strCommand}" . (' ' x ($iCommandMaxLen - length($strCommand))) . ' ' . - manGetFormatText($strSummary, 80, $iCommandMaxLen + 4); - } - - # Output options - my $bFirst = true; - $strManPage .= "\n\n" . - 'OPTIONS'; - - foreach my $strSection (sort(keys(%{$hOptionList}))) - { - $strManPage .= ($bFirst ?'' : "\n") . "\n " . ucfirst($strSection) . ' Options:'; - - foreach my $strOption (sort(keys(%{$$hOptionList{$strSection}}))) - { - my $hOption = $$hOptionList{$strSection}{$strOption}; - - # Construct the default - my $strCommand = grep(/$strSection/i, cfgDefineCommandList()) ? $strSection : undef; - my $strDefault = docConfigOptionDefault($strOption, $strCommand); - - if (defined($strDefault)) - { - if ($strOption eq CFGOPT_REPO_HOST_CMD || $strOption eq CFGOPT_PG_HOST_CMD) - { - $strDefault = PROJECT_EXE; - } - elsif ($$hConfigDefine{$strOption}{&CFGDEF_TYPE} eq &CFGDEF_TYPE_BOOLEAN) - { - $strDefault = $strDefault ? 'y' : 'n'; - } - } - # - # use Data::Dumper; confess Dumper($$hOption{&CONFIG_HELP_SUMMARY}); - - # Construct the summary - my $strSummary = $oManifest->variableReplace($self->{oDocRender}->processText($$hOption{&CONFIG_HELP_SUMMARY})); - - $strSummary = $strSummary . (defined($strDefault) ? " [default=${strDefault}]" : ''); - - # Output the summary - $strManPage .= - "\n " . "--${strOption}" . (' ' x ($iOptionMaxLen - length($strOption))) . ' ' . - manGetFormatText($strSummary, 80, $iOptionMaxLen + 8); - } - - $bFirst = false; - } - - # Write files, examples, and references - $strManPage .= "\n\n" . - "FILES\n" . - "\n" . - ' ' . docConfigOptionDefault(CFGOPT_CONFIG) . "\n" . - ' ' . docConfigOptionDefault(CFGOPT_REPO_PATH) . "\n" . - ' ' . docConfigOptionDefault(CFGOPT_LOG_PATH) . "\n" . - ' ' . docConfigOptionDefault(CFGOPT_SPOOL_PATH) . "\n" . - ' ' . docConfigOptionDefault(CFGOPT_LOCK_PATH) . "\n" . - "\n" . - "EXAMPLES\n" . - "\n" . - " * Create a backup of the PostgreSQL `main` cluster:\n" . - "\n" . - ' $ ' . PROJECT_EXE . ' --' . CFGOPT_STANZA . "=main backup\n" . - "\n" . - ' The `main` cluster should be configured in `' . docConfigOptionDefault(CFGOPT_CONFIG) . "`\n" . - "\n" . - " * Show all available backups:\n" . - "\n" . - ' $ ' . PROJECT_EXE . ' ' . CFGCMD_INFO . "\n" . - "\n" . - " * Show all available backups for a specific cluster:\n" . - "\n" . - ' $ ' . PROJECT_EXE . ' --' . CFGOPT_STANZA . '=main ' . CFGCMD_INFO . "\n" . - "\n" . - " * Show backup specific options:\n" . - "\n" . - ' $ ' . PROJECT_EXE . ' ' . CFGCMD_HELP . ' ' . CFGCMD_BACKUP . "\n" . - "\n" . - "SEE ALSO\n" . - "\n" . - ' /usr/share/doc/' . PROJECT_EXE . "-doc/html/index.html\n" . - ' ' . $oManifest->variableReplace('{[backrest-url-base]}') . "\n"; - - return $strManPage; -} - -# Helper function for manGet() used to format text by indenting and splitting -sub manGetFormatText -{ - my $strLine = shift; - my $iLength = shift; - my $iIndentRest = shift; - - my $strPart; - my $strResult; - my $bFirst = true; - - do - { - my $iIndent = $bFirst ? 0 : $iIndentRest; - - ($strPart, $strLine) = stringSplit($strLine, ' ', $iLength - $iIndentRest); - - $strResult .= ($bFirst ? '' : "\n") . (' ' x $iIndent) . trim($strPart); - - $bFirst = false; - } - while (defined($strLine)); - - return $strResult; -} - 1; diff --git a/doc/src/command/build/build.c b/doc/src/command/build/build.c index b336ce4ec..92d0d54ba 100644 --- a/doc/src/command/build/build.c +++ b/doc/src/command/build/build.c @@ -3,6 +3,7 @@ Build Command ***********************************************************************************************************************************/ #include "build.auto.h" +#include "command/build/man.h" #include "command/build/reference.h" #include "common/debug.h" #include "common/log.h" @@ -22,6 +23,8 @@ cmdBuild(const String *const pathRepo) Storage *const storageRepo = storagePosixNewP(pathRepo, .write = true); const BldCfg bldCfg = bldCfgParse(storageRepo); const BldHlp bldHlp = bldHlpParse(storageRepo, bldCfg, true); + XmlNode *const xml = xmlDocumentRoot( + xmlDocumentNewBuf(storageGetP(storageNewReadP(storageRepo, STRDEF("doc/xml/index.xml"))))); storagePutP( storageNewWriteP(storageRepo, STRDEF("doc/output/xml/command.xml")), @@ -29,6 +32,9 @@ cmdBuild(const String *const pathRepo) storagePutP( storageNewWriteP(storageRepo, STRDEF("doc/output/xml/configuration.xml")), xmlDocumentBuf(referenceConfigurationRender(&bldCfg, &bldHlp))); + storagePutP( + storageNewWriteP(storageRepo, STRDEF("doc/output/man/" PROJECT_BIN ".1.txt")), + BUFSTR(referenceManRender(xml, &bldCfg, &bldHlp))); } MEM_CONTEXT_TEMP_END(); diff --git a/doc/src/command/build/man.c b/doc/src/command/build/man.c new file mode 100644 index 000000000..30e161d6b --- /dev/null +++ b/doc/src/command/build/man.c @@ -0,0 +1,299 @@ +/*********************************************************************************************************************************** +Build Manual Page Reference +***********************************************************************************************************************************/ +#include "build.auto.h" + +#include "build/common/string.h" +#include "build/help/render.h" +#include "command/build/man.h" +#include "command/help/help.h" +#include "common/debug.h" +#include "common/log.h" +#include "version.h" + +/*********************************************************************************************************************************** +Define the console width - use a fixed width of 80 since this should be safe on virtually all consoles +***********************************************************************************************************************************/ +#define CONSOLE_WIDTH 80 + +/*********************************************************************************************************************************** +Basic variable replacement. This only handles a few cases but will error in case of unknown variables. +***********************************************************************************************************************************/ +String * +referenceManReplace(String *const string) +{ + FUNCTION_TEST_BEGIN(); + FUNCTION_LOG_PARAM(STRING, string); + FUNCTION_TEST_END(); + + String *const result = strCat(strNew(), string); + + strReplace(result, STRDEF("{[postgres]}"), STRDEF("PostgreSQL")); + strReplace(result, STRDEF("{[project]}"), STRDEF("pgBackRest")); + + if (strstr(strZ(result), "{[") != NULL) + THROW_FMT(AssertError, "unreplaced variable(s) in: %s", strZ(string)); + + FUNCTION_TEST_RETURN(STRING, result); +} + +/**********************************************************************************************************************************/ +typedef struct ReferenceManSectionData +{ + const String *name; // Section name + List *optList; // Option list +} ReferenceManSectionData; + +// Helper to calculate max width +static size_t +referenceManMaxWidth(const size_t widthMax, const String *const string) +{ + FUNCTION_TEST_BEGIN(); + FUNCTION_LOG_PARAM(SIZE, widthMax); + FUNCTION_LOG_PARAM(STRING, string); + FUNCTION_TEST_END(); + + if (strSize(string) > widthMax) + FUNCTION_TEST_RETURN(SIZE, strSize(string)); + + FUNCTION_TEST_RETURN(SIZE, widthMax); +} + +String * +referenceManRender(const XmlNode *const indexRoot, const BldCfg *const bldCfg, const BldHlp *const bldHlp) +{ + FUNCTION_LOG_BEGIN(logLevelDebug); + FUNCTION_LOG_PARAM(XML_NODE, indexRoot); + FUNCTION_LOG_PARAM_P(VOID, bldCfg); + FUNCTION_LOG_PARAM_P(VOID, bldHlp); + FUNCTION_LOG_END(); + + String *const result = strNew(); + + MEM_CONTEXT_TEMP_BEGIN() + { + // Output header + const String *const subtitle = referenceManReplace(xmlNodeAttribute(indexRoot, STRDEF("subtitle"))); + const String *const description = helpRenderText( + referenceManReplace(xmlNodeContent(xmlNodeChild(indexRoot, STRDEF("description"), true))), false, false, 2, true, + CONSOLE_WIDTH); + + strCatFmt( + result, + "NAME\n" + " " PROJECT_NAME " - %s\n\n" + "SYNOPSIS\n" + " " PROJECT_BIN " [options] [command]\n\n" + "DESCRIPTION\n" + "%s\n", + strZ(subtitle), strZ(description)); + + // Output commands + // ------------------------------------------------------------------------------------------------------------------------- + // Determine max command width + size_t commandWidthMax = 0; + + for (unsigned int cmdIdx = 0; cmdIdx < lstSize(bldHlp->cmdList); cmdIdx++) + { + const BldHlpCommand *const cmdHlp = lstGet(bldHlp->cmdList, cmdIdx); + const BldCfgCommand *const cmdCfg = lstFind(bldCfg->cmdList, &cmdHlp->name); + ASSERT(cmdCfg != NULL); + + // Skip internal commands + if (cmdCfg->internal) + continue; + + // Update max command width + commandWidthMax = referenceManMaxWidth(commandWidthMax, cmdHlp->name); + } + + // Output commands + strCatZ(result, "\nCOMMANDS\n"); + + for (unsigned int cmdIdx = 0; cmdIdx < lstSize(bldHlp->cmdList); cmdIdx++) + { + const BldHlpCommand *const cmdHlp = lstGet(bldHlp->cmdList, cmdIdx); + const BldCfgCommand *const cmdCfg = lstFind(bldCfg->cmdList, &cmdHlp->name); + ASSERT(cmdCfg != NULL); + + // Skip internal commands + if (cmdCfg->internal) + continue; + + strCatFmt( + result, " %s %*s%s\n", strZ(cmdHlp->name), + (int)(commandWidthMax - strSize(cmdHlp->name)), "", + strZ(helpRenderText(bldHlpRenderXml(cmdHlp->summary), false, false, commandWidthMax + 4, false, CONSOLE_WIDTH))); + } + + // Output options + // ------------------------------------------------------------------------------------------------------------------------- + // Determine max option width and build list of sections/options + List *const sectionList = lstNewP(sizeof(ReferenceManSectionData), .comparator = lstComparatorStr); + size_t optionWidthMax = 0; + + for (unsigned int cmdIdx = 0; cmdIdx < lstSize(bldHlp->cmdList); cmdIdx++) + { + const BldHlpCommand *const cmdHlp = lstGet(bldHlp->cmdList, cmdIdx); + const BldCfgCommand *const cmdCfg = lstFind(bldCfg->cmdList, &cmdHlp->name); + ASSERT(cmdCfg != NULL); + + // Skip internal commands + if (cmdCfg->internal) + continue; + + if (cmdHlp->optList != NULL) + { + // Get section name + const String *const sectionName = strFirstUpper(strDup(cmdHlp->name)); + + for (unsigned int optIdx = 0; optIdx < lstSize(cmdHlp->optList); optIdx++) + { + const BldHlpOption *const optHlp = lstGet(cmdHlp->optList, optIdx); + const BldCfgOption *const optCfg = lstFind(bldCfg->optList, &optHlp->name); + ASSERT(optCfg != NULL); + + // Skip internal options + const BldCfgOptionCommand *const optCmdCfg = lstFind(optCfg->cmdList, &cmdHlp->name); + ASSERT(optCmdCfg != NULL); + + if (optCmdCfg->internal) + continue; + + // Update max option width + optionWidthMax = referenceManMaxWidth(optionWidthMax, optHlp->name); + + // Add/get section + ReferenceManSectionData *section = lstFind(sectionList, §ionName); + + if (section == NULL) + { + MEM_CONTEXT_OBJ_BEGIN(sectionList) + { + const ReferenceManSectionData sectionNew = + { + .name = strDup(sectionName), + .optList = lstNewP(sizeof(BldHlpOption), .comparator = lstComparatorStr), + }; + + section = lstAdd(sectionList, §ionNew); + } + MEM_CONTEXT_OBJ_END(); + } + + // Add option + lstAdd(section->optList, optHlp); + } + } + } + + for (unsigned int optIdx = 0; optIdx < lstSize(bldHlp->optList); optIdx++) + { + const BldHlpOption *const optHlp = lstGet(bldHlp->optList, optIdx); + const BldCfgOption *const optCfg = lstFind(bldCfg->optList, &optHlp->name); + ASSERT(optCfg != NULL); + + // Skip if option is internal + if (optCfg->internal) + continue; + + // Update max option width + optionWidthMax = referenceManMaxWidth(optionWidthMax, optHlp->name); + + // Get section name + const String *const sectionName = optHlp->section == NULL ? STRDEF("General") : strFirstUpper(strDup(optHlp->section)); + + // Add/get section + ReferenceManSectionData *section = lstFind(sectionList, §ionName); + + if (section == NULL) + { + MEM_CONTEXT_OBJ_BEGIN(sectionList) + { + const ReferenceManSectionData sectionNew = + { + .name = strDup(sectionName), + .optList = lstNewP(sizeof(BldHlpOption), .comparator = lstComparatorStr), + }; + + section = lstAdd(sectionList, §ionNew); + } + MEM_CONTEXT_OBJ_END(); + } + + // Add option + lstAdd(section->optList, optHlp); + lstSort(section->optList, sortOrderAsc); + } + + lstSort(sectionList, sortOrderAsc); + + // Output options + strCatZ(result, "\nOPTIONS\n"); + + for (unsigned int sectionIdx = 0; sectionIdx < lstSize(sectionList); sectionIdx++) + { + const ReferenceManSectionData *const section = lstGet(sectionList, sectionIdx); + + if (sectionIdx != 0) + strCatZ(result, "\n"); + + strCatFmt(result, " %s Options:\n", strZ(section->name)); + + for (unsigned int optIdx = 0; optIdx < lstSize(section->optList); optIdx++) + { + const BldHlpOption *const optHlp = lstGet(section->optList, optIdx); + + strCatFmt( + result, " --%s %*s%s\n", strZ(optHlp->name), + (int)(optionWidthMax - strSize(optHlp->name)), "", + strZ(helpRenderText(bldHlpRenderXml(optHlp->summary), false, false, optionWidthMax + 8, false, CONSOLE_WIDTH))); + } + } + + // Output files + // ------------------------------------------------------------------------------------------------------------------------- + strCatZ( + result, + "\nFILES\n" + " " CFGOPTDEF_CONFIG_PATH "/" PROJECT_CONFIG_FILE "\n" + " /var/lib/pgbackrest\n" + " /var/log/pgbackrest\n" + " /var/spool/pgbackrest\n" + " /tmp/pgbackrest\n"); + + // Output examples + // ------------------------------------------------------------------------------------------------------------------------- + strCatZ( + result, + "\nEXAMPLES\n" + " * Create a backup of the PostgreSQL `main` cluster:\n" + "\n" + " $ pgbackrest --stanza=main backup\n" + "\n" + " The `main` cluster should be configured in `" CFGOPTDEF_CONFIG_PATH "/" PROJECT_CONFIG_FILE "`\n" + "\n" + " * Show all available backups:\n" + "\n" + " $ pgbackrest info\n" + "\n" + " * Show all available backups for a specific cluster:\n" + "\n" + " $ pgbackrest --stanza=main info\n" + "\n" + " * Show backup specific options:\n" + "\n" + " $ pgbackrest help backup\n"); + + // Output see also + // ------------------------------------------------------------------------------------------------------------------------- + strCatZ( + result, + "\nSEE ALSO\n" + " /usr/share/doc/pgbackrest-doc/html/index.html\n" + " http://www.pgbackrest.org\n"); + } + MEM_CONTEXT_TEMP_END(); + + FUNCTION_LOG_RETURN(STRING, result); +} diff --git a/doc/src/command/build/man.h b/doc/src/command/build/man.h new file mode 100644 index 000000000..fc52bdf27 --- /dev/null +++ b/doc/src/command/build/man.h @@ -0,0 +1,17 @@ +/*********************************************************************************************************************************** +Build Manual Page Reference +***********************************************************************************************************************************/ +#ifndef DOC_COMMAND_BUILD_MAN_H +#define DOC_COMMAND_BUILD_MAN_H + +#include "build/config/parse.h" +#include "build/help/parse.h" +#include "common/type/xml.h" + +/*********************************************************************************************************************************** +Functions +***********************************************************************************************************************************/ +// Build manual page reference +String *referenceManRender(const XmlNode *indexRoot, const BldCfg *bldCfg, const BldHlp *bldHlp); + +#endif diff --git a/doc/src/meson.build b/doc/src/meson.build index d65fc201c..8f2b382ed 100644 --- a/doc/src/meson.build +++ b/doc/src/meson.build @@ -32,10 +32,12 @@ src_doc = [ '../../src/build/common/yaml.c', '../../src/build/config/parse.c', '../../src/build/help/parse.c', + '../../src/build/help/render.c', '../../src/command/command.c', '../../src/command/exit.c', '../../src/command/help/help.c', '../../src/common/compress/bz2/common.c', + '../../src/common/compress/bz2/compress.c', '../../src/common/compress/bz2/decompress.c', '../../src/common/ini.c', '../../src/common/io/fd.c', @@ -47,6 +49,7 @@ src_doc = [ '../../src/config/config.c', '../../src/config/parse.c', 'command/build/build.c', + 'command/build/man.c', 'command/build/reference.c', 'config/load.c', 'main.c', diff --git a/src/build/help/render.c b/src/build/help/render.c index fa28c6b12..0c95e8fa1 100644 --- a/src/build/help/render.c +++ b/src/build/help/render.c @@ -117,7 +117,7 @@ bldHlpRenderXmlNode(const xmlNodePtr xml) return result; } -static String * +String * bldHlpRenderXml(const XmlNode *const xml) { return strTrim(bldHlpRenderXmlNode(*(const xmlNodePtr *)xml)); diff --git a/src/build/help/render.h b/src/build/help/render.h index f9f97ff6c..bd482f839 100644 --- a/src/build/help/render.h +++ b/src/build/help/render.h @@ -13,4 +13,7 @@ Functions // Render help void bldHlpRender(const Storage *const storageRepo, const BldCfg bldCfg, const BldHlp bldHlp); +// Render xml as text +String *bldHlpRenderXml(const XmlNode *xml); + #endif diff --git a/src/command/help/help.c b/src/command/help/help.c index bf73cccb9..fbc28ed4c 100644 --- a/src/command/help/help.c +++ b/src/command/help/help.c @@ -93,10 +93,8 @@ helpRenderSplitSize(const String *string, const char *delimiter, size_t size) FUNCTION_TEST_RETURN(STRING_LIST, this); } -/*********************************************************************************************************************************** -Helper function for helpRender() to make output look good on a console -***********************************************************************************************************************************/ -static String * +/**********************************************************************************************************************************/ +FN_EXTERN String * helpRenderText( const String *const text, const bool internal, const bool beta, const size_t indent, const bool indentFirst, const size_t length) diff --git a/src/command/help/help.h b/src/command/help/help.h index aeef6f39b..908b174ce 100644 --- a/src/command/help/help.h +++ b/src/command/help/help.h @@ -12,4 +12,7 @@ Functions // Render help and output to stdout FN_EXTERN void cmdHelp(const Buffer *const helpData); +// Render help text +FN_EXTERN String *helpRenderText(const String *text, bool internal, bool beta, size_t indent, bool indentFirst, size_t length); + #endif diff --git a/test/define.yaml b/test/define.yaml index fd90a9324..81acfc8f6 100644 --- a/test/define.yaml +++ b/test/define.yaml @@ -992,6 +992,7 @@ unit: coverage: - doc/command/build/build + - doc/command/build/man - doc/command/build/reference # ********************************************************************************************************************************** diff --git a/test/src/module/doc/buildTest.c b/test/src/module/doc/buildTest.c index 31ba9a381..34ebfd5e9 100644 --- a/test/src/module/doc/buildTest.c +++ b/test/src/module/doc/buildTest.c @@ -19,6 +19,11 @@ testRun(void) // ***************************************************************************************************************************** if (testBegin("cmdBuild()")) { + // ------------------------------------------------------------------------------------------------------------------------- + TEST_TITLE("referenceManReplace()"); + + TEST_ERROR(referenceManReplace(strNewZ("TEST {[")), AssertError, "unreplaced variable(s) in: TEST {["); + // ------------------------------------------------------------------------------------------------------------------------- TEST_TITLE("referenceCommandSection()"); @@ -35,6 +40,14 @@ testRun(void) // ------------------------------------------------------------------------------------------------------------------------- TEST_TITLE("parse and render"); + HRN_STORAGE_PUT_Z( + storageTest, "doc/xml/index.xml", + "\n" + "\n" + "\n" + "{[project]} is a reliable backup and restore solution for {[postgres]}...\n" + "\n"); + HRN_STORAGE_PUT_Z( storageTest, "src/build/config/config.yaml", "command:\n" @@ -95,6 +108,13 @@ testRun(void) " deprecate:\n" " frc: {}\n" "\n" + " internal-opt:\n" + " type: boolean\n" + " internal: true\n" + " default: true\n" + " command:\n" + " backup: {}\n" + "\n" " stanza:\n" " type: string\n" " default: demo\n" @@ -176,6 +196,13 @@ testRun(void) " y" " \n" "\n" + " \n" + "\n" "