1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-03-03 14:52:21 +02:00

Convert configuration optional rules to pack format.

The previous format was custom for configuration parsing and was not as expressive as the pack format. An immediate benefit is that commands with the same optional rules are merged.

Defaults are now represented correctly (not multiplied), which simplifies the option default functions used by help.
This commit is contained in:
David Steele 2021-10-16 12:35:47 -04:00
parent 360cff94e4
commit 6b9e19d423
13 changed files with 3506 additions and 1191 deletions

View File

@ -31,8 +31,12 @@ Option type constants
***********************************************************************************************************************************/
STRING_EXTERN(OPT_TYPE_BOOLEAN_STR, OPT_TYPE_BOOLEAN);
STRING_EXTERN(OPT_TYPE_HASH_STR, OPT_TYPE_HASH);
STRING_EXTERN(OPT_TYPE_INTEGER_STR, OPT_TYPE_INTEGER);
STRING_EXTERN(OPT_TYPE_LIST_STR, OPT_TYPE_LIST);
STRING_EXTERN(OPT_TYPE_PATH_STR, OPT_TYPE_PATH);
STRING_EXTERN(OPT_TYPE_SIZE_STR, OPT_TYPE_SIZE);
STRING_EXTERN(OPT_TYPE_STRING_STR, OPT_TYPE_STRING);
STRING_EXTERN(OPT_TYPE_STRING_ID_STR, OPT_TYPE_STRING_ID);
STRING_EXTERN(OPT_TYPE_TIME_STR, OPT_TYPE_TIME);
/***********************************************************************************************************************************

View File

@ -33,10 +33,18 @@ Option type constants
STRING_DECLARE(OPT_TYPE_BOOLEAN_STR);
#define OPT_TYPE_HASH "hash"
STRING_DECLARE(OPT_TYPE_HASH_STR);
#define OPT_TYPE_INTEGER "integer"
STRING_DECLARE(OPT_TYPE_INTEGER_STR);
#define OPT_TYPE_LIST "list"
STRING_DECLARE(OPT_TYPE_LIST_STR);
#define OPT_TYPE_PATH "path"
STRING_DECLARE(OPT_TYPE_PATH_STR);
#define OPT_TYPE_SIZE "size"
STRING_DECLARE(OPT_TYPE_SIZE_STR);
#define OPT_TYPE_STRING "string"
STRING_DECLARE(OPT_TYPE_STRING_STR);
#define OPT_TYPE_STRING_ID "stringId"
STRING_DECLARE(OPT_TYPE_STRING_ID_STR);
#define OPT_TYPE_TIME "time"
STRING_DECLARE(OPT_TYPE_TIME_STR);

View File

@ -297,128 +297,227 @@ bldCfgRenderLf(String *const config, const bool lf)
strCatZ(config, "\n");
}
static const String *
bldCfgRenderValue(const String *const type, const String *const value)
// Helper to get var-128 encoding size
static size_t
bldCfgRenderVar128Size(uint64_t value)
{
// Translate boolean values
if (strEq(type, OPT_TYPE_BOOLEAN_STR))
size_t result = 1;
while (value >= 0x80)
{
if (strEq(value, FALSE_STR))
return ZERO_STR;
CHECK(strEq(value, TRUE_STR));
return strNewZ("1");
value >>= 7;
result++;
}
// Translate time values
else if (strEq(type, OPT_TYPE_TIME_STR))
return strNewFmt("%" PRId64, (int64_t)(cvtZToDouble(strZ(value)) * 1000));
return value;
return result;
}
// Helper to render enum string
static String *
bldCfgRenderEnumStr(const String *const source)
{
String *const result = strNew();
bool priorSpecial = false;
for (unsigned int sourceIdx = 0; sourceIdx < strSize(source); sourceIdx++)
{
switch (strZ(source)[sourceIdx])
{
case '"':
case '/':
case ' ':
case '.':
{
if (sourceIdx != 0 && !priorSpecial)
strCatChr(result, '_');
switch (strZ(source)[sourceIdx])
{
case '"':
strCatZ(result, "QT");
break;
case '/':
strCatZ(result, "FS");
break;
case ' ':
strCatZ(result, "SP");
break;
case '.':
strCatZ(result, "DT");
break;
}
if (sourceIdx != strSize(source) - 1)
strCatChr(result, '_');
priorSpecial = true;
break;
}
default:
strCatChr(result, strZ(source)[sourceIdx]);
priorSpecial = false;
break;
}
}
return result;
}
// Helper to render scalars
static String *
bldCfgRenderScalar(const String *const scalar, const String *const optType)
{
if (strEq(optType, OPT_TYPE_STRING_ID_STR))
return strNewFmt("PARSE_RULE_VAL_STRID(%s)", strZ(bldEnum("parseRuleValStrId", scalar)));
if (strEq(optType, OPT_TYPE_STRING_STR))
return strNewFmt("PARSE_RULE_VAL_STR(parseRuleValStr%s)", strZ(bldCfgRenderEnumStr(scalar)));
if (strEq(optType, OPT_TYPE_BOOLEAN_STR))
return strNewFmt("PARSE_RULE_VAL_BOOL_%s", strEqZ(scalar, "true") ? "TRUE" : "FALSE");
int64_t value = 0;
if (strEq(optType, OPT_TYPE_TIME_STR))
{
value = (int64_t)(cvtZToDouble(strZ(scalar)) * 1000);
}
else
{
CHECK(strEq(optType, OPT_TYPE_SIZE_STR) || strEq(optType, OPT_TYPE_INTEGER_STR));
value = cvtZToInt64(strZ(scalar));
}
return strNewFmt("PARSE_RULE_VAL_INT(%s)", strZ(bldEnum("parseRuleValInt", strNewFmt("%" PRId64, value))));
}
// Helper to render validity
static String *
bldCfgRenderValid(const BldCfgOptionDepend *const depend)
{
ASSERT(depend != NULL);
String *const result = strNew();
strCatFmt(
result,
" PARSE_RULE_OPTIONAL_DEPEND\n"
" (\n"
" PARSE_RULE_VAL_OPT(%s),\n",
strZ(bldEnum("cfgOpt", depend->option->name)));
if (depend->valueList != NULL)
{
for (unsigned int valueIdx = 0; valueIdx < strLstSize(depend->valueList); valueIdx++)
{
strCatFmt(
result,
" %s,\n",
strZ(
bldCfgRenderScalar(
strLstGet(depend->valueList, valueIdx),
strEq(depend->option->type, OPT_TYPE_STRING_STR) ? OPT_TYPE_STRING_ID_STR : depend->option->type)));
}
}
strCatZ(
result,
" )");
return result;
}
// Helper to render allow range
static String *
bldCfgRenderAllowRange(const String *const allowRangeMin, const String *const allowRangeMax, const String *const optType)
{
ASSERT(allowRangeMin != NULL);
ASSERT(allowRangeMax != NULL);
ASSERT(optType != NULL);
return strNewFmt(
" PARSE_RULE_OPTIONAL_ALLOW_RANGE\n"
" (\n"
" %s,\n"
" %s,\n"
" )",
strZ(bldCfgRenderScalar(allowRangeMin, optType)),
strZ(bldCfgRenderScalar(allowRangeMax, optType)));
}
// Helper to render allow list
static bool
bldCfgRenderAllowList(String *const config, const StringList *const allowList, const bool command)
static String *
bldCfgRenderAllowList(const StringList *const allowList, const String *const optType)
{
if (allowList != NULL)
ASSERT(allowList != NULL);
ASSERT(optType != NULL);
String *const result = strNew();
strCatZ(
result,
" PARSE_RULE_OPTIONAL_ALLOW_LIST\n"
" (\n");
for (unsigned int allowIdx = 0; allowIdx < strLstSize(allowList); allowIdx++)
{
const char *indent = command ? " " : " ";
const String *const value = strLstGet(allowList, allowIdx);
strCatFmt(
config,
"%sPARSE_RULE_OPTION_OPTIONAL_ALLOW_LIST\n"
"%s(\n",
indent, indent);
for (unsigned int allowIdx = 0; allowIdx < strLstSize(allowList); allowIdx++)
{
strCatFmt(
config,
"%s \"%s\"%s\n",
indent, strZ(strLstGet(allowList, allowIdx)),
allowIdx < strLstSize(allowList) - 1 ? "," : "");
}
strCatFmt(config, "%s),\n", indent);
return true;
strCatFmt(result, " %s,\n", strZ(bldCfgRenderScalar(value, optType)));
}
return false;
strCatZ(result, " )");
return result;
}
// Helper to render default
static bool
static String *
bldCfgRenderDefault(
String *const config, const String *const type, const String *const defaultValue,
const bool defaultLiteral, const bool command, bool multi)
const String *const defaultValue, const bool defaultLiteral, const String *const optType)
{
if (defaultValue != NULL)
ASSERT(defaultValue != NULL);
ASSERT(optType != NULL);
String *const result = strNew();
strCatZ(
result,
" PARSE_RULE_OPTIONAL_DEFAULT\n"
" (\n");
if (!strEq(optType, OPT_TYPE_STRING_STR) && !strEq(optType, OPT_TYPE_PATH_STR) && !strEq(optType, OPT_TYPE_STRING_ID_STR))
strCatFmt(result, " %s,\n", strZ(bldCfgRenderScalar(defaultValue, optType)));
if (!strEq(optType, OPT_TYPE_BOOLEAN_STR))
{
const char *indent = command ? " " : " ";
bldCfgRenderLf(config, multi);
strCatFmt(
config,
"%sPARSE_RULE_OPTION_OPTIONAL_DEFAULT(%s%s%s),\n",
indent, defaultLiteral ? "" : "\"", strZ(bldCfgRenderValue(type, defaultValue)), defaultLiteral ? "" : "\"");
return false;
result,
" %s,\n",
strZ(
bldCfgRenderScalar(
strNewFmt("%s%s%s", defaultLiteral ? "" : "\"", strZ(defaultValue), defaultLiteral ? "" : "\""),
OPT_TYPE_STRING_STR)));
}
return multi;
}
strCatZ(result, " )");
// Helper to render depend
static bool
bldCfgRenderDepend(String *const config, const BldCfgOptionDepend *const depend, const bool command, const bool multi)
{
if (depend != NULL)
{
const char *indent = command ? " " : " ";
bldCfgRenderLf(config, multi);
if (depend->valueList != NULL)
{
strCatFmt(
config,
"%sPARSE_RULE_OPTION_OPTIONAL_DEPEND_LIST\n"
"%s(\n"
"%s %s,\n",
indent, indent, indent, strZ(bldEnum("cfgOpt", depend->option->name)));
for (unsigned int valueIdx = 0; valueIdx < strLstSize(depend->valueList); valueIdx++)
{
strCatFmt(
config, "%s \"%s\"%s\n", indent,
strZ(bldCfgRenderValue(depend->option->type, strLstGet(depend->valueList, valueIdx))),
valueIdx < strLstSize(depend->valueList) - 1 ? "," : "");
}
strCatFmt(config, "%s),\n", indent);
return true;
}
else
{
strCatFmt(
config,
"%sPARSE_RULE_OPTION_OPTIONAL_DEPEND(%s),\n",
indent, strZ(bldEnum("cfgOpt", depend->option->name)));
return false;
}
}
return multi;
return result;
}
static void
bldCfgRenderParseAutoC(const Storage *const storageRepo, const BldCfg bldCfg)
{
String *const config = bldHeader(CONFIG_MODULE, PARSE_AUTO_COMMENT);
String *const config = strNew();
StringList *const ruleIntList = strLstNew();
StringList *const ruleStrList = strLstNew();
StringList *const ruleStrIdList = strLstNew();
// Command parse rules
// -----------------------------------------------------------------------------------------------------------------------------
@ -427,7 +526,18 @@ bldCfgRenderParseAutoC(const Storage *const storageRepo, const BldCfg bldCfg)
"\n"
COMMENT_BLOCK_BEGIN "\n"
"Command parse data\n"
COMMENT_BLOCK_END "\n"
COMMENT_BLOCK_END "\n");
strCatFmt(
config, "%s\n",
strZ(
bldDefineRender(
STRDEF("PARSE_RULE_VAL_CMD(value)"),
strNewFmt("PARSE_RULE_U32_%zu(value)", bldCfgRenderVar128Size(lstSize(bldCfg.cmdList) - 1)))));
strCatFmt(
config,
"\n"
"static const ParseRuleCommand parseRuleCommand[CFG_COMMAND_TOTAL] =\n"
"{\n");
@ -523,7 +633,18 @@ bldCfgRenderParseAutoC(const Storage *const storageRepo, const BldCfg bldCfg)
"\n"
COMMENT_BLOCK_BEGIN "\n"
"Option parse data\n"
COMMENT_BLOCK_END "\n"
COMMENT_BLOCK_END "\n");
strCatFmt(
config, "%s\n",
strZ(
bldDefineRender(
STRDEF("PARSE_RULE_VAL_OPT(value)"),
strNewFmt("PARSE_RULE_U32_%zu(value)", bldCfgRenderVar128Size(lstSize(bldCfg.optList) - 1)))));
strCatFmt(
config,
"\n"
"static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =\n"
"{\n");
@ -536,6 +657,8 @@ bldCfgRenderParseAutoC(const Storage *const storageRepo, const BldCfg bldCfg)
for (unsigned int optIdx = 0; optIdx < lstSize(bldCfg.optList); optIdx++)
{
const BldCfgOption *const opt = lstGet(bldCfg.optList, optIdx);
StringList *const ruleDataList = strLstNew();
bool ruleInt = false;
bldCfgRenderLf(config, optIdx != 0);
@ -622,82 +745,293 @@ bldCfgRenderParseAutoC(const Storage *const storageRepo, const BldCfg bldCfg)
}
}
// Build optional data
String *const configOptional = strNew();
// Determine if the option has an allow list. This will decide whether it is treated as a String or StringId. This should be
// replaced with a StringId type.
bool allowList = opt->allowList != NULL;
for (unsigned int optCmdIdx = 0; optCmdIdx < lstSize(opt->cmdList); optCmdIdx++)
{
if (((BldCfgOptionCommand *)lstGet(opt->cmdList, optCmdIdx))->allowList != NULL)
allowList = true;
}
// Build default optional rules
KeyValue *const optionalDefaultRule = kvNew();
const Variant *const ruleDepend = VARSTRDEF("01-depend");
const Variant *const ruleAllowRange = VARSTRDEF("02-allow-range");
const Variant *const ruleAllowList = VARSTRDEF("03-allow-list");
const Variant *const ruleDefault = VARSTRDEF("04-default");
const Variant *const ruleRequire = VARSTRDEF("05-require");
const Variant *const ruleList[] = {ruleDepend, ruleAllowRange, ruleAllowList, ruleDefault, ruleRequire};
if (opt->depend)
kvAdd(optionalDefaultRule, ruleDepend, VARSTR(bldCfgRenderValid(opt->depend)));
if (opt->allowRangeMin != NULL)
{
CHECK(opt->allowRangeMax != NULL);
const String *allowRangeMin = opt->allowRangeMin;
const String *allowRangeMax = opt->allowRangeMax;
kvAdd(
optionalDefaultRule, ruleAllowRange,
VARSTR(bldCfgRenderAllowRange(opt->allowRangeMin, opt->allowRangeMax, opt->type)));
if (strEq(opt->type, OPT_TYPE_TIME_STR))
{
allowRangeMin = strNewFmt("%" PRId64, (int64_t)(cvtZToDouble(strZ(allowRangeMin)) * 1000));
allowRangeMax = strNewFmt("%" PRId64, (int64_t)(cvtZToDouble(strZ(allowRangeMax)) * 1000));
strLstAddIfMissing(ruleDataList, strNewFmt("%" PRId64, (int64_t)(cvtZToDouble(strZ(opt->allowRangeMin)) * 1000)));
strLstAddIfMissing(ruleDataList, strNewFmt("%" PRId64, (int64_t)(cvtZToDouble(strZ(opt->allowRangeMax)) * 1000)));
}
else
{
strLstAddIfMissing(ruleDataList, opt->allowRangeMin);
strLstAddIfMissing(ruleDataList, opt->allowRangeMax);
}
strCatFmt(
configOptional,
" PARSE_RULE_OPTION_OPTIONAL_ALLOW_RANGE(%s, %s),\n",
strZ(allowRangeMin), strZ(allowRangeMax));
}
bool multi = bldCfgRenderAllowList(configOptional, opt->allowList, false);
multi = bldCfgRenderDepend(configOptional, opt->depend, false, multi);
multi = bldCfgRenderDefault(configOptional, opt->type, opt->defaultValue, opt->defaultLiteral, false, multi);
if (opt->allowList != NULL)
{
kvAdd(
optionalDefaultRule, ruleAllowList,
VARSTR(
bldCfgRenderAllowList(
opt->allowList, strEq(opt->type, OPT_TYPE_STRING_STR) ? OPT_TYPE_STRING_ID_STR : opt->type)));
for (unsigned int allowIdx = 0; allowIdx < strLstSize(opt->allowList); allowIdx++)
strLstAddIfMissing(ruleDataList, strLstGet(opt->allowList, allowIdx));
}
if (opt->defaultValue != NULL)
{
kvAdd(
optionalDefaultRule, ruleDefault,
VARSTR(
bldCfgRenderDefault(
opt->defaultValue, opt->defaultLiteral,
strEq(opt->type, OPT_TYPE_STRING_STR) && allowList ? OPT_TYPE_STRING_ID_STR : opt->type)));
if (!strEq(opt->type, OPT_TYPE_BOOLEAN_STR))
{
if (strEq(opt->type, OPT_TYPE_TIME_STR))
{
strLstAddIfMissing(
ruleDataList, strNewFmt("%" PRId64, (int64_t)(cvtZToDouble(strZ(opt->defaultValue)) * 1000)));
}
else
strLstAddIfMissing(ruleDataList, opt->defaultValue);
strLstAddIfMissing(
ruleStrList,
strNewFmt("%s%s%s", opt->defaultLiteral ? "" : "\"", strZ(opt->defaultValue), opt->defaultLiteral ? "" : "\""));
}
}
// Build command optional rules
KeyValue *const optionalCmdRule = kvNew();
// Build optional data for commands
for (unsigned int optCmdIdx = 0; optCmdIdx < lstSize(opt->cmdList); optCmdIdx++)
{
BldCfgOptionCommand *const optCmd = lstGet(opt->cmdList, optCmdIdx);
String *const configCommand = strNew();
KeyValue *const optionalCmdRuleType = kvNew();
bool multi = bldCfgRenderAllowList(configCommand, optCmd->allowList, true);
multi = bldCfgRenderDepend(configCommand, optCmd->depend, true, multi);
multi = bldCfgRenderDefault(configCommand, opt->type, optCmd->defaultValue, false, true, multi);
// Depends
if (optCmd->depend != NULL)
kvAdd(optionalCmdRuleType, ruleDepend, VARSTR(bldCfgRenderValid(optCmd->depend)));
if (optCmd->required != opt->required)
// Allow lists
if (optCmd->allowList != NULL)
{
bldCfgRenderLf(configCommand, multi);
kvAdd(
optionalCmdRuleType, ruleAllowList,
VARSTR(
bldCfgRenderAllowList(
optCmd->allowList, strEq(opt->type, OPT_TYPE_STRING_STR) ? OPT_TYPE_STRING_ID_STR : opt->type)));
strCatFmt(
configCommand,
" PARSE_RULE_OPTION_OPTIONAL_REQUIRED(%s),\n",
cvtBoolToConstZ(optCmd->required));
for (unsigned int allowIdx = 0; allowIdx < strLstSize(optCmd->allowList); allowIdx++)
strLstAddIfMissing(ruleDataList, strLstGet(optCmd->allowList, allowIdx));
}
if (!strEmpty(configCommand))
// Defaults
if (optCmd->defaultValue != NULL)
{
bldCfgRenderLf(configOptional, !strEmpty(configOptional));
kvAdd(
optionalCmdRuleType, ruleDefault,
VARSTR(
bldCfgRenderDefault(
optCmd->defaultValue, opt->defaultLiteral,
strEq(opt->type, OPT_TYPE_STRING_STR) && allowList ? OPT_TYPE_STRING_ID_STR : opt->type)));
strCatFmt(
configOptional,
" PARSE_RULE_OPTION_OPTIONAL_COMMAND_OVERRIDE\n"
" (\n"
" PARSE_RULE_OPTION_OPTIONAL_COMMAND(%s),\n"
"\n"
"%s"
" )\n",
strZ(bldEnum("cfgCmd", optCmd->name)), strZ(configCommand));
if (!strEq(opt->type, OPT_TYPE_BOOLEAN_STR))
{
if (strEq(opt->type, OPT_TYPE_TIME_STR))
{
strLstAddIfMissing(
ruleDataList, strNewFmt("%" PRId64, (int64_t)(cvtZToDouble(strZ(optCmd->defaultValue)) * 1000)));
}
else
strLstAddIfMissing(ruleDataList, optCmd->defaultValue);
strLstAddIfMissing(
ruleStrList,
strNewFmt(
"%s%s%s", opt->defaultLiteral ? "" : "\"", strZ(optCmd->defaultValue),
opt->defaultLiteral ? "" : "\""));
}
}
// Requires
if (optCmd->required != opt->required)
{
kvAdd(
optionalCmdRuleType, ruleRequire,
VARSTR(strNewFmt(" PARSE_RULE_OPTIONAL_%sREQUIRED()", optCmd->required ? "" : "NOT_")));
}
// Add defaults
if (varLstSize(kvKeyList(optionalCmdRuleType)) > 0)
{
for (unsigned int ruleIdx = 0; ruleIdx < sizeof(ruleList) / sizeof(Variant *); ruleIdx++)
{
if (kvKeyExists(optionalCmdRuleType, ruleList[ruleIdx]))
kvAdd(optionalCmdRule, VARSTR(optCmd->name), kvGet(optionalCmdRuleType, ruleList[ruleIdx]));
else if (kvKeyExists(optionalDefaultRule, ruleList[ruleIdx]))
kvAdd(optionalCmdRule, VARSTR(optCmd->name), kvGet(optionalDefaultRule, ruleList[ruleIdx]));
}
}
}
// Add optional data
if (!strEmpty(configOptional))
// Add optional rules
unsigned int optionalCmdRuleSize = varLstSize(kvKeyList(optionalCmdRule));
unsigned int optionalDefaultRuleSize = varLstSize(kvKeyList(optionalDefaultRule));
if (optionalCmdRuleSize != 0 || optionalDefaultRuleSize != 0)
{
strCatFmt(
strCatZ(
config,
"\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n"
" (\n"
"%s"
" ),\n",
strZ(configOptional));
" PARSE_RULE_OPTIONAL\n"
" (\n");
if (optionalCmdRuleSize != 0)
{
KeyValue *const combine = kvNew();
for (unsigned int ruleIdx = 0; ruleIdx < optionalCmdRuleSize; ruleIdx++)
{
const Variant *const cmd = varLstGet(kvKeyList(optionalCmdRule), ruleIdx);
const VariantList *const groupList = kvGetList(optionalCmdRule, cmd);
String *const group = strNew();
for (unsigned int groupIdx = 0; groupIdx < varLstSize(groupList); groupIdx++)
{
strCatFmt(
group,
"\n"
"%s,\n",
strZ(varStr(varLstGet(groupList, groupIdx))));
}
kvAdd(combine, VARSTR(group), cmd);
}
unsigned int combineSize = varLstSize(kvKeyList(combine));
for (unsigned int ruleIdx = 0; ruleIdx < combineSize; ruleIdx++)
{
const Variant *const group = varLstGet(kvKeyList(combine), ruleIdx);
const VariantList *const cmdList = kvGetList(combine, group);
if (ruleIdx != 0)
strCatChr(config, '\n');
strCatZ(
config,
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" PARSE_RULE_FILTER_CMD\n"
" (\n");
for (unsigned int cmdIdx = 0; cmdIdx < varLstSize(cmdList); cmdIdx++)
{
strCatFmt(
config,
" PARSE_RULE_VAL_CMD(%s),\n",
strZ(bldEnum("cfgCmd", varStr(varLstGet(cmdList, cmdIdx)))));
}
strCatFmt(
config,
" ),\n"
"%s"
" ),\n",
strZ(varStr(group)));
}
}
if (optionalDefaultRuleSize != 0)
{
if (optionalCmdRuleSize != 0)
strCatChr(config, '\n');
strCatZ(
config,
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n");
for (unsigned int ruleIdx = 0; ruleIdx < optionalDefaultRuleSize; ruleIdx++)
{
const Variant *const key = varLstGet(kvKeyList(optionalDefaultRule), ruleIdx);
if (ruleIdx != 0)
strCatChr(config, '\n');
strCatFmt(
config,
"%s,\n",
strZ(varStr(kvGet(optionalDefaultRule, key))));
}
strCatZ(config, " ),\n");
}
strCatZ(config, " ),\n");
}
strCatZ(config, " ),\n");
// Build rule values
if (strEq(opt->type, OPT_TYPE_BOOLEAN_STR))
continue;
StringList *ruleAddList = NULL;
if (strEq(opt->type, OPT_TYPE_STRING_STR) || strEq(opt->type, OPT_TYPE_PATH_STR))
{
if (allowList)
ruleAddList = ruleStrIdList;
else
ruleAddList = ruleStrList;
}
else
{
ruleAddList = ruleIntList;
ruleInt = true;
}
for (unsigned int ruleDataIdx = 0; ruleDataIdx < strLstSize(ruleDataList); ruleDataIdx++)
{
if (ruleInt)
strLstAddIfMissing(ruleAddList, strNewFmt("%20s", strZ(strLstGet(ruleDataList, ruleDataIdx))));
else
{
if (allowList)
strLstAddIfMissing(ruleAddList, strLstGet(ruleDataList, ruleDataIdx));
else
{
strLstAddIfMissing(
ruleAddList,
strNewFmt(
"%s%s%s", opt->defaultLiteral ? "" : "\"", strZ(strLstGet(ruleDataList, ruleDataIdx)),
opt->defaultLiteral ? "" : "\""));
}
}
}
}
strCatZ(
@ -790,9 +1124,138 @@ bldCfgRenderParseAutoC(const Storage *const storageRepo, const BldCfg bldCfg)
strCatZ(config, "};\n");
// Rule Strings
// -----------------------------------------------------------------------------------------------------------------------------
String *const configVal = strNew();
strLstSort(ruleStrList, sortOrderAsc);
strCatFmt(
configVal,
"\n"
COMMENT_BLOCK_BEGIN "\n"
"Rule Strings\n"
COMMENT_BLOCK_END "\n");
strCatFmt(
configVal, "%s\n",
strZ(
bldDefineRender(
STRDEF("PARSE_RULE_VAL_STR(value)"),
strNewFmt("PARSE_RULE_U32_%zu(value)", bldCfgRenderVar128Size(strLstSize(ruleStrList) - 1)))));
strCatFmt(
configVal,
"\n"
"static const StringPub parseRuleValueStr[] =\n"
"{\n");
for (unsigned int ruleStrIdx = 0; ruleStrIdx < strLstSize(ruleStrList); ruleStrIdx++)
strCatFmt(configVal, " PARSE_RULE_STRPUB(%s),\n", strZ(strLstGet(ruleStrList, ruleStrIdx)));
strCatZ(configVal, "};\n");
strCatZ(
configVal,
"\n"
"typedef enum\n"
"{\n");
for (unsigned int ruleStrIdx = 0; ruleStrIdx < strLstSize(ruleStrList); ruleStrIdx++)
{
strCatFmt(
configVal, " %s,\n",
strZ(strNewFmt("parseRuleValStr%s", strZ(bldCfgRenderEnumStr(strLstGet(ruleStrList, ruleStrIdx))))));
}
strCatZ(configVal, "} ParseRuleValueStr;\n");
// Rule StringIds
// -----------------------------------------------------------------------------------------------------------------------------
strLstSort(ruleStrIdList, sortOrderAsc);
strCatFmt(
configVal,
"\n"
COMMENT_BLOCK_BEGIN "\n"
"Rule StringIds\n"
COMMENT_BLOCK_END "\n");
strCatFmt(
configVal, "%s\n",
strZ(
bldDefineRender(
STRDEF("PARSE_RULE_VAL_STRID(value)"),
strNewFmt("PARSE_RULE_U32_%zu(value)", bldCfgRenderVar128Size(strLstSize(ruleStrIdList) - 1)))));
strCatFmt(
configVal,
"\n"
"static const StringId parseRuleValueStrId[] =\n"
"{\n");
for (unsigned int ruleStrIdIdx = 0; ruleStrIdIdx < strLstSize(ruleStrIdList); ruleStrIdIdx++)
strCatFmt(configVal, " %s,\n", strZ(bldStrId(strZ(strLstGet(ruleStrIdList, ruleStrIdIdx)))));
strCatZ(configVal, "};\n");
strCatZ(
configVal,
"\n"
"typedef enum\n"
"{\n");
for (unsigned int ruleStrIdIdx = 0; ruleStrIdIdx < strLstSize(ruleStrIdList); ruleStrIdIdx++)
strCatFmt(configVal, " %s,\n", strZ(bldEnum("parseRuleValStrId", strLstGet(ruleStrIdList, ruleStrIdIdx))));
strCatZ(configVal, "} ParseRuleValueStrId;\n");
// Rule Ints
// -----------------------------------------------------------------------------------------------------------------------------
strLstSort(ruleIntList, sortOrderAsc);
strCatFmt(
configVal,
"\n"
COMMENT_BLOCK_BEGIN "\n"
"Rule Ints\n"
COMMENT_BLOCK_END "\n");
strCatFmt(
configVal, "%s\n",
strZ(
bldDefineRender(
STRDEF("PARSE_RULE_VAL_INT(value)"),
strNewFmt("PARSE_RULE_U32_%zu(value)", bldCfgRenderVar128Size(strLstSize(ruleIntList) - 1)))));
strCatFmt(
configVal,
"\n"
"static const int64_t parseRuleValueInt[] =\n"
"{\n");
for (unsigned int ruleIntIdx = 0; ruleIntIdx < strLstSize(ruleIntList); ruleIntIdx++)
strCatFmt(configVal, " %s,\n", strZ(strTrim(strLstGet(ruleIntList, ruleIntIdx))));
strCatZ(configVal, "};\n");
strCatZ(
configVal,
"\n"
"typedef enum\n"
"{\n");
for (unsigned int ruleIntIdx = 0; ruleIntIdx < strLstSize(ruleIntList); ruleIntIdx++)
strCatFmt(configVal, " %s,\n", strZ(bldEnum("parseRuleValInt", strLstGet(ruleIntList, ruleIntIdx))));
strCatZ(configVal, "} ParseRuleValueInt;\n");
// Write to storage
// -----------------------------------------------------------------------------------------------------------------------------
bldPut(storageRepo, "src/config/parse.auto.c", BUFSTR(config));
bldPut(
storageRepo, "src/config/parse.auto.c",
BUFSTR(strNewFmt("%s%s%s", strZ(bldHeader(CONFIG_MODULE, PARSE_AUTO_COMMENT)), strZ(configVal), strZ(config))));
}
/**********************************************************************************************************************************/

View File

@ -449,7 +449,7 @@ helpRender(const Buffer *const helpData)
strFirstLower(summary);
// Output current and default values if they exist
const String *defaultValue = helpRenderValue(cfgOptionDefault(optionId), cfgParseOptionType(optionId));
const String *defaultValue = cfgOptionDefault(optionId);
const String *value = NULL;
if (cfgOptionIdxSource(optionId, 0) != cfgSourceDefault)
@ -516,7 +516,7 @@ helpRender(const Buffer *const helpData)
helpRenderText(optionData[option.id].description, optionData[option.id].internal, 0, true, CONSOLE_WIDTH)));
// Output current and default values if they exist
const String *defaultValue = helpRenderValue(cfgOptionDefault(option.id), cfgParseOptionType(option.id));
const String *defaultValue = cfgOptionDefault(option.id);
const String *value = NULL;
if (cfgOptionIdxSource(option.id, 0) != cfgSourceDefault)

View File

@ -377,41 +377,7 @@ cfgOptionIdxTotal(ConfigOption optionId)
}
/**********************************************************************************************************************************/
static Variant *
cfgOptionDefaultValue(ConfigOption optionId)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(ENUM, optionId);
FUNCTION_TEST_END();
Variant *result;
Variant *defaultValue = varNewStrZ(cfgParseOptionDefault(cfgCommand(), optionId));
switch (cfgParseOptionType(optionId))
{
case cfgOptTypeBoolean:
result = varNewBool(varBoolForce(defaultValue));
break;
case cfgOptTypeInteger:
case cfgOptTypeSize:
case cfgOptTypeTime:
result = varNewInt64(varInt64Force(defaultValue));
break;
case cfgOptTypePath:
case cfgOptTypeString:
result = varDup(defaultValue);
break;
default:
THROW_FMT(AssertError, "default value not available for option type %u", cfgParseOptionType(optionId));
}
FUNCTION_TEST_RETURN(result);
}
const Variant *
const String *
cfgOptionDefault(ConfigOption optionId)
{
FUNCTION_TEST_BEGIN();
@ -422,16 +388,7 @@ cfgOptionDefault(ConfigOption optionId)
ASSERT(optionId < CFG_OPTION_TOTAL);
if (configLocal->option[optionId].defaultValue == NULL)
{
if (cfgParseOptionDefault(cfgCommand(), optionId) != NULL)
{
MEM_CONTEXT_BEGIN(configLocal->memContext)
{
configLocal->option[optionId].defaultValue = cfgOptionDefaultValue(optionId);
}
MEM_CONTEXT_END();
}
}
configLocal->option[optionId].defaultValue = cfgParseOptionDefault(cfgCommand(), optionId);
FUNCTION_TEST_RETURN(configLocal->option[optionId].defaultValue);
}
@ -450,15 +407,18 @@ cfgOptionDefaultSet(ConfigOption optionId, const Variant *defaultValue)
MEM_CONTEXT_BEGIN(configLocal->memContext)
{
// Duplicate into this context
defaultValue = varDup(defaultValue);
// Set the default value
configLocal->option[optionId].defaultValue = varDup(defaultValue);
configLocal->option[optionId].defaultValue = varStr(defaultValue);
// Copy the value to option indexes that are marked as default so the default can be retrieved quickly
for (unsigned int optionIdx = 0; optionIdx < cfgOptionIdxTotal(optionId); optionIdx++)
{
if (configLocal->option[optionId].index[optionIdx].source == cfgSourceDefault)
{
configLocal->option[optionId].index[optionIdx].value = configLocal->option[optionId].defaultValue;
configLocal->option[optionId].index[optionIdx].value = defaultValue;
configLocal->option[optionId].index[optionIdx].display = NULL;
}
}

View File

@ -170,9 +170,6 @@ void cfgCommandSet(ConfigCommand commandId, ConfigCommandRole commandRoleId);
// pgBackRest exe
const String *cfgExe(void);
// Option default - should only be called by the help command
const Variant *cfgOptionDefault(ConfigOption optionId);
// Set option default. Option defaults are generally not set in advance because the vast majority of them are never used. It is
// more efficient to generate them when they are requested. Some defaults are (e.g. the exe path) are set at runtime.
void cfgOptionDefaultSet(ConfigOption optionId, const Variant *defaultValue);

View File

@ -65,7 +65,7 @@ typedef struct Config
bool valid; // Is option valid for current command?
bool group; // In a group?
unsigned int groupId; // Id if in a group
const Variant *defaultValue; // Default value
const String *defaultValue; // Default value
ConfigOptionValue *index; // List of indexed values (only 1 unless the option is indexed)
} option[CFG_OPTION_TOTAL];
} Config;
@ -94,6 +94,9 @@ unsigned int cfgOptionGroupId(ConfigOption optionId);
/***********************************************************************************************************************************
Option Functions
***********************************************************************************************************************************/
// Option default - should only be called by the help command
const String *cfgOptionDefault(ConfigOption optionId);
// Format a variant for display using the supplied option type. cfgOptionDisplay()/cfgOptionIdxDisplay() should be used whenever
// possible, but sometimes the variant needs to be manipulated before being formatted.
const String *cfgOptionDisplayVar(const Variant *const value, const ConfigOptionType optionType);

File diff suppressed because it is too large Load Diff

View File

@ -50,12 +50,6 @@ Prefix for environment variables
// In some environments this will not be extern'd
extern char **environ;
/***********************************************************************************************************************************
Option value constants
***********************************************************************************************************************************/
VARIANT_STRDEF_STATIC(OPTION_VALUE_0, ZERO_Z);
VARIANT_STRDEF_STATIC(OPTION_VALUE_1, ONE_Z);
/***********************************************************************************************************************************
Define how a command is parsed
***********************************************************************************************************************************/
@ -133,24 +127,29 @@ typedef struct ParseRuleOption
bool group:1; // In a group?
unsigned int groupId:1; // Id if in a group
bool deprecateMatch:1; // Does a deprecated name exactly match the option name?
unsigned int packSize:7; // Size of optional data in pack format
uint32_t commandRoleValid[CFG_COMMAND_ROLE_TOTAL]; // Valid for the command role?
const void **data; // Optional data and command overrides
const unsigned char *pack; // Optional data in pack format
} ParseRuleOption;
// Define additional types of data that can be associated with an option. Because these types are rare they are not given dedicated
// fields and are instead packed into an array which is read at runtime. This may seem inefficient but they are only accessed a
// single time during parse so space efficiency is more important than performance.
// fields and are instead packed and read at runtime. This may seem inefficient but they are only accessed a single time during
// parse so space efficiency is more important than performance.
typedef enum
{
parseRuleOptionDataTypeEnd, // Indicates there is no more data
parseRuleOptionDataTypeAllowList,
parseRuleOptionDataTypeAllowRange,
parseRuleOptionDataTypeCommand,
parseRuleOptionDataTypeDefault,
parseRuleOptionDataTypeDepend,
parseRuleOptionDataTypeRequired,
} ParseRuleOptionDataType;
parseRuleOptionalTypeValid = 1,
parseRuleOptionalTypeAllowRange,
parseRuleOptionalTypeAllowList,
parseRuleOptionalTypeDefault,
parseRuleOptionalTypeRequired,
} ParseRuleOptionalType;
// Optional rule filter types
typedef enum
{
parseRuleFilterTypeCommand = 1,
} ParseRuleFilterType;
// Macros used to define parse rules in parse.auto.c in a format that diffs well
#define PARSE_RULE_OPTION(...) \
@ -204,45 +203,46 @@ typedef enum
#define PARSE_RULE_OPTION_COMMAND(commandParam) \
| (1 << commandParam)
#define PARSE_RULE_OPTION_OPTIONAL_PUSH_LIST(type, size, data, ...) \
(const void *)((uint32_t)type << 24 | (uint32_t)size << 16 | (uint32_t)data), __VA_ARGS__
#define PARSE_RULE_STRPUB(value) {.buffer = (char *)value, .size = sizeof(value) - 1}
#define PARSE_RULE_OPTION_OPTIONAL_LIST(...) \
.data = (const void *[]){__VA_ARGS__ NULL}
// Macros used to define optional parse rules in pack format
#define PARSE_RULE_VARINT_01(value) \
value
#define PARSE_RULE_VARINT_02(value) \
0x80 | (value & 0x7f), (value >> 7) & 0x7f
#define PARSE_RULE_OPTION_OPTIONAL_PUSH(type, size, data) \
(const void *)((uint32_t)type << 24 | (uint32_t)size << 16 | (uint32_t)data)
#define PARSE_RULE_BOOL_TRUE 0x28
#define PARSE_RULE_BOOL_FALSE 0x20
#define PARSE_RULE_U32_1(value) 0x88, PARSE_RULE_VARINT_01(value)
#define PARSE_RULE_U32_2(value) 0x88, PARSE_RULE_VARINT_02(value)
#define PARSE_RULE_OPTION_OPTIONAL_COMMAND_OVERRIDE(...) \
__VA_ARGS__
#define PARSE_RULE_PACK(...) __VA_ARGS__ 0x00
#define PARSE_RULE_PACK_SIZE(...) \
0xf0, 0x02, sizeof((const unsigned char []){PARSE_RULE_PACK(__VA_ARGS__)}), \
PARSE_RULE_PACK(__VA_ARGS__)
#define PARSE_RULE_OPTION_OPTIONAL_COMMAND(command) \
PARSE_RULE_OPTION_OPTIONAL_PUSH(parseRuleOptionDataTypeCommand, 0, command)
#define PARSE_RULE_VAL_BOOL_TRUE PARSE_RULE_BOOL_TRUE
#define PARSE_RULE_VAL_BOOL_FALSE PARSE_RULE_BOOL_FALSE
#define PARSE_RULE_OPTION_OPTIONAL_ALLOW_LIST(...) \
PARSE_RULE_OPTION_OPTIONAL_PUSH_LIST( \
parseRuleOptionDataTypeAllowList, sizeof((const char *[]){__VA_ARGS__}) / sizeof(const char *), 0, __VA_ARGS__)
#define PARSE_RULE_OPTIONAL(...) \
.packSize = sizeof((const unsigned char []){PARSE_RULE_PACK(__VA_ARGS__)}), \
.pack = (const unsigned char []){PARSE_RULE_PACK(__VA_ARGS__)}
#define PARSE_RULE_OPTIONAL_GROUP(...) PARSE_RULE_PACK_SIZE(__VA_ARGS__)
#define PARSE_RULE_OPTION_OPTIONAL_ALLOW_RANGE(rangeMinParam, rangeMaxParam) \
PARSE_RULE_OPTION_OPTIONAL_PUSH_LIST( \
parseRuleOptionDataTypeAllowRange, 4, 0, \
(const void *)(intptr_t)(int32_t)((int64_t)rangeMinParam >> 32), \
(const void *)(intptr_t)(int32_t)((int64_t)rangeMinParam & 0xFFFFFFFF), \
(const void *)(intptr_t)(int32_t)((int64_t)rangeMaxParam >> 32), \
(const void *)(intptr_t)(int32_t)((int64_t)rangeMaxParam & 0xFFFFFFFF))
#define PARSE_RULE_FILTER_CMD(...) \
PARSE_RULE_PACK_SIZE(PARSE_RULE_U32_1(parseRuleFilterTypeCommand), __VA_ARGS__)
#define PARSE_RULE_OPTION_OPTIONAL_DEFAULT(defaultParam) \
PARSE_RULE_OPTION_OPTIONAL_PUSH_LIST(parseRuleOptionDataTypeDefault, 1, 0, defaultParam)
#define PARSE_RULE_OPTION_OPTIONAL_DEPEND(optionDepend) \
PARSE_RULE_OPTION_OPTIONAL_PUSH(parseRuleOptionDataTypeDepend, 0, optionDepend)
#define PARSE_RULE_OPTION_OPTIONAL_DEPEND_LIST(optionDepend, ...) \
PARSE_RULE_OPTION_OPTIONAL_PUSH_LIST( \
parseRuleOptionDataTypeDepend, sizeof((const char *[]){__VA_ARGS__}) / sizeof(const char *), optionDepend, __VA_ARGS__)
#define PARSE_RULE_OPTION_OPTIONAL_REQUIRED(requiredParam) \
PARSE_RULE_OPTION_OPTIONAL_PUSH(parseRuleOptionDataTypeRequired, 0, requiredParam)
#define PARSE_RULE_OPTIONAL_DEPEND(...) \
PARSE_RULE_U32_1(parseRuleOptionalTypeValid), PARSE_RULE_PACK_SIZE(__VA_ARGS__)
#define PARSE_RULE_OPTIONAL_ALLOW_LIST(...) \
PARSE_RULE_U32_1(parseRuleOptionalTypeAllowList), PARSE_RULE_PACK_SIZE(__VA_ARGS__)
#define PARSE_RULE_OPTIONAL_ALLOW_RANGE(...) \
PARSE_RULE_U32_1(parseRuleOptionalTypeAllowRange), PARSE_RULE_PACK_SIZE(__VA_ARGS__)
#define PARSE_RULE_OPTIONAL_DEFAULT(...) \
PARSE_RULE_U32_1(parseRuleOptionalTypeDefault), PARSE_RULE_PACK_SIZE(__VA_ARGS__)
#define PARSE_RULE_OPTIONAL_REQUIRED(...) \
PARSE_RULE_U32_1(parseRuleOptionalTypeRequired), PARSE_RULE_PACK_SIZE(__VA_ARGS__)
#define PARSE_RULE_OPTIONAL_NOT_REQUIRED(...) PARSE_RULE_OPTIONAL_REQUIRED(__VA_ARGS__)
/***********************************************************************************************************************************
Define option deprecations
@ -260,84 +260,6 @@ Include automatically generated parse data
***********************************************************************************************************************************/
#include "config/parse.auto.c"
/***********************************************************************************************************************************
Find optional data for a command and option
***********************************************************************************************************************************/
// Extract an int64 from optional data list
#define PARSE_RULE_DATA_INT64(data, index) \
((int64_t)(intptr_t)data.list[index] << 32 | (int64_t)(intptr_t)data.list[index + 1])
// Extracted option data
typedef struct ParseRuleOptionData
{
bool found; // Was the data found?
int data; // Data value
unsigned int listSize; // Data list size
const void **list; // Data list
} ParseRuleOptionData;
static ParseRuleOptionData
parseRuleOptionDataFind(ParseRuleOptionDataType typeFind, ConfigCommand commandId, ConfigOption optionId)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(ENUM, typeFind);
FUNCTION_TEST_PARAM(ENUM, commandId);
FUNCTION_TEST_PARAM(ENUM, optionId);
FUNCTION_TEST_END();
ParseRuleOptionData result = {0};
const void **dataList = parseRuleOption[optionId].data;
// Only proceed if there is data
if (dataList != NULL)
{
ParseRuleOptionDataType type;
unsigned int offset = 0;
unsigned int size;
int data;
unsigned int commandCurrent = UINT_MAX;
// Loop through all data
do
{
// Extract data
type = (ParseRuleOptionDataType)(((uintptr_t)dataList[offset] >> 24) & 0xFF);
size = ((uintptr_t)dataList[offset] >> 16) & 0xFF;
data = (uintptr_t)dataList[offset] & 0xFFFF;
// If a command block then set the current command
if (type == parseRuleOptionDataTypeCommand)
{
// If data was not found in the expected command then there's nothing more to look for
if (commandCurrent == commandId)
break;
// Set the current command
commandCurrent = (unsigned int)data;
}
// Only find type if not in a command block yet or in the expected command
else if (type == typeFind && (commandCurrent == UINT_MAX || commandCurrent == commandId))
{
// Store the data found
result.found = true;
result.data = data;
result.listSize = size;
result.list = &dataList[offset + 1];
// If found in the expected command block then nothing more to look for
if (commandCurrent == commandId)
break;
}
offset += size + 1;
}
while (type != parseRuleOptionDataTypeEnd);
}
FUNCTION_TEST_RETURN(result);
}
/***********************************************************************************************************************************
Struct to hold options parsed from the command line
***********************************************************************************************************************************/
@ -739,8 +661,356 @@ cfgParseOption(const String *const optionCandidate, const CfgParseOptionParam pa
FUNCTION_TEST_RETURN((CfgParseOptionResult){0});
}
/***********************************************************************************************************************************
Get the underlying data type for an option
***********************************************************************************************************************************/
typedef enum
{
cfgOptDataTypeBoolean, // Boolean
cfgOptDataTypeInteger, // Signed 64-bit integer
cfgOptDataTypeString, // String
} ConfigOptionDataType;
static ConfigOptionDataType
cfgParseOptionDataType(ConfigOption optionId)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(ENUM, optionId);
FUNCTION_TEST_END();
ASSERT(optionId < CFG_OPTION_TOTAL);
switch (parseRuleOption[optionId].type)
{
case cfgOptTypeBoolean:
FUNCTION_TEST_RETURN(cfgOptDataTypeBoolean);
default:
break;
}
ASSERT(
parseRuleOption[optionId].type == cfgOptTypeHash || parseRuleOption[optionId].type == cfgOptTypeList ||
parseRuleOption[optionId].type == cfgOptTypePath || parseRuleOption[optionId].type == cfgOptTypeString);
FUNCTION_TEST_RETURN(cfgOptDataTypeString);
}
/***********************************************************************************************************************************
Find an optional rule
***********************************************************************************************************************************/
typedef struct CfgParseOptionalRuleState
{
PackRead *pack;
unsigned int typeNext;
bool done;
// Valid
const unsigned char *valid;
size_t validSize;
// Allow range
int64_t allowRangeMin;
int64_t allowRangeMax;
// Allow list
const unsigned char *allowList;
size_t allowListSize;
// Default
const Variant *defaultValue;
const String *defaultRaw;
// Required
bool required;
} CfgParseOptionalRuleState;
static bool
cfgParseOptionalFilterDepend(PackRead *const filter, const Config *const config, const unsigned int optionListIdx)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(PACK_READ, filter);
FUNCTION_TEST_PARAM_P(VOID, config);
FUNCTION_TEST_PARAM(UINT, optionListIdx);
FUNCTION_TEST_END();
// Get the depend option value
const ConfigOption dependId = (ConfigOption)pckReadU32P(filter);
ASSERT(config->option[dependId].index != NULL);
const Variant *const dependValue = config->option[dependId].index[optionListIdx].value;
// Is the dependency resolved?
bool result = false;
if (dependValue != NULL)
{
// If a depend list exists, make sure the value is in the list
if (pckReadNext(filter))
{
StringId dependValueStrId = 0;
if (cfgParseOptionDataType(dependId) == cfgOptDataTypeString)
{
TRY_BEGIN()
{
dependValueStrId = strIdFromStr(stringIdBit5, varStr(dependValue));
}
CATCH_ANY()
{
dependValueStrId = strIdFromStr(stringIdBit6, varStr(dependValue));
}
TRY_END();
}
do
{
switch (cfgParseOptionDataType(dependId))
{
case cfgOptDataTypeBoolean:
result = pckReadBoolP(filter) == varBool(dependValue);
break;
default:
{
ASSERT(cfgParseOptionDataType(dependId) == cfgOptDataTypeString);
if (parseRuleValueStrId[pckReadU32P(filter)] == dependValueStrId)
result = true;
break;
}
}
}
while (pckReadNext(filter));
}
else
result = true;
}
FUNCTION_TEST_RETURN(result);
}
static bool
cfgParseOptionalRule(
CfgParseOptionalRuleState *optionalRules, ParseRuleOptionalType optionalRuleType, ConfigCommand commandId,
const ConfigOption optionId)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM_P(VOID, optionalRules);
FUNCTION_TEST_PARAM(ENUM, optionalRuleType);
FUNCTION_TEST_PARAM(ENUM, commandId);
FUNCTION_TEST_PARAM(ENUM, optionId);
FUNCTION_TEST_END();
ASSERT(optionalRuleType != 0);
ASSERT(commandId < CFG_COMMAND_TOTAL);
ASSERT(optionId < CFG_OPTION_TOTAL);
bool result = false;
// Check for optional rules
if (!optionalRules->done && parseRuleOption[optionId].pack != NULL)
{
// Initialize optional rules
if (optionalRules->pack == NULL)
{
PackRead *const groupList = pckReadNewC(parseRuleOption[optionId].pack, parseRuleOption[optionId].packSize);
MEM_CONTEXT_TEMP_BEGIN()
{
// Seach for a matching group
do
{
// Get the group pack
PackRead *group = pckReadPackReadConstP(groupList);
// Process filters if any
pckReadNext(group);
if (pckReadType(group) == pckTypePack)
{
// Check for filter match
bool match = false;
PackRead *const filter = pckReadPackReadConstP(group);
const ParseRuleFilterType filterType = (ParseRuleFilterType)pckReadU32P(filter);
switch (filterType)
{
default:
{
ASSERT(filterType == parseRuleFilterTypeCommand);
while (pckReadNext(filter))
{
if ((ConfigCommand)pckReadU32P(filter) == commandId)
{
match = true;
break;
}
}
break;
}
}
// Filter did not match
if (!match)
group = NULL;
}
// If the group matched
if (group != NULL)
{
// Get first optional rule type. This is needed since pckReadNext() has already been called and it cannot
// be called again until data is read.
optionalRules->typeNext = pckReadU32P(group);
ASSERT(optionalRules->typeNext != 0);
// Move group to prior context and stop searching
optionalRules->pack = pckReadMove(group, memContextPrior());
break;
}
}
while (pckReadNext(groupList));
}
MEM_CONTEXT_TEMP_END();
// If no group matched then done
if (optionalRules->pack == NULL)
{
optionalRules->done = true;
FUNCTION_TEST_RETURN(false);
}
}
// Search for the specified optional rule
do
{
// Read the next optional rule type if it has not already been read
if (optionalRules->typeNext == 0)
{
// If there are no more rules then done
if (!pckReadNext(optionalRules->pack))
{
optionalRules->done = true;
FUNCTION_TEST_RETURN(false);
}
optionalRules->typeNext = pckReadU32P(optionalRules->pack);
ASSERT(optionalRules->typeNext != 0);
}
// If this is the requested optional rule
if (optionalRules->typeNext == optionalRuleType)
{
// Optional rule was found
result = true;
// Process optional rule
switch (optionalRuleType)
{
case parseRuleOptionalTypeValid:
{
pckReadNext(optionalRules->pack);
optionalRules->valid = pckReadBufPtr(optionalRules->pack);
optionalRules->validSize = pckReadSize(optionalRules->pack);
pckReadConsume(optionalRules->pack);
break;
}
case parseRuleOptionalTypeAllowList:
{
pckReadNext(optionalRules->pack);
optionalRules->allowList = pckReadBufPtr(optionalRules->pack);
optionalRules->allowListSize = pckReadSize(optionalRules->pack);
pckReadConsume(optionalRules->pack);
break;
}
case parseRuleOptionalTypeAllowRange:
{
PackRead *const ruleData = pckReadPackReadConstP(optionalRules->pack);
optionalRules->allowRangeMin = parseRuleValueInt[pckReadU32P(ruleData)];
optionalRules->allowRangeMax = parseRuleValueInt[pckReadU32P(ruleData)];
break;
}
case parseRuleOptionalTypeDefault:
{
PackRead *const ruleData = pckReadPackReadConstP(optionalRules->pack);
pckReadNext(ruleData);
switch (pckReadType(ruleData))
{
case pckTypeBool:
optionalRules->defaultValue = pckReadBoolP(ruleData) ? BOOL_TRUE_VAR : BOOL_FALSE_VAR;
optionalRules->defaultRaw = varBool(optionalRules->defaultValue) ? Y_STR : N_STR;
break;
default:
{
switch (parseRuleOption[optionId].type)
{
case cfgOptTypeInteger:
case cfgOptTypeTime:
case cfgOptTypeSize:
{
optionalRules->defaultValue = varNewInt64(parseRuleValueInt[pckReadU32P(ruleData)]);
optionalRules->defaultRaw = (const String *)&parseRuleValueStr[pckReadU32P(ruleData)];
break;
}
case cfgOptTypePath:
case cfgOptTypeString:
{
optionalRules->defaultRaw = (const String *)&parseRuleValueStr[pckReadU32P(ruleData)];
optionalRules->defaultValue = varNewStr(optionalRules->defaultRaw);
break;
}
}
}
}
break;
}
default:
{
ASSERT(optionalRuleType == parseRuleOptionalTypeRequired);
optionalRules->required = !parseRuleOption[optionId].required;
}
}
}
// Else not the requested optional rule
else
{
// If the optional rule type is greater than requested then return. The optional rule may be requested later.
if (optionalRules->typeNext > optionalRuleType)
FUNCTION_TEST_RETURN(false);
// Consume the unused optional rule
pckReadConsume(optionalRules->pack);
}
// Type will need to be read again on next iteration
optionalRules->typeNext = 0;
}
while (!result);
}
FUNCTION_TEST_RETURN(result);
}
/**********************************************************************************************************************************/
const char *
const String *
cfgParseOptionDefault(ConfigCommand commandId, ConfigOption optionId)
{
FUNCTION_TEST_BEGIN();
@ -751,12 +1021,18 @@ cfgParseOptionDefault(ConfigCommand commandId, ConfigOption optionId)
ASSERT(commandId < CFG_COMMAND_TOTAL);
ASSERT(optionId < CFG_OPTION_TOTAL);
ParseRuleOptionData data = parseRuleOptionDataFind(parseRuleOptionDataTypeDefault, commandId, optionId);
const String *result = NULL;
if (data.found)
FUNCTION_TEST_RETURN((const char *)data.list[0]);
MEM_CONTEXT_TEMP_BEGIN()
{
CfgParseOptionalRuleState optionalRules = {0};
FUNCTION_TEST_RETURN(NULL);
if (cfgParseOptionalRule(&optionalRules, parseRuleOptionalTypeDefault, commandId, optionId))
result = optionalRules.defaultRaw;
}
MEM_CONTEXT_TEMP_END();
FUNCTION_TEST_RETURN(result);
}
/**********************************************************************************************************************************/
@ -810,10 +1086,23 @@ cfgParseOptionRequired(ConfigCommand commandId, ConfigOption optionId)
ASSERT(commandId < CFG_COMMAND_TOTAL);
ASSERT(optionId < CFG_OPTION_TOTAL);
ParseRuleOptionData data = parseRuleOptionDataFind(parseRuleOptionDataTypeRequired, commandId, optionId);
bool found = false;
bool result = false;
if (data.found)
FUNCTION_TEST_RETURN((bool)data.data);
MEM_CONTEXT_TEMP_BEGIN()
{
CfgParseOptionalRuleState optionalRules = {0};
if (cfgParseOptionalRule(&optionalRules, parseRuleOptionalTypeRequired, commandId, optionId))
{
found = true;
result = optionalRules.required;
}
}
MEM_CONTEXT_TEMP_END();
if (found)
FUNCTION_TEST_RETURN(result);
FUNCTION_TEST_RETURN(parseRuleOption[optionId].required);
}
@ -1503,8 +1792,10 @@ configParse(const Storage *storage, unsigned int argListSize, const char *argLis
// ---------------------------------------------------------------------------------------------------------------------
// Load the configuration file(s)
String *configString = cfgFileLoad(
storage, parseOptionList, STR(cfgParseOptionDefault(config->command, cfgOptConfig)),
STR(cfgParseOptionDefault(config->command, cfgOptConfigIncludePath)), PGBACKREST_CONFIG_ORIG_PATH_FILE_STR);
storage, parseOptionList,
(const String *)&parseRuleValueStr[parseRuleValStrCFGOPTDEF_CONFIG_PATH_SP_QT_FS_QT_SP_PROJECT_CONFIG_FILE],
(const String *)&parseRuleValueStr[parseRuleValStrCFGOPTDEF_CONFIG_PATH_SP_QT_FS_QT_SP_PROJECT_CONFIG_INCLUDE_PATH],
PGBACKREST_CONFIG_ORIG_PATH_FILE_STR);
if (configString != NULL)
{
@ -1800,115 +2091,87 @@ configParse(const Storage *storage, unsigned int argListSize, const char *argLis
// Initialize option value and set negate and reset flag
*configOptionValue = (ConfigOptionValue){.negate = parseOptionValue->negate, .reset = parseOptionValue->reset};
// Check option dependencies
bool dependResolved = true;
ParseRuleOptionData depend = parseRuleOptionDataFind(parseRuleOptionDataTypeDepend, config->command, optionId);
// Is the option valid?
bool valid = true;
CfgParseOptionalRuleState optionalRules = {0};
if (depend.found)
if (cfgParseOptionalRule(&optionalRules, parseRuleOptionalTypeValid, config->command, optionId))
{
ConfigOption dependOptionId = (ConfigOption)depend.data;
ConfigOptionType dependOptionType = cfgParseOptionType(dependOptionId);
PackRead *filter = pckReadNewC(optionalRules.valid, optionalRules.validSize);
valid = cfgParseOptionalFilterDepend(filter, config, optionListIdx);
ASSERT(config->option[dependOptionId].index != NULL);
// Get the depend option value
const Variant *dependValue = config->option[dependOptionId].index[optionListIdx].value;
if (dependValue != NULL)
// If depend not resolved and option value is set on the command-line then error. It is OK to have
// unresolved options in the config file because they may be there for another command. For instance,
// spool-path is only loaded for the archive-push command when archive-async=y, and the presence of
// spool-path in the config file should not cause an error here, it will just end up null.
if (!valid && optionSet && parseOptionValue->source == cfgSourceParam)
{
if (dependOptionType == cfgOptTypeBoolean)
{
if (varBool(dependValue))
dependValue = OPTION_VALUE_1;
else
dependValue = OPTION_VALUE_0;
}
}
PackRead *filter = pckReadNewC(optionalRules.valid, optionalRules.validSize);
ConfigOption dependId = pckReadU32P(filter);
// Can't resolve if the depend option value is null
if (dependValue == NULL)
{
dependResolved = false;
// Get depend option name
const String *dependOptionName = STR(cfgParseOptionKeyIdxName(dependId, optionKeyIdx));
// If depend not resolved and option value is set on the command-line then error. See unresolved list
// depend below for a detailed explanation.
if (optionSet && parseOptionValue->source == cfgSourceParam)
// If depend value is not set
ASSERT(config->option[dependId].index != NULL);
if (config->option[dependId].index[optionListIdx].value == NULL)
{
THROW_FMT(
OptionInvalidError, "option '%s' not valid without option '%s'",
cfgParseOptionKeyIdxName(optionId, optionKeyIdx),
cfgParseOptionKeyIdxName(dependOptionId, optionKeyIdx));
cfgParseOptionKeyIdxName(optionId, optionKeyIdx), strZ(dependOptionName));
}
}
// If a depend list exists, make sure the value is in the list
else if (depend.listSize > 0)
{
dependResolved = false;
for (unsigned int listIdx = 0; listIdx < depend.listSize; listIdx++)
// Build type dependent error data
const String *errorValue = EMPTY_STR;
switch(cfgParseOptionDataType(dependId))
{
if (strEqZ(varStr(dependValue), (const char *)depend.list[listIdx]))
case cfgOptDataTypeBoolean:
{
dependResolved = true;
if (!pckReadBoolP(filter))
dependOptionName = strNewFmt("no-%s", strZ(dependOptionName));
break;
}
}
// If depend not resolved and option value is set on the command-line then error. It's OK to have
// unresolved options in the config file because they may be there for another command. For instance,
// spool-path is only loaded for the archive-push command when archive-async=y, and the presence of
// spool-path in the config file should not cause an error here, it will just end up null.
if (!dependResolved && optionSet && parseOptionValue->source == cfgSourceParam)
{
// Get the depend option name
const String *dependOptionName = STR(cfgParseOptionKeyIdxName(dependOptionId, optionKeyIdx));
// Build the list of possible depend values
StringList *dependValueList = strLstNew();
for (unsigned int listIdx = 0; listIdx < depend.listSize; listIdx++)
default:
{
const char *dependValue = (const char *)depend.list[listIdx];
ASSERT(cfgParseOptionDataType(dependId) == cfgOptDataTypeString);
// Build list based on depend option type
if (dependOptionType == cfgOptTypeBoolean)
String *const errorList = strNew();
unsigned int validSize = 0;
while (pckReadNext(filter))
{
// Boolean outputs depend option name as no-* when false
if (strcmp(dependValue, ZERO_Z) == 0)
{
dependOptionName =
strNewFmt("no-%s", cfgParseOptionKeyIdxName(dependOptionId, optionKeyIdx));
}
strCatFmt(
errorList, "%s'%s'", validSize != 0 ? ", " : "",
strZ(strIdToStr(parseRuleValueStrId[pckReadU32P(filter)])));
validSize++;
}
ASSERT(validSize > 0);
if (validSize == 1)
errorValue = strNewFmt(" = %s", strZ(errorList));
else
{
ASSERT(dependOptionType == cfgOptTypePath || dependOptionType == cfgOptTypeString);
strLstAdd(dependValueList, strNewFmt("'%s'", dependValue));
}
errorValue = strNewFmt(" in (%s)", strZ(errorList));
}
// Build the error string
const String *errorValue = EMPTY_STR;
if (strLstSize(dependValueList) == 1)
errorValue = strNewFmt(" = %s", strZ(strLstGet(dependValueList, 0)));
else if (strLstSize(dependValueList) > 1)
errorValue = strNewFmt(" in (%s)", strZ(strLstJoin(dependValueList, ", ")));
// Throw the error
THROW(
OptionInvalidError,
strZ(
strNewFmt(
"option '%s' not valid without option '%s'%s",
cfgParseOptionKeyIdxName(optionId, optionKeyIdx), strZ(dependOptionName),
strZ(errorValue))));
}
THROW(
OptionInvalidError,
strZ(
strNewFmt(
"option '%s' not valid without option '%s'%s", cfgParseOptionKeyIdxName(optionId, optionKeyIdx),
strZ(dependOptionName), strZ(errorValue))));
}
pckReadFree(filter);
}
// Is the option resolved?
if (dependResolved)
if (valid)
{
// Is the option set?
if (optionSet)
@ -1961,13 +2224,12 @@ configParse(const Storage *storage, unsigned int argListSize, const char *argLis
{
String *value = strLstGet(parseOptionValue->valueList, 0);
const String *valueAllow = value;
int64_t valueInt64 = 0;
// If a numeric type check that the value is valid
if (optionType == cfgOptTypeInteger || optionType == cfgOptTypeSize ||
optionType == cfgOptTypeTime)
{
int64_t valueInt64 = 0;
// Preserve original value to display
MEM_CONTEXT_BEGIN(config->memContext)
{
@ -2021,13 +2283,9 @@ configParse(const Storage *storage, unsigned int argListSize, const char *argLis
}
TRY_END();
// Check value range
ParseRuleOptionData allowRange = parseRuleOptionDataFind(
parseRuleOptionDataTypeAllowRange, config->command, optionId);
if (allowRange.found &&
(valueInt64 < PARSE_RULE_DATA_INT64(allowRange, 0) ||
valueInt64 > PARSE_RULE_DATA_INT64(allowRange, 2)))
if (cfgParseOptionalRule(
&optionalRules, parseRuleOptionalTypeAllowRange, config->command, optionId) &&
(valueInt64 < optionalRules.allowRangeMin || valueInt64 > optionalRules.allowRangeMax))
{
THROW_FMT(
OptionInvalidValueError, "'%s' is out of range for '%s' option", strZ(value),
@ -2077,23 +2335,67 @@ configParse(const Storage *storage, unsigned int argListSize, const char *argLis
}
// If the option has an allow list then check it
ParseRuleOptionData allowList = parseRuleOptionDataFind(
parseRuleOptionDataTypeAllowList, config->command, optionId);
if (allowList.found)
if (cfgParseOptionalRule(
&optionalRules, parseRuleOptionalTypeAllowList, config->command, optionId))
{
unsigned int listIdx = 0;
PackRead *const allowList = pckReadNewC(optionalRules.allowList, optionalRules.allowListSize);
bool allowListFound = false;
for (; listIdx < allowList.listSize; listIdx++)
if (parseRuleOption[optionId].type == cfgOptTypeString)
{
if (strEqZ(valueAllow, (const char *)allowList.list[listIdx]))
break;
bool valueValid = true;
StringId value = 0;
TRY_BEGIN()
{
TRY_BEGIN()
{
value = strIdFromStr(stringIdBit5, valueAllow);
}
CATCH_ANY()
{
value = strIdFromStr(stringIdBit6, valueAllow);
}
TRY_END();
}
CATCH_ANY()
{
valueValid = false;
}
TRY_END();
if (valueValid)
{
while (pckReadNext(allowList))
{
if (parseRuleValueStrId[pckReadU32P(allowList)] == value)
{
allowListFound = true;
break;
}
}
}
}
else
{
ASSERT(parseRuleOption[optionId].type == cfgOptTypeSize);
while (pckReadNext(allowList))
{
if (parseRuleValueInt[pckReadU32P(allowList)] == valueInt64)
{
allowListFound = true;
break;
}
}
}
if (listIdx == allowList.listSize)
pckReadFree(allowList);
if (!allowListFound)
{
THROW_FMT(
OptionInvalidValueError, "'%s' is not allowed for '%s' option", strZ(value),
OptionInvalidValueError, "'%s' is not allowed for '%s' option", strZ(valueAllow),
cfgParseOptionKeyIdxName(optionId, optionKeyIdx));
}
}
@ -2104,46 +2406,41 @@ configParse(const Storage *storage, unsigned int argListSize, const char *argLis
// Else try to set a default
else
{
// Get the default value for this option
const char *value = cfgParseOptionDefault(config->command, optionId);
bool found = false;
MEM_CONTEXT_BEGIN(config->memContext)
{
found = cfgParseOptionalRule(
&optionalRules, parseRuleOptionalTypeDefault, config->command, optionId);
}
MEM_CONTEXT_END();
// If the option has a default
if (value != NULL)
if (found)
{
MEM_CONTEXT_BEGIN(config->memContext)
{
// This would typically be a switch but since not all cases are covered it would require a
// separate function which does not seem worth it. The eventual plan is to have all the defaults
// represented as constants so they can be assigned directly without creating variants.
if (optionType == cfgOptTypeBoolean)
configOptionValue->value = strcmp(value, ONE_Z) == 0 ? BOOL_TRUE_VAR : BOOL_FALSE_VAR;
else if (optionType == cfgOptTypePath || optionType == cfgOptTypeString)
configOptionValue->value = varNewStrZ(value);
else
{
ASSERT(
optionType == cfgOptTypeInteger || optionType == cfgOptTypeSize ||
optionType == cfgOptTypeTime);
configOptionValue->value = varNewInt64(cvtZToInt64(value));
}
}
MEM_CONTEXT_END();
configOptionValue->value = optionalRules.defaultValue;
}
// Else error if option is required and help was not requested
else if (cfgParseOptionRequired(config->command, optionId) && !config->help)
else
{
const char *hint = "";
const bool required = cfgParseOptionalRule(
&optionalRules, parseRuleOptionalTypeRequired, config->command, optionId) ?
optionalRules.required : parseRuleOption[optionId].required;
if (parseRuleOption[optionId].section == cfgSectionStanza)
hint = "\nHINT: does this stanza exist?";
THROW_FMT(
OptionRequiredError, "%s command requires option: %s%s", cfgParseCommandName(config->command),
cfgParseOptionKeyIdxName(optionId, optionKeyIdx), hint);
if (required && !config->help)
{
THROW_FMT(
OptionRequiredError, "%s command requires option: %s%s",
cfgParseCommandName(config->command),
cfgParseOptionKeyIdxName(optionId, optionKeyIdx),
parseRuleOption[optionId].section == cfgSectionStanza ?
"\nHINT: does this stanza exist?" : "");
}
}
}
}
pckReadFree(optionalRules.pack);
}
}
}

View File

@ -62,7 +62,7 @@ typedef struct CfgParseOptionResult
CfgParseOptionResult cfgParseOption(const String *const optionName, const CfgParseOptionParam param);
// Default value for the option
const char *cfgParseOptionDefault(ConfigCommand commandId, ConfigOption optionId);
const String *cfgParseOptionDefault(ConfigCommand commandId, ConfigOption optionId);
// Option name from id
const char *cfgParseOptionName(ConfigOption optionId);

View File

@ -228,8 +228,28 @@ testRun(void)
" timeout:\n"
" type: time\n"
" default: 10\n"
" command:\n"
" backup:\n"
" default: 20\n"
" archive-get:\n"
" default: 30\n"
" allow-range: [5, 50]\n"
"\n"
" buffer-size:\n"
" section: global\n"
" type: size\n"
" command:\n"
" backup: {}\n"
" archive-get:\n"
" default: 32768\n"
" allow-list:\n"
" - 32768\n"
" allow-list:\n"
" - 8192\n"
" - 16384\n"
" command-role:\n"
" main: {}\n"
"\n"
" compress-type:\n"
" section: global\n"
" type: string\n"
@ -267,6 +287,17 @@ testRun(void)
" default: CFGOPTDEF_CONFIG_PATH \"/\" PROJECT_CONFIG_FILE\n"
" default-literal: true\n"
" negate: true\n"
" command:\n"
" backup: {}\n"
" archive-get:\n"
" default: CFGOPTDEF_CONFIG_PATH \"/.\" PROJECT_CONFIG_FILE\n"
"\n"
" config-include:\n"
" section: global\n"
" type: path\n"
" default: /include\n"
" command-role:\n"
" main: {}\n"
"\n"
" log-level-console:\n"
" section: global\n"
@ -311,7 +342,8 @@ testRun(void)
" type: boolean\n"
" default: true\n"
" command:\n"
" backup: {}\n"
" backup:\n"
" default: false\n"
" command-role:\n"
" main: {}\n"
" deprecate:\n"
@ -323,6 +355,8 @@ testRun(void)
" default: false\n"
" depend:\n"
" option: online\n"
" list:\n"
" - true\n"
" command:\n"
" backup: {}\n"
" command-role:\n"
@ -345,11 +379,20 @@ testRun(void)
" section: stanza\n"
" group: pg\n"
" type: string\n"
" command:\n"
" archive-get:\n"
" default: host1\n"
" backup: {}\n"
" deprecate:\n"
" pg?-host: {}\n");
TEST_RESULT_VOID(bldCfgRender(storageTest, bldCfgParse(storageTest)), "parse and render");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("bldCfgRenderVar128Size()");
TEST_RESULT_UINT(bldCfgRenderVar128Size(10000), 2, "check size");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("check config.auto.h");
@ -383,17 +426,19 @@ testRun(void)
"Option constants\n"
COMMENT_BLOCK_END "\n"
"#define CFGOPT_BACKUP_STANDBY \"backup-standby\"\n"
"#define CFGOPT_BUFFER_SIZE \"buffer-size\"\n"
"#define CFGOPT_COMPRESS_LEVEL \"compress-level\"\n"
"#define CFGOPT_COMPRESS_LEVEL_NETWORK \"compress-level-network\"\n"
"#define CFGOPT_COMPRESS_TYPE \"compress-type\"\n"
"#define CFGOPT_CONFIG \"config\"\n"
"#define CFGOPT_CONFIG_INCLUDE \"config-include\"\n"
"#define CFGOPT_LOG_LEVEL_CONSOLE \"log-level-console\"\n"
"#define CFGOPT_LOG_LEVEL_FILE \"log-level-file\"\n"
"#define CFGOPT_ONLINE \"online\"\n"
"#define CFGOPT_STANZA \"stanza\"\n"
"#define CFGOPT_TIMEOUT \"timeout\"\n"
"\n"
"#define CFG_OPTION_TOTAL 12\n"
"#define CFG_OPTION_TOTAL 14\n"
"\n"
COMMENT_BLOCK_BEGIN "\n"
"Option value constants\n"
@ -443,10 +488,12 @@ testRun(void)
"typedef enum\n"
"{\n"
" cfgOptBackupStandby,\n"
" cfgOptBufferSize,\n"
" cfgOptCompressLevel,\n"
" cfgOptCompressLevelNetwork,\n"
" cfgOptCompressType,\n"
" cfgOptConfig,\n"
" cfgOptConfigInclude,\n"
" cfgOptLogLevelConsole,\n"
" cfgOptLogLevelFile,\n"
" cfgOptOnline,\n"
@ -471,8 +518,101 @@ testRun(void)
COMMENT_BLOCK_END "\n"
"\n"
COMMENT_BLOCK_BEGIN "\n"
"Rule Strings\n"
COMMENT_BLOCK_END "\n"
"#define PARSE_RULE_VAL_STR(value) PARSE_RULE_U32_1(value)\n"
"\n"
"static const StringPub parseRuleValueStr[] =\n"
"{\n"
" PARSE_RULE_STRPUB(\"/include\"),\n"
" PARSE_RULE_STRPUB(\"10\"),\n"
" PARSE_RULE_STRPUB(\"20\"),\n"
" PARSE_RULE_STRPUB(\"30\"),\n"
" PARSE_RULE_STRPUB(\"32768\"),\n"
" PARSE_RULE_STRPUB(\"gz\"),\n"
" PARSE_RULE_STRPUB(\"host1\"),\n"
" PARSE_RULE_STRPUB(\"info\"),\n"
" PARSE_RULE_STRPUB(\"warn\"),\n"
" PARSE_RULE_STRPUB(CFGOPTDEF_CONFIG_PATH \"/\" PROJECT_CONFIG_FILE),\n"
" PARSE_RULE_STRPUB(CFGOPTDEF_CONFIG_PATH \"/.\" PROJECT_CONFIG_FILE),\n"
"};\n"
"\n"
"typedef enum\n"
"{\n"
" parseRuleValStrQT_FS_include_QT,\n"
" parseRuleValStrQT_10_QT,\n"
" parseRuleValStrQT_20_QT,\n"
" parseRuleValStrQT_30_QT,\n"
" parseRuleValStrQT_32768_QT,\n"
" parseRuleValStrQT_gz_QT,\n"
" parseRuleValStrQT_host1_QT,\n"
" parseRuleValStrQT_info_QT,\n"
" parseRuleValStrQT_warn_QT,\n"
" parseRuleValStrCFGOPTDEF_CONFIG_PATH_SP_QT_FS_QT_SP_PROJECT_CONFIG_FILE,\n"
" parseRuleValStrCFGOPTDEF_CONFIG_PATH_SP_QT_FS_DT_QT_SP_PROJECT_CONFIG_FILE,\n"
"} ParseRuleValueStr;\n"
"\n"
COMMENT_BLOCK_BEGIN "\n"
"Rule StringIds\n"
COMMENT_BLOCK_END "\n"
"#define PARSE_RULE_VAL_STRID(value) PARSE_RULE_U32_1(value)\n"
"\n"
"static const StringId parseRuleValueStrId[] =\n"
"{\n"
" STRID6(\"debug1\", 0x7475421441),\n"
" STRID5(\"error\", 0x127ca450),\n"
" STRID5(\"info\", 0x799c90),\n"
" STRID5(\"off\", 0x18cf0),\n"
" STRID5(\"warn\", 0x748370),\n"
"};\n"
"\n"
"typedef enum\n"
"{\n"
" parseRuleValStrIdDebug1,\n"
" parseRuleValStrIdError,\n"
" parseRuleValStrIdInfo,\n"
" parseRuleValStrIdOff,\n"
" parseRuleValStrIdWarn,\n"
"} ParseRuleValueStrId;\n"
"\n"
COMMENT_BLOCK_BEGIN "\n"
"Rule Ints\n"
COMMENT_BLOCK_END "\n"
"#define PARSE_RULE_VAL_INT(value) PARSE_RULE_U32_1(value)\n"
"\n"
"static const int64_t parseRuleValueInt[] =\n"
"{\n"
" 0,\n"
" 9,\n"
" 5000,\n"
" 8192,\n"
" 10000,\n"
" 16384,\n"
" 20000,\n"
" 30000,\n"
" 32768,\n"
" 50000,\n"
"};\n"
"\n"
"typedef enum\n"
"{\n"
" parseRuleValInt0,\n"
" parseRuleValInt9,\n"
" parseRuleValInt5000,\n"
" parseRuleValInt8192,\n"
" parseRuleValInt10000,\n"
" parseRuleValInt16384,\n"
" parseRuleValInt20000,\n"
" parseRuleValInt30000,\n"
" parseRuleValInt32768,\n"
" parseRuleValInt50000,\n"
"} ParseRuleValueInt;\n"
"\n"
COMMENT_BLOCK_BEGIN "\n"
"Command parse data\n"
COMMENT_BLOCK_END "\n"
"#define PARSE_RULE_VAL_CMD(value) PARSE_RULE_U32_1(value)\n"
"\n"
"static const ParseRuleCommand parseRuleCommand[CFG_COMMAND_TOTAL] =\n"
"{\n"
COMMENT_SEPARATOR "\n"
@ -560,6 +700,8 @@ testRun(void)
COMMENT_BLOCK_BEGIN "\n"
"Option parse data\n"
COMMENT_BLOCK_END "\n"
"#define PARSE_RULE_VAL_OPT(value) PARSE_RULE_U32_1(value)\n"
"\n"
"static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =\n"
"{\n"
COMMENT_SEPARATOR "\n"
@ -577,10 +719,68 @@ testRun(void)
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
" ),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n"
" PARSE_RULE_OPTIONAL\n"
" (\n"
" PARSE_RULE_OPTION_OPTIONAL_DEPEND(cfgOptOnline),\n"
" PARSE_RULE_OPTION_OPTIONAL_DEFAULT(\"0\"),\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" PARSE_RULE_OPTIONAL_DEPEND\n"
" (\n"
" PARSE_RULE_VAL_OPT(cfgOptOnline),\n"
" PARSE_RULE_VAL_BOOL_TRUE,\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_DEFAULT\n"
" (\n"
" PARSE_RULE_VAL_BOOL_FALSE,\n"
" ),\n"
" ),\n"
" ),\n"
" ),\n"
"\n"
COMMENT_SEPARATOR "\n"
" PARSE_RULE_OPTION\n"
" (\n"
" PARSE_RULE_OPTION_NAME(\"buffer-size\"),\n"
" PARSE_RULE_OPTION_TYPE(cfgOptTypeSize),\n"
" PARSE_RULE_OPTION_RESET(true),\n"
" PARSE_RULE_OPTION_REQUIRED(true),\n"
" PARSE_RULE_OPTION_SECTION(cfgSectionGlobal),\n"
"\n"
" PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST\n"
" (\n"
" PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet)\n"
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL\n"
" (\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" PARSE_RULE_FILTER_CMD\n"
" (\n"
" PARSE_RULE_VAL_CMD(cfgCmdArchiveGet),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_ALLOW_LIST\n"
" (\n"
" PARSE_RULE_VAL_INT(parseRuleValInt32768),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_DEFAULT\n"
" (\n"
" PARSE_RULE_VAL_INT(parseRuleValInt32768),\n"
" PARSE_RULE_VAL_STR(parseRuleValStrQT_32768_QT),\n"
" ),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" PARSE_RULE_OPTIONAL_ALLOW_LIST\n"
" (\n"
" PARSE_RULE_VAL_INT(parseRuleValInt8192),\n"
" PARSE_RULE_VAL_INT(parseRuleValInt16384),\n"
" ),\n"
" ),\n"
" ),\n"
" ),\n"
"\n"
@ -616,16 +816,35 @@ testRun(void)
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
" ),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n"
" PARSE_RULE_OPTIONAL\n"
" (\n"
" PARSE_RULE_OPTION_OPTIONAL_ALLOW_RANGE(0, 9),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_COMMAND_OVERRIDE\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" PARSE_RULE_OPTION_OPTIONAL_COMMAND(cfgCmdArchiveGet),\n"
" PARSE_RULE_FILTER_CMD\n"
" (\n"
" PARSE_RULE_VAL_CMD(cfgCmdArchiveGet),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_DEPEND(cfgOptConfig),\n"
" )\n"
" PARSE_RULE_OPTIONAL_DEPEND\n"
" (\n"
" PARSE_RULE_VAL_OPT(cfgOptConfig),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_ALLOW_RANGE\n"
" (\n"
" PARSE_RULE_VAL_INT(parseRuleValInt0),\n"
" PARSE_RULE_VAL_INT(parseRuleValInt9),\n"
" ),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" PARSE_RULE_OPTIONAL_ALLOW_RANGE\n"
" (\n"
" PARSE_RULE_VAL_INT(parseRuleValInt0),\n"
" PARSE_RULE_VAL_INT(parseRuleValInt9),\n"
" ),\n"
" ),\n"
" ),\n"
" ),\n"
"\n"
@ -662,22 +881,42 @@ testRun(void)
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
" ),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n"
" PARSE_RULE_OPTIONAL\n"
" (\n"
" PARSE_RULE_OPTION_OPTIONAL_ALLOW_RANGE(0, 9),\n"
" PARSE_RULE_OPTION_OPTIONAL_DEPEND_LIST\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" cfgOptCompressType,\n"
" \"none\",\n"
" \"gz\"\n"
" PARSE_RULE_FILTER_CMD\n"
" (\n"
" PARSE_RULE_VAL_CMD(cfgCmdArchiveGet),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_DEPEND\n"
" (\n"
" PARSE_RULE_VAL_OPT(cfgOptConfig),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_ALLOW_RANGE\n"
" (\n"
" PARSE_RULE_VAL_INT(parseRuleValInt0),\n"
" PARSE_RULE_VAL_INT(parseRuleValInt9),\n"
" ),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_COMMAND_OVERRIDE\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" PARSE_RULE_OPTION_OPTIONAL_COMMAND(cfgCmdArchiveGet),\n"
" PARSE_RULE_OPTIONAL_DEPEND\n"
" (\n"
" PARSE_RULE_VAL_OPT(cfgOptCompressType),\n"
" PARSE_RULE_VAL_STRID(parseRuleValStrIdNone),\n"
" PARSE_RULE_VAL_STRID(parseRuleValStrIdGz),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_DEPEND(cfgOptConfig),\n"
" )\n"
" PARSE_RULE_OPTIONAL_ALLOW_RANGE\n"
" (\n"
" PARSE_RULE_VAL_INT(parseRuleValInt0),\n"
" PARSE_RULE_VAL_INT(parseRuleValInt9),\n"
" ),\n"
" ),\n"
" ),\n"
" ),\n"
"\n"
@ -690,16 +929,33 @@ testRun(void)
" PARSE_RULE_OPTION_REQUIRED(true),\n"
" PARSE_RULE_OPTION_SECTION(cfgSectionGlobal),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n"
" PARSE_RULE_OPTIONAL\n"
" (\n"
" PARSE_RULE_OPTION_OPTIONAL_DEFAULT(\"gz\"),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_COMMAND_OVERRIDE\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" PARSE_RULE_OPTION_OPTIONAL_COMMAND(cfgCmdArchiveGet),\n"
" PARSE_RULE_FILTER_CMD\n"
" (\n"
" PARSE_RULE_VAL_CMD(cfgCmdArchiveGet),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_DEPEND(cfgOptConfig),\n"
" )\n"
" PARSE_RULE_OPTIONAL_DEPEND\n"
" (\n"
" PARSE_RULE_VAL_OPT(cfgOptConfig),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_DEFAULT\n"
" (\n"
" PARSE_RULE_VAL_STR(parseRuleValStrQT_gz_QT),\n"
" ),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" PARSE_RULE_OPTIONAL_DEFAULT\n"
" (\n"
" PARSE_RULE_VAL_STR(parseRuleValStrQT_gz_QT),\n"
" ),\n"
" ),\n"
" ),\n"
" ),\n"
"\n"
@ -735,9 +991,55 @@ testRun(void)
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
" ),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n"
" PARSE_RULE_OPTIONAL\n"
" (\n"
" PARSE_RULE_OPTION_OPTIONAL_DEFAULT(CFGOPTDEF_CONFIG_PATH \"/\" PROJECT_CONFIG_FILE),\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" PARSE_RULE_FILTER_CMD\n"
" (\n"
" PARSE_RULE_VAL_CMD(cfgCmdArchiveGet),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_DEFAULT\n"
" (\n"
" PARSE_RULE_VAL_STR(parseRuleValStrCFGOPTDEF_CONFIG_PATH_SP_QT_FS_DT_QT_SP_PROJECT_CONFIG_FILE),\n"
" ),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" PARSE_RULE_OPTIONAL_DEFAULT\n"
" (\n"
" PARSE_RULE_VAL_STR(parseRuleValStrCFGOPTDEF_CONFIG_PATH_SP_QT_FS_QT_SP_PROJECT_CONFIG_FILE),\n"
" ),\n"
" ),\n"
" ),\n"
" ),\n"
"\n"
COMMENT_SEPARATOR "\n"
" PARSE_RULE_OPTION\n"
" (\n"
" PARSE_RULE_OPTION_NAME(\"config-include\"),\n"
" PARSE_RULE_OPTION_TYPE(cfgOptTypePath),\n"
" PARSE_RULE_OPTION_RESET(true),\n"
" PARSE_RULE_OPTION_REQUIRED(true),\n"
" PARSE_RULE_OPTION_SECTION(cfgSectionGlobal),\n"
"\n"
" PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST\n"
" (\n"
" PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet)\n"
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL\n"
" (\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" PARSE_RULE_OPTIONAL_DEFAULT\n"
" (\n"
" PARSE_RULE_VAL_STR(parseRuleValStrQT_FS_include_QT),\n"
" ),\n"
" ),\n"
" ),\n"
" ),\n"
"\n"
@ -773,17 +1075,23 @@ testRun(void)
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
" ),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n"
" PARSE_RULE_OPTIONAL\n"
" (\n"
" PARSE_RULE_OPTION_OPTIONAL_ALLOW_LIST\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" \"off\",\n"
" \"error\",\n"
" \"warn\",\n"
" \"debug1\"\n"
" ),\n"
" PARSE_RULE_OPTIONAL_ALLOW_LIST\n"
" (\n"
" PARSE_RULE_VAL_STRID(parseRuleValStrIdOff),\n"
" PARSE_RULE_VAL_STRID(parseRuleValStrIdError),\n"
" PARSE_RULE_VAL_STRID(parseRuleValStrIdWarn),\n"
" PARSE_RULE_VAL_STRID(parseRuleValStrIdDebug1),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_DEFAULT(\"warn\"),\n"
" PARSE_RULE_OPTIONAL_DEFAULT\n"
" (\n"
" PARSE_RULE_VAL_STR(parseRuleValStrQT_warn_QT),\n"
" ),\n"
" ),\n"
" ),\n"
" ),\n"
"\n"
@ -818,37 +1126,50 @@ testRun(void)
" PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet)\n"
" ),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n"
" PARSE_RULE_OPTIONAL\n"
" (\n"
" PARSE_RULE_OPTION_OPTIONAL_ALLOW_LIST\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" \"off\",\n"
" \"error\",\n"
" \"warn\",\n"
" \"debug1\"\n"
" PARSE_RULE_FILTER_CMD\n"
" (\n"
" PARSE_RULE_VAL_CMD(cfgCmdBackup),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_DEPEND\n"
" (\n"
" PARSE_RULE_VAL_OPT(cfgOptLogLevelConsole),\n"
" PARSE_RULE_VAL_STRID(parseRuleValStrIdWarn),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_ALLOW_LIST\n"
" (\n"
" PARSE_RULE_VAL_STRID(parseRuleValStrIdOff),\n"
" PARSE_RULE_VAL_STRID(parseRuleValStrIdWarn),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_DEFAULT\n"
" (\n"
" PARSE_RULE_VAL_STR(parseRuleValStrQT_warn_QT),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_NOT_REQUIRED(),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_DEFAULT(\"info\"),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_COMMAND_OVERRIDE\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" PARSE_RULE_OPTION_OPTIONAL_COMMAND(cfgCmdBackup),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_ALLOW_LIST\n"
" PARSE_RULE_OPTIONAL_ALLOW_LIST\n"
" (\n"
" \"off\",\n"
" \"warn\"\n"
" PARSE_RULE_VAL_STRID(parseRuleValStrIdOff),\n"
" PARSE_RULE_VAL_STRID(parseRuleValStrIdError),\n"
" PARSE_RULE_VAL_STRID(parseRuleValStrIdWarn),\n"
" PARSE_RULE_VAL_STRID(parseRuleValStrIdDebug1),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_DEPEND_LIST\n"
" PARSE_RULE_OPTIONAL_DEFAULT\n"
" (\n"
" cfgOptLogLevelConsole,\n"
" \"warn\"\n"
" PARSE_RULE_VAL_STR(parseRuleValStrQT_info_QT),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_DEFAULT(\"warn\"),\n"
" PARSE_RULE_OPTION_OPTIONAL_REQUIRED(false),\n"
" )\n"
" ),\n"
" ),\n"
" ),\n"
"\n"
@ -865,9 +1186,28 @@ testRun(void)
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
" ),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n"
" PARSE_RULE_OPTIONAL\n"
" (\n"
" PARSE_RULE_OPTION_OPTIONAL_DEFAULT(\"1\"),\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" PARSE_RULE_FILTER_CMD\n"
" (\n"
" PARSE_RULE_VAL_CMD(cfgCmdBackup),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_DEFAULT\n"
" (\n"
" PARSE_RULE_VAL_BOOL_FALSE,\n"
" ),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" PARSE_RULE_OPTIONAL_DEFAULT\n"
" (\n"
" PARSE_RULE_VAL_BOOL_TRUE,\n"
" ),\n"
" ),\n"
" ),\n"
" ),\n"
"\n"
@ -904,6 +1244,22 @@ testRun(void)
" PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet)\n"
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL\n"
" (\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" PARSE_RULE_FILTER_CMD\n"
" (\n"
" PARSE_RULE_VAL_CMD(cfgCmdArchiveGet),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_DEFAULT\n"
" (\n"
" PARSE_RULE_VAL_STR(parseRuleValStrQT_host1_QT),\n"
" ),\n"
" ),\n"
" ),\n"
" ),\n"
"\n"
COMMENT_SEPARATOR "\n"
@ -1007,10 +1363,62 @@ testRun(void)
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
" ),\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n"
" PARSE_RULE_OPTIONAL\n"
" (\n"
" PARSE_RULE_OPTION_OPTIONAL_ALLOW_RANGE(5000, 50000),\n"
" PARSE_RULE_OPTION_OPTIONAL_DEFAULT(\"10000\"),\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" PARSE_RULE_FILTER_CMD\n"
" (\n"
" PARSE_RULE_VAL_CMD(cfgCmdArchiveGet),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_ALLOW_RANGE\n"
" (\n"
" PARSE_RULE_VAL_INT(parseRuleValInt5000),\n"
" PARSE_RULE_VAL_INT(parseRuleValInt50000),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_DEFAULT\n"
" (\n"
" PARSE_RULE_VAL_INT(parseRuleValInt30000),\n"
" PARSE_RULE_VAL_STR(parseRuleValStrQT_30_QT),\n"
" ),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" PARSE_RULE_FILTER_CMD\n"
" (\n"
" PARSE_RULE_VAL_CMD(cfgCmdBackup),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_ALLOW_RANGE\n"
" (\n"
" PARSE_RULE_VAL_INT(parseRuleValInt5000),\n"
" PARSE_RULE_VAL_INT(parseRuleValInt50000),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_DEFAULT\n"
" (\n"
" PARSE_RULE_VAL_INT(parseRuleValInt20000),\n"
" PARSE_RULE_VAL_STR(parseRuleValStrQT_20_QT),\n"
" ),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_GROUP\n"
" (\n"
" PARSE_RULE_OPTIONAL_ALLOW_RANGE\n"
" (\n"
" PARSE_RULE_VAL_INT(parseRuleValInt5000),\n"
" PARSE_RULE_VAL_INT(parseRuleValInt50000),\n"
" ),\n"
"\n"
" PARSE_RULE_OPTIONAL_DEFAULT\n"
" (\n"
" PARSE_RULE_VAL_INT(parseRuleValInt10000),\n"
" PARSE_RULE_VAL_STR(parseRuleValStrQT_10_QT),\n"
" ),\n"
" ),\n"
" ),\n"
" ),\n"
"};\n"
@ -1079,7 +1487,9 @@ testRun(void)
"static const ConfigOption optionResolveOrder[] =\n"
"{\n"
" cfgOptStanza,\n"
" cfgOptBufferSize,\n"
" cfgOptConfig,\n"
" cfgOptConfigInclude,\n"
" cfgOptLogLevelConsole,\n"
" cfgOptLogLevelFile,\n"
" cfgOptOnline,\n"

View File

@ -127,6 +127,7 @@ testRun(void)
TEST_RESULT_STR_Z(helpRenderValue(varNewStrZ("test-string"), cfgOptTypeString), "test-string", "string");
TEST_RESULT_STR_Z(helpRenderValue(varNewInt64(1234), cfgOptTypeInteger), "1234", "int");
TEST_RESULT_STR_Z(helpRenderValue(varNewInt64(1234000), cfgOptTypeTime), "1234", "time");
TEST_RESULT_STR_Z(helpRenderValue(NULL, cfgOptTypeString), NULL, "null");
}
// *****************************************************************************************************************************

View File

@ -50,6 +50,37 @@ testRun(void)
TEST_RESULT_UINT(sizeof(ParseRuleOption), TEST_64BIT() ? 40 : 28, "ParseRuleOption size");
TEST_RESULT_UINT(sizeof(ParseRuleOptionDeprecate), TEST_64BIT() ? 16 : 12, "ParseRuleOptionDeprecate size");
// Each pack must be <= 127 bytes because only one byte is used for the size. If this check fails then the size of
// PARSE_RULE_PACK_SIZE must be increased. There would be little cost of increasing this as a preventative measure but a
// check would still be required, so may as well be as efficient as possible.
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("check that no packs are > 127 bytes");
unsigned int packOver127 = 0;
unsigned int packTotal = 0;
size_t packMaxSize = 0;
const char *packMaxName = NULL;
size_t packTotalSize = 0;
for (unsigned int optIdx = 0; optIdx < CFG_OPTION_TOTAL; optIdx++)
{
packOver127 += parseRuleOption[optIdx].packSize > 127;
packTotal += parseRuleOption[optIdx].pack != NULL;
packTotalSize += parseRuleOption[optIdx].packSize;
if (parseRuleOption[optIdx].packSize > packMaxSize)
{
packMaxName = parseRuleOption[optIdx].name;
packMaxSize = parseRuleOption[optIdx].packSize;
}
}
TEST_RESULT_UINT(packOver127, 0, "no packs over 127 bytes");
TEST_LOG_FMT("total size of option packs is %zu bytes", packTotalSize);
TEST_LOG_FMT("avg option pack size is %0.2f bytes", (float)packTotalSize / (float)packTotal);
TEST_LOG_FMT("max option pack size is '%s' at %zu bytes", packMaxName, packMaxSize);
TEST_LOG_FMT("total options with packs is %u (out of %d options) ", packTotal, CFG_OPTION_TOTAL);
}
// Config functions that are not tested with parse
@ -161,9 +192,10 @@ testRun(void)
HRN_SYSTEM_FMT("mv %s/global-backup.conf %s/global-backup.confsave", strZ(configIncludePath), strZ(configIncludePath));
// Set up defaults
String *backupCmdDefConfigValue = strNewZ(cfgParseOptionDefault(cfgParseCommandId(TEST_COMMAND_BACKUP), cfgOptConfig));
String *backupCmdDefConfigInclPathValue = strNewZ(
cfgParseOptionDefault(cfgParseCommandId(TEST_COMMAND_BACKUP), cfgOptConfigIncludePath));
const String *const backupCmdDefConfigValue =
(const String *)&parseRuleValueStr[parseRuleValStrCFGOPTDEF_CONFIG_PATH_SP_QT_FS_QT_SP_PROJECT_CONFIG_FILE];
const String *const backupCmdDefConfigInclPathValue =
(const String *)&parseRuleValueStr[parseRuleValStrCFGOPTDEF_CONFIG_PATH_SP_QT_FS_QT_SP_PROJECT_CONFIG_INCLUDE_PATH];
const String *oldConfigDefault = STRDEF(TEST_PATH PGBACKREST_CONFIG_ORIG_PATH_FILE);
// Create the option structure and initialize with 0
@ -1133,6 +1165,32 @@ testRun(void)
configParse(storageTest, strLstSize(argList), strLstPtr(argList), false), OptionInvalidError,
"option 'recovery-option' not valid for command 'backup'");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("option value invalid (string)");
argList = strLstNew();
strLstAddZ(argList, TEST_BACKREST_EXE);
hrnCfgArgRawZ(argList, cfgOptPgPath, "/path/to/db");
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
hrnCfgArgRawZ(argList, cfgOptType, "^bogus");
strLstAddZ(argList, TEST_COMMAND_RESTORE);
TEST_ERROR(
configParse(storageTest, strLstSize(argList), strLstPtr(argList), false), OptionInvalidValueError,
"'^bogus' is not allowed for 'type' option");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("option value invalid (size)");
argList = strLstNew();
strLstAddZ(argList, TEST_BACKREST_EXE);
hrnCfgArgRawZ(argList, cfgOptPgPath, "/path/to/db");
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
hrnCfgArgRawZ(argList, cfgOptBufferSize, "777");
strLstAddZ(argList, TEST_COMMAND_RESTORE);
TEST_ERROR(
configParse(storageTest, strLstSize(argList), strLstPtr(argList), false), OptionInvalidValueError,
"'777' is not allowed for 'buffer-size' option");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("option value not in allowed list");
@ -1618,21 +1676,24 @@ testRun(void)
TEST_RESULT_STR(cfgOptionIdxStrNull(cfgOptPgHost, 1), NULL, "pg2-host is NULL");
TEST_RESULT_STR(cfgOptionStrNull(cfgOptPgHost), NULL, "pg2-host is NULL");
TEST_ERROR(cfgOptionIdxStr(cfgOptPgHost, 1), AssertError, "option 'pg2-host' is null but non-null was requested");
TEST_RESULT_UINT(cfgOptionUInt64(cfgOptIoTimeout), 60000, "io-timeout is set");
TEST_RESULT_BOOL(varBool(cfgOptionDefault(cfgOptBackupStandby)), false, "backup-standby default is false");
TEST_RESULT_BOOL(varBool(cfgOptionDefault(cfgOptBackupStandby)), false, "backup-standby default is false (again)");
TEST_RESULT_BOOL(cfgParseOptionRequired(cfgCmdBackup, cfgOptPgHost), false, "pg-host is not required for backup");
TEST_RESULT_BOOL(cfgParseOptionRequired(cfgCmdInfo, cfgOptStanza), false, "stanza is not required for info");
TEST_RESULT_STR_Z(cfgOptionDefault(cfgOptBackupStandby), "n", "backup-standby default is false");
TEST_RESULT_STR_Z(cfgOptionDefault(cfgOptBackupStandby), "n", "backup-standby default is false (again)");
TEST_RESULT_PTR(cfgOptionDefault(cfgOptPgHost), NULL, "pg-host default is NULL");
TEST_RESULT_STR_Z(varStr(cfgOptionDefault(cfgOptLogLevelConsole)), "warn", "log-level-console default is warn");
TEST_RESULT_INT(varInt64(cfgOptionDefault(cfgOptPgPort)), 5432, "pg-port default is 5432");
TEST_RESULT_STR_Z(cfgOptionDefault(cfgOptLogLevelConsole), "warn", "log-level-console default is warn");
TEST_RESULT_STR_Z(cfgOptionDefault(cfgOptPgPort), "5432", "pg-port default is 5432");
TEST_RESULT_STR_Z(cfgOptionDisplay(cfgOptPgPort), "5432", "pg-port display is 5432");
TEST_RESULT_INT(varInt64(cfgOptionDefault(cfgOptDbTimeout)), 1800000, "db-timeout default is 1800000");
TEST_RESULT_STR_Z(cfgOptionDefault(cfgOptDbTimeout), "1800", "db-timeout default is 1800");
TEST_RESULT_VOID(cfgOptionDefaultSet(cfgOptPgSocketPath, VARSTRDEF("/default")), "set pg-socket-path default");
TEST_RESULT_STR_Z(cfgOptionIdxStr(cfgOptPgSocketPath, 0), "/path/to/socket", "pg1-socket-path unchanged");
TEST_RESULT_STR_Z(cfgOptionIdxStr(cfgOptPgSocketPath, 1), "/default", "pg2-socket-path is new default");
TEST_RESULT_STR_Z(cfgOptionIdxDisplay(cfgOptPgSocketPath, 1), "/default", "pg2-socket-path display");
TEST_ERROR(cfgOptionDefaultValue(cfgOptDbInclude), AssertError, "default value not available for option type 3");
TEST_ERROR(cfgOptionDisplay(cfgOptTarget), AssertError, "option 'target' is not valid for the current command");
TEST_ERROR(cfgOptionLst(cfgOptDbInclude), AssertError, "option 'db-include' is not valid for the current command");
TEST_ERROR(cfgOptionKv(cfgOptPgPath), AssertError, "option 'pg1-path' is type 4 but 3 was requested");