mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2024-12-14 10:13:05 +02:00
f520ecc89a
The help data can be represented more compactly in a pack and this separates data needed for help from data needed for parsing, freeing each to have a more appropriate representation.
499 lines
16 KiB
Perl
499 lines
16 KiB
Perl
####################################################################################################################################
|
|
# Auto-Generate Command and Option Help Pack
|
|
####################################################################################################################################
|
|
package pgBackRestBuild::Config::BuildHelp;
|
|
|
|
use strict;
|
|
use warnings FATAL => qw(all);
|
|
use Carp qw(confess);
|
|
use English '-no_match_vars';
|
|
|
|
use Cwd qw(abs_path);
|
|
use Exporter qw(import);
|
|
our @EXPORT = qw();
|
|
use File::Basename qw(dirname);
|
|
|
|
use pgBackRestDoc::Common::DocConfig;
|
|
use pgBackRestDoc::Common::DocRender;
|
|
use pgBackRestDoc::Common::Log;
|
|
use pgBackRestDoc::Common::String;
|
|
use pgBackRestDoc::ProjectInfo;
|
|
|
|
use pgBackRestBuild::Build::Common;
|
|
use pgBackRestBuild::Config::Build;
|
|
use pgBackRestBuild::Config::Data;
|
|
|
|
####################################################################################################################################
|
|
# Constants
|
|
####################################################################################################################################
|
|
use constant BLDLCL_FILE_DEFINE => 'help';
|
|
|
|
use constant BLDLCL_DATA_COMMAND => '01-command';
|
|
|
|
####################################################################################################################################
|
|
# Definitions for data to build
|
|
####################################################################################################################################
|
|
my $strSummary = 'Help Definition Pack';
|
|
|
|
my $rhBuild =
|
|
{
|
|
&BLD_FILE =>
|
|
{
|
|
&BLDLCL_FILE_DEFINE =>
|
|
{
|
|
&BLD_SUMMARY => $strSummary,
|
|
|
|
&BLD_DATA =>
|
|
{
|
|
&BLDLCL_DATA_COMMAND =>
|
|
{
|
|
&BLD_SUMMARY => 'Command help',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
####################################################################################################################################
|
|
# Format pack tag
|
|
####################################################################################################################################
|
|
use constant PCK_TYPE_ARRAY => 'pckTypeArray';
|
|
use constant PCK_TYPE_BOOL => 'pckTypeBool';
|
|
use constant PCK_TYPE_OBJ => 'pckTypeObj';
|
|
use constant PCK_TYPE_STR => 'pckTypeStr';
|
|
|
|
# Pack an unsigned 64-bit integer to base-128 varint encoding and output to hex. This is a simplified version of
|
|
# pckWriteUInt64Internal() so see that function for more information.
|
|
sub packIntFormat
|
|
{
|
|
my $iValue = shift;
|
|
|
|
my $strResult = '';
|
|
|
|
while ($iValue >= 0x80)
|
|
{
|
|
# Encode the lower order 7 bits, adding the continuation bit to indicate there is more data
|
|
$strResult .= sprintf(" 0x%02X,", ($iValue & 0x7f) | 0x80);
|
|
|
|
# Shift the value to remove bits that have been encoded
|
|
$iValue >>= 7;
|
|
}
|
|
|
|
return $strResult . sprintf(" 0x%02X,", $iValue);
|
|
}
|
|
|
|
# Write pack field tag and data. This is a cut down version of pckWriteTag() so see that function for more information.
|
|
sub packTagFormat
|
|
{
|
|
my $strName = shift;
|
|
my $strType = shift;
|
|
my $iDelta = shift;
|
|
my $xData = shift;
|
|
my $iIndent = shift;
|
|
|
|
my $strIndent = ' ' x $iIndent;
|
|
|
|
# Pack delta bits and determine value for various pack. See pckWriteTag() for more detailed information.
|
|
my $iValue = undef;
|
|
my $iBits = undef;
|
|
|
|
if ($strType eq PCK_TYPE_STR || $strType eq PCK_TYPE_BOOL)
|
|
{
|
|
$iBits = $iDelta & 0x3;
|
|
$iDelta >>= 2;
|
|
|
|
if ($iDelta != 0)
|
|
{
|
|
$iBits |= 0x4;
|
|
}
|
|
|
|
if ($strType eq PCK_TYPE_STR)
|
|
{
|
|
$iBits |= 0x8;
|
|
$iValue = length($xData);
|
|
}
|
|
else
|
|
{
|
|
$iBits |= $xData ? 0x8 : 0;
|
|
undef($xData);
|
|
}
|
|
}
|
|
elsif ($strType eq PCK_TYPE_ARRAY || $strType eq PCK_TYPE_OBJ)
|
|
{
|
|
$iBits |= $iDelta & 0x7;
|
|
$iDelta >>= 3;
|
|
|
|
if ($iDelta != 0)
|
|
{
|
|
$iBits |= 0x8;
|
|
}
|
|
}
|
|
|
|
# Output pack type and bits
|
|
my $strResult = "${strIndent}${strType} << 4";
|
|
|
|
if ($iBits != 0)
|
|
{
|
|
$strResult .= sprintf(" | 0x%02X", $iBits);
|
|
}
|
|
|
|
$strResult .= ',';
|
|
|
|
# Output additional id delta when present
|
|
if ($iDelta > 0)
|
|
{
|
|
$strResult .= packIntFormat($iDelta);
|
|
}
|
|
|
|
# Output value when present
|
|
if (defined($iValue))
|
|
{
|
|
$strResult .= packIntFormat($iValue);
|
|
}
|
|
|
|
# Output pack name
|
|
$strResult .= " // ${strName}";
|
|
|
|
# Output data in hex format
|
|
if (defined($xData) && length($xData) > 0)
|
|
{
|
|
$strResult .= "\n${strIndent} ";
|
|
my $iLength = length($strIndent) + 4;
|
|
my $bLastLF = false;
|
|
my $bFirst = true;
|
|
|
|
# Loop through all chars
|
|
foreach my $iChar (unpack("W*", $xData))
|
|
{
|
|
# Encode char to hex
|
|
my $strOut = sprintf("0x%02X,", $iChar);
|
|
|
|
# Break on linefeeds to prevent diffs within a paragraph of text from cascading through all the data
|
|
if ($bLastLF && $iChar != 0xA)
|
|
{
|
|
$strResult .= "\n${strIndent} ";
|
|
$iLength = length($strIndent) + 4;
|
|
$bFirst = true;
|
|
}
|
|
|
|
$bLastLF = $iChar == 0xA;
|
|
|
|
# If this hex would exceed the line length then break and write on the next line
|
|
if ($iLength + length($strOut) + 1 > 132)
|
|
{
|
|
$strResult .= "\n${strIndent} ${strOut}";
|
|
$iLength = length($strIndent) + 4 + length($strOut);
|
|
}
|
|
# Else append the hex
|
|
else
|
|
{
|
|
$strResult .= ($bFirst ? '' : ' ') . "${strOut}";
|
|
$iLength += length($strOut) + ($bFirst ? 0 : 1);
|
|
$bFirst = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $strResult . "\n";
|
|
}
|
|
|
|
####################################################################################################################################
|
|
# Build help data
|
|
####################################################################################################################################
|
|
sub buildConfigHelp
|
|
{
|
|
# Load help data
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
require pgBackRestDoc::Common::Doc;
|
|
require pgBackRestDoc::Common::DocManifest;
|
|
|
|
my $strDocPath = abs_path(dirname($0) . '/../doc');
|
|
|
|
my $oStorageDoc = new pgBackRestTest::Common::Storage(
|
|
$strDocPath, new pgBackRestTest::Common::StoragePosix({bFileSync => false, bPathSync => false}));
|
|
|
|
my @stryEmpty = [];
|
|
my $oManifest = new pgBackRestDoc::Common::DocManifest(
|
|
$oStorageDoc, \@stryEmpty, \@stryEmpty, \@stryEmpty, \@stryEmpty, undef, $strDocPath, false, false);
|
|
|
|
my $oDocRender = new pgBackRestDoc::Common::DocRender('text', $oManifest, false);
|
|
my $oDocConfig =
|
|
new pgBackRestDoc::Common::DocConfig(
|
|
new pgBackRestDoc::Common::Doc("${strDocPath}/xml/reference.xml"), $oDocRender);
|
|
my $hConfigHelp = $oDocConfig->{oConfigHash};
|
|
|
|
# Build command help
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
my $rhCommandDefine = cfgDefineCommand();
|
|
|
|
my $strBuildSource =
|
|
"static const unsigned char helpDataPack[] =\n" .
|
|
"{\n" .
|
|
" // Commands\n" .
|
|
" // " . (qw{-} x 125) . "\n";
|
|
|
|
$strBuildSource .= packTagFormat("Commands begin", PCK_TYPE_ARRAY, 0, undef, 4);
|
|
|
|
foreach my $strCommand (sort(keys(%{$rhCommandDefine})))
|
|
{
|
|
my $rhCommand = $rhCommandDefine->{$strCommand};
|
|
my $iDelta = 0;
|
|
|
|
# Get command help
|
|
my $rhCommandHelp = $hConfigHelp->{&CONFIG_HELP_COMMAND}{$strCommand};
|
|
|
|
if (!defined($rhCommandHelp))
|
|
{
|
|
confess "no help for command ${strCommand}"
|
|
}
|
|
|
|
# Build command data
|
|
$strBuildSource .=
|
|
"\n" .
|
|
" // ${strCommand} command\n" .
|
|
" // " . (qw{-} x 121) . "\n";
|
|
|
|
if ($rhCommand->{&CFGDEF_INTERNAL})
|
|
{
|
|
$strBuildSource .= packTagFormat("Internal", PCK_TYPE_BOOL, 0, true, 8);
|
|
}
|
|
else
|
|
{
|
|
$iDelta++;
|
|
}
|
|
|
|
my $strSummary = trim($oManifest->variableReplace($oDocRender->processText($rhCommandHelp->{&CONFIG_HELP_SUMMARY})));
|
|
|
|
if (length($strSummary) > 72)
|
|
{
|
|
confess("summary for command '${strCommand}' may not be greater than 72 characters");
|
|
}
|
|
|
|
$strBuildSource .= packTagFormat("Summary", PCK_TYPE_STR, $iDelta, $strSummary, 8);
|
|
$iDelta = 0;
|
|
|
|
$strBuildSource .= packTagFormat(
|
|
"Description", PCK_TYPE_STR, 0,
|
|
trim($oManifest->variableReplace($oDocRender->processText($rhCommandHelp->{&CONFIG_HELP_DESCRIPTION}))), 8);
|
|
};
|
|
|
|
$strBuildSource .=
|
|
"\n" .
|
|
" 0x00, // Commands end\n";
|
|
|
|
# Build option help
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
my $rhConfigDefine = cfgDefine();
|
|
|
|
$strBuildSource .=
|
|
"\n" .
|
|
" // Options\n" .
|
|
" // " . (qw{-} x 125) . "\n";
|
|
|
|
$strBuildSource .= packTagFormat("Options begin", PCK_TYPE_ARRAY, 0, undef, 4);
|
|
|
|
my $iDelta = 0;
|
|
|
|
foreach my $strOption (sort(keys(%{$rhConfigDefine})))
|
|
{
|
|
my $bFirst = true;
|
|
my $bInternal = false;
|
|
|
|
# Build option data
|
|
my $rhOption = $rhConfigDefine->{$strOption};
|
|
|
|
# Get option help
|
|
my $rhOptionHelp = $hConfigHelp->{&CONFIG_HELP_OPTION}{$strOption};
|
|
|
|
# Build command data
|
|
$strBuildSource .=
|
|
"\n" .
|
|
" // ${strOption} option\n" .
|
|
" // " . (qw{-} x 121) . "\n";
|
|
|
|
# Internal
|
|
if ($rhOption->{&CFGDEF_INTERNAL})
|
|
{
|
|
$strBuildSource .= packTagFormat("Internal", PCK_TYPE_BOOL, $iDelta, true, 8);
|
|
$iDelta = 0;
|
|
$bInternal = true;
|
|
}
|
|
else
|
|
{
|
|
$iDelta++;
|
|
}
|
|
|
|
if (defined($rhOptionHelp))
|
|
{
|
|
# Section
|
|
my $strSection = $rhOptionHelp->{&CONFIG_HELP_SECTION};
|
|
|
|
if (defined($strSection))
|
|
{
|
|
if (length($strSection) > 72)
|
|
{
|
|
confess("section for option '${strOption}' may not be greater than 72 characters");
|
|
}
|
|
|
|
$strBuildSource .= packTagFormat("Section", PCK_TYPE_STR, $iDelta, $strSection, 8);
|
|
$iDelta = 0;
|
|
}
|
|
else
|
|
{
|
|
$iDelta++;
|
|
}
|
|
|
|
# Summary
|
|
my $strSummary = trim($oManifest->variableReplace($oDocRender->processText($rhOptionHelp->{&CONFIG_HELP_SUMMARY})));
|
|
|
|
if (length($strSummary) > 72)
|
|
{
|
|
confess("summary for option '${strOption}' may not be greater than 72 characters");
|
|
}
|
|
|
|
$strBuildSource .= packTagFormat("Summary", PCK_TYPE_STR, $iDelta, $strSummary, 8);
|
|
$iDelta = 0;
|
|
|
|
# Description
|
|
$strBuildSource .= packTagFormat(
|
|
"Description", PCK_TYPE_STR, $iDelta,
|
|
trim($oManifest->variableReplace($oDocRender->processText($rhOptionHelp->{&CONFIG_HELP_DESCRIPTION}))), 8);
|
|
|
|
$bFirst = false;
|
|
}
|
|
else
|
|
{
|
|
$iDelta += 3;
|
|
}
|
|
|
|
# Output deprecated names
|
|
my $stryDeprecatedName = $rhOptionHelp->{&CONFIG_HELP_NAME_ALT};
|
|
|
|
if (defined($stryDeprecatedName))
|
|
{
|
|
$strBuildSource .=
|
|
($bFirst ? '' : "\n") .
|
|
packTagFormat("Deprecated names begin", PCK_TYPE_ARRAY, $iDelta, undef, 8);
|
|
$iDelta = 0;
|
|
|
|
foreach my $strDeprecatedName (@{$stryDeprecatedName})
|
|
{
|
|
$strBuildSource .= packTagFormat($strDeprecatedName, PCK_TYPE_STR, 0, $strDeprecatedName, 12);
|
|
}
|
|
|
|
$strBuildSource .=
|
|
" 0x00, // Deprecated names end\n";
|
|
|
|
$bFirst = false;
|
|
}
|
|
else
|
|
{
|
|
$iDelta++;
|
|
}
|
|
|
|
# Command overrides
|
|
my $strBuildSourceCommands;
|
|
my $iCommandId = 0;
|
|
my $iLastCommandId = 0;
|
|
|
|
foreach my $strCommand (sort(keys(%{$rhCommandDefine})))
|
|
{
|
|
my $rhCommand = $rhOption->{&CFGDEF_COMMAND}{$strCommand};
|
|
my $iDeltaCommand = 0;
|
|
my $strBuildSourceCommand;
|
|
|
|
if (defined($rhCommand))
|
|
{
|
|
if ($bInternal && defined($rhCommand->{&CFGDEF_INTERNAL}) && !$rhCommand->{&CFGDEF_INTERNAL})
|
|
{
|
|
confess("option '${strOption}' is internal but command '${strCommand}' override is not");
|
|
}
|
|
|
|
# Internal
|
|
if (defined($rhCommand->{&CFGDEF_INTERNAL}) && $bInternal != $rhCommand->{&CFGDEF_INTERNAL})
|
|
{
|
|
$strBuildSourceCommand .=
|
|
packTagFormat("Internal", PCK_TYPE_BOOL, $iDeltaCommand, true, 16);
|
|
$iDeltaCommand = 0;
|
|
}
|
|
else
|
|
{
|
|
$iDeltaCommand++;
|
|
}
|
|
|
|
my $rhCommandHelp = $hConfigHelp->{&CONFIG_HELP_COMMAND}{$strCommand}{&CONFIG_HELP_OPTION}{$strOption};
|
|
|
|
if (defined($rhCommandHelp->{&CONFIG_HELP_SOURCE}) &&
|
|
$rhCommandHelp->{&CONFIG_HELP_SOURCE} eq CONFIG_HELP_SOURCE_COMMAND)
|
|
{
|
|
# Summary
|
|
my $strSummary = trim(
|
|
$oManifest->variableReplace($oDocRender->processText($rhCommandHelp->{&CONFIG_HELP_SUMMARY})));
|
|
|
|
if (length($strSummary) > 72)
|
|
{
|
|
confess("summary for command '${strCommand}' option '${strOption}' may not be greater than 72 characters");
|
|
}
|
|
|
|
$strBuildSourceCommand .=
|
|
packTagFormat("Summary", PCK_TYPE_STR, $iDeltaCommand, $strSummary, 16);
|
|
$iDeltaCommand = 0;
|
|
|
|
# Description
|
|
$strBuildSourceCommand .= packTagFormat(
|
|
"Description", PCK_TYPE_STR, $iDeltaCommand,
|
|
trim($oManifest->variableReplace($oDocRender->processText($rhCommandHelp->{&CONFIG_HELP_DESCRIPTION}))),
|
|
16);
|
|
}
|
|
|
|
if (defined($strBuildSourceCommand))
|
|
{
|
|
$strBuildSourceCommands .=
|
|
"\n" .
|
|
packTagFormat(
|
|
"Command ${strCommand} override begin", PCK_TYPE_OBJ, $iCommandId - $iLastCommandId, undef, 12) .
|
|
$strBuildSourceCommand .
|
|
" 0x00, // Command ${strCommand} override end\n";
|
|
|
|
$iLastCommandId = $iCommandId + 1;
|
|
}
|
|
}
|
|
|
|
$iCommandId++;
|
|
}
|
|
|
|
if (defined($strBuildSourceCommands))
|
|
{
|
|
$strBuildSource .=
|
|
($bFirst ? '' : "\n") .
|
|
packTagFormat("Command overrides begin", PCK_TYPE_ARRAY, $iDelta, undef, 8) .
|
|
$strBuildSourceCommands . "\n" .
|
|
" 0x00, // Command overrides end\n";
|
|
$iDelta = 0;
|
|
|
|
$bFirst = false;
|
|
}
|
|
else
|
|
{
|
|
$iDelta++;
|
|
}
|
|
}
|
|
|
|
$strBuildSource .=
|
|
"\n" .
|
|
" 0x00, // Options end\n";
|
|
|
|
$strBuildSource .=
|
|
"\n" .
|
|
" 0x00, // Pack end\n" .
|
|
"};\n";
|
|
|
|
$rhBuild->{&BLD_FILE}{&BLDLCL_FILE_DEFINE}{&BLD_DATA}{&BLDLCL_DATA_COMMAND}{&BLD_SOURCE} = $strBuildSource;
|
|
|
|
return $rhBuild;
|
|
}
|
|
|
|
push @EXPORT, qw(buildConfigHelp);
|
|
|
|
1;
|