1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-14 10:13:05 +02:00
pgbackrest/doc/lib/BackRestDoc/Common/DocConfig.pm
David Steele 404284b90f Add internal flag for commands.
Allow commands to be skipped by default in the command help but still work if help is requested for the command directly.  There may be other uses for the flag in the future.

Update help for ls now that it is exposed.
2019-05-28 12:18:05 -04:00

890 lines
33 KiB
Perl

####################################################################################################################################
# DOC CONFIG MODULE
####################################################################################################################################
package BackRestDoc::Common::DocConfig;
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use Exporter qw(import);
our @EXPORT = qw();
use File::Basename qw(dirname);
use pgBackRest::Common::Log;
use pgBackRest::Common::String;
use pgBackRestBuild::Config::Data;
use pgBackRest::Version;
####################################################################################################################################
# Help types
####################################################################################################################################
use constant CONFIG_HELP_COMMAND => 'command';
push @EXPORT, qw(CONFIG_HELP_COMMAND);
use constant CONFIG_HELP_CURRENT => 'current';
use constant CONFIG_HELP_DEFAULT => 'default';
use constant CONFIG_HELP_DESCRIPTION => 'description';
push @EXPORT, qw(CONFIG_HELP_DESCRIPTION);
use constant CONFIG_HELP_EXAMPLE => 'example';
use constant CONFIG_HELP_NAME => 'name';
use constant CONFIG_HELP_NAME_ALT => 'name-alt';
push @EXPORT, qw(CONFIG_HELP_NAME_ALT);
use constant CONFIG_HELP_OPTION => 'option';
push @EXPORT, qw(CONFIG_HELP_OPTION);
use constant CONFIG_HELP_SECTION => 'section';
push @EXPORT, qw(CONFIG_HELP_SECTION);
use constant CONFIG_HELP_SUMMARY => 'summary';
push @EXPORT, qw(CONFIG_HELP_SUMMARY);
use constant CONFIG_HELP_SOURCE => 'source';
push @EXPORT, qw(CONFIG_HELP_SOURCE);
use constant CONFIG_HELP_SOURCE_DEFAULT => 'default';
use constant CONFIG_HELP_SOURCE_SECTION => CONFIG_HELP_SECTION;
use constant CONFIG_HELP_SOURCE_COMMAND => CONFIG_HELP_COMMAND;
push @EXPORT, qw(CONFIG_HELP_SOURCE_COMMAND);
####################################################################################################################################
# Config Section Types
####################################################################################################################################
use constant CFGDEF_COMMAND => 'command';
use constant CFGDEF_GENERAL => 'general';
use constant CFGDEF_LOG => 'log';
use constant CFGDEF_REPOSITORY => 'repository';
####################################################################################################################################
# Option define hash
####################################################################################################################################
my $rhConfigDefine = cfgDefine();
####################################################################################################################################
# Returns the option defines based on the command.
####################################################################################################################################
sub docConfigCommandDefine
{
my $strOption = shift;
my $strCommand = shift;
if (defined($strCommand))
{
return defined($rhConfigDefine->{$strOption}{&CFGDEF_COMMAND}) &&
defined($rhConfigDefine->{$strOption}{&CFGDEF_COMMAND}{$strCommand}) &&
ref($rhConfigDefine->{$strOption}{&CFGDEF_COMMAND}{$strCommand}) eq 'HASH' ?
$rhConfigDefine->{$strOption}{&CFGDEF_COMMAND}{$strCommand} : undef;
}
return;
}
####################################################################################################################################
# Does the option have a default for this command?
####################################################################################################################################
sub docConfigOptionDefault
{
my $strOption = shift;
my $strCommand = shift;
# Get the command define
my $oCommandDefine = docConfigCommandDefine($strOption, $strCommand);
# Check for default in command
my $strDefault = defined($oCommandDefine) ? $$oCommandDefine{&CFGDEF_DEFAULT} : undef;
# If defined return, else try to grab the global default
return defined($strDefault) ? $strDefault : $rhConfigDefine->{$strOption}{&CFGDEF_DEFAULT};
}
push @EXPORT, qw(docConfigOptionDefault);
####################################################################################################################################
# Get the allowed setting range for the option if it exists
####################################################################################################################################
sub docConfigOptionRange
{
my $strOption = shift;
my $strCommand = shift;
# Get the command define
my $oCommandDefine = docConfigCommandDefine($strOption, $strCommand);
# Check for default in command
if (defined($oCommandDefine) && defined($$oCommandDefine{&CFGDEF_ALLOW_RANGE}))
{
return $$oCommandDefine{&CFGDEF_ALLOW_RANGE}[0], $$oCommandDefine{&CFGDEF_ALLOW_RANGE}[1];
}
# If defined return, else try to grab the global default
return $rhConfigDefine->{$strOption}{&CFGDEF_ALLOW_RANGE}[0], $rhConfigDefine->{$strOption}{&CFGDEF_ALLOW_RANGE}[1];
}
push @EXPORT, qw(docConfigOptionRange);
####################################################################################################################################
# Get the option type
####################################################################################################################################
sub docConfigOptionType
{
my $strOption = shift;
return $rhConfigDefine->{$strOption}{&CFGDEF_TYPE};
}
push @EXPORT, qw(docConfigOptionType);
####################################################################################################################################
# Test the option type
####################################################################################################################################
sub docConfigOptionTypeTest
{
my $strOption = shift;
my $strType = shift;
return docConfigOptionType($strOption) eq $strType;
}
push @EXPORT, qw(docConfigOptionTypeTest);
####################################################################################################################################
# CONSTRUCTOR
####################################################################################################################################
sub new
{
my $class = shift; # Class name
# Create the class hash
my $self = {};
bless $self, $class;
# Assign function parameters, defaults, and log debug info
(
my $strOperation,
$self->{oDoc},
$self->{oDocRender}
) =
logDebugParam
(
__PACKAGE__ . '->new', \@_,
{name => 'oDoc'},
{name => 'oDocRender', required => false}
);
$self->process();
# Return from function and log return values if any
return logDebugReturn
(
$strOperation,
{name => 'self', value => $self}
);
}
####################################################################################################################################
# process
#
# Parse the xml doc into commands and options.
####################################################################################################################################
sub process
{
my $self = shift;
# Assign function parameters, defaults, and log debug info
my $strOperation = logDebugParam(__PACKAGE__ . '->process');
# Iterate through all commands
my $oDoc = $self->{oDoc};
my $oConfigHash = {};
foreach my $strCommand (cfgDefineCommandList())
{
if ($strCommand eq CFGCMD_REMOTE || $strCommand eq CFGCMD_LOCAL || $strCommand eq CFGCMD_ARCHIVE_GET_ASYNC ||
$strCommand eq CFGCMD_ARCHIVE_PUSH_ASYNC)
{
next;
}
my $oCommandDoc = $oDoc->nodeGet('operation')->nodeGet('command-list')->nodeGetById('command', $strCommand);
$$oConfigHash{&CONFIG_HELP_COMMAND}{$strCommand} = {};
my $oCommand = $$oConfigHash{&CONFIG_HELP_COMMAND}{$strCommand};
$$oCommand{&CONFIG_HELP_SUMMARY} = $oCommandDoc->nodeGet('summary')->textGet();
$$oCommand{&CONFIG_HELP_DESCRIPTION} = $oCommandDoc->textGet();
}
# Iterate through all options
my $oOptionDefine = cfgDefine();
foreach my $strOption (sort(keys(%{$oOptionDefine})))
{
# Skip options that are internal only for all commands (test options)
next if $oOptionDefine->{$strOption}{&CFGDEF_INTERNAL};
# Iterate through all commands
my @stryCommandList = sort(keys(%{defined($$oOptionDefine{$strOption}{&CFGDEF_COMMAND}) ?
$$oOptionDefine{$strOption}{&CFGDEF_COMMAND} : $$oConfigHash{&CONFIG_HELP_COMMAND}}));
foreach my $strCommand (@stryCommandList)
{
if (!defined($$oConfigHash{&CONFIG_HELP_COMMAND}{$strCommand}))
{
next;
}
if (ref(\$$oOptionDefine{$strOption}{&CFGDEF_COMMAND}{$strCommand}) eq 'SCALAR' &&
$$oOptionDefine{$strOption}{&CFGDEF_COMMAND}{$strCommand} == false)
{
next;
}
# Skip options that are internal only for the current command
next if $oOptionDefine->{$strOption}{&CFGDEF_COMMAND}{$strCommand}{&CFGDEF_INTERNAL};
my $oCommandDoc = $oDoc->nodeGet('operation')->nodeGet('command-list')->nodeGetById('command', $strCommand);
# First check if the option is documented in the command
my $oOptionDoc;
my $strOptionSource;
my $oCommandOptionList = $oCommandDoc->nodeGet('option-list', false);
if (defined($oCommandOptionList))
{
$oOptionDoc = $oCommandOptionList->nodeGetById('option', $strOption, false);
$strOptionSource = CONFIG_HELP_SOURCE_COMMAND if (defined($oOptionDoc));
}
# If the option wasn't found keep looking
my $strSection;
if (!defined($oOptionDoc))
{
# Next see if it's documented in the section
if (defined($$oOptionDefine{$strOption}{&CFGDEF_SECTION}))
{
# &log(INFO, " trying section ${strSection}");
foreach my $oSectionNode ($oDoc->nodeGet('config')->nodeGet('config-section-list')->nodeList())
{
my $oOptionDocCheck = $oSectionNode->nodeGetById('config-key-list')
->nodeGetById('config-key', $strOption, false);
if ($oOptionDocCheck)
{
if (defined($oOptionDoc))
{
confess 'option exists in more than one section';
}
$oOptionDoc = $oOptionDocCheck;
$strOptionSource = CONFIG_HELP_SOURCE_SECTION;
$strSection = $oSectionNode->paramGet('id');
}
}
}
# If no section is defined then look in the default command option list
else
{
$oOptionDoc = $oDoc->nodeGet('operation')->nodeGet('operation-general')->nodeGet('option-list')
->nodeGetById('option', $strOption, false);
$strOptionSource = CONFIG_HELP_SOURCE_DEFAULT if (defined($oOptionDoc));
}
}
# If the option wasn't found then error
if (!defined($oOptionDoc))
{
confess &log(ERROR, "unable to find option '${strOption}' for command '${strCommand}'")
}
# if the option is documented in the command then it should be accessible from the command line only.
if (!defined($strSection))
{
if (defined($$oOptionDefine{$strOption}{&CFGDEF_SECTION}))
{
&log(ERROR,
"option ${strOption} defined in command ${strCommand} must not have " . CFGDEF_SECTION .
" defined");
}
}
# Store the option in the command
$$oConfigHash{&CONFIG_HELP_COMMAND}{$strCommand}{&CONFIG_HELP_OPTION}{$strOption}{&CONFIG_HELP_SOURCE} =
$strOptionSource;
my $oCommandOption = $$oConfigHash{&CONFIG_HELP_COMMAND}{$strCommand}{&CONFIG_HELP_OPTION}{$strOption};
$$oCommandOption{&CONFIG_HELP_SUMMARY} = $oOptionDoc->nodeGet('summary')->textGet();
$$oCommandOption{&CONFIG_HELP_DESCRIPTION} = $oOptionDoc->textGet();
$$oCommandOption{&CONFIG_HELP_EXAMPLE} = $oOptionDoc->fieldGet('example');
$$oCommandOption{&CONFIG_HELP_NAME} = $oOptionDoc->paramGet('name');
# Generate a list of alternate names
if (defined($rhConfigDefine->{$strOption}{&CFGDEF_NAME_ALT}))
{
my $rhNameAlt = {};
foreach my $strNameAlt (sort(keys(%{$rhConfigDefine->{$strOption}{&CFGDEF_NAME_ALT}})))
{
$strNameAlt =~ s/\?//g;
if ($strNameAlt ne $strOption)
{
$rhNameAlt->{$strNameAlt} = true;
}
}
my @stryNameAlt = sort(keys(%{$rhNameAlt}));
if (@stryNameAlt > 0)
{
if (@stryNameAlt != 1)
{
confess &log(
ERROR, "multiple alt names are not supported for option '${strOption}': " . join(', ', @stryNameAlt));
}
$oCommandOption->{&CONFIG_HELP_NAME_ALT} = \@stryNameAlt;
}
}
# If the option did not come from the command also store in global option list. This prevents duplication of commonly
# used options.
if ($strOptionSource ne CONFIG_HELP_SOURCE_COMMAND)
{
$$oConfigHash{&CONFIG_HELP_OPTION}{$strOption}{&CONFIG_HELP_SUMMARY} = $$oCommandOption{&CONFIG_HELP_SUMMARY};
my $oOption = $$oConfigHash{&CONFIG_HELP_OPTION}{$strOption};
if (defined($strSection))
{
$$oOption{&CONFIG_HELP_SECTION} = $strSection;
}
$$oOption{&CONFIG_HELP_NAME} = $oOptionDoc->paramGet('name');
$oOption->{&CONFIG_HELP_NAME_ALT} = $oCommandOption->{&CONFIG_HELP_NAME_ALT};
$$oOption{&CONFIG_HELP_DESCRIPTION} = $$oCommandOption{&CONFIG_HELP_DESCRIPTION};
$$oOption{&CONFIG_HELP_EXAMPLE} = $oOptionDoc->fieldGet('example');
}
}
}
# Store the config hash
$self->{oConfigHash} = $oConfigHash;
# Return from function and log return values if any
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}})))
{
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)
{
$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}})))
{
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};
# Contruct 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;
}
####################################################################################################################################
# helpConfigDocGet
#
# Get the xml for configuration help.
####################################################################################################################################
sub helpConfigDocGet
{
my $self = shift;
# Assign function parameters, defaults, and log debug info
my $strOperation = logDebugParam(__PACKAGE__ . '->helpConfigDocGet');
# Build a hash of the sections
my $oConfigHash = $self->{oConfigHash};
my $oConfigDoc = $self->{oDoc}->nodeGet('config');
my $oSectionHash = {};
foreach my $strOption (sort(keys(%{$$oConfigHash{&CONFIG_HELP_OPTION}})))
{
my $oOption = $$oConfigHash{&CONFIG_HELP_OPTION}{$strOption};
if (defined($$oOption{&CONFIG_HELP_SECTION}))
{
$$oSectionHash{$$oOption{&CONFIG_HELP_SECTION}}{$strOption} = true;
}
}
my $oDoc = new BackRestDoc::Common::Doc();
$oDoc->paramSet('title', $oConfigDoc->paramGet('title'));
# set the description for use as a meta tag
$oDoc->fieldSet('description', $oConfigDoc->fieldGet('description'));
# Output the introduction
my $oIntroSectionDoc = $oDoc->nodeAdd('section', undef, {id => 'introduction'});
$oIntroSectionDoc->nodeAdd('title')->textSet('Introduction');
$oIntroSectionDoc->textSet($oConfigDoc->textGet());
foreach my $strSection (sort(keys(%{$oSectionHash})))
{
my $oSectionElement = $oDoc->nodeAdd('section', undef, {id => "section-${strSection}"});
my $oSectionDoc = $oConfigDoc->nodeGet('config-section-list')->nodeGetById('config-section', $strSection);
# Set the summary text for the section
$oSectionElement->textSet($oSectionDoc->textGet());
$oSectionElement->
nodeAdd('title')->textSet(
{name => 'text',
children=> [$oSectionDoc->paramGet('name') . ' Options (', {name => 'id', value => $strSection}, ')']});
foreach my $strOption (sort(keys(%{$$oSectionHash{$strSection}})))
{
$self->helpOptionGet(undef, $strOption, $oSectionElement, $$oConfigHash{&CONFIG_HELP_OPTION}{$strOption});
}
}
# Return from function and log return values if any
return logDebugReturn
(
$strOperation,
{name => 'oDoc', value => $oDoc}
);
}
####################################################################################################################################
# helpCommandDocGet
#
# Get the xml for command help.
####################################################################################################################################
sub helpCommandDocGet
{
my $self = shift;
# Assign function parameters, defaults, and log debug info
my $strOperation = logDebugParam(__PACKAGE__ . '->helpCommandDocGet');
# Working variables
my $oConfigHash = $self->{oConfigHash};
my $oOperationDoc = $self->{oDoc}->nodeGet('operation');
my $oOptionDefine = cfgDefine();
my $oDoc = new BackRestDoc::Common::Doc();
$oDoc->paramSet('title', $oOperationDoc->paramGet('title'));
# set the description for use as a meta tag
$oDoc->fieldSet('description', $oOperationDoc->fieldGet('description'));
# Output the introduction
my $oIntroSectionDoc = $oDoc->nodeAdd('section', undef, {id => 'introduction'});
$oIntroSectionDoc->nodeAdd('title')->textSet('Introduction');
$oIntroSectionDoc->textSet($oOperationDoc->textGet());
foreach my $strCommand (sort(keys(%{$$oConfigHash{&CONFIG_HELP_COMMAND}})))
{
my $oCommandHash = $$oConfigHash{&CONFIG_HELP_COMMAND}{$strCommand};
my $oSectionElement = $oDoc->nodeAdd('section', undef, {id => "command-${strCommand}"});
my $oCommandDoc = $oOperationDoc->nodeGet('command-list')->nodeGetById('command', $strCommand);
$oSectionElement->
nodeAdd('title')->textSet(
{name => 'text',
children=> [$oCommandDoc->paramGet('name') . ' Command (', {name => 'id', value => $strCommand}, ')']});
$oSectionElement->textSet($$oCommandHash{&CONFIG_HELP_DESCRIPTION});
# use Data::doc;
# confess Dumper($oDoc->{oDoc});
if (defined($$oCommandHash{&CONFIG_HELP_OPTION}))
{
my $oCategory = {};
foreach my $strOption (sort(keys(%{$$oCommandHash{&CONFIG_HELP_OPTION}})))
{
# Skip secure options that can't be defined on the command line
next if ($rhConfigDefine->{$strOption}{&CFGDEF_SECURE});
my ($oOption, $strCategory) = helpCommandDocGetOptionFind($oConfigHash, $oOptionDefine, $strCommand, $strOption);
$$oCategory{$strCategory}{$strOption} = $oOption;
}
# Iterate sections
foreach my $strCategory (sort(keys(%{$oCategory})))
{
my $oOptionListElement = $oSectionElement->nodeAdd(
'section', undef, {id => "category-${strCategory}", toc => 'n'});
$oOptionListElement->
nodeAdd('title')->textSet(ucfirst($strCategory) . ' Options');
# Iterate options
foreach my $strOption (sort(keys(%{$$oCategory{$strCategory}})))
{
$self->helpOptionGet($strCommand, $strOption, $oOptionListElement,
$$oCommandHash{&CONFIG_HELP_OPTION}{$strOption});
}
}
}
}
# Return from function and log return values if any
return logDebugReturn
(
$strOperation,
{name => 'oDoc', value => $oDoc}
);
}
# Helper function for helpCommandDocGet() to find options. The option may be stored with the command or in the option list depending
# on whether it's generic or command-specific
sub helpCommandDocGetOptionFind
{
my $oConfigHelpData = shift;
my $oOptionDefine = shift;
my $strCommand = shift;
my $strOption = shift;
my $strSection = CONFIG_HELP_COMMAND;
my $oOption = $$oConfigHelpData{&CONFIG_HELP_COMMAND}{$strCommand}{&CONFIG_HELP_OPTION}{$strOption};
if ($$oOption{&CONFIG_HELP_SOURCE} eq CONFIG_HELP_SOURCE_DEFAULT)
{
$strSection = CFGDEF_GENERAL;
}
elsif ($$oOption{&CONFIG_HELP_SOURCE} eq CONFIG_HELP_SOURCE_SECTION)
{
$oOption = $$oConfigHelpData{&CONFIG_HELP_OPTION}{$strOption};
if (defined($$oOption{&CONFIG_HELP_SECTION}) && $strSection ne $strCommand)
{
$strSection = $$oOption{&CONFIG_HELP_SECTION};
}
if (($strSection ne CFGDEF_GENERAL && $strSection ne CFGDEF_LOG &&
$strSection ne CFGDEF_REPOSITORY && $strSection ne CFGDEF_SECTION_STANZA) ||
$strSection eq $strCommand)
{
$strSection = CONFIG_HELP_COMMAND;
}
}
return $oOption, $strSection;
}
####################################################################################################################################
# helpOptionGet
#
# Get the xml for an option.
####################################################################################################################################
sub helpOptionGet
{
my $self = shift;
my $strCommand = shift;
my $strOption = shift;
my $oParentElement = shift;
my $oOptionHash = shift;
# Create the option section
my $oOptionElement = $oParentElement->nodeAdd(
'section', undef, {id => "option-${strOption}", toc => defined($strCommand) ? 'n' : 'y'});
# Set the option section title
$oOptionElement->
nodeAdd('title')->textSet(
{name => 'text',
children=> [$$oOptionHash{&CONFIG_HELP_NAME} . ' Option (', {name => 'id', value => "--${strOption}"}, ')']});
# Add the option summary and description
$oOptionElement->
nodeAdd('p')->textSet($$oOptionHash{&CONFIG_HELP_SUMMARY});
$oOptionElement->
nodeAdd('p')->textSet($$oOptionHash{&CONFIG_HELP_DESCRIPTION});
# Get the default value (or required=n if there is no default)
my $strCodeBlock;
if (defined(docConfigOptionDefault($strOption, $strCommand)))
{
my $strDefault;
if ($strOption eq CFGOPT_REPO_HOST_CMD || $strOption eq CFGOPT_PG_HOST_CMD)
{
$strDefault = '[INSTALL-PATH]/' . PROJECT_EXE;
}
else
{
if (docConfigOptionTypeTest($strOption, CFGDEF_TYPE_BOOLEAN))
{
$strDefault = docConfigOptionDefault($strOption, $strCommand) ? 'y' : 'n';
}
else
{
$strDefault = docConfigOptionDefault($strOption, $strCommand);
}
}
$strCodeBlock = "default: ${strDefault}";
}
# This won't work correctly until there is some notion of dependency
# elsif (optionRequired($strOption, $strCommand))
# {
# $strCodeBlock = 'required: y';
# }
# Get the allowed range if it exists
my ($strRangeMin, $strRangeMax) = docConfigOptionRange($strOption, $strCommand);
if (defined($strRangeMin))
{
$strCodeBlock .= (defined($strCodeBlock) ? "\n" : '') . "allowed: ${strRangeMin}-${strRangeMax}";
}
# Get the example
my $strExample;
my $strOptionPrefix = $rhConfigDefine->{$strOption}{&CFGDEF_PREFIX};
my $strOptionIndex = defined($strOptionPrefix) ?
"${strOptionPrefix}1-" . substr($strOption, length($strOptionPrefix) + 1) : $strOption;
if (defined($strCommand))
{
if (docConfigOptionTypeTest($strOption, CFGDEF_TYPE_BOOLEAN))
{
if ($$oOptionHash{&CONFIG_HELP_EXAMPLE} ne 'n' && $$oOptionHash{&CONFIG_HELP_EXAMPLE} ne 'y')
{
confess &log(ERROR, "option ${strOption} example should be boolean but value is: " .
$$oOptionHash{&CONFIG_HELP_EXAMPLE});
}
$strExample = '--' . ($$oOptionHash{&CONFIG_HELP_EXAMPLE} eq 'n' ? 'no-' : '') . $strOptionIndex;
}
else
{
$strExample = "--${strOptionIndex}=" . $$oOptionHash{&CONFIG_HELP_EXAMPLE};
}
}
else
{
$strExample = "${strOptionIndex}=" . $$oOptionHash{&CONFIG_HELP_EXAMPLE};
}
$strCodeBlock .= (defined($strCodeBlock) ? "\n" : '') . "example: ${strExample}";
$oOptionElement->
nodeAdd('code-block')->valueSet($strCodeBlock);
# Output deprecated names
if (defined($oOptionHash->{&CONFIG_HELP_NAME_ALT}))
{
my $strCaption = 'Deprecated Name' . (@{$oOptionHash->{&CONFIG_HELP_NAME_ALT}} > 1 ? 's' : '');
$oOptionElement->
nodeAdd('p')->textSet("${strCaption}: " . join(', ', @{$oOptionHash->{&CONFIG_HELP_NAME_ALT}}));
}
}
1;