mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2024-12-14 10:13:05 +02:00
404284b90f
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.
890 lines
33 KiB
Perl
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;
|