You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-07-30 19:23:04 +03:00
Rename BackRestDoc Perl module to pgBackRestDoc.
This is consistent with the way BackRest and BackRest test were renamed way back in 18fd2523
.
More modules will be moving to pgBackRestDoc soon so renaming now reduces churn later.
This commit is contained in:
833
doc/lib/pgBackRestDoc/Common/Doc.pm
Normal file
833
doc/lib/pgBackRestDoc/Common/Doc.pm
Normal file
@ -0,0 +1,833 @@
|
||||
####################################################################################################################################
|
||||
# DOC MODULE
|
||||
####################################################################################################################################
|
||||
package pgBackRestDoc::Common::Doc;
|
||||
|
||||
use strict;
|
||||
use warnings FATAL => qw(all);
|
||||
use Carp qw(confess);
|
||||
use English '-no_match_vars';
|
||||
|
||||
use Cwd qw(abs_path);
|
||||
use File::Basename qw(dirname);
|
||||
use Scalar::Util qw(blessed);
|
||||
use XML::Checker::Parser;
|
||||
|
||||
use pgBackRestDoc::Common::Log;
|
||||
use pgBackRestDoc::Common::String;
|
||||
|
||||
####################################################################################################################################
|
||||
# CONSTRUCTOR
|
||||
####################################################################################################################################
|
||||
sub new
|
||||
{
|
||||
my $class = shift; # Class name
|
||||
|
||||
# Create the class hash
|
||||
my $self = {};
|
||||
bless $self, $class;
|
||||
|
||||
$self->{strClass} = $class;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
(
|
||||
my $strOperation,
|
||||
$self->{strFileName},
|
||||
my $strSgmlSearchPath,
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->new', \@_,
|
||||
{name => 'strFileName', required => false},
|
||||
{name => 'strSgmlSearchPath', required => false},
|
||||
);
|
||||
|
||||
# Load the doc from a file if one has been defined
|
||||
if (defined($self->{strFileName}))
|
||||
{
|
||||
my $oParser = XML::Checker::Parser->new(ErrorContext => 2, Style => 'Tree');
|
||||
$oParser->set_sgml_search_path(
|
||||
defined($strSgmlSearchPath) ? $strSgmlSearchPath : dirname(dirname(abs_path($0))) . '/doc/xml/dtd');
|
||||
|
||||
my $oTree;
|
||||
|
||||
eval
|
||||
{
|
||||
local $XML::Checker::FAIL = sub
|
||||
{
|
||||
my $iCode = shift;
|
||||
|
||||
die XML::Checker::error_string($iCode, @_);
|
||||
};
|
||||
|
||||
$oTree = $oParser->parsefile($self->{strFileName});
|
||||
|
||||
return true;
|
||||
}
|
||||
# Report any error that stopped parsing
|
||||
or do
|
||||
{
|
||||
my $strException = $EVAL_ERROR;
|
||||
$strException =~ s/at \/.*?$//s; # remove module line number
|
||||
die "malformed xml in '$self->{strFileName}':\n" . trim($strException);
|
||||
};
|
||||
|
||||
# Parse and build the doc
|
||||
$self->{oDoc} = $self->build($self->parse(${$oTree}[0], ${$oTree}[1]));
|
||||
}
|
||||
# Else create a blank doc
|
||||
else
|
||||
{
|
||||
$self->{oDoc} = {name => 'doc', children => []};
|
||||
}
|
||||
|
||||
$self->{strName} = 'root';
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'self', value => $self}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# parse
|
||||
#
|
||||
# Parse the xml doc into a more usable hash and array structure.
|
||||
####################################################################################################################################
|
||||
sub parse
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$strName,
|
||||
$oyNode
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->parse', \@_,
|
||||
{name => 'strName', trace => true},
|
||||
{name => 'oyNode', trace => true}
|
||||
);
|
||||
|
||||
my %oOut;
|
||||
my $iIndex = 0;
|
||||
my $bText = $strName eq 'text' || $strName eq 'li' || $strName eq 'p' || $strName eq 'title' ||
|
||||
$strName eq 'summary' || $strName eq 'table-cell' || $strName eq 'table-column' || $strName eq 'list-item' ||
|
||||
$strName eq 'admonition';
|
||||
|
||||
# Store the node name
|
||||
$oOut{name} = $strName;
|
||||
|
||||
if (keys(%{$$oyNode[$iIndex]}))
|
||||
{
|
||||
$oOut{param} = $$oyNode[$iIndex];
|
||||
}
|
||||
|
||||
$iIndex++;
|
||||
|
||||
# Look for strings and children
|
||||
while (defined($$oyNode[$iIndex]))
|
||||
{
|
||||
# Process string data
|
||||
if (ref(\$$oyNode[$iIndex]) eq 'SCALAR' && $$oyNode[$iIndex] eq '0')
|
||||
{
|
||||
$iIndex++;
|
||||
my $strBuffer = $$oyNode[$iIndex++];
|
||||
|
||||
# Strip tabs, CRs, and LFs
|
||||
$strBuffer =~ s/\t|\r//g;
|
||||
|
||||
# If anything is left
|
||||
if (length($strBuffer) > 0)
|
||||
{
|
||||
# If text node then create array entries for strings
|
||||
if ($bText)
|
||||
{
|
||||
if (!defined($oOut{children}))
|
||||
{
|
||||
$oOut{children} = [];
|
||||
}
|
||||
|
||||
push(@{$oOut{children}}, $strBuffer);
|
||||
}
|
||||
# Don't allow strings mixed with children
|
||||
elsif (length(trim($strBuffer)) > 0)
|
||||
{
|
||||
if (defined($oOut{children}))
|
||||
{
|
||||
confess "text mixed with children in node ${strName} (spaces count)";
|
||||
}
|
||||
|
||||
if (defined($oOut{value}))
|
||||
{
|
||||
confess "value is already defined in node ${strName} - this shouldn't happen";
|
||||
}
|
||||
|
||||
# Don't allow text mixed with
|
||||
$oOut{value} = $strBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
# Process a child
|
||||
else
|
||||
{
|
||||
if (defined($oOut{value}) && $bText)
|
||||
{
|
||||
confess "text mixed with children in node ${strName} before child " . $$oyNode[$iIndex++] . " (spaces count)";
|
||||
}
|
||||
|
||||
if (!defined($oOut{children}))
|
||||
{
|
||||
$oOut{children} = [];
|
||||
}
|
||||
|
||||
push(@{$oOut{children}}, $self->parse($$oyNode[$iIndex++], $$oyNode[$iIndex++]));
|
||||
}
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'oDoc', value => \%oOut, trace => true}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# build
|
||||
#
|
||||
# Restructure the doc to make walking it easier.
|
||||
####################################################################################################################################
|
||||
sub build
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$oDoc
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->build', \@_,
|
||||
{name => 'oDoc', trace => true}
|
||||
);
|
||||
|
||||
# Initialize the node object
|
||||
my $oOut = {name => $$oDoc{name}, children => [], value => $$oDoc{value}};
|
||||
my $strError = "in node $$oDoc{name}";
|
||||
|
||||
# Get all params
|
||||
if (defined($$oDoc{param}))
|
||||
{
|
||||
for my $strParam (keys %{$$oDoc{param}})
|
||||
{
|
||||
$$oOut{param}{$strParam} = $$oDoc{param}{$strParam};
|
||||
}
|
||||
}
|
||||
|
||||
if ($$oDoc{name} eq 'p' || $$oDoc{name} eq 'title' || $$oDoc{name} eq 'summary' ||
|
||||
$$oDoc{name} eq 'table-cell' || $$oDoc{name} eq 'table-column' || $$oDoc{name} eq 'list-item' ||
|
||||
$$oDoc{name} eq 'admonition')
|
||||
{
|
||||
$$oOut{field}{text} = $oDoc;
|
||||
}
|
||||
elsif (defined($$oDoc{children}))
|
||||
{
|
||||
for (my $iIndex = 0; $iIndex < @{$$oDoc{children}}; $iIndex++)
|
||||
{
|
||||
my $oSub = $$oDoc{children}[$iIndex];
|
||||
my $strName = $$oSub{name};
|
||||
|
||||
if ($strName eq 'text')
|
||||
{
|
||||
$$oOut{field}{text} = $oSub;
|
||||
}
|
||||
elsif ((defined($$oSub{value}) && !defined($$oSub{param})) && $strName ne 'code-block')
|
||||
{
|
||||
$$oOut{field}{$strName} = $$oSub{value};
|
||||
}
|
||||
elsif (!defined($$oSub{children}) && !defined($$oSub{value}) && !defined($$oSub{param}))
|
||||
{
|
||||
$$oOut{field}{$strName} = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
push(@{$$oOut{children}}, $self->build($oSub));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'oDoc', value => $oOut, trace => true}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# nodeGetById
|
||||
#
|
||||
# Return a node by name - error if more than one exists
|
||||
####################################################################################################################################
|
||||
sub nodeGetById
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$strName,
|
||||
$strId,
|
||||
$bRequired,
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . 'nodeGetById', \@_,
|
||||
{name => 'strName', trace => true},
|
||||
{name => 'strId', required => false, trace => true},
|
||||
{name => 'bRequired', default => true, trace => true}
|
||||
);
|
||||
|
||||
my $oDoc = $self->{oDoc};
|
||||
my $oNode;
|
||||
|
||||
for (my $iIndex = 0; $iIndex < @{$$oDoc{children}}; $iIndex++)
|
||||
{
|
||||
if ((defined($strName) && $$oDoc{children}[$iIndex]{name} eq $strName) &&
|
||||
(!defined($strId) || $$oDoc{children}[$iIndex]{param}{id} eq $strId))
|
||||
{
|
||||
if (!defined($oNode))
|
||||
{
|
||||
$oNode = $$oDoc{children}[$iIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
confess "found more than one child ${strName} in node $$oDoc{name}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!defined($oNode) && $bRequired)
|
||||
{
|
||||
confess "unable to find child ${strName}" . (defined($strId) ? " (${strId})" : '') . " in node $$oDoc{name}";
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'oNodeDoc', value => $self->nodeBless($oNode), trace => true}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# nodeGet
|
||||
#
|
||||
# Return a node by name - error if more than one exists
|
||||
####################################################################################################################################
|
||||
sub nodeGet
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
return $self->nodeGetById(shift, undef, shift);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# nodeTest
|
||||
#
|
||||
# Test that a node exists
|
||||
####################################################################################################################################
|
||||
sub nodeTest
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
return defined($self->nodeGetById(shift, undef, false));
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# nodeAdd
|
||||
#
|
||||
# Add a node to to the current doc's child list
|
||||
####################################################################################################################################
|
||||
sub nodeAdd
|
||||
{
|
||||
my $self = shift;
|
||||
my $strName = shift;
|
||||
my $strValue = shift;
|
||||
my $oParam = shift;
|
||||
my $oField = shift;
|
||||
|
||||
my $oDoc = $self->{oDoc};
|
||||
my $oNode = {name => $strName, value => $strValue, param => $oParam, field => $oField};
|
||||
|
||||
push(@{$$oDoc{children}}, $oNode);
|
||||
|
||||
return $self->nodeBless($oNode);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# nodeBless
|
||||
#
|
||||
# Make a new Doc object from a node.
|
||||
####################################################################################################################################
|
||||
sub nodeBless
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$oNode
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->nodeBless', \@_,
|
||||
{name => 'oNode', required => false, trace => true}
|
||||
);
|
||||
|
||||
my $oDoc;
|
||||
|
||||
if (defined($oNode))
|
||||
{
|
||||
$oDoc = {};
|
||||
bless $oDoc, $self->{strClass};
|
||||
|
||||
$oDoc->{strClass} = $self->{strClass};
|
||||
$oDoc->{strName} = $$oNode{name};
|
||||
$oDoc->{oDoc} = $oNode;
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'oDoc', value => $oDoc, trace => true}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# nodeList
|
||||
#
|
||||
# Get a list of nodes.
|
||||
####################################################################################################################################
|
||||
sub nodeList
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$strName,
|
||||
$bRequired,
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->nodeList', \@_,
|
||||
{name => 'strName', required => false, trace => true},
|
||||
{name => 'bRequired', default => true, trace => true},
|
||||
);
|
||||
|
||||
my $oDoc = $self->{oDoc};
|
||||
my @oyNode;
|
||||
|
||||
if (defined($$oDoc{children}))
|
||||
{
|
||||
for (my $iIndex = 0; $iIndex < @{$$oDoc{children}}; $iIndex++)
|
||||
{
|
||||
if (!defined($strName) || $$oDoc{children}[$iIndex]{name} eq $strName)
|
||||
{
|
||||
if (ref(\$$oDoc{children}[$iIndex]) eq "SCALAR")
|
||||
{
|
||||
push(@oyNode, $$oDoc{children}[$iIndex]);
|
||||
}
|
||||
else
|
||||
{
|
||||
push(@oyNode, $self->nodeBless($$oDoc{children}[$iIndex]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (@oyNode == 0 && $bRequired)
|
||||
{
|
||||
confess 'unable to find ' . (defined($strName) ? "children named '${strName}'" : 'any children') . " in node $$oDoc{name}";
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'oyNode', value => \@oyNode, trace => true}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# nodeRemove
|
||||
#
|
||||
# Remove a child node.
|
||||
####################################################################################################################################
|
||||
sub nodeRemove
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$oChildRemove
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->nodeRemove', \@_,
|
||||
{name => 'oChildRemove', required => false, trace => true}
|
||||
);
|
||||
|
||||
my $bRemove = false;
|
||||
my $oDoc = $self->{oDoc};
|
||||
|
||||
# Error if there are no children
|
||||
if (!defined($$oDoc{children}))
|
||||
{
|
||||
confess &log(ERROR, "node has no children");
|
||||
}
|
||||
|
||||
for (my $iIndex = 0; $iIndex < @{$$oDoc{children}}; $iIndex++)
|
||||
{
|
||||
if ($$oDoc{children}[$iIndex] == $oChildRemove->{oDoc})
|
||||
{
|
||||
splice(@{$$oDoc{children}}, $iIndex, 1);
|
||||
$bRemove = true;
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bRemove)
|
||||
{
|
||||
confess &log(ERROR, "child was not found in node, could not be removed");
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn($strOperation);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# nodeReplace
|
||||
#
|
||||
# Replace a child node with one or more child nodes.
|
||||
####################################################################################################################################
|
||||
sub nodeReplace
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$oChildRemove,
|
||||
$oyChildReplace,
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->nodeReplace', \@_,
|
||||
{name => 'oChildRemove', trace => true},
|
||||
{name => 'oChildReplace', trace => true},
|
||||
);
|
||||
|
||||
my $bReplace = false;
|
||||
my $iReplaceIdx = undef;
|
||||
my $iReplaceTotal = undef;
|
||||
my $oDoc = $self->{oDoc};
|
||||
|
||||
# Error if there are no children
|
||||
if (!defined($$oDoc{children}))
|
||||
{
|
||||
confess &log(ERROR, "node has no children");
|
||||
}
|
||||
|
||||
for (my $iIndex = 0; $iIndex < @{$$oDoc{children}}; $iIndex++)
|
||||
{
|
||||
if ($$oDoc{children}[$iIndex] == $oChildRemove->{oDoc})
|
||||
{
|
||||
splice(@{$$oDoc{children}}, $iIndex, 1);
|
||||
splice(@{$$oDoc{children}}, $iIndex, 0, @{$oyChildReplace});
|
||||
|
||||
$iReplaceIdx = $iIndex;
|
||||
$iReplaceTotal = scalar(@{$oyChildReplace});
|
||||
$bReplace = true;
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bReplace)
|
||||
{
|
||||
confess &log(ERROR, "child was not found in node, could not be replaced");
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'iReplaceIdx', value => $iReplaceIdx, trace => true},
|
||||
{name => 'iReplaceTotal', value => $iReplaceTotal, trace => true},
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# nameGet
|
||||
####################################################################################################################################
|
||||
sub nameGet
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my $strOperation = logDebugParam(__PACKAGE__ . '->nameGet');
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'strName', value => ${$self->{oDoc}}{name}, trace => true}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# valueGet
|
||||
####################################################################################################################################
|
||||
sub valueGet
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my $strOperation = logDebugParam(__PACKAGE__ . '->valueGet');
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'strValue', value => ${$self->{oDoc}}{value}, trace => true}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# valueSet
|
||||
####################################################################################################################################
|
||||
sub valueSet
|
||||
{
|
||||
my $self = shift;
|
||||
my $strValue = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my $strOperation = logDebugParam(__PACKAGE__ . '->valueSet');
|
||||
|
||||
# Set the value
|
||||
${$self->{oDoc}}{value} = $strValue;
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn($strOperation);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# paramGet
|
||||
#
|
||||
# Get a parameter from a node.
|
||||
####################################################################################################################################
|
||||
sub paramGet
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$strName,
|
||||
$bRequired,
|
||||
$strDefault,
|
||||
$strType
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->paramGet', \@_,
|
||||
{name => 'strName', trace => true},
|
||||
{name => 'bRequired', default => true, trace => true},
|
||||
{name => 'strDefault', required => false, trace => true},
|
||||
{name => 'strType', default => 'param', trace => true}
|
||||
);
|
||||
|
||||
my $strValue = ${$self->{oDoc}}{$strType}{$strName};
|
||||
|
||||
if (!defined($strValue))
|
||||
{
|
||||
if ($bRequired)
|
||||
{
|
||||
confess "${strType} '${strName}' is required in node '$self->{strName}'";
|
||||
}
|
||||
|
||||
$strValue = $strDefault;
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'strValue', value => $strValue, trace => true}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# paramTest
|
||||
#
|
||||
# Test that a parameter exists or has a certain value.
|
||||
####################################################################################################################################
|
||||
sub paramTest
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$strName,
|
||||
$strExpectedValue,
|
||||
$strType
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->paramTest', \@_,
|
||||
{name => 'strName', trace => true},
|
||||
{name => 'strExpectedValue', required => false, trace => true},
|
||||
{name => 'strType', default => 'param', trace => true}
|
||||
);
|
||||
|
||||
my $bResult = true;
|
||||
my $strValue = $self->paramGet($strName, false, undef, $strType);
|
||||
|
||||
if (!defined($strValue))
|
||||
{
|
||||
$bResult = false;
|
||||
}
|
||||
elsif (defined($strExpectedValue) && $strValue ne $strExpectedValue)
|
||||
{
|
||||
$bResult = false;
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'bResult', value => $bResult, trace => true}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# paramSet
|
||||
#
|
||||
# Set a parameter in a node.
|
||||
####################################################################################################################################
|
||||
sub paramSet
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$strName,
|
||||
$strValue,
|
||||
$strType
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->paramSet', \@_,
|
||||
{name => 'strName', trace => true},
|
||||
{name => 'strValue', required => false, trace => true},
|
||||
{name => 'strType', default => 'param', trace => true}
|
||||
);
|
||||
|
||||
${$self->{oDoc}}{$strType}{$strName} = $strValue;
|
||||
|
||||
# Return from function and log return values if any
|
||||
logDebugReturn($strOperation);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# fieldGet
|
||||
#
|
||||
# Get a field from a node.
|
||||
####################################################################################################################################
|
||||
sub fieldGet
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
return $self->paramGet(shift, shift, shift, 'field');
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# fieldTest
|
||||
#
|
||||
# Test if a field exists.
|
||||
####################################################################################################################################
|
||||
sub fieldTest
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
return $self->paramTest(shift, shift, 'field');
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# textGet
|
||||
#
|
||||
# Get a field from a node.
|
||||
####################################################################################################################################
|
||||
sub textGet
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
return $self->nodeBless($self->paramGet('text', shift, shift, 'field'));
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# textSet
|
||||
#
|
||||
# Get a field from a node.
|
||||
####################################################################################################################################
|
||||
sub textSet
|
||||
{
|
||||
my $self = shift;
|
||||
my $oText = shift;
|
||||
|
||||
if (blessed($oText) && $oText->isa('pgBackRestDoc::Common::Doc'))
|
||||
{
|
||||
$oText = $oText->{oDoc};
|
||||
}
|
||||
elsif (ref($oText) ne 'HASH')
|
||||
{
|
||||
$oText = {name => 'text', children => [$oText]};
|
||||
}
|
||||
|
||||
return $self->paramSet('text', $oText, 'field');
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# fieldSet
|
||||
#
|
||||
# Set a parameter in a node.
|
||||
####################################################################################################################################
|
||||
sub fieldSet
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
$self->paramSet(shift, shift, 'field');
|
||||
}
|
||||
|
||||
1;
|
885
doc/lib/pgBackRestDoc/Common/DocConfig.pm
Normal file
885
doc/lib/pgBackRestDoc/Common/DocConfig.pm
Normal file
@ -0,0 +1,885 @@
|
||||
####################################################################################################################################
|
||||
# DOC CONFIG MODULE
|
||||
####################################################################################################################################
|
||||
package pgBackRestDoc::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::Version;
|
||||
|
||||
use pgBackRestBuild::Config::Data;
|
||||
|
||||
use pgBackRestDoc::Common::Log;
|
||||
use pgBackRestDoc::Common::String;
|
||||
|
||||
####################################################################################################################################
|
||||
# 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())
|
||||
{
|
||||
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};
|
||||
|
||||
# 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;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# 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 pgBackRestDoc::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 pgBackRestDoc::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;
|
1144
doc/lib/pgBackRestDoc/Common/DocExecute.pm
Normal file
1144
doc/lib/pgBackRestDoc/Common/DocExecute.pm
Normal file
File diff suppressed because it is too large
Load Diff
771
doc/lib/pgBackRestDoc/Common/DocManifest.pm
Normal file
771
doc/lib/pgBackRestDoc/Common/DocManifest.pm
Normal file
@ -0,0 +1,771 @@
|
||||
####################################################################################################################################
|
||||
# DOC MANIFEST MODULE
|
||||
####################################################################################################################################
|
||||
package pgBackRestDoc::Common::DocManifest;
|
||||
|
||||
use strict;
|
||||
use warnings FATAL => qw(all);
|
||||
use Carp qw(confess);
|
||||
|
||||
use Cwd qw(abs_path);
|
||||
use Exporter qw(import);
|
||||
our @EXPORT = qw();
|
||||
use File::Basename qw(dirname);
|
||||
use JSON::PP;
|
||||
|
||||
use pgBackRestDoc::Common::Log;
|
||||
use pgBackRestDoc::Common::String;
|
||||
|
||||
####################################################################################################################################
|
||||
# File constants
|
||||
####################################################################################################################################
|
||||
use constant FILE_MANIFEST => 'manifest.xml';
|
||||
|
||||
####################################################################################################################################
|
||||
# Render constants
|
||||
####################################################################################################################################
|
||||
use constant RENDER => 'render';
|
||||
use constant RENDER_COMPACT => 'compact';
|
||||
push @EXPORT, qw(RENDER_COMPACT);
|
||||
use constant RENDER_FILE => 'file';
|
||||
use constant RENDER_MENU => 'menu';
|
||||
push @EXPORT, qw(RENDER_MENU);
|
||||
use constant RENDER_PRETTY => 'pretty';
|
||||
push @EXPORT, qw(RENDER_PRETTY);
|
||||
|
||||
use constant RENDER_TYPE => 'type';
|
||||
use constant RENDER_TYPE_HTML => 'html';
|
||||
push @EXPORT, qw(RENDER_TYPE_HTML);
|
||||
use constant RENDER_TYPE_MARKDOWN => 'markdown';
|
||||
push @EXPORT, qw(RENDER_TYPE_MARKDOWN);
|
||||
use constant RENDER_TYPE_PDF => 'pdf';
|
||||
push @EXPORT, qw(RENDER_TYPE_PDF);
|
||||
|
||||
####################################################################################################################################
|
||||
# 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->{oStorage},
|
||||
$self->{stryRequire},
|
||||
$self->{stryInclude},
|
||||
$self->{stryExclude},
|
||||
$self->{rhKeyVariableOverride},
|
||||
my $rhVariableOverride,
|
||||
$self->{strDocPath},
|
||||
$self->{bDeploy},
|
||||
$self->{bCacheOnly},
|
||||
$self->{bPre},
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->new', \@_,
|
||||
{name => 'oStorage'},
|
||||
{name => 'stryRequire'},
|
||||
{name => 'stryInclude'},
|
||||
{name => 'stryExclude'},
|
||||
{name => 'rhKeyVariableOverride', required => false},
|
||||
{name => 'rhVariableOverride', required => false},
|
||||
{name => 'strDocPath', required => false},
|
||||
{name => 'bDeploy', required => false},
|
||||
{name => 'bCacheOnly', required => false},
|
||||
{name => 'bPre', required => false, default => false},
|
||||
);
|
||||
|
||||
# Set the bin path
|
||||
$self->{strBinPath} = abs_path(dirname($0));
|
||||
|
||||
# Set the base path if it was not passed in
|
||||
if (!defined($self->{strDocPath}))
|
||||
{
|
||||
$self->{strDocPath} = $self->{strBinPath};
|
||||
}
|
||||
|
||||
# Set cache file names
|
||||
$self->{strExeCacheLocal} = $self->{strDocPath} . "/output/exe.cache";
|
||||
$self->{strExeCacheDeploy} = $self->{strDocPath} . "/resource/exe.cache";
|
||||
|
||||
# Load the manifest
|
||||
$self->{oManifestXml} = new pgBackRestDoc::Common::Doc("$self->{strDocPath}/manifest.xml");
|
||||
|
||||
# Iterate the sources
|
||||
$self->{oManifest} = {};
|
||||
|
||||
foreach my $oSource ($self->{oManifestXml}->nodeGet('source-list')->nodeList('source'))
|
||||
{
|
||||
my $oSourceHash = {};
|
||||
my $strKey = $oSource->paramGet('key');
|
||||
my $strSourceType = $oSource->paramGet('type', false);
|
||||
|
||||
logDebugMisc
|
||||
(
|
||||
$strOperation, 'load source',
|
||||
{name => 'strKey', value => $strKey},
|
||||
{name => 'strSourceType', value => $strSourceType}
|
||||
);
|
||||
|
||||
# Skip sources in exclude list
|
||||
if (grep(/^$strKey$/, @{$self->{stryExclude}}))
|
||||
{
|
||||
next;
|
||||
}
|
||||
|
||||
$$oSourceHash{doc} = new pgBackRestDoc::Common::Doc("$self->{strDocPath}/xml/${strKey}.xml");
|
||||
|
||||
# Read variables from source
|
||||
$self->variableListParse($$oSourceHash{doc}->nodeGet('variable-list', false), $rhVariableOverride);
|
||||
|
||||
${$self->{oManifest}}{source}{$strKey} = $oSourceHash;
|
||||
${$self->{oManifest}}{source}{$strKey}{strSourceType} = $strSourceType;
|
||||
}
|
||||
|
||||
# Iterate the renderers
|
||||
foreach my $oRender ($self->{oManifestXml}->nodeGet('render-list')->nodeList('render'))
|
||||
{
|
||||
my $oRenderHash = {};
|
||||
my $strType = $oRender->paramGet(RENDER_TYPE);
|
||||
|
||||
# Only one instance of each render type can be defined
|
||||
if (defined(${$self->{oManifest}}{&RENDER}{$strType}))
|
||||
{
|
||||
confess &log(ERROR, "render ${strType} has already been defined");
|
||||
}
|
||||
|
||||
# Get the file param
|
||||
$${oRenderHash}{file} = $oRender->paramGet(RENDER_FILE, false);
|
||||
$${oRenderHash}{&RENDER_COMPACT} = $oRender->paramGet(RENDER_COMPACT, false, 'n') eq 'y' ? true : false;
|
||||
$${oRenderHash}{&RENDER_PRETTY} = $oRender->paramGet(RENDER_PRETTY, false, 'n') eq 'y' ? true : false;
|
||||
$${oRenderHash}{&RENDER_MENU} = false;
|
||||
|
||||
logDebugMisc
|
||||
(
|
||||
$strOperation, ' load render',
|
||||
{name => 'strType', value => $strType},
|
||||
{name => 'strFile', value => $${oRenderHash}{file}}
|
||||
);
|
||||
|
||||
# Error if file is set and render type is not pdf
|
||||
if (defined($${oRenderHash}{file}) && $strType ne RENDER_TYPE_PDF)
|
||||
{
|
||||
confess &log(ERROR, 'only the pdf render type can have file set')
|
||||
}
|
||||
|
||||
# Iterate the render sources
|
||||
foreach my $oRenderOut ($oRender->nodeList('render-source'))
|
||||
{
|
||||
my $oRenderOutHash = {};
|
||||
my $strKey = $oRenderOut->paramGet('key');
|
||||
my $strSource = $oRenderOut->paramGet('source', false, $strKey);
|
||||
|
||||
# Skip sources in exclude list
|
||||
if (grep(/^$strSource$/, @{$self->{stryExclude}}))
|
||||
{
|
||||
next;
|
||||
}
|
||||
|
||||
# Skip sources not in include list
|
||||
if (@{$self->{stryInclude}} > 0 && !grep(/^$strSource$/, @{$self->{stryInclude}}))
|
||||
{
|
||||
next;
|
||||
}
|
||||
|
||||
# Preserve natural order
|
||||
push(@{$${oRenderHash}{stryOrder}}, $strKey);
|
||||
|
||||
$$oRenderOutHash{source} = $strSource;
|
||||
|
||||
# Get the filename
|
||||
if (defined($oRenderOut->paramGet('file', false)))
|
||||
{
|
||||
if ($strType eq RENDER_TYPE_HTML || $strType eq RENDER_TYPE_MARKDOWN)
|
||||
{
|
||||
$$oRenderOutHash{file} = $oRenderOut->paramGet('file');
|
||||
}
|
||||
else
|
||||
{
|
||||
confess &log(ERROR, "file is only valid with html or markdown render types");
|
||||
}
|
||||
}
|
||||
|
||||
# Get the menu caption
|
||||
if (defined($oRenderOut->paramGet('menu', false)) && $strType ne RENDER_TYPE_HTML)
|
||||
{
|
||||
confess &log(ERROR, "menu is only valid with html render type");
|
||||
}
|
||||
|
||||
if (defined($oRenderOut->paramGet('menu', false)))
|
||||
{
|
||||
$${oRenderHash}{&RENDER_MENU} = true;
|
||||
|
||||
if ($strType eq RENDER_TYPE_HTML)
|
||||
{
|
||||
$$oRenderOutHash{menu} = $oRenderOut->paramGet('menu', false);
|
||||
}
|
||||
else
|
||||
{
|
||||
confess &log(ERROR, 'only the html render type can have menu set');
|
||||
}
|
||||
}
|
||||
|
||||
logDebugMisc
|
||||
(
|
||||
$strOperation, ' load render source',
|
||||
{name => 'strKey', value => $strKey},
|
||||
{name => 'strSource', value => $strSource},
|
||||
{name => 'strMenu', value => $${oRenderOutHash}{menu}}
|
||||
);
|
||||
|
||||
$${oRenderHash}{out}{$strKey} = $oRenderOutHash;
|
||||
}
|
||||
|
||||
${$self->{oManifest}}{render}{$strType} = $oRenderHash;
|
||||
}
|
||||
|
||||
# Set the doc path variable
|
||||
$self->variableSet('doc-path', $self->{strDocPath});
|
||||
|
||||
# Read variables from manifest
|
||||
$self->variableListParse($self->{oManifestXml}->nodeGet('variable-list', false), $rhVariableOverride);
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'self', value => $self}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# isBackRest
|
||||
#
|
||||
# Until all the backrest specific code can be abstracted, this function will identify when BackRest docs are being built.
|
||||
####################################################################################################################################
|
||||
sub isBackRest
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
return($self->variableTest('project-exe', 'pgbackrest'));
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# Evaluate the if condition for a node
|
||||
####################################################################################################################################
|
||||
sub evaluateIf
|
||||
{
|
||||
my $self = shift;
|
||||
my $oNode = shift;
|
||||
|
||||
my $bIf = true;
|
||||
|
||||
# Evaluate if condition
|
||||
if (defined($oNode->paramGet('if', false)))
|
||||
{
|
||||
my $strIf = $self->variableReplace($oNode->paramGet('if'));
|
||||
|
||||
# In this case we really do want to evaluate the contents and not treat it as a literal
|
||||
$bIf = eval($strIf);
|
||||
|
||||
# Error if the eval failed
|
||||
if ($@)
|
||||
{
|
||||
confess &log(ERROR, "unable to evaluate '${strIf}': $@");
|
||||
}
|
||||
}
|
||||
|
||||
return $bIf;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# variableListParse
|
||||
#
|
||||
# Parse a variable list and store variables.
|
||||
####################################################################################################################################
|
||||
sub variableListParse
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$oVariableList,
|
||||
$rhVariableOverride
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->variableListParse', \@_,
|
||||
{name => '$oVariableList', required => false},
|
||||
{name => '$rhVariableOverride', required => false}
|
||||
);
|
||||
|
||||
if (defined($oVariableList))
|
||||
{
|
||||
foreach my $oVariable ($oVariableList->nodeList('variable'))
|
||||
{
|
||||
if ($self->evaluateIf($oVariable))
|
||||
{
|
||||
my $strKey = $oVariable->paramGet('key');
|
||||
my $strValue = $self->variableReplace($oVariable->valueGet());
|
||||
|
||||
if ($oVariable->paramTest('eval', 'y'))
|
||||
{
|
||||
# In this case we really do want to evaluate the contents of strValue and not treat it as a literal.
|
||||
$strValue = eval($strValue);
|
||||
|
||||
if ($@)
|
||||
{
|
||||
confess &log(ERROR, "unable to evaluate ${strKey}: $@\n" . $oVariable->valueGet());
|
||||
}
|
||||
}
|
||||
|
||||
$self->variableSet($strKey, defined($rhVariableOverride->{$strKey}) ? $rhVariableOverride->{$strKey} : $strValue);
|
||||
|
||||
logDebugMisc
|
||||
(
|
||||
$strOperation, ' load variable',
|
||||
{name => 'strKey', value => $strKey},
|
||||
{name => 'strValue', value => $strValue}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn($strOperation);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# variableReplace
|
||||
#
|
||||
# Replace variables in the string.
|
||||
####################################################################################################################################
|
||||
sub variableReplace
|
||||
{
|
||||
my $self = shift;
|
||||
my $strBuffer = shift;
|
||||
my $strType = shift;
|
||||
|
||||
if (!defined($strBuffer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach my $strName (sort(keys(%{$self->{oVariable}})))
|
||||
{
|
||||
my $strValue = $self->{oVariable}{$strName};
|
||||
|
||||
$strBuffer =~ s/\{\[$strName\]\}/$strValue/g;
|
||||
}
|
||||
|
||||
if (defined($strType) && $strType eq 'latex')
|
||||
{
|
||||
$strBuffer =~ s/\\\_/\_/g;
|
||||
$strBuffer =~ s/\_/\\\_/g;
|
||||
$strBuffer =~ s/\\\#/\#/g;
|
||||
$strBuffer =~ s/\#/\\\#/g;
|
||||
}
|
||||
|
||||
return $strBuffer;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# variableSet
|
||||
#
|
||||
# Set a variable to be replaced later.
|
||||
####################################################################################################################################
|
||||
sub variableSet
|
||||
{
|
||||
my $self = shift;
|
||||
my $strKey = shift;
|
||||
my $strValue = shift;
|
||||
my $bForce = shift;
|
||||
|
||||
if (defined(${$self->{oVariable}}{$strKey}) && (!defined($bForce) || !$bForce))
|
||||
{
|
||||
confess &log(ERROR, "${strKey} variable is already defined");
|
||||
}
|
||||
|
||||
${$self->{oVariable}}{$strKey} = $self->variableReplace($strValue);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# variableGet
|
||||
#
|
||||
# Get the current value of a variable.
|
||||
####################################################################################################################################
|
||||
sub variableGet
|
||||
{
|
||||
my $self = shift;
|
||||
my $strKey = shift;
|
||||
|
||||
return ${$self->{oVariable}}{$strKey};
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# variableTest
|
||||
#
|
||||
# Test that a variable is defined or has an expected value.
|
||||
####################################################################################################################################
|
||||
sub variableTest
|
||||
{
|
||||
my $self = shift;
|
||||
my $strKey = shift;
|
||||
my $strExpectedValue = shift;
|
||||
|
||||
# Get the variable
|
||||
my $strValue = ${$self->{oVariable}}{$strKey};
|
||||
|
||||
# Return false if it is not defined
|
||||
if (!defined($strValue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
# Return false if it does not equal the expected value
|
||||
if (defined($strExpectedValue) && $strValue ne $strExpectedValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# Get list of source documents
|
||||
####################################################################################################################################
|
||||
sub sourceList
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my ($strOperation) = logDebugParam(__PACKAGE__ . '->sourceList');
|
||||
|
||||
# Check that sources exist
|
||||
my @strySource;
|
||||
|
||||
if (defined(${$self->{oManifest}}{source}))
|
||||
{
|
||||
@strySource = sort(keys(%{${$self->{oManifest}}{source}}));
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'strySource', value => \@strySource}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# sourceGet
|
||||
####################################################################################################################################
|
||||
sub sourceGet
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$strSource
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->sourceGet', \@_,
|
||||
{name => 'strSource', trace => true}
|
||||
);
|
||||
|
||||
if (!defined(${$self->{oManifest}}{source}{$strSource}))
|
||||
{
|
||||
confess &log(ERROR, "source ${strSource} does not exist");
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'oSource', value => ${$self->{oManifest}}{source}{$strSource}}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# renderList
|
||||
####################################################################################################################################
|
||||
sub renderList
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my ($strOperation) = logDebugParam(__PACKAGE__ . '->renderList');
|
||||
|
||||
# Check that the render output exists
|
||||
my @stryRender;
|
||||
|
||||
if (defined(${$self->{oManifest}}{render}))
|
||||
{
|
||||
@stryRender = sort(keys(%{${$self->{oManifest}}{render}}));
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'stryRender', value => \@stryRender}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# renderGet
|
||||
####################################################################################################################################
|
||||
sub renderGet
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$strType
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->renderGet', \@_,
|
||||
{name => 'strType', trace => true}
|
||||
);
|
||||
|
||||
# Check that the render exists
|
||||
if (!defined(${$self->{oManifest}}{render}{$strType}))
|
||||
{
|
||||
confess &log(ERROR, "render type ${strType} does not exist");
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'oRenderOut', value => ${$self->{oManifest}}{render}{$strType}}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# renderOutList
|
||||
####################################################################################################################################
|
||||
sub renderOutList
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$strType
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->renderOutList', \@_,
|
||||
{name => 'strType'}
|
||||
);
|
||||
|
||||
# Check that the render output exists
|
||||
my @stryRenderOut;
|
||||
|
||||
if (defined(${$self->{oManifest}}{render}{$strType}))
|
||||
{
|
||||
@stryRenderOut = sort(keys(%{${$self->{oManifest}}{render}{$strType}{out}}));
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'stryRenderOut', value => \@stryRenderOut}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# renderOutGet
|
||||
####################################################################################################################################
|
||||
sub renderOutGet
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$strType,
|
||||
$strKey,
|
||||
$bIgnoreMissing,
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->renderOutGet', \@_,
|
||||
{name => 'strType', trace => true},
|
||||
{name => 'strKey', trace => true},
|
||||
{name => 'bIgnoreMissing', default => false, trace => true},
|
||||
);
|
||||
|
||||
if (!defined(${$self->{oManifest}}{render}{$strType}{out}{$strKey}) && !$bIgnoreMissing)
|
||||
{
|
||||
confess &log(ERROR, "render out ${strKey} does not exist");
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'oRenderOut', value => ${$self->{oManifest}}{render}{$strType}{out}{$strKey}}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# cacheKey
|
||||
####################################################################################################################################
|
||||
sub cacheKey
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my ($strOperation) = logDebugParam(__PACKAGE__ . '->cacheKey');
|
||||
|
||||
# Generate a cache key from the variable override
|
||||
my $strVariableKey = JSON::PP->new()->canonical()->allow_nonref()->encode($self->{rhKeyVariableOverride});
|
||||
|
||||
if ($strVariableKey eq '{}')
|
||||
{
|
||||
$strVariableKey = 'default';
|
||||
}
|
||||
|
||||
my $strRequire = defined($self->{stryRequire}) && @{$self->{stryRequire}} > 0 ?
|
||||
join("\n", @{$self->{stryRequire}}) : 'all';
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'strVariableKey', value => $strVariableKey},
|
||||
{name => 'strRequire', value => $strRequire},
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# cacheRead
|
||||
####################################################################################################################################
|
||||
sub cacheRead
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my ($strOperation) = logDebugParam(__PACKAGE__ . '->cacheRead');
|
||||
|
||||
$self->{hCache} = undef;
|
||||
|
||||
my $strCacheFile = $self->{bDeploy} ? $self->{strExeCacheDeploy} : $self->{strExeCacheLocal};
|
||||
|
||||
if (!$self->storage()->exists($strCacheFile) && !$self->{bDeploy})
|
||||
{
|
||||
$strCacheFile = $self->{strExeCacheDeploy};
|
||||
}
|
||||
|
||||
if ($self->storage()->exists($strCacheFile))
|
||||
{
|
||||
my ($strCacheKey, $strRequire) = $self->cacheKey();
|
||||
my $oJSON = JSON::PP->new()->allow_nonref();
|
||||
$self->{hCache} = $oJSON->decode(${$self->storage()->get($strCacheFile)});
|
||||
|
||||
foreach my $strSource (sort(keys(%{${$self->{oManifest}}{source}})))
|
||||
{
|
||||
my $hSource = ${$self->{oManifest}}{source}{$strSource};
|
||||
|
||||
if (defined(${$self->{hCache}}{$strCacheKey}{$strRequire}{$strSource}))
|
||||
{
|
||||
$$hSource{hyCache} = ${$self->{hCache}}{$strCacheKey}{$strRequire}{$strSource};
|
||||
&log(DETAIL, "cache load $strSource (key = ${strCacheKey}, require = ${strRequire})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn($strOperation);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# cacheWrite
|
||||
####################################################################################################################################
|
||||
sub cacheWrite
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my ($strOperation) = logDebugParam(__PACKAGE__ . '->cacheWrite');
|
||||
|
||||
my $strCacheFile = $self->{bDeploy} ? $self->{strExeCacheDeploy} : $self->{strExeCacheLocal};
|
||||
my ($strCacheKey, $strRequire) = $self->cacheKey();
|
||||
|
||||
foreach my $strSource (sort(keys(%{${$self->{oManifest}}{source}})))
|
||||
{
|
||||
my $hSource = ${$self->{oManifest}}{source}{$strSource};
|
||||
|
||||
if (defined($$hSource{hyCache}))
|
||||
{
|
||||
${$self->{hCache}}{$strCacheKey}{$strRequire}{$strSource} = $$hSource{hyCache};
|
||||
&log(DETAIL, "cache load $strSource (key = ${strCacheKey}, require = ${strRequire})");
|
||||
}
|
||||
}
|
||||
|
||||
if (defined($self->{hCache}))
|
||||
{
|
||||
my $oJSON = JSON::PP->new()->canonical()->allow_nonref()->pretty();
|
||||
$self->storage()->put($strCacheFile, $oJSON->encode($self->{hCache}));
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn($strOperation);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# cacheReset
|
||||
####################################################################################################################################
|
||||
sub cacheReset
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$strSource
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->cacheReset', \@_,
|
||||
{name => 'strSource', trace => true}
|
||||
);
|
||||
|
||||
if ($self->{bCacheOnly})
|
||||
{
|
||||
confess &log(ERROR, 'Cache reset disabled by --cache-only option');
|
||||
}
|
||||
|
||||
&log(WARN, "Cache will be reset for source ${strSource} and rendering retried automatically");
|
||||
delete(${$self->{oManifest}}{source}{$strSource}{hyCache});
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn($strOperation);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# Getters
|
||||
####################################################################################################################################
|
||||
sub storage {shift->{oStorage}};
|
||||
|
||||
1;
|
1054
doc/lib/pgBackRestDoc/Common/DocRender.pm
Normal file
1054
doc/lib/pgBackRestDoc/Common/DocRender.pm
Normal file
File diff suppressed because it is too large
Load Diff
258
doc/lib/pgBackRestDoc/Common/Exception.pm
Normal file
258
doc/lib/pgBackRestDoc/Common/Exception.pm
Normal file
@ -0,0 +1,258 @@
|
||||
####################################################################################################################################
|
||||
# COMMON EXCEPTION MODULE
|
||||
####################################################################################################################################
|
||||
package pgBackRestDoc::Common::Exception;
|
||||
|
||||
use strict;
|
||||
use warnings FATAL => qw(all);
|
||||
use Carp qw(confess longmess);
|
||||
|
||||
use Scalar::Util qw(blessed);
|
||||
|
||||
use Exporter qw(import);
|
||||
our @EXPORT = qw();
|
||||
|
||||
####################################################################################################################################
|
||||
# Error Definitions
|
||||
####################################################################################################################################
|
||||
use constant ERROR_MINIMUM => 25;
|
||||
push @EXPORT, qw(ERROR_MINIMUM);
|
||||
use constant ERROR_MAXIMUM => 125;
|
||||
push @EXPORT, qw(ERROR_MAXIMUM);
|
||||
|
||||
use constant ERROR_ASSERT => 25;
|
||||
push @EXPORT, qw(ERROR_ASSERT);
|
||||
use constant ERROR_CHECKSUM => 26;
|
||||
push @EXPORT, qw(ERROR_CHECKSUM);
|
||||
use constant ERROR_CONFIG => 27;
|
||||
push @EXPORT, qw(ERROR_CONFIG);
|
||||
use constant ERROR_FILE_INVALID => 28;
|
||||
push @EXPORT, qw(ERROR_FILE_INVALID);
|
||||
use constant ERROR_FORMAT => 29;
|
||||
push @EXPORT, qw(ERROR_FORMAT);
|
||||
use constant ERROR_OPTION_INVALID_VALUE => 32;
|
||||
push @EXPORT, qw(ERROR_OPTION_INVALID_VALUE);
|
||||
use constant ERROR_POSTMASTER_RUNNING => 38;
|
||||
push @EXPORT, qw(ERROR_POSTMASTER_RUNNING);
|
||||
use constant ERROR_PATH_NOT_EMPTY => 40;
|
||||
push @EXPORT, qw(ERROR_PATH_NOT_EMPTY);
|
||||
use constant ERROR_FILE_OPEN => 41;
|
||||
push @EXPORT, qw(ERROR_FILE_OPEN);
|
||||
use constant ERROR_FILE_READ => 42;
|
||||
push @EXPORT, qw(ERROR_FILE_READ);
|
||||
use constant ERROR_ARCHIVE_MISMATCH => 44;
|
||||
push @EXPORT, qw(ERROR_ARCHIVE_MISMATCH);
|
||||
use constant ERROR_ARCHIVE_DUPLICATE => 45;
|
||||
push @EXPORT, qw(ERROR_ARCHIVE_DUPLICATE);
|
||||
use constant ERROR_PATH_CREATE => 47;
|
||||
push @EXPORT, qw(ERROR_PATH_CREATE);
|
||||
use constant ERROR_LOCK_ACQUIRE => 50;
|
||||
push @EXPORT, qw(ERROR_LOCK_ACQUIRE);
|
||||
use constant ERROR_BACKUP_MISMATCH => 51;
|
||||
push @EXPORT, qw(ERROR_BACKUP_MISMATCH);
|
||||
use constant ERROR_PATH_OPEN => 53;
|
||||
push @EXPORT, qw(ERROR_PATH_OPEN);
|
||||
use constant ERROR_PATH_SYNC => 54;
|
||||
push @EXPORT, qw(ERROR_PATH_SYNC);
|
||||
use constant ERROR_FILE_MISSING => 55;
|
||||
push @EXPORT, qw(ERROR_FILE_MISSING);
|
||||
use constant ERROR_DB_CONNECT => 56;
|
||||
push @EXPORT, qw(ERROR_DB_CONNECT);
|
||||
use constant ERROR_DB_QUERY => 57;
|
||||
push @EXPORT, qw(ERROR_DB_QUERY);
|
||||
use constant ERROR_DB_MISMATCH => 58;
|
||||
push @EXPORT, qw(ERROR_DB_MISMATCH);
|
||||
use constant ERROR_PATH_REMOVE => 61;
|
||||
push @EXPORT, qw(ERROR_PATH_REMOVE);
|
||||
use constant ERROR_STOP => 62;
|
||||
push @EXPORT, qw(ERROR_STOP);
|
||||
use constant ERROR_FILE_WRITE => 64;
|
||||
push @EXPORT, qw(ERROR_FILE_WRITE);
|
||||
use constant ERROR_FEATURE_NOT_SUPPORTED => 67;
|
||||
push @EXPORT, qw(ERROR_FEATURE_NOT_SUPPORTED);
|
||||
use constant ERROR_ARCHIVE_COMMAND_INVALID => 68;
|
||||
push @EXPORT, qw(ERROR_ARCHIVE_COMMAND_INVALID);
|
||||
use constant ERROR_LINK_EXPECTED => 69;
|
||||
push @EXPORT, qw(ERROR_LINK_EXPECTED);
|
||||
use constant ERROR_LINK_DESTINATION => 70;
|
||||
push @EXPORT, qw(ERROR_LINK_DESTINATION);
|
||||
use constant ERROR_PATH_MISSING => 73;
|
||||
push @EXPORT, qw(ERROR_PATH_MISSING);
|
||||
use constant ERROR_FILE_MOVE => 74;
|
||||
push @EXPORT, qw(ERROR_FILE_MOVE);
|
||||
use constant ERROR_PATH_TYPE => 77;
|
||||
push @EXPORT, qw(ERROR_PATH_TYPE);
|
||||
use constant ERROR_DB_MISSING => 80;
|
||||
push @EXPORT, qw(ERROR_DB_MISSING);
|
||||
use constant ERROR_DB_INVALID => 81;
|
||||
push @EXPORT, qw(ERROR_DB_INVALID);
|
||||
use constant ERROR_ARCHIVE_TIMEOUT => 82;
|
||||
push @EXPORT, qw(ERROR_ARCHIVE_TIMEOUT);
|
||||
use constant ERROR_ARCHIVE_DISABLED => 87;
|
||||
push @EXPORT, qw(ERROR_ARCHIVE_DISABLED);
|
||||
use constant ERROR_FILE_OWNER => 88;
|
||||
push @EXPORT, qw(ERROR_FILE_OWNER);
|
||||
use constant ERROR_PATH_EXISTS => 92;
|
||||
push @EXPORT, qw(ERROR_PATH_EXISTS);
|
||||
use constant ERROR_FILE_EXISTS => 93;
|
||||
push @EXPORT, qw(ERROR_FILE_EXISTS);
|
||||
use constant ERROR_CRYPTO => 95;
|
||||
push @EXPORT, qw(ERROR_CRYPTO);
|
||||
use constant ERROR_INVALID => 123;
|
||||
push @EXPORT, qw(ERROR_INVALID);
|
||||
use constant ERROR_UNHANDLED => 124;
|
||||
push @EXPORT, qw(ERROR_UNHANDLED);
|
||||
use constant ERROR_UNKNOWN => 125;
|
||||
push @EXPORT, qw(ERROR_UNKNOWN);
|
||||
|
||||
####################################################################################################################################
|
||||
# CONSTRUCTOR
|
||||
####################################################################################################################################
|
||||
sub new
|
||||
{
|
||||
my $class = shift; # Class name
|
||||
my $strLevel = shift; # Log level
|
||||
my $iCode = shift; # Error code
|
||||
my $strMessage = shift; # ErrorMessage
|
||||
my $strTrace = shift; # Stack trace
|
||||
my $rExtra = shift; # Extra info used exclusively by the logging system
|
||||
my $bErrorC = shift; # Is this a C error?
|
||||
|
||||
if ($iCode < ERROR_MINIMUM || $iCode > ERROR_MAXIMUM)
|
||||
{
|
||||
$iCode = ERROR_INVALID;
|
||||
}
|
||||
|
||||
# Create the class hash
|
||||
my $self = {};
|
||||
bless $self, $class;
|
||||
|
||||
# Initialize exception
|
||||
$self->{strLevel} = $strLevel;
|
||||
$self->{iCode} = $iCode;
|
||||
$self->{strMessage} = $strMessage;
|
||||
$self->{strTrace} = $strTrace;
|
||||
$self->{rExtra} = $rExtra;
|
||||
$self->{bErrorC} = $bErrorC ? 1 : 0;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# level
|
||||
####################################################################################################################################
|
||||
sub level
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
return $self->{strLevel};
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# CODE
|
||||
####################################################################################################################################
|
||||
sub code
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
return $self->{iCode};
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# extra
|
||||
####################################################################################################################################
|
||||
sub extra
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
return $self->{rExtra};
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# MESSAGE
|
||||
####################################################################################################################################
|
||||
sub message
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
return $self->{strMessage};
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# TRACE
|
||||
####################################################################################################################################
|
||||
sub trace
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
return $self->{strTrace};
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# isException - is this a structured exception or a default Perl exception?
|
||||
####################################################################################################################################
|
||||
sub isException
|
||||
{
|
||||
my $roException = shift;
|
||||
|
||||
# Only check if defined
|
||||
if (defined($roException) && defined($$roException))
|
||||
{
|
||||
# If a standard Exception
|
||||
if (blessed($$roException))
|
||||
{
|
||||
return $$roException->isa('pgBackRestDoc::Common::Exception') ? 1 : 0;
|
||||
}
|
||||
# Else if a specially formatted string from the C library
|
||||
elsif ($$roException =~ /^PGBRCLIB\:[0-9]+\:/)
|
||||
{
|
||||
# Split message and discard the first part used for identification
|
||||
my @stryException = split(/\:/, $$roException);
|
||||
shift(@stryException);
|
||||
|
||||
# Construct exception fields
|
||||
my $iCode = shift(@stryException) + 0;
|
||||
my $strTrace = shift(@stryException) . qw{:} . shift(@stryException);
|
||||
my $strMessage = join(':', @stryException);
|
||||
|
||||
# Create exception
|
||||
$$roException = new pgBackRestDoc::Common::Exception("ERROR", $iCode, $strMessage, $strTrace, undef, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
push @EXPORT, qw(isException);
|
||||
|
||||
####################################################################################################################################
|
||||
# exceptionCode
|
||||
#
|
||||
# Extract the error code from an exception - if a Perl exception return ERROR_UNKNOWN.
|
||||
####################################################################################################################################
|
||||
sub exceptionCode
|
||||
{
|
||||
my $oException = shift;
|
||||
|
||||
return isException(\$oException) ? $oException->code() : ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
push @EXPORT, qw(exceptionCode);
|
||||
|
||||
####################################################################################################################################
|
||||
# exceptionMessage
|
||||
#
|
||||
# Extract the error message from an exception - if a Perl exception return bare exception.
|
||||
####################################################################################################################################
|
||||
sub exceptionMessage
|
||||
{
|
||||
my $oException = shift;
|
||||
|
||||
return isException(\$oException) ? $oException->message() : $oException;
|
||||
}
|
||||
|
||||
push @EXPORT, qw(exceptionMessage);
|
||||
|
||||
1;
|
868
doc/lib/pgBackRestDoc/Common/Ini.pm
Normal file
868
doc/lib/pgBackRestDoc/Common/Ini.pm
Normal file
@ -0,0 +1,868 @@
|
||||
####################################################################################################################################
|
||||
# COMMON INI MODULE
|
||||
####################################################################################################################################
|
||||
package pgBackRestDoc::Common::Ini;
|
||||
|
||||
use strict;
|
||||
use warnings FATAL => qw(all);
|
||||
use Carp qw(confess);
|
||||
use English '-no_match_vars';
|
||||
|
||||
use Digest::SHA qw(sha1_hex);
|
||||
use Exporter qw(import);
|
||||
our @EXPORT = qw();
|
||||
use File::Basename qw(dirname);
|
||||
use JSON::PP;
|
||||
use Storable qw(dclone);
|
||||
|
||||
use pgBackRest::Version;
|
||||
|
||||
use pgBackRestDoc::Common::Exception;
|
||||
use pgBackRestDoc::Common::Log;
|
||||
use pgBackRestDoc::Common::String;
|
||||
|
||||
####################################################################################################################################
|
||||
# Boolean constants
|
||||
####################################################################################################################################
|
||||
use constant INI_TRUE => JSON::PP::true;
|
||||
push @EXPORT, qw(INI_TRUE);
|
||||
use constant INI_FALSE => JSON::PP::false;
|
||||
push @EXPORT, qw(INI_FALSE);
|
||||
|
||||
####################################################################################################################################
|
||||
# Ini control constants
|
||||
####################################################################################################################################
|
||||
use constant INI_SECTION_BACKREST => 'backrest';
|
||||
push @EXPORT, qw(INI_SECTION_BACKREST);
|
||||
|
||||
use constant INI_KEY_CHECKSUM => 'backrest-checksum';
|
||||
push @EXPORT, qw(INI_KEY_CHECKSUM);
|
||||
use constant INI_KEY_FORMAT => 'backrest-format';
|
||||
push @EXPORT, qw(INI_KEY_FORMAT);
|
||||
use constant INI_KEY_VERSION => 'backrest-version';
|
||||
push @EXPORT, qw(INI_KEY_VERSION);
|
||||
|
||||
use constant INI_SECTION_CIPHER => 'cipher';
|
||||
push @EXPORT, qw(INI_SECTION_CIPHER);
|
||||
|
||||
use constant INI_KEY_CIPHER_PASS => 'cipher-pass';
|
||||
push @EXPORT, qw(INI_KEY_CIPHER_PASS);
|
||||
|
||||
####################################################################################################################################
|
||||
# Ini file copy extension
|
||||
####################################################################################################################################
|
||||
use constant INI_COPY_EXT => '.copy';
|
||||
push @EXPORT, qw(INI_COPY_EXT);
|
||||
|
||||
####################################################################################################################################
|
||||
# Ini sort orders
|
||||
####################################################################################################################################
|
||||
use constant INI_SORT_FORWARD => 'forward';
|
||||
push @EXPORT, qw(INI_SORT_FORWARD);
|
||||
use constant INI_SORT_REVERSE => 'reverse';
|
||||
push @EXPORT, qw(INI_SORT_REVERSE);
|
||||
use constant INI_SORT_NONE => 'none';
|
||||
push @EXPORT, qw(INI_SORT_NONE);
|
||||
|
||||
####################################################################################################################################
|
||||
# new()
|
||||
####################################################################################################################################
|
||||
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->{oStorage},
|
||||
$self->{strFileName},
|
||||
my $bLoad,
|
||||
my $strContent,
|
||||
$self->{iInitFormat},
|
||||
$self->{strInitVersion},
|
||||
my $bIgnoreMissing,
|
||||
$self->{strCipherPass}, # Passphrase to read/write the file
|
||||
my $strCipherPassSub, # Passphrase to read/write subsequent files
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->new', \@_,
|
||||
{name => 'oStorage', trace => true},
|
||||
{name => 'strFileName', trace => true},
|
||||
{name => 'bLoad', optional => true, default => true, trace => true},
|
||||
{name => 'strContent', optional => true, trace => true},
|
||||
{name => 'iInitFormat', optional => true, default => REPOSITORY_FORMAT, trace => true},
|
||||
{name => 'strInitVersion', optional => true, default => PROJECT_VERSION, trace => true},
|
||||
{name => 'bIgnoreMissing', optional => true, default => false, trace => true},
|
||||
{name => 'strCipherPass', optional => true, trace => true},
|
||||
{name => 'strCipherPassSub', optional => true, trace => true},
|
||||
);
|
||||
|
||||
# Set changed to false
|
||||
$self->{bModified} = false;
|
||||
|
||||
# Set exists to false
|
||||
$self->{bExists} = false;
|
||||
|
||||
# Load the file if requested
|
||||
if ($bLoad)
|
||||
{
|
||||
$self->load($bIgnoreMissing);
|
||||
}
|
||||
# Load from a string if provided
|
||||
elsif (defined($strContent))
|
||||
{
|
||||
$self->{oContent} = iniParse($strContent);
|
||||
$self->headerCheck();
|
||||
}
|
||||
|
||||
# Initialize if not loading the file and not loading from string or if a load was attempted and the file does not exist
|
||||
if (!$self->{bExists} && !defined($strContent))
|
||||
{
|
||||
$self->numericSet(INI_SECTION_BACKREST, INI_KEY_FORMAT, undef, $self->{iInitFormat});
|
||||
$self->set(INI_SECTION_BACKREST, INI_KEY_VERSION, undef, $self->{strInitVersion});
|
||||
|
||||
# Determine if the passphrase section should be set
|
||||
if (defined($self->{strCipherPass}) && defined($strCipherPassSub))
|
||||
{
|
||||
$self->set(INI_SECTION_CIPHER, INI_KEY_CIPHER_PASS, undef, $strCipherPassSub);
|
||||
}
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# loadVersion() - load a version (main or copy) of the ini file
|
||||
####################################################################################################################################
|
||||
sub loadVersion
|
||||
{
|
||||
my $self = shift;
|
||||
my $bCopy = shift;
|
||||
my $bIgnoreError = shift;
|
||||
|
||||
# Load main
|
||||
my $rstrContent = $self->{oStorage}->get(
|
||||
$self->{oStorage}->openRead($self->{strFileName} . ($bCopy ? INI_COPY_EXT : ''),
|
||||
{bIgnoreMissing => $bIgnoreError, strCipherPass => $self->{strCipherPass}}));
|
||||
|
||||
# If the file exists then attempt to parse it
|
||||
if (defined($rstrContent))
|
||||
{
|
||||
my $rhContent = iniParse($$rstrContent, {bIgnoreInvalid => $bIgnoreError});
|
||||
|
||||
# If the content is valid then check the header
|
||||
if (defined($rhContent))
|
||||
{
|
||||
$self->{oContent} = $rhContent;
|
||||
|
||||
# If the header is invalid then undef content
|
||||
if (!$self->headerCheck({bIgnoreInvalid => $bIgnoreError}))
|
||||
{
|
||||
delete($self->{oContent});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return defined($self->{oContent});
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# load() - load the ini
|
||||
####################################################################################################################################
|
||||
sub load
|
||||
{
|
||||
my $self = shift;
|
||||
my $bIgnoreMissing = shift;
|
||||
|
||||
# If main was not loaded then try the copy
|
||||
if (!$self->loadVersion(false, true))
|
||||
{
|
||||
if (!$self->loadVersion(true, true))
|
||||
{
|
||||
return if $bIgnoreMissing;
|
||||
|
||||
confess &log(ERROR, "unable to open $self->{strFileName} or $self->{strFileName}" . INI_COPY_EXT, ERROR_FILE_MISSING);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{bExists} = true;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# headerCheck() - check that version and checksum in header are as expected
|
||||
####################################################################################################################################
|
||||
sub headerCheck
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$bIgnoreInvalid,
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->headerCheck', \@_,
|
||||
{name => 'bIgnoreInvalid', optional => true, default => false, trace => true},
|
||||
);
|
||||
|
||||
# Eval so exceptions can be ignored on bIgnoreInvalid
|
||||
my $bValid = true;
|
||||
|
||||
eval
|
||||
{
|
||||
|
||||
# Make sure the ini is valid by testing checksum
|
||||
my $strChecksum = $self->get(INI_SECTION_BACKREST, INI_KEY_CHECKSUM, undef, false);
|
||||
my $strTestChecksum = $self->hash();
|
||||
|
||||
if (!defined($strChecksum) || $strChecksum ne $strTestChecksum)
|
||||
{
|
||||
confess &log(ERROR,
|
||||
"invalid checksum in '$self->{strFileName}', expected '${strTestChecksum}' but found " .
|
||||
(defined($strChecksum) ? "'${strChecksum}'" : '[undef]'),
|
||||
ERROR_CHECKSUM);
|
||||
}
|
||||
|
||||
# Make sure that the format is current, otherwise error
|
||||
my $iFormat = $self->get(INI_SECTION_BACKREST, INI_KEY_FORMAT, undef, false, 0);
|
||||
|
||||
if ($iFormat != $self->{iInitFormat})
|
||||
{
|
||||
confess &log(ERROR,
|
||||
"invalid format in '$self->{strFileName}', expected $self->{iInitFormat} but found ${iFormat}", ERROR_FORMAT);
|
||||
}
|
||||
|
||||
# Check if the version has changed
|
||||
if (!$self->test(INI_SECTION_BACKREST, INI_KEY_VERSION, undef, $self->{strInitVersion}))
|
||||
{
|
||||
$self->set(INI_SECTION_BACKREST, INI_KEY_VERSION, undef, $self->{strInitVersion});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
or do
|
||||
{
|
||||
# Confess the error if it should not be ignored
|
||||
if (!$bIgnoreInvalid)
|
||||
{
|
||||
confess $EVAL_ERROR;
|
||||
}
|
||||
|
||||
# Return false when errors are ignored
|
||||
$bValid = false;
|
||||
};
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'bValid', value => $bValid, trace => true}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# iniParse() - parse from standard INI format to a hash.
|
||||
####################################################################################################################################
|
||||
push @EXPORT, qw(iniParse);
|
||||
|
||||
sub iniParse
|
||||
{
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$strContent,
|
||||
$bRelaxed,
|
||||
$bIgnoreInvalid,
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '::iniParse', \@_,
|
||||
{name => 'strContent', required => false, trace => true},
|
||||
{name => 'bRelaxed', optional => true, default => false, trace => true},
|
||||
{name => 'bIgnoreInvalid', optional => true, default => false, trace => true},
|
||||
);
|
||||
|
||||
# Ini content
|
||||
my $oContent = undef;
|
||||
my $strSection;
|
||||
|
||||
# Create the JSON object
|
||||
my $oJSON = JSON::PP->new()->allow_nonref();
|
||||
|
||||
# Eval so exceptions can be ignored on bIgnoreInvalid
|
||||
eval
|
||||
{
|
||||
# Read the INI file
|
||||
foreach my $strLine (split("\n", defined($strContent) ? $strContent : ''))
|
||||
{
|
||||
$strLine = trim($strLine);
|
||||
|
||||
# Skip lines that are blank or comments
|
||||
if ($strLine ne '' && $strLine !~ '^[ ]*#.*')
|
||||
{
|
||||
# Get the section
|
||||
if (index($strLine, '[') == 0)
|
||||
{
|
||||
$strSection = substr($strLine, 1, length($strLine) - 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!defined($strSection))
|
||||
{
|
||||
confess &log(ERROR, "key/value pair '${strLine}' found outside of a section", ERROR_CONFIG);
|
||||
}
|
||||
|
||||
# Get key and value
|
||||
my $iIndex = index($strLine, '=');
|
||||
|
||||
if ($iIndex == -1)
|
||||
{
|
||||
confess &log(ERROR, "unable to find '=' in '${strLine}'", ERROR_CONFIG);
|
||||
}
|
||||
|
||||
my $strKey = substr($strLine, 0, $iIndex);
|
||||
my $strValue = substr($strLine, $iIndex + 1);
|
||||
|
||||
# If relaxed then read the value directly
|
||||
if ($bRelaxed)
|
||||
{
|
||||
if (defined($oContent->{$strSection}{$strKey}))
|
||||
{
|
||||
if (ref($oContent->{$strSection}{$strKey}) ne 'ARRAY')
|
||||
{
|
||||
$oContent->{$strSection}{$strKey} = [$oContent->{$strSection}{$strKey}];
|
||||
}
|
||||
|
||||
push(@{$oContent->{$strSection}{$strKey}}, $strValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oContent->{$strSection}{$strKey} = $strValue;
|
||||
}
|
||||
}
|
||||
# Else read the value as stricter JSON
|
||||
else
|
||||
{
|
||||
${$oContent}{$strSection}{$strKey} = $oJSON->decode($strValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Error if the file is empty
|
||||
if (!($bRelaxed || defined($oContent)))
|
||||
{
|
||||
confess &log(ERROR, 'no key/value pairs found', ERROR_CONFIG);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
or do
|
||||
{
|
||||
# Confess the error if it should not be ignored
|
||||
if (!$bIgnoreInvalid)
|
||||
{
|
||||
confess $EVAL_ERROR;
|
||||
}
|
||||
|
||||
# Undef content when errors are ignored
|
||||
undef($oContent);
|
||||
};
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'oContent', value => $oContent, trace => true}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# save() - save the file.
|
||||
####################################################################################################################################
|
||||
sub save
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Save only if modified
|
||||
if ($self->{bModified})
|
||||
{
|
||||
# Calculate the hash
|
||||
$self->hash();
|
||||
|
||||
# Save the file
|
||||
$self->{oStorage}->put($self->{strFileName}, iniRender($self->{oContent}), {strCipherPass => $self->{strCipherPass}});
|
||||
|
||||
if ($self->{oStorage}->can('pathSync'))
|
||||
{
|
||||
$self->{oStorage}->pathSync(dirname($self->{strFileName}));
|
||||
}
|
||||
|
||||
$self->{oStorage}->put($self->{strFileName} . INI_COPY_EXT, iniRender($self->{oContent}),
|
||||
{strCipherPass => $self->{strCipherPass}});
|
||||
|
||||
if ($self->{oStorage}->can('pathSync'))
|
||||
{
|
||||
$self->{oStorage}->pathSync(dirname($self->{strFileName}));
|
||||
}
|
||||
|
||||
$self->{bModified} = false;
|
||||
|
||||
# Indicate the file now exists
|
||||
$self->{bExists} = true;
|
||||
|
||||
# File was saved
|
||||
return true;
|
||||
}
|
||||
|
||||
# File was not saved
|
||||
return false;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# saveCopy - save only a copy of the file.
|
||||
####################################################################################################################################
|
||||
sub saveCopy
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
if ($self->{oStorage}->exists($self->{strFileName}))
|
||||
{
|
||||
confess &log(ASSERT, "cannot save copy only when '$self->{strFileName}' exists");
|
||||
}
|
||||
|
||||
$self->hash();
|
||||
$self->{oStorage}->put($self->{strFileName} . INI_COPY_EXT, iniRender($self->{oContent}),
|
||||
{strCipherPass => $self->{strCipherPass}});
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# iniRender() - render hash to standard INI format.
|
||||
####################################################################################################################################
|
||||
push @EXPORT, qw(iniRender);
|
||||
|
||||
sub iniRender
|
||||
{
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$oContent,
|
||||
$bRelaxed,
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '::iniRender', \@_,
|
||||
{name => 'oContent', trace => true},
|
||||
{name => 'bRelaxed', default => false, trace => true},
|
||||
);
|
||||
|
||||
# Open the ini file for writing
|
||||
my $strContent = '';
|
||||
my $bFirst = true;
|
||||
|
||||
# Create the JSON object canonical so that fields are alpha ordered to pass unit tests
|
||||
my $oJSON = JSON::PP->new()->canonical()->allow_nonref();
|
||||
|
||||
# Write the INI file
|
||||
foreach my $strSection (sort(keys(%$oContent)))
|
||||
{
|
||||
# Add a linefeed between sections
|
||||
if (!$bFirst)
|
||||
{
|
||||
$strContent .= "\n";
|
||||
}
|
||||
|
||||
# Write the section
|
||||
$strContent .= "[${strSection}]\n";
|
||||
|
||||
# Iterate through all keys in the section
|
||||
foreach my $strKey (sort(keys(%{$oContent->{$strSection}})))
|
||||
{
|
||||
# If the value is a hash then convert it to JSON, otherwise store as is
|
||||
my $strValue = ${$oContent}{$strSection}{$strKey};
|
||||
|
||||
# If relaxed then store as old-style config
|
||||
if ($bRelaxed)
|
||||
{
|
||||
# If the value is an array then save each element to a separate key/value pair
|
||||
if (ref($strValue) eq 'ARRAY')
|
||||
{
|
||||
foreach my $strArrayValue (@{$strValue})
|
||||
{
|
||||
$strContent .= "${strKey}=${strArrayValue}\n";
|
||||
}
|
||||
}
|
||||
# Else write a standard key/value pair
|
||||
else
|
||||
{
|
||||
$strContent .= "${strKey}=${strValue}\n";
|
||||
}
|
||||
}
|
||||
# Else write as stricter JSON
|
||||
else
|
||||
{
|
||||
# Skip the checksum for now but write all other key/value pairs
|
||||
if (!($strSection eq INI_SECTION_BACKREST && $strKey eq INI_KEY_CHECKSUM))
|
||||
{
|
||||
$strContent .= "${strKey}=" . $oJSON->encode($strValue) . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$bFirst = false;
|
||||
}
|
||||
|
||||
# If there is a checksum write it at the end of the file. Having the checksum at the end of the file allows some major
|
||||
# performance optimizations which we won't implement in Perl, but will make the C code much more efficient.
|
||||
if (!$bRelaxed && defined($oContent->{&INI_SECTION_BACKREST}) && defined($oContent->{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM}))
|
||||
{
|
||||
$strContent .=
|
||||
"\n[" . INI_SECTION_BACKREST . "]\n" .
|
||||
INI_KEY_CHECKSUM . '=' . $oJSON->encode($oContent->{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM}) . "\n";
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'strContent', value => $strContent, trace => true}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# hash() - generate hash for the manifest.
|
||||
####################################################################################################################################
|
||||
sub hash
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Remove the old checksum
|
||||
delete($self->{oContent}{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM});
|
||||
|
||||
# Set the new checksum
|
||||
$self->{oContent}{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM} =
|
||||
sha1_hex(JSON::PP->new()->canonical()->allow_nonref()->encode($self->{oContent}));
|
||||
|
||||
return $self->{oContent}{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM};
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# get() - get a value.
|
||||
####################################################################################################################################
|
||||
sub get
|
||||
{
|
||||
my $self = shift;
|
||||
my $strSection = shift;
|
||||
my $strKey = shift;
|
||||
my $strSubKey = shift;
|
||||
my $bRequired = shift;
|
||||
my $oDefault = shift;
|
||||
|
||||
# Parameter constraints
|
||||
if (!defined($strSection))
|
||||
{
|
||||
confess &log(ASSERT, 'strSection is required');
|
||||
}
|
||||
|
||||
if (defined($strSubKey) && !defined($strKey))
|
||||
{
|
||||
confess &log(ASSERT, "strKey is required when strSubKey '${strSubKey}' is requested");
|
||||
}
|
||||
|
||||
# Get the result
|
||||
my $oResult = $self->{oContent}->{$strSection};
|
||||
|
||||
if (defined($strKey) && defined($oResult))
|
||||
{
|
||||
$oResult = $oResult->{$strKey};
|
||||
|
||||
if (defined($strSubKey) && defined($oResult))
|
||||
{
|
||||
$oResult = $oResult->{$strSubKey};
|
||||
}
|
||||
}
|
||||
|
||||
# When result is not defined
|
||||
if (!defined($oResult))
|
||||
{
|
||||
# Error if a result is required
|
||||
if (!defined($bRequired) || $bRequired)
|
||||
{
|
||||
confess &log(ASSERT, "strSection '$strSection'" . (defined($strKey) ? ", strKey '$strKey'" : '') .
|
||||
(defined($strSubKey) ? ", strSubKey '$strSubKey'" : '') . ' is required but not defined');
|
||||
}
|
||||
|
||||
# Return default if specified
|
||||
if (defined($oDefault))
|
||||
{
|
||||
return $oDefault;
|
||||
}
|
||||
}
|
||||
|
||||
return $oResult
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# boolGet() - get a boolean value.
|
||||
####################################################################################################################################
|
||||
sub boolGet
|
||||
{
|
||||
my $self = shift;
|
||||
my $strSection = shift;
|
||||
my $strKey = shift;
|
||||
my $strSubKey = shift;
|
||||
my $bRequired = shift;
|
||||
my $bDefault = shift;
|
||||
|
||||
return $self->get(
|
||||
$strSection, $strKey, $strSubKey, $bRequired,
|
||||
defined($bDefault) ? ($bDefault ? INI_TRUE : INI_FALSE) : undef) ? true : false;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# numericGet() - get a numeric value.
|
||||
####################################################################################################################################
|
||||
sub numericGet
|
||||
{
|
||||
my $self = shift;
|
||||
my $strSection = shift;
|
||||
my $strKey = shift;
|
||||
my $strSubKey = shift;
|
||||
my $bRequired = shift;
|
||||
my $nDefault = shift;
|
||||
|
||||
return $self->get($strSection, $strKey, $strSubKey, $bRequired, defined($nDefault) ? $nDefault + 0 : undef) + 0;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# set - set a value.
|
||||
####################################################################################################################################
|
||||
sub set
|
||||
{
|
||||
my $self = shift;
|
||||
my $strSection = shift;
|
||||
my $strKey = shift;
|
||||
my $strSubKey = shift;
|
||||
my $oValue = shift;
|
||||
|
||||
# Parameter constraints
|
||||
if (!(defined($strSection) && defined($strKey)))
|
||||
{
|
||||
confess &log(ASSERT, 'strSection and strKey are required');
|
||||
}
|
||||
|
||||
my $oCurrentValue;
|
||||
|
||||
if (defined($strSubKey))
|
||||
{
|
||||
$oCurrentValue = \$self->{oContent}{$strSection}{$strKey}{$strSubKey};
|
||||
}
|
||||
else
|
||||
{
|
||||
$oCurrentValue = \$self->{oContent}{$strSection}{$strKey};
|
||||
}
|
||||
|
||||
if (!defined($$oCurrentValue) ||
|
||||
defined($oCurrentValue) != defined($oValue) ||
|
||||
${dclone($oCurrentValue)} ne ${dclone(\$oValue)})
|
||||
{
|
||||
$$oCurrentValue = $oValue;
|
||||
|
||||
if (!$self->{bModified})
|
||||
{
|
||||
$self->{bModified} = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# boolSet - set a boolean value.
|
||||
####################################################################################################################################
|
||||
sub boolSet
|
||||
{
|
||||
my $self = shift;
|
||||
my $strSection = shift;
|
||||
my $strKey = shift;
|
||||
my $strSubKey = shift;
|
||||
my $bValue = shift;
|
||||
|
||||
$self->set($strSection, $strKey, $strSubKey, $bValue ? INI_TRUE : INI_FALSE);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# numericSet - set a numeric value.
|
||||
####################################################################################################################################
|
||||
sub numericSet
|
||||
{
|
||||
my $self = shift;
|
||||
my $strSection = shift;
|
||||
my $strKey = shift;
|
||||
my $strSubKey = shift;
|
||||
my $nValue = shift;
|
||||
|
||||
$self->set($strSection, $strKey, $strSubKey, defined($nValue) ? $nValue + 0 : undef);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# remove - remove a value.
|
||||
####################################################################################################################################
|
||||
sub remove
|
||||
{
|
||||
my $self = shift;
|
||||
my $strSection = shift;
|
||||
my $strKey = shift;
|
||||
my $strSubKey = shift;
|
||||
|
||||
# Test if the value exists
|
||||
if ($self->test($strSection, $strKey, $strSubKey))
|
||||
{
|
||||
# Remove a subkey
|
||||
if (defined($strSubKey))
|
||||
{
|
||||
delete($self->{oContent}{$strSection}{$strKey}{$strSubKey});
|
||||
}
|
||||
|
||||
# Remove a key
|
||||
if (defined($strKey))
|
||||
{
|
||||
if (!defined($strSubKey))
|
||||
{
|
||||
delete($self->{oContent}{$strSection}{$strKey});
|
||||
}
|
||||
|
||||
# Remove the section if it is now empty
|
||||
if (keys(%{$self->{oContent}{$strSection}}) == 0)
|
||||
{
|
||||
delete($self->{oContent}{$strSection});
|
||||
}
|
||||
}
|
||||
|
||||
# Remove a section
|
||||
if (!defined($strKey))
|
||||
{
|
||||
delete($self->{oContent}{$strSection});
|
||||
}
|
||||
|
||||
# Record changes
|
||||
if (!$self->{bModified})
|
||||
{
|
||||
$self->{bModified} = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# keys - get the list of keys in a section.
|
||||
####################################################################################################################################
|
||||
sub keys
|
||||
{
|
||||
my $self = shift;
|
||||
my $strSection = shift;
|
||||
my $strSortOrder = shift;
|
||||
|
||||
if ($self->test($strSection))
|
||||
{
|
||||
if (!defined($strSortOrder) || $strSortOrder eq INI_SORT_FORWARD)
|
||||
{
|
||||
return (sort(keys(%{$self->get($strSection)})));
|
||||
}
|
||||
elsif ($strSortOrder eq INI_SORT_REVERSE)
|
||||
{
|
||||
return (sort {$b cmp $a} (keys(%{$self->get($strSection)})));
|
||||
}
|
||||
elsif ($strSortOrder eq INI_SORT_NONE)
|
||||
{
|
||||
return (keys(%{$self->get($strSection)}));
|
||||
}
|
||||
else
|
||||
{
|
||||
confess &log(ASSERT, "invalid strSortOrder '${strSortOrder}'");
|
||||
}
|
||||
}
|
||||
|
||||
my @stryEmptyArray;
|
||||
return @stryEmptyArray;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# test - test a value.
|
||||
#
|
||||
# Test a value to see if it equals the supplied test value. If no test value is given, tests that the section, key, or subkey
|
||||
# is defined.
|
||||
####################################################################################################################################
|
||||
sub test
|
||||
{
|
||||
my $self = shift;
|
||||
my $strSection = shift;
|
||||
my $strValue = shift;
|
||||
my $strSubValue = shift;
|
||||
my $strTest = shift;
|
||||
|
||||
# Get the value
|
||||
my $strResult = $self->get($strSection, $strValue, $strSubValue, false);
|
||||
|
||||
# Is there a result
|
||||
if (defined($strResult))
|
||||
{
|
||||
# Is there a value to test against?
|
||||
if (defined($strTest))
|
||||
{
|
||||
# Make sure these are explicit strings or Devel::Cover thinks they are equal if one side is a boolean
|
||||
return ($strResult . '') eq ($strTest . '') ? true : false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# boolTest - test a boolean value, see test().
|
||||
####################################################################################################################################
|
||||
sub boolTest
|
||||
{
|
||||
my $self = shift;
|
||||
my $strSection = shift;
|
||||
my $strValue = shift;
|
||||
my $strSubValue = shift;
|
||||
my $bTest = shift;
|
||||
|
||||
return $self->test($strSection, $strValue, $strSubValue, defined($bTest) ? ($bTest ? INI_TRUE : INI_FALSE) : undef);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# cipherPassSub - gets the passphrase (if it exists) used to read/write subsequent files
|
||||
####################################################################################################################################
|
||||
sub cipherPassSub
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
return $self->get(INI_SECTION_CIPHER, INI_KEY_CIPHER_PASS, undef, false);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# Properties.
|
||||
####################################################################################################################################
|
||||
sub modified {shift->{bModified}} # Has the data been modified since last load/save?
|
||||
sub exists {shift->{bExists}} # Is the data persisted to file?
|
||||
sub cipherPass {shift->{strCipherPass}} # Return passphrase (will be undef if repo not encrypted)
|
||||
|
||||
1;
|
804
doc/lib/pgBackRestDoc/Common/Log.pm
Normal file
804
doc/lib/pgBackRestDoc/Common/Log.pm
Normal file
@ -0,0 +1,804 @@
|
||||
####################################################################################################################################
|
||||
# COMMON LOG MODULE
|
||||
####################################################################################################################################
|
||||
package pgBackRestDoc::Common::Log;
|
||||
|
||||
use strict;
|
||||
use warnings FATAL => qw(all);
|
||||
use Carp qw(confess longmess);
|
||||
use English '-no_match_vars';
|
||||
|
||||
use Exporter qw(import);
|
||||
our @EXPORT = qw();
|
||||
use Fcntl qw(:DEFAULT :flock);
|
||||
use File::Basename qw(dirname);
|
||||
use Scalar::Util qw(blessed reftype);
|
||||
use Time::HiRes qw(gettimeofday usleep);
|
||||
|
||||
use pgBackRestDoc::Common::Exception;
|
||||
use pgBackRestDoc::Common::String;
|
||||
|
||||
####################################################################################################################################
|
||||
# Boolean constants
|
||||
####################################################################################################################################
|
||||
use constant true => 1;
|
||||
push @EXPORT, qw(true);
|
||||
use constant false => 0;
|
||||
push @EXPORT, qw(false);
|
||||
|
||||
####################################################################################################################################
|
||||
# Log level constants
|
||||
####################################################################################################################################
|
||||
use constant TRACE => 'TRACE';
|
||||
push @EXPORT, qw(TRACE);
|
||||
use constant DEBUG => 'DEBUG';
|
||||
push @EXPORT, qw(DEBUG);
|
||||
use constant DETAIL => 'DETAIL';
|
||||
push @EXPORT, qw(DETAIL);
|
||||
use constant INFO => 'INFO';
|
||||
push @EXPORT, qw(INFO);
|
||||
use constant WARN => 'WARN';
|
||||
push @EXPORT, qw(WARN);
|
||||
use constant PROTOCOL => 'PROTOCOL';
|
||||
push @EXPORT, qw(PROTOCOL);
|
||||
use constant ERROR => 'ERROR';
|
||||
push @EXPORT, qw(ERROR);
|
||||
use constant ASSERT => 'ASSERT';
|
||||
push @EXPORT, qw(ASSERT);
|
||||
use constant OFF => 'OFF';
|
||||
push @EXPORT, qw(OFF);
|
||||
|
||||
####################################################################################################################################
|
||||
# Log levels ranked by severity
|
||||
####################################################################################################################################
|
||||
my %oLogLevelRank;
|
||||
|
||||
$oLogLevelRank{TRACE}{rank} = 8;
|
||||
$oLogLevelRank{DEBUG}{rank} = 7;
|
||||
$oLogLevelRank{DETAIL}{rank} = 6;
|
||||
$oLogLevelRank{INFO}{rank} = 5;
|
||||
$oLogLevelRank{WARN}{rank} = 4;
|
||||
$oLogLevelRank{PROTOCOL}{rank} = 3;
|
||||
$oLogLevelRank{ERROR}{rank} = 2;
|
||||
$oLogLevelRank{ASSERT}{rank} = 1;
|
||||
$oLogLevelRank{OFF}{rank} = 0;
|
||||
|
||||
####################################################################################################################################
|
||||
# Module globals
|
||||
####################################################################################################################################
|
||||
my $hLogFile = undef;
|
||||
my $strLogFileCache = undef;
|
||||
|
||||
my $strLogLevelFile = OFF;
|
||||
my $strLogLevelConsole = OFF;
|
||||
my $strLogLevelStdErr = WARN;
|
||||
my $bLogTimestamp = true;
|
||||
|
||||
# Size of the process id log field
|
||||
my $iLogProcessSize = 2;
|
||||
|
||||
# Flags to limit banner printing until there is actual output
|
||||
my $bLogFileExists;
|
||||
my $bLogFileFirst;
|
||||
|
||||
# Allow log to be globally enabled or disabled with logEnable() and logDisable()
|
||||
my $bLogDisable = 0;
|
||||
|
||||
# Allow errors to be logged as warnings
|
||||
my $bLogWarnOnError = 0;
|
||||
|
||||
# Store the last logged error
|
||||
my $oErrorLast;
|
||||
|
||||
####################################################################################################################################
|
||||
# logFileSet - set the file messages will be logged to
|
||||
####################################################################################################################################
|
||||
sub logFileSet
|
||||
{
|
||||
my $oStorage = shift;
|
||||
my $strFile = shift;
|
||||
my $bLogFileFirstParam = shift;
|
||||
|
||||
# Only open the log file if file logging is enabled
|
||||
if ($strLogLevelFile ne OFF)
|
||||
{
|
||||
$oStorage->pathCreate(dirname($strFile), {strMode => '0750', bIgnoreExists => true, bCreateParent => true});
|
||||
|
||||
$strFile .= '.log';
|
||||
$bLogFileExists = -e $strFile ? true : false;
|
||||
$bLogFileFirst = defined($bLogFileFirstParam) ? $bLogFileFirstParam : false;
|
||||
|
||||
if (!sysopen($hLogFile, $strFile, O_WRONLY | O_CREAT | O_APPEND, oct('0640')))
|
||||
{
|
||||
logErrorResult(ERROR_FILE_OPEN, "unable to open log file '${strFile}'", $OS_ERROR);
|
||||
}
|
||||
|
||||
# Write out anything that was cached before the file was opened
|
||||
if (defined($strLogFileCache))
|
||||
{
|
||||
logBanner();
|
||||
syswrite($hLogFile, $strLogFileCache);
|
||||
undef($strLogFileCache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
push @EXPORT, qw(logFileSet);
|
||||
|
||||
####################################################################################################################################
|
||||
# logBanner
|
||||
#
|
||||
# Output a banner on the first log entry written to a file
|
||||
####################################################################################################################################
|
||||
sub logBanner
|
||||
{
|
||||
if ($bLogFileFirst)
|
||||
{
|
||||
if ($bLogFileExists)
|
||||
{
|
||||
syswrite($hLogFile, "\n");
|
||||
}
|
||||
|
||||
syswrite($hLogFile, "-------------------PROCESS START-------------------\n");
|
||||
}
|
||||
|
||||
$bLogFileFirst = false;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# logLevelSet - set the log level for file and console
|
||||
####################################################################################################################################
|
||||
sub logLevelSet
|
||||
{
|
||||
my $strLevelFileParam = shift;
|
||||
my $strLevelConsoleParam = shift;
|
||||
my $strLevelStdErrParam = shift;
|
||||
my $bLogTimestampParam = shift;
|
||||
my $iLogProcessMax = shift;
|
||||
|
||||
if (defined($strLevelFileParam))
|
||||
{
|
||||
if (!defined($oLogLevelRank{uc($strLevelFileParam)}{rank}))
|
||||
{
|
||||
confess &log(ERROR, "file log level ${strLevelFileParam} does not exist");
|
||||
}
|
||||
|
||||
$strLogLevelFile = uc($strLevelFileParam);
|
||||
}
|
||||
|
||||
if (defined($strLevelConsoleParam))
|
||||
{
|
||||
if (!defined($oLogLevelRank{uc($strLevelConsoleParam)}{rank}))
|
||||
{
|
||||
confess &log(ERROR, "console log level ${strLevelConsoleParam} does not exist");
|
||||
}
|
||||
|
||||
$strLogLevelConsole = uc($strLevelConsoleParam);
|
||||
}
|
||||
|
||||
if (defined($strLevelStdErrParam))
|
||||
{
|
||||
if (!defined($oLogLevelRank{uc($strLevelStdErrParam)}{rank}))
|
||||
{
|
||||
confess &log(ERROR, "stdout log level ${strLevelStdErrParam} does not exist");
|
||||
}
|
||||
|
||||
$strLogLevelStdErr = uc($strLevelStdErrParam);
|
||||
}
|
||||
|
||||
if (defined($bLogTimestampParam))
|
||||
{
|
||||
$bLogTimestamp = $bLogTimestampParam;
|
||||
}
|
||||
|
||||
if (defined($iLogProcessMax))
|
||||
{
|
||||
$iLogProcessSize = $iLogProcessMax > 99 ? 3 : 2;
|
||||
}
|
||||
}
|
||||
|
||||
push @EXPORT, qw(logLevelSet);
|
||||
|
||||
####################################################################################################################################
|
||||
# logDisable
|
||||
####################################################################################################################################
|
||||
sub logDisable
|
||||
{
|
||||
$bLogDisable++;
|
||||
}
|
||||
|
||||
push @EXPORT, qw(logDisable);
|
||||
|
||||
####################################################################################################################################
|
||||
# logEnable
|
||||
####################################################################################################################################
|
||||
sub logEnable
|
||||
{
|
||||
$bLogDisable--;
|
||||
}
|
||||
|
||||
push @EXPORT, qw(logEnable);
|
||||
|
||||
####################################################################################################################################
|
||||
# logWarnOnErrorDisable
|
||||
####################################################################################################################################
|
||||
sub logWarnOnErrorDisable
|
||||
{
|
||||
$bLogWarnOnError--;
|
||||
}
|
||||
|
||||
push @EXPORT, qw(logWarnOnErrorDisable);
|
||||
|
||||
####################################################################################################################################
|
||||
# logWarnOnErrorEnable - when an error is thrown, log it as a warning instead
|
||||
####################################################################################################################################
|
||||
sub logWarnOnErrorEnable
|
||||
{
|
||||
$bLogWarnOnError++;
|
||||
}
|
||||
|
||||
push @EXPORT, qw(logWarnOnErrorEnable);
|
||||
|
||||
####################################################################################################################################
|
||||
# logDebugParam
|
||||
#
|
||||
# Log parameters passed to functions.
|
||||
####################################################################################################################################
|
||||
use constant DEBUG_PARAM => '()';
|
||||
|
||||
sub logDebugParam
|
||||
{
|
||||
my $strFunction = shift;
|
||||
my $oyParamRef = shift;
|
||||
|
||||
return logDebugProcess($strFunction, DEBUG_PARAM, undef, $oyParamRef, @_);
|
||||
}
|
||||
|
||||
push @EXPORT, qw(logDebugParam);
|
||||
|
||||
####################################################################################################################################
|
||||
# logDebugReturn
|
||||
#
|
||||
# Log values returned from functions.
|
||||
####################################################################################################################################
|
||||
use constant DEBUG_RETURN => '=>';
|
||||
|
||||
sub logDebugReturn
|
||||
{
|
||||
my $strFunction = shift;
|
||||
|
||||
return logDebugProcess($strFunction, DEBUG_RETURN, undef, undef, @_);
|
||||
}
|
||||
|
||||
push @EXPORT, qw(logDebugReturn);
|
||||
|
||||
####################################################################################################################################
|
||||
# logDebugMisc
|
||||
#
|
||||
# Log misc values and details during execution.
|
||||
####################################################################################################################################
|
||||
use constant DEBUG_MISC => '';
|
||||
|
||||
sub logDebugMisc
|
||||
{
|
||||
my $strFunction = shift;
|
||||
my $strDetail = shift;
|
||||
|
||||
return logDebugProcess($strFunction, DEBUG_MISC, $strDetail, undef, @_);
|
||||
}
|
||||
|
||||
push @EXPORT, qw(logDebugMisc);
|
||||
|
||||
####################################################################################################################################
|
||||
# logDebugProcess
|
||||
####################################################################################################################################
|
||||
sub logDebugProcess
|
||||
{
|
||||
my $strFunction = shift;
|
||||
my $strType = shift;
|
||||
my $strDetail = shift;
|
||||
my $oyParamRef = shift;
|
||||
|
||||
my $iIndex = 0;
|
||||
my $oParamHash = {};
|
||||
my @oyResult;
|
||||
my $bLogTrace = true;
|
||||
|
||||
if ($strType eq DEBUG_PARAM)
|
||||
{
|
||||
push @oyResult, $strFunction;
|
||||
}
|
||||
|
||||
# Process each parameter hash
|
||||
my $oParam = shift;
|
||||
my $bOptionalBlock = false;
|
||||
|
||||
# Strip the package name off strFunction if it's pgBackRest
|
||||
$strFunction =~ s/^pgBackRest[^\:]*\:\://;
|
||||
|
||||
while (defined($oParam))
|
||||
{
|
||||
my $strParamName = $$oParam{name};
|
||||
my $bParamOptional = defined($oParam->{optional}) && $oParam->{optional};
|
||||
my $bParamRequired = !defined($oParam->{required}) || $oParam->{required};
|
||||
my $oValue;
|
||||
|
||||
# Should the param be redacted?
|
||||
$oParamHash->{$strParamName}{redact} = $oParam->{redact} ? true : false;
|
||||
|
||||
# If param is optional then the optional block has been entered
|
||||
if ($bParamOptional)
|
||||
{
|
||||
if (defined($oParam->{required}))
|
||||
{
|
||||
confess &log(ASSERT, "cannot define 'required' for optional parameter '${strParamName}'");
|
||||
}
|
||||
|
||||
$bParamRequired = false;
|
||||
$bOptionalBlock = true;
|
||||
}
|
||||
|
||||
# Don't allow non-optional parameters once optional block has started
|
||||
if ($bParamOptional != $bOptionalBlock)
|
||||
{
|
||||
confess &log(ASSERT, "non-optional parameter '${strParamName}' invalid after optional parameters");
|
||||
}
|
||||
|
||||
# Push the return value into the return value array
|
||||
if ($strType eq DEBUG_PARAM)
|
||||
{
|
||||
if ($bParamOptional)
|
||||
{
|
||||
$oValue = $$oyParamRef[$iIndex]->{$strParamName};
|
||||
}
|
||||
else
|
||||
{
|
||||
$oValue = $$oyParamRef[$iIndex];
|
||||
}
|
||||
|
||||
if (defined($oValue))
|
||||
{
|
||||
push(@oyResult, $oValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
push(@oyResult, $${oParam}{default});
|
||||
$$oParamHash{$strParamName}{default} = true;
|
||||
}
|
||||
|
||||
$oValue = $oyResult[-1];
|
||||
|
||||
if (!defined($oValue) && $bParamRequired)
|
||||
{
|
||||
confess &log(ASSERT, "${strParamName} is required in ${strFunction}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ref($$oParam{value}) eq 'ARRAY')
|
||||
{
|
||||
if (defined($$oParam{ref}) && $$oParam{ref})
|
||||
{
|
||||
push(@oyResult, $$oParam{value});
|
||||
}
|
||||
else
|
||||
{
|
||||
push(@oyResult, @{$$oParam{value}});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
push(@oyResult, $$oParam{value});
|
||||
}
|
||||
|
||||
$oValue = $$oParam{value};
|
||||
}
|
||||
|
||||
if (!defined($$oParam{log}) || $$oParam{log})
|
||||
{
|
||||
# If the parameter is a hash but not blessed then represent it as a string
|
||||
# ??? This should go away once the inputs to logDebug can be changed
|
||||
if (ref($oValue) eq 'HASH' && !blessed($oValue))
|
||||
{
|
||||
$$oParamHash{$strParamName}{value} = '[hash]';
|
||||
}
|
||||
# Else log the parameter value exactly
|
||||
else
|
||||
{
|
||||
$$oParamHash{$strParamName}{value} = $oValue;
|
||||
}
|
||||
|
||||
# There are certain return values that it's wasteful to generate debug logging for
|
||||
if (!($strParamName eq 'self') &&
|
||||
(!defined($$oParam{trace}) || !$$oParam{trace}))
|
||||
{
|
||||
$bLogTrace = false;
|
||||
}
|
||||
}
|
||||
|
||||
# Get the next parameter hash
|
||||
$oParam = shift;
|
||||
|
||||
if (!$bParamOptional)
|
||||
{
|
||||
$iIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
if (defined($strDetail) && $iIndex == 0)
|
||||
{
|
||||
$bLogTrace = false;
|
||||
}
|
||||
|
||||
logDebugOut($strFunction, $strType, $strDetail, $oParamHash, $bLogTrace ? TRACE : DEBUG);
|
||||
|
||||
# If there are one or zero return values then just return a scalar (this will be undef if there are no return values)
|
||||
if (@oyResult == 1)
|
||||
{
|
||||
return $oyResult[0];
|
||||
}
|
||||
|
||||
# Else return an array containing return values
|
||||
return @oyResult;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# logDebugBuild
|
||||
####################################################################################################################################
|
||||
sub logDebugBuild
|
||||
{
|
||||
my $strValue = shift;
|
||||
|
||||
my $rResult;
|
||||
|
||||
# Value is undefined
|
||||
if (!defined($strValue))
|
||||
{
|
||||
$rResult = \'[undef]';
|
||||
}
|
||||
# Value is not a ref, but return it as a ref for efficiency
|
||||
elsif (!ref($strValue))
|
||||
{
|
||||
$rResult = \$strValue;
|
||||
}
|
||||
# Value is a hash
|
||||
elsif (ref($strValue) eq 'HASH')
|
||||
{
|
||||
my $strValueHash;
|
||||
|
||||
for my $strSubValue (sort(keys(%{$strValue})))
|
||||
{
|
||||
$strValueHash .=
|
||||
(defined($strValueHash) ? ', ' : '{') . "${strSubValue} => " . ${logDebugBuild($strValue->{$strSubValue})};
|
||||
}
|
||||
|
||||
$rResult = \(defined($strValueHash) ? $strValueHash . '}' : '{}');
|
||||
}
|
||||
# Value is an array
|
||||
elsif (ref($strValue) eq 'ARRAY')
|
||||
{
|
||||
my $strValueArray;
|
||||
|
||||
for my $strSubValue (@{$strValue})
|
||||
{
|
||||
$strValueArray .= (defined($strValueArray) ? ', ' : '(') . ${logDebugBuild($strSubValue)};
|
||||
}
|
||||
|
||||
$rResult = \(defined($strValueArray) ? $strValueArray . ')' : '()');
|
||||
}
|
||||
# Else some other type ??? For the moment this is forced to object to not make big log changes
|
||||
else
|
||||
{
|
||||
$rResult = \('[object]');
|
||||
}
|
||||
|
||||
return $rResult;
|
||||
}
|
||||
|
||||
push @EXPORT, qw(logDebugBuild);
|
||||
|
||||
####################################################################################################################################
|
||||
# logDebugOut
|
||||
####################################################################################################################################
|
||||
use constant DEBUG_STRING_MAX_LEN => 1024;
|
||||
|
||||
sub logDebugOut
|
||||
{
|
||||
my $strFunction = shift;
|
||||
my $strType = shift;
|
||||
my $strMessage = shift;
|
||||
my $oParamHash = shift;
|
||||
my $strLevel = shift;
|
||||
|
||||
$strLevel = defined($strLevel) ? $strLevel : DEBUG;
|
||||
|
||||
if ($oLogLevelRank{$strLevel}{rank} <= $oLogLevelRank{$strLogLevelConsole}{rank} ||
|
||||
$oLogLevelRank{$strLevel}{rank} <= $oLogLevelRank{$strLogLevelFile}{rank} ||
|
||||
$oLogLevelRank{$strLevel}{rank} <= $oLogLevelRank{$strLogLevelStdErr}{rank})
|
||||
{
|
||||
if (defined($oParamHash))
|
||||
{
|
||||
my $strParamSet;
|
||||
|
||||
foreach my $strParam (sort(keys(%$oParamHash)))
|
||||
{
|
||||
if (defined($strParamSet))
|
||||
{
|
||||
$strParamSet .= ', ';
|
||||
}
|
||||
|
||||
my $strValueRef = defined($oParamHash->{$strParam}{value}) ? logDebugBuild($oParamHash->{$strParam}{value}) : undef;
|
||||
my $bDefault =
|
||||
defined($$strValueRef) && defined($$oParamHash{$strParam}{default}) ? $$oParamHash{$strParam}{default} : false;
|
||||
|
||||
$strParamSet .=
|
||||
"${strParam} = " .
|
||||
($oParamHash->{$strParam}{redact} && defined($$strValueRef) ? '<redacted>' :
|
||||
($bDefault ? '<' : '') .
|
||||
(defined($$strValueRef) ?
|
||||
($strParam =~ /^(b|is)/ ? ($$strValueRef ? 'true' : 'false'):
|
||||
(length($$strValueRef) > DEBUG_STRING_MAX_LEN ?
|
||||
substr($$strValueRef, 0, DEBUG_STRING_MAX_LEN) . ' ... <truncated>':
|
||||
$$strValueRef)) : '[undef]') .
|
||||
($bDefault ? '>' : ''));
|
||||
}
|
||||
|
||||
if (defined($strMessage))
|
||||
{
|
||||
$strMessage = $strMessage . (defined($strParamSet) ? ": ${strParamSet}" : '');
|
||||
}
|
||||
else
|
||||
{
|
||||
$strMessage = $strParamSet;
|
||||
}
|
||||
}
|
||||
|
||||
&log($strLevel, "${strFunction}${strType}" . (defined($strMessage) ? ": $strMessage" : ''));
|
||||
}
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# logException
|
||||
####################################################################################################################################
|
||||
sub logException
|
||||
{
|
||||
my $oException = shift;
|
||||
|
||||
return &log($oException->level(), $oException->message(), $oException->code(), undef, undef, undef, $oException->extra());
|
||||
}
|
||||
|
||||
push @EXPORT, qw(logException);
|
||||
|
||||
####################################################################################################################################
|
||||
# logErrorResult
|
||||
####################################################################################################################################
|
||||
sub logErrorResult
|
||||
{
|
||||
my $iCode = shift;
|
||||
my $strMessage = shift;
|
||||
my $strResult = shift;
|
||||
|
||||
confess &log(ERROR, $strMessage . (defined($strResult) ? ': ' . trim($strResult) : ''), $iCode);
|
||||
}
|
||||
|
||||
push @EXPORT, qw(logErrorResult);
|
||||
|
||||
####################################################################################################################################
|
||||
# LOG - log messages
|
||||
####################################################################################################################################
|
||||
sub log
|
||||
{
|
||||
my $strLevel = shift;
|
||||
my $strMessage = shift;
|
||||
my $iCode = shift;
|
||||
my $bSuppressLog = shift;
|
||||
my $iIndent = shift;
|
||||
my $iProcessId = shift;
|
||||
my $rExtra = shift;
|
||||
|
||||
# Set defaults
|
||||
$bSuppressLog = defined($bSuppressLog) ? $bSuppressLog : false;
|
||||
|
||||
# Initialize rExtra
|
||||
if (!defined($rExtra))
|
||||
{
|
||||
$rExtra =
|
||||
{
|
||||
bLogFile => false,
|
||||
bLogConsole => false,
|
||||
};
|
||||
}
|
||||
|
||||
# Set operational variables
|
||||
my $strMessageFormat = $strMessage;
|
||||
my $iLogLevelRank = $oLogLevelRank{$strLevel}{rank};
|
||||
|
||||
# Level rank must be valid
|
||||
if (!defined($iLogLevelRank))
|
||||
{
|
||||
confess &log(ASSERT, "log level ${strLevel} does not exist");
|
||||
}
|
||||
|
||||
# If message was undefined then set default message
|
||||
if (!defined($strMessageFormat))
|
||||
{
|
||||
$strMessageFormat = '(undefined)';
|
||||
}
|
||||
|
||||
# Set the error code
|
||||
if ($strLevel eq ASSERT)
|
||||
{
|
||||
$iCode = ERROR_ASSERT;
|
||||
}
|
||||
elsif ($strLevel eq ERROR && !defined($iCode))
|
||||
{
|
||||
$iCode = ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
$strMessageFormat = (defined($iCode) ? sprintf('[%03d]: ', $iCode) : '') . $strMessageFormat;
|
||||
|
||||
# Indent subsequent lines of the message if it has more than one line - makes the log more readable
|
||||
if (defined($iIndent))
|
||||
{
|
||||
my $strIndent = ' ' x $iIndent;
|
||||
$strMessageFormat =~ s/\n/\n${strIndent}/g;
|
||||
}
|
||||
else
|
||||
{
|
||||
# Indent subsequent message lines so they align
|
||||
$bLogTimestamp ?
|
||||
$strMessageFormat =~ s/\n/\n /g :
|
||||
$strMessageFormat =~ s/\n/\n /g
|
||||
}
|
||||
|
||||
# Indent TRACE and debug levels so they are distinct from normal messages
|
||||
if ($strLevel eq TRACE)
|
||||
{
|
||||
$strMessageFormat =~ s/\n/\n /g;
|
||||
$strMessageFormat = ' ' . $strMessageFormat;
|
||||
}
|
||||
elsif ($strLevel eq DEBUG)
|
||||
{
|
||||
$strMessageFormat =~ s/\n/\n /g;
|
||||
$strMessageFormat = ' ' . $strMessageFormat;
|
||||
}
|
||||
|
||||
# Format the message text
|
||||
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
|
||||
|
||||
# If logging warnings as errors then change the display level and rank. These will be used to determine if the message will be
|
||||
# displayed or not.
|
||||
my $strDisplayLevel = ($bLogWarnOnError && $strLevel eq ERROR ? WARN : $strLevel);
|
||||
my $iLogDisplayLevelRank = ($bLogWarnOnError && $strLevel eq ERROR ? $oLogLevelRank{$strDisplayLevel}{rank} : $iLogLevelRank);
|
||||
|
||||
$strMessageFormat =
|
||||
($bLogTimestamp ? timestampFormat() . sprintf('.%03d ', (gettimeofday() - int(gettimeofday())) * 1000) : '') .
|
||||
sprintf('P%0*d', $iLogProcessSize, defined($iProcessId) ? $iProcessId : 0) .
|
||||
(' ' x (7 - length($strDisplayLevel))) . "${strDisplayLevel}: ${strMessageFormat}\n";
|
||||
|
||||
# Skip output if disabled
|
||||
if (!$bLogDisable)
|
||||
{
|
||||
# Output to stderr if configured log level setting rank is greater than the display level rank.
|
||||
if (!$rExtra->{bLogConsole} && $iLogDisplayLevelRank <= $oLogLevelRank{$strLogLevelStdErr}{rank})
|
||||
{
|
||||
if ($strLogLevelStdErr ne PROTOCOL)
|
||||
{
|
||||
syswrite(*STDERR, $strDisplayLevel . (defined($iCode) ? sprintf(' [%03d]: ', $iCode) : '') . ': ');
|
||||
}
|
||||
|
||||
syswrite(*STDERR, "${strMessage}\n");
|
||||
$rExtra->{bLogConsole} = true;
|
||||
}
|
||||
# Else output to stdout if configured log level setting rank is greater than the display level rank
|
||||
elsif (!$rExtra->{bLogConsole} && $iLogDisplayLevelRank <= $oLogLevelRank{$strLogLevelConsole}{rank})
|
||||
{
|
||||
if (!$bSuppressLog)
|
||||
{
|
||||
syswrite(*STDOUT, $strMessageFormat);
|
||||
|
||||
# This is here for debugging purposes - it's not clear how best to make it into a switch
|
||||
# if ($strLevel eq ASSERT || $strLevel eq ERROR)
|
||||
# {
|
||||
# my $strStackTrace = longmess() . "\n";
|
||||
# $strStackTrace =~ s/\n/\n /g;
|
||||
# syswrite(*STDOUT, $strStackTrace);
|
||||
# }
|
||||
}
|
||||
|
||||
$rExtra->{bLogConsole} = true;
|
||||
}
|
||||
|
||||
# Output to file if configured log level setting rank is greater than the display level rank or test flag is set.
|
||||
if (!$rExtra->{bLogLogFile} && $iLogDisplayLevelRank <= $oLogLevelRank{$strLogLevelFile}{rank})
|
||||
{
|
||||
if (defined($hLogFile) || (defined($strLogLevelFile) && $strLogLevelFile ne OFF))
|
||||
{
|
||||
if (!$bSuppressLog)
|
||||
{
|
||||
if (defined($hLogFile))
|
||||
{
|
||||
logBanner();
|
||||
syswrite($hLogFile, $strMessageFormat);
|
||||
}
|
||||
else
|
||||
{
|
||||
$strLogFileCache .= $strMessageFormat;
|
||||
}
|
||||
|
||||
if ($strDisplayLevel eq ASSERT ||
|
||||
($strDisplayLevel eq ERROR && ($strLogLevelFile eq DEBUG || $strLogLevelFile eq TRACE)))
|
||||
{
|
||||
my $strStackTrace = longmess() . "\n";
|
||||
$strStackTrace =~ s/\n/\n /g;
|
||||
|
||||
if (defined($hLogFile))
|
||||
{
|
||||
syswrite($hLogFile, $strStackTrace);
|
||||
}
|
||||
else
|
||||
{
|
||||
$strLogFileCache .= $strStackTrace;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$rExtra->{bLogFile} = true;
|
||||
}
|
||||
}
|
||||
|
||||
# Return a typed exception if code is defined
|
||||
if (defined($iCode))
|
||||
{
|
||||
$oErrorLast = new pgBackRestDoc::Common::Exception($strLevel, $iCode, $strMessage, longmess(), $rExtra);
|
||||
return $oErrorLast;
|
||||
}
|
||||
|
||||
# Return the message so it can be used in a confess
|
||||
return $strMessage;
|
||||
}
|
||||
|
||||
push @EXPORT, qw(log);
|
||||
|
||||
####################################################################################################################################
|
||||
# logErrorLast - get the last logged error
|
||||
####################################################################################################################################
|
||||
sub logErrorLast
|
||||
{
|
||||
return $oErrorLast;
|
||||
}
|
||||
|
||||
push @EXPORT, qw(logErrorLast);
|
||||
|
||||
####################################################################################################################################
|
||||
# logLevel - get the current log levels
|
||||
####################################################################################################################################
|
||||
sub logLevel
|
||||
{
|
||||
return ($strLogLevelFile, $strLogLevelConsole, $strLogLevelStdErr, $bLogTimestamp);
|
||||
}
|
||||
|
||||
push @EXPORT, qw(logLevel);
|
||||
|
||||
####################################################################################################################################
|
||||
# logFileCacheClear - Clear the log file cache
|
||||
####################################################################################################################################
|
||||
sub logFileCacheClear
|
||||
{
|
||||
undef($strLogFileCache);
|
||||
}
|
||||
|
||||
push @EXPORT, qw(logFileCacheClear);
|
||||
|
||||
####################################################################################################################################
|
||||
# logFileCache - Get the log file cache
|
||||
####################################################################################################################################
|
||||
sub logFileCache
|
||||
{
|
||||
return $strLogFileCache;
|
||||
}
|
||||
|
||||
push @EXPORT, qw(logFileCache);
|
||||
|
||||
1;
|
121
doc/lib/pgBackRestDoc/Common/String.pm
Normal file
121
doc/lib/pgBackRestDoc/Common/String.pm
Normal file
@ -0,0 +1,121 @@
|
||||
####################################################################################################################################
|
||||
# COMMON STRING MODULE
|
||||
####################################################################################################################################
|
||||
package pgBackRestDoc::Common::String;
|
||||
|
||||
use strict;
|
||||
use warnings FATAL => qw(all);
|
||||
use Carp qw(confess longmess);
|
||||
|
||||
use Exporter qw(import);
|
||||
our @EXPORT = qw();
|
||||
use File::Basename qw(dirname);
|
||||
|
||||
####################################################################################################################################
|
||||
# trim
|
||||
#
|
||||
# Trim whitespace.
|
||||
####################################################################################################################################
|
||||
sub trim
|
||||
{
|
||||
my $strBuffer = shift;
|
||||
|
||||
if (!defined($strBuffer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$strBuffer =~ s/^\s+|\s+$//g;
|
||||
|
||||
return $strBuffer;
|
||||
}
|
||||
|
||||
push @EXPORT, qw(trim);
|
||||
|
||||
####################################################################################################################################
|
||||
# coalesce - return first defined parameter
|
||||
####################################################################################################################################
|
||||
sub coalesce
|
||||
{
|
||||
foreach my $strParam (@_)
|
||||
{
|
||||
if (defined($strParam))
|
||||
{
|
||||
return $strParam;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
push @EXPORT, qw(coalesce);
|
||||
|
||||
####################################################################################################################################
|
||||
# timestampFormat
|
||||
#
|
||||
# Get standard timestamp format (or formatted as specified).
|
||||
####################################################################################################################################
|
||||
sub timestampFormat
|
||||
{
|
||||
my $strFormat = shift;
|
||||
my $lTime = shift;
|
||||
|
||||
if (!defined($strFormat))
|
||||
{
|
||||
$strFormat = '%4d-%02d-%02d %02d:%02d:%02d';
|
||||
}
|
||||
|
||||
if (!defined($lTime))
|
||||
{
|
||||
$lTime = time();
|
||||
}
|
||||
|
||||
my ($iSecond, $iMinute, $iHour, $iMonthDay, $iMonth, $iYear, $iWeekDay, $iYearDay, $bIsDst) = localtime($lTime);
|
||||
|
||||
if ($strFormat eq "%4d")
|
||||
{
|
||||
return sprintf($strFormat, $iYear + 1900)
|
||||
}
|
||||
else
|
||||
{
|
||||
return sprintf($strFormat, $iYear + 1900, $iMonth + 1, $iMonthDay, $iHour, $iMinute, $iSecond);
|
||||
}
|
||||
}
|
||||
|
||||
push @EXPORT, qw(timestampFormat);
|
||||
|
||||
####################################################################################################################################
|
||||
# stringSplit
|
||||
####################################################################################################################################
|
||||
sub stringSplit
|
||||
{
|
||||
my $strString = shift;
|
||||
my $strChar = shift;
|
||||
my $iLength = shift;
|
||||
|
||||
if (length($strString) <= $iLength)
|
||||
{
|
||||
return $strString, undef;
|
||||
}
|
||||
|
||||
my $iPos = index($strString, $strChar);
|
||||
|
||||
if ($iPos == -1)
|
||||
{
|
||||
return $strString, undef;
|
||||
}
|
||||
|
||||
my $iNewPos = $iPos;
|
||||
|
||||
while ($iNewPos != -1 && $iNewPos + 1 < $iLength)
|
||||
{
|
||||
$iPos = $iNewPos;
|
||||
$iNewPos = index($strString, $strChar, $iPos + 1);
|
||||
}
|
||||
|
||||
return substr($strString, 0, $iPos + 1), substr($strString, $iPos + 1);
|
||||
}
|
||||
|
||||
push @EXPORT, qw(stringSplit);
|
||||
|
||||
1;
|
Reference in New Issue
Block a user