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_BOOLEAN_STR, OPT_TYPE_BOOLEAN);
STRING_EXTERN(OPT_TYPE_HASH_STR, OPT_TYPE_HASH); 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_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_STR, OPT_TYPE_STRING);
STRING_EXTERN(OPT_TYPE_STRING_ID_STR, OPT_TYPE_STRING_ID);
STRING_EXTERN(OPT_TYPE_TIME_STR, OPT_TYPE_TIME); STRING_EXTERN(OPT_TYPE_TIME_STR, OPT_TYPE_TIME);
/*********************************************************************************************************************************** /***********************************************************************************************************************************

View File

@ -33,10 +33,18 @@ Option type constants
STRING_DECLARE(OPT_TYPE_BOOLEAN_STR); STRING_DECLARE(OPT_TYPE_BOOLEAN_STR);
#define OPT_TYPE_HASH "hash" #define OPT_TYPE_HASH "hash"
STRING_DECLARE(OPT_TYPE_HASH_STR); STRING_DECLARE(OPT_TYPE_HASH_STR);
#define OPT_TYPE_INTEGER "integer"
STRING_DECLARE(OPT_TYPE_INTEGER_STR);
#define OPT_TYPE_LIST "list" #define OPT_TYPE_LIST "list"
STRING_DECLARE(OPT_TYPE_LIST_STR); 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" #define OPT_TYPE_STRING "string"
STRING_DECLARE(OPT_TYPE_STRING_STR); STRING_DECLARE(OPT_TYPE_STRING_STR);
#define OPT_TYPE_STRING_ID "stringId"
STRING_DECLARE(OPT_TYPE_STRING_ID_STR);
#define OPT_TYPE_TIME "time" #define OPT_TYPE_TIME "time"
STRING_DECLARE(OPT_TYPE_TIME_STR); STRING_DECLARE(OPT_TYPE_TIME_STR);

View File

@ -297,128 +297,227 @@ bldCfgRenderLf(String *const config, const bool lf)
strCatZ(config, "\n"); strCatZ(config, "\n");
} }
static const String * // Helper to get var-128 encoding size
bldCfgRenderValue(const String *const type, const String *const value) static size_t
bldCfgRenderVar128Size(uint64_t value)
{ {
// Translate boolean values size_t result = 1;
if (strEq(type, OPT_TYPE_BOOLEAN_STR))
while (value >= 0x80)
{ {
if (strEq(value, FALSE_STR)) value >>= 7;
return ZERO_STR; result++;
CHECK(strEq(value, TRUE_STR));
return strNewZ("1");
} }
// 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 // Helper to render allow list
static bool static String *
bldCfgRenderAllowList(String *const config, const StringList *const allowList, const bool command) 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( strCatFmt(result, " %s,\n", strZ(bldCfgRenderScalar(value, optType)));
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;
} }
return false; strCatZ(result, " )");
return result;
} }
// Helper to render default // Helper to render default
static bool static String *
bldCfgRenderDefault( bldCfgRenderDefault(
String *const config, const String *const type, const String *const defaultValue, const String *const defaultValue, const bool defaultLiteral, const String *const optType)
const bool defaultLiteral, const bool command, bool multi)
{ {
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( strCatFmt(
config, result,
"%sPARSE_RULE_OPTION_OPTIONAL_DEFAULT(%s%s%s),\n", " %s,\n",
indent, defaultLiteral ? "" : "\"", strZ(bldCfgRenderValue(type, defaultValue)), defaultLiteral ? "" : "\""); strZ(
bldCfgRenderScalar(
return false; strNewFmt("%s%s%s", defaultLiteral ? "" : "\"", strZ(defaultValue), defaultLiteral ? "" : "\""),
OPT_TYPE_STRING_STR)));
} }
return multi; strCatZ(result, " )");
}
// Helper to render depend return result;
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;
} }
static void static void
bldCfgRenderParseAutoC(const Storage *const storageRepo, const BldCfg bldCfg) 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 // Command parse rules
// ----------------------------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------------------------
@ -427,7 +526,18 @@ bldCfgRenderParseAutoC(const Storage *const storageRepo, const BldCfg bldCfg)
"\n" "\n"
COMMENT_BLOCK_BEGIN "\n" COMMENT_BLOCK_BEGIN "\n"
"Command parse data\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" "static const ParseRuleCommand parseRuleCommand[CFG_COMMAND_TOTAL] =\n"
"{\n"); "{\n");
@ -523,7 +633,18 @@ bldCfgRenderParseAutoC(const Storage *const storageRepo, const BldCfg bldCfg)
"\n" "\n"
COMMENT_BLOCK_BEGIN "\n" COMMENT_BLOCK_BEGIN "\n"
"Option parse data\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" "static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =\n"
"{\n"); "{\n");
@ -536,6 +657,8 @@ bldCfgRenderParseAutoC(const Storage *const storageRepo, const BldCfg bldCfg)
for (unsigned int optIdx = 0; optIdx < lstSize(bldCfg.optList); optIdx++) for (unsigned int optIdx = 0; optIdx < lstSize(bldCfg.optList); optIdx++)
{ {
const BldCfgOption *const opt = lstGet(bldCfg.optList, optIdx); const BldCfgOption *const opt = lstGet(bldCfg.optList, optIdx);
StringList *const ruleDataList = strLstNew();
bool ruleInt = false;
bldCfgRenderLf(config, optIdx != 0); bldCfgRenderLf(config, optIdx != 0);
@ -622,82 +745,293 @@ bldCfgRenderParseAutoC(const Storage *const storageRepo, const BldCfg bldCfg)
} }
} }
// Build optional data // Determine if the option has an allow list. This will decide whether it is treated as a String or StringId. This should be
String *const configOptional = strNew(); // 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) if (opt->allowRangeMin != NULL)
{ {
CHECK(opt->allowRangeMax != NULL); kvAdd(
optionalDefaultRule, ruleAllowRange,
const String *allowRangeMin = opt->allowRangeMin; VARSTR(bldCfgRenderAllowRange(opt->allowRangeMin, opt->allowRangeMax, opt->type)));
const String *allowRangeMax = opt->allowRangeMax;
if (strEq(opt->type, OPT_TYPE_TIME_STR)) if (strEq(opt->type, OPT_TYPE_TIME_STR))
{ {
allowRangeMin = strNewFmt("%" PRId64, (int64_t)(cvtZToDouble(strZ(allowRangeMin)) * 1000)); strLstAddIfMissing(ruleDataList, strNewFmt("%" PRId64, (int64_t)(cvtZToDouble(strZ(opt->allowRangeMin)) * 1000)));
allowRangeMax = strNewFmt("%" PRId64, (int64_t)(cvtZToDouble(strZ(allowRangeMax)) * 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); if (opt->allowList != NULL)
multi = bldCfgRenderDepend(configOptional, opt->depend, false, multi); {
multi = bldCfgRenderDefault(configOptional, opt->type, opt->defaultValue, opt->defaultLiteral, false, multi); 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++) for (unsigned int optCmdIdx = 0; optCmdIdx < lstSize(opt->cmdList); optCmdIdx++)
{ {
BldCfgOptionCommand *const optCmd = lstGet(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); // Depends
multi = bldCfgRenderDepend(configCommand, optCmd->depend, true, multi); if (optCmd->depend != NULL)
multi = bldCfgRenderDefault(configCommand, opt->type, optCmd->defaultValue, false, true, multi); 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( for (unsigned int allowIdx = 0; allowIdx < strLstSize(optCmd->allowList); allowIdx++)
configCommand, strLstAddIfMissing(ruleDataList, strLstGet(optCmd->allowList, allowIdx));
" PARSE_RULE_OPTION_OPTIONAL_REQUIRED(%s),\n",
cvtBoolToConstZ(optCmd->required));
} }
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( if (!strEq(opt->type, OPT_TYPE_BOOLEAN_STR))
configOptional, {
" PARSE_RULE_OPTION_OPTIONAL_COMMAND_OVERRIDE\n" if (strEq(opt->type, OPT_TYPE_TIME_STR))
" (\n" {
" PARSE_RULE_OPTION_OPTIONAL_COMMAND(%s),\n" strLstAddIfMissing(
"\n" ruleDataList, strNewFmt("%" PRId64, (int64_t)(cvtZToDouble(strZ(optCmd->defaultValue)) * 1000)));
"%s" }
" )\n", else
strZ(bldEnum("cfgCmd", optCmd->name)), strZ(configCommand)); 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 // Add optional rules
if (!strEmpty(configOptional)) unsigned int optionalCmdRuleSize = varLstSize(kvKeyList(optionalCmdRule));
unsigned int optionalDefaultRuleSize = varLstSize(kvKeyList(optionalDefaultRule));
if (optionalCmdRuleSize != 0 || optionalDefaultRuleSize != 0)
{ {
strCatFmt( strCatZ(
config, config,
"\n" "\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n" " PARSE_RULE_OPTIONAL\n"
" (\n" " (\n");
"%s"
" ),\n", if (optionalCmdRuleSize != 0)
strZ(configOptional)); {
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"); 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( strCatZ(
@ -790,9 +1124,138 @@ bldCfgRenderParseAutoC(const Storage *const storageRepo, const BldCfg bldCfg)
strCatZ(config, "};\n"); 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 // 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); strFirstLower(summary);
// Output current and default values if they exist // 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; const String *value = NULL;
if (cfgOptionIdxSource(optionId, 0) != cfgSourceDefault) 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))); helpRenderText(optionData[option.id].description, optionData[option.id].internal, 0, true, CONSOLE_WIDTH)));
// Output current and default values if they exist // 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; const String *value = NULL;
if (cfgOptionIdxSource(option.id, 0) != cfgSourceDefault) if (cfgOptionIdxSource(option.id, 0) != cfgSourceDefault)

View File

@ -377,41 +377,7 @@ cfgOptionIdxTotal(ConfigOption optionId)
} }
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
static Variant * const String *
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 *
cfgOptionDefault(ConfigOption optionId) cfgOptionDefault(ConfigOption optionId)
{ {
FUNCTION_TEST_BEGIN(); FUNCTION_TEST_BEGIN();
@ -422,16 +388,7 @@ cfgOptionDefault(ConfigOption optionId)
ASSERT(optionId < CFG_OPTION_TOTAL); ASSERT(optionId < CFG_OPTION_TOTAL);
if (configLocal->option[optionId].defaultValue == NULL) if (configLocal->option[optionId].defaultValue == NULL)
{ configLocal->option[optionId].defaultValue = cfgParseOptionDefault(cfgCommand(), optionId);
if (cfgParseOptionDefault(cfgCommand(), optionId) != NULL)
{
MEM_CONTEXT_BEGIN(configLocal->memContext)
{
configLocal->option[optionId].defaultValue = cfgOptionDefaultValue(optionId);
}
MEM_CONTEXT_END();
}
}
FUNCTION_TEST_RETURN(configLocal->option[optionId].defaultValue); FUNCTION_TEST_RETURN(configLocal->option[optionId].defaultValue);
} }
@ -450,15 +407,18 @@ cfgOptionDefaultSet(ConfigOption optionId, const Variant *defaultValue)
MEM_CONTEXT_BEGIN(configLocal->memContext) MEM_CONTEXT_BEGIN(configLocal->memContext)
{ {
// Duplicate into this context
defaultValue = varDup(defaultValue);
// Set the default value // 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 // 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++) for (unsigned int optionIdx = 0; optionIdx < cfgOptionIdxTotal(optionId); optionIdx++)
{ {
if (configLocal->option[optionId].index[optionIdx].source == cfgSourceDefault) 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; configLocal->option[optionId].index[optionIdx].display = NULL;
} }
} }

View File

@ -170,9 +170,6 @@ void cfgCommandSet(ConfigCommand commandId, ConfigCommandRole commandRoleId);
// pgBackRest exe // pgBackRest exe
const String *cfgExe(void); 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 // 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. // 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); 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 valid; // Is option valid for current command?
bool group; // In a group? bool group; // In a group?
unsigned int groupId; // Id if 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) ConfigOptionValue *index; // List of indexed values (only 1 unless the option is indexed)
} option[CFG_OPTION_TOTAL]; } option[CFG_OPTION_TOTAL];
} Config; } Config;
@ -94,6 +94,9 @@ unsigned int cfgOptionGroupId(ConfigOption optionId);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Option Functions 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 // 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. // possible, but sometimes the variant needs to be manipulated before being formatted.
const String *cfgOptionDisplayVar(const Variant *const value, const ConfigOptionType optionType); 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 // In some environments this will not be extern'd
extern char **environ; 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 Define how a command is parsed
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
@ -133,24 +127,29 @@ typedef struct ParseRuleOption
bool group:1; // In a group? bool group:1; // In a group?
unsigned int groupId:1; // Id if in a group unsigned int groupId:1; // Id if in a group
bool deprecateMatch:1; // Does a deprecated name exactly match the option name? 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? 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; } ParseRuleOption;
// Define additional types of data that can be associated with an option. Because these types are rare they are not given dedicated // 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 // fields and are instead packed and read at runtime. This may seem inefficient but they are only accessed a single time during
// single time during parse so space efficiency is more important than performance. // parse so space efficiency is more important than performance.
typedef enum typedef enum
{ {
parseRuleOptionDataTypeEnd, // Indicates there is no more data parseRuleOptionalTypeValid = 1,
parseRuleOptionDataTypeAllowList, parseRuleOptionalTypeAllowRange,
parseRuleOptionDataTypeAllowRange, parseRuleOptionalTypeAllowList,
parseRuleOptionDataTypeCommand, parseRuleOptionalTypeDefault,
parseRuleOptionDataTypeDefault, parseRuleOptionalTypeRequired,
parseRuleOptionDataTypeDepend, } ParseRuleOptionalType;
parseRuleOptionDataTypeRequired,
} ParseRuleOptionDataType; // 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 // Macros used to define parse rules in parse.auto.c in a format that diffs well
#define PARSE_RULE_OPTION(...) \ #define PARSE_RULE_OPTION(...) \
@ -204,45 +203,46 @@ typedef enum
#define PARSE_RULE_OPTION_COMMAND(commandParam) \ #define PARSE_RULE_OPTION_COMMAND(commandParam) \
| (1 << commandParam) | (1 << commandParam)
#define PARSE_RULE_OPTION_OPTIONAL_PUSH_LIST(type, size, data, ...) \ #define PARSE_RULE_STRPUB(value) {.buffer = (char *)value, .size = sizeof(value) - 1}
(const void *)((uint32_t)type << 24 | (uint32_t)size << 16 | (uint32_t)data), __VA_ARGS__
#define PARSE_RULE_OPTION_OPTIONAL_LIST(...) \ // Macros used to define optional parse rules in pack format
.data = (const void *[]){__VA_ARGS__ NULL} #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) \ #define PARSE_RULE_BOOL_TRUE 0x28
(const void *)((uint32_t)type << 24 | (uint32_t)size << 16 | (uint32_t)data) #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(...) \ #define PARSE_RULE_PACK(...) __VA_ARGS__ 0x00
__VA_ARGS__ #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) \ #define PARSE_RULE_VAL_BOOL_TRUE PARSE_RULE_BOOL_TRUE
PARSE_RULE_OPTION_OPTIONAL_PUSH(parseRuleOptionDataTypeCommand, 0, command) #define PARSE_RULE_VAL_BOOL_FALSE PARSE_RULE_BOOL_FALSE
#define PARSE_RULE_OPTION_OPTIONAL_ALLOW_LIST(...) \ #define PARSE_RULE_OPTIONAL(...) \
PARSE_RULE_OPTION_OPTIONAL_PUSH_LIST( \ .packSize = sizeof((const unsigned char []){PARSE_RULE_PACK(__VA_ARGS__)}), \
parseRuleOptionDataTypeAllowList, sizeof((const char *[]){__VA_ARGS__}) / sizeof(const char *), 0, __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) \ #define PARSE_RULE_FILTER_CMD(...) \
PARSE_RULE_OPTION_OPTIONAL_PUSH_LIST( \ PARSE_RULE_PACK_SIZE(PARSE_RULE_U32_1(parseRuleFilterTypeCommand), __VA_ARGS__)
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_OPTION_OPTIONAL_DEFAULT(defaultParam) \ #define PARSE_RULE_OPTIONAL_DEPEND(...) \
PARSE_RULE_OPTION_OPTIONAL_PUSH_LIST(parseRuleOptionDataTypeDefault, 1, 0, defaultParam) PARSE_RULE_U32_1(parseRuleOptionalTypeValid), PARSE_RULE_PACK_SIZE(__VA_ARGS__)
#define PARSE_RULE_OPTIONAL_ALLOW_LIST(...) \
#define PARSE_RULE_OPTION_OPTIONAL_DEPEND(optionDepend) \ PARSE_RULE_U32_1(parseRuleOptionalTypeAllowList), PARSE_RULE_PACK_SIZE(__VA_ARGS__)
PARSE_RULE_OPTION_OPTIONAL_PUSH(parseRuleOptionDataTypeDepend, 0, optionDepend) #define PARSE_RULE_OPTIONAL_ALLOW_RANGE(...) \
PARSE_RULE_U32_1(parseRuleOptionalTypeAllowRange), PARSE_RULE_PACK_SIZE(__VA_ARGS__)
#define PARSE_RULE_OPTION_OPTIONAL_DEPEND_LIST(optionDepend, ...) \ #define PARSE_RULE_OPTIONAL_DEFAULT(...) \
PARSE_RULE_OPTION_OPTIONAL_PUSH_LIST( \ PARSE_RULE_U32_1(parseRuleOptionalTypeDefault), PARSE_RULE_PACK_SIZE(__VA_ARGS__)
parseRuleOptionDataTypeDepend, sizeof((const char *[]){__VA_ARGS__}) / sizeof(const char *), optionDepend, __VA_ARGS__) #define PARSE_RULE_OPTIONAL_REQUIRED(...) \
PARSE_RULE_U32_1(parseRuleOptionalTypeRequired), PARSE_RULE_PACK_SIZE(__VA_ARGS__)
#define PARSE_RULE_OPTION_OPTIONAL_REQUIRED(requiredParam) \ #define PARSE_RULE_OPTIONAL_NOT_REQUIRED(...) PARSE_RULE_OPTIONAL_REQUIRED(__VA_ARGS__)
PARSE_RULE_OPTION_OPTIONAL_PUSH(parseRuleOptionDataTypeRequired, 0, requiredParam)
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Define option deprecations Define option deprecations
@ -260,84 +260,6 @@ Include automatically generated parse data
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
#include "config/parse.auto.c" #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 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}); 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) cfgParseOptionDefault(ConfigCommand commandId, ConfigOption optionId)
{ {
FUNCTION_TEST_BEGIN(); FUNCTION_TEST_BEGIN();
@ -751,12 +1021,18 @@ cfgParseOptionDefault(ConfigCommand commandId, ConfigOption optionId)
ASSERT(commandId < CFG_COMMAND_TOTAL); ASSERT(commandId < CFG_COMMAND_TOTAL);
ASSERT(optionId < CFG_OPTION_TOTAL); ASSERT(optionId < CFG_OPTION_TOTAL);
ParseRuleOptionData data = parseRuleOptionDataFind(parseRuleOptionDataTypeDefault, commandId, optionId); const String *result = NULL;
if (data.found) MEM_CONTEXT_TEMP_BEGIN()
FUNCTION_TEST_RETURN((const char *)data.list[0]); {
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(commandId < CFG_COMMAND_TOTAL);
ASSERT(optionId < CFG_OPTION_TOTAL); ASSERT(optionId < CFG_OPTION_TOTAL);
ParseRuleOptionData data = parseRuleOptionDataFind(parseRuleOptionDataTypeRequired, commandId, optionId); bool found = false;
bool result = false;
if (data.found) MEM_CONTEXT_TEMP_BEGIN()
FUNCTION_TEST_RETURN((bool)data.data); {
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); 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) // Load the configuration file(s)
String *configString = cfgFileLoad( String *configString = cfgFileLoad(
storage, parseOptionList, STR(cfgParseOptionDefault(config->command, cfgOptConfig)), storage, parseOptionList,
STR(cfgParseOptionDefault(config->command, cfgOptConfigIncludePath)), PGBACKREST_CONFIG_ORIG_PATH_FILE_STR); (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) 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 // Initialize option value and set negate and reset flag
*configOptionValue = (ConfigOptionValue){.negate = parseOptionValue->negate, .reset = parseOptionValue->reset}; *configOptionValue = (ConfigOptionValue){.negate = parseOptionValue->negate, .reset = parseOptionValue->reset};
// Check option dependencies // Is the option valid?
bool dependResolved = true; bool valid = true;
ParseRuleOptionData depend = parseRuleOptionDataFind(parseRuleOptionDataTypeDepend, config->command, optionId); CfgParseOptionalRuleState optionalRules = {0};
if (depend.found) if (cfgParseOptionalRule(&optionalRules, parseRuleOptionalTypeValid, config->command, optionId))
{ {
ConfigOption dependOptionId = (ConfigOption)depend.data; PackRead *filter = pckReadNewC(optionalRules.valid, optionalRules.validSize);
ConfigOptionType dependOptionType = cfgParseOptionType(dependOptionId); valid = cfgParseOptionalFilterDepend(filter, config, optionListIdx);
ASSERT(config->option[dependOptionId].index != 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,
// Get the depend option value // spool-path is only loaded for the archive-push command when archive-async=y, and the presence of
const Variant *dependValue = config->option[dependOptionId].index[optionListIdx].value; // 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 (dependValue != NULL)
{ {
if (dependOptionType == cfgOptTypeBoolean) PackRead *filter = pckReadNewC(optionalRules.valid, optionalRules.validSize);
{ ConfigOption dependId = pckReadU32P(filter);
if (varBool(dependValue))
dependValue = OPTION_VALUE_1;
else
dependValue = OPTION_VALUE_0;
}
}
// Can't resolve if the depend option value is null // Get depend option name
if (dependValue == NULL) const String *dependOptionName = STR(cfgParseOptionKeyIdxName(dependId, optionKeyIdx));
{
dependResolved = false;
// If depend not resolved and option value is set on the command-line then error. See unresolved list // If depend value is not set
// depend below for a detailed explanation. ASSERT(config->option[dependId].index != NULL);
if (optionSet && parseOptionValue->source == cfgSourceParam)
if (config->option[dependId].index[optionListIdx].value == NULL)
{ {
THROW_FMT( THROW_FMT(
OptionInvalidError, "option '%s' not valid without option '%s'", OptionInvalidError, "option '%s' not valid without option '%s'",
cfgParseOptionKeyIdxName(optionId, optionKeyIdx), cfgParseOptionKeyIdxName(optionId, optionKeyIdx), strZ(dependOptionName));
cfgParseOptionKeyIdxName(dependOptionId, optionKeyIdx));
} }
}
// 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; break;
} }
}
// If depend not resolved and option value is set on the command-line then error. It's OK to have default:
// 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++)
{ {
const char *dependValue = (const char *)depend.list[listIdx]; ASSERT(cfgParseOptionDataType(dependId) == cfgOptDataTypeString);
// Build list based on depend option type String *const errorList = strNew();
if (dependOptionType == cfgOptTypeBoolean) unsigned int validSize = 0;
while (pckReadNext(filter))
{ {
// Boolean outputs depend option name as no-* when false strCatFmt(
if (strcmp(dependValue, ZERO_Z) == 0) errorList, "%s'%s'", validSize != 0 ? ", " : "",
{ strZ(strIdToStr(parseRuleValueStrId[pckReadU32P(filter)])));
dependOptionName =
strNewFmt("no-%s", cfgParseOptionKeyIdxName(dependOptionId, optionKeyIdx)); validSize++;
}
} }
ASSERT(validSize > 0);
if (validSize == 1)
errorValue = strNewFmt(" = %s", strZ(errorList));
else else
{ errorValue = strNewFmt(" in (%s)", strZ(errorList));
ASSERT(dependOptionType == cfgOptTypePath || dependOptionType == cfgOptTypeString);
strLstAdd(dependValueList, strNewFmt("'%s'", dependValue));
}
} }
// 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 (valid)
if (dependResolved)
{ {
// Is the option set? // Is the option set?
if (optionSet) if (optionSet)
@ -1961,13 +2224,12 @@ configParse(const Storage *storage, unsigned int argListSize, const char *argLis
{ {
String *value = strLstGet(parseOptionValue->valueList, 0); String *value = strLstGet(parseOptionValue->valueList, 0);
const String *valueAllow = value; const String *valueAllow = value;
int64_t valueInt64 = 0;
// If a numeric type check that the value is valid // If a numeric type check that the value is valid
if (optionType == cfgOptTypeInteger || optionType == cfgOptTypeSize || if (optionType == cfgOptTypeInteger || optionType == cfgOptTypeSize ||
optionType == cfgOptTypeTime) optionType == cfgOptTypeTime)
{ {
int64_t valueInt64 = 0;
// Preserve original value to display // Preserve original value to display
MEM_CONTEXT_BEGIN(config->memContext) MEM_CONTEXT_BEGIN(config->memContext)
{ {
@ -2021,13 +2283,9 @@ configParse(const Storage *storage, unsigned int argListSize, const char *argLis
} }
TRY_END(); TRY_END();
// Check value range if (cfgParseOptionalRule(
ParseRuleOptionData allowRange = parseRuleOptionDataFind( &optionalRules, parseRuleOptionalTypeAllowRange, config->command, optionId) &&
parseRuleOptionDataTypeAllowRange, config->command, optionId); (valueInt64 < optionalRules.allowRangeMin || valueInt64 > optionalRules.allowRangeMax))
if (allowRange.found &&
(valueInt64 < PARSE_RULE_DATA_INT64(allowRange, 0) ||
valueInt64 > PARSE_RULE_DATA_INT64(allowRange, 2)))
{ {
THROW_FMT( THROW_FMT(
OptionInvalidValueError, "'%s' is out of range for '%s' option", strZ(value), 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 // If the option has an allow list then check it
ParseRuleOptionData allowList = parseRuleOptionDataFind( if (cfgParseOptionalRule(
parseRuleOptionDataTypeAllowList, config->command, optionId); &optionalRules, parseRuleOptionalTypeAllowList, config->command, optionId))
if (allowList.found)
{ {
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])) bool valueValid = true;
break; 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( 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)); cfgParseOptionKeyIdxName(optionId, optionKeyIdx));
} }
} }
@ -2104,46 +2406,41 @@ configParse(const Storage *storage, unsigned int argListSize, const char *argLis
// Else try to set a default // Else try to set a default
else else
{ {
// Get the default value for this option bool found = false;
const char *value = cfgParseOptionDefault(config->command, optionId);
MEM_CONTEXT_BEGIN(config->memContext)
{
found = cfgParseOptionalRule(
&optionalRules, parseRuleOptionalTypeDefault, config->command, optionId);
}
MEM_CONTEXT_END();
// If the option has a default // If the option has a default
if (value != NULL) if (found)
{ {
MEM_CONTEXT_BEGIN(config->memContext) configOptionValue->value = optionalRules.defaultValue;
{
// 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();
} }
// Else error if option is required and help was not requested // 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) if (required && !config->help)
hint = "\nHINT: does this stanza exist?"; {
THROW_FMT(
THROW_FMT( OptionRequiredError, "%s command requires option: %s%s",
OptionRequiredError, "%s command requires option: %s%s", cfgParseCommandName(config->command), cfgParseCommandName(config->command),
cfgParseOptionKeyIdxName(optionId, optionKeyIdx), hint); 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); CfgParseOptionResult cfgParseOption(const String *const optionName, const CfgParseOptionParam param);
// Default value for the option // Default value for the option
const char *cfgParseOptionDefault(ConfigCommand commandId, ConfigOption optionId); const String *cfgParseOptionDefault(ConfigCommand commandId, ConfigOption optionId);
// Option name from id // Option name from id
const char *cfgParseOptionName(ConfigOption optionId); const char *cfgParseOptionName(ConfigOption optionId);

View File

@ -228,8 +228,28 @@ testRun(void)
" timeout:\n" " timeout:\n"
" type: time\n" " type: time\n"
" default: 10\n" " default: 10\n"
" command:\n"
" backup:\n"
" default: 20\n"
" archive-get:\n"
" default: 30\n"
" allow-range: [5, 50]\n" " allow-range: [5, 50]\n"
"\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" " compress-type:\n"
" section: global\n" " section: global\n"
" type: string\n" " type: string\n"
@ -267,6 +287,17 @@ testRun(void)
" default: CFGOPTDEF_CONFIG_PATH \"/\" PROJECT_CONFIG_FILE\n" " default: CFGOPTDEF_CONFIG_PATH \"/\" PROJECT_CONFIG_FILE\n"
" default-literal: true\n" " default-literal: true\n"
" negate: 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" "\n"
" log-level-console:\n" " log-level-console:\n"
" section: global\n" " section: global\n"
@ -311,7 +342,8 @@ testRun(void)
" type: boolean\n" " type: boolean\n"
" default: true\n" " default: true\n"
" command:\n" " command:\n"
" backup: {}\n" " backup:\n"
" default: false\n"
" command-role:\n" " command-role:\n"
" main: {}\n" " main: {}\n"
" deprecate:\n" " deprecate:\n"
@ -323,6 +355,8 @@ testRun(void)
" default: false\n" " default: false\n"
" depend:\n" " depend:\n"
" option: online\n" " option: online\n"
" list:\n"
" - true\n"
" command:\n" " command:\n"
" backup: {}\n" " backup: {}\n"
" command-role:\n" " command-role:\n"
@ -345,11 +379,20 @@ testRun(void)
" section: stanza\n" " section: stanza\n"
" group: pg\n" " group: pg\n"
" type: string\n" " type: string\n"
" command:\n"
" archive-get:\n"
" default: host1\n"
" backup: {}\n"
" deprecate:\n" " deprecate:\n"
" pg?-host: {}\n"); " pg?-host: {}\n");
TEST_RESULT_VOID(bldCfgRender(storageTest, bldCfgParse(storageTest)), "parse and render"); 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"); TEST_TITLE("check config.auto.h");
@ -383,17 +426,19 @@ testRun(void)
"Option constants\n" "Option constants\n"
COMMENT_BLOCK_END "\n" COMMENT_BLOCK_END "\n"
"#define CFGOPT_BACKUP_STANDBY \"backup-standby\"\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 \"compress-level\"\n"
"#define CFGOPT_COMPRESS_LEVEL_NETWORK \"compress-level-network\"\n" "#define CFGOPT_COMPRESS_LEVEL_NETWORK \"compress-level-network\"\n"
"#define CFGOPT_COMPRESS_TYPE \"compress-type\"\n" "#define CFGOPT_COMPRESS_TYPE \"compress-type\"\n"
"#define CFGOPT_CONFIG \"config\"\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_CONSOLE \"log-level-console\"\n"
"#define CFGOPT_LOG_LEVEL_FILE \"log-level-file\"\n" "#define CFGOPT_LOG_LEVEL_FILE \"log-level-file\"\n"
"#define CFGOPT_ONLINE \"online\"\n" "#define CFGOPT_ONLINE \"online\"\n"
"#define CFGOPT_STANZA \"stanza\"\n" "#define CFGOPT_STANZA \"stanza\"\n"
"#define CFGOPT_TIMEOUT \"timeout\"\n" "#define CFGOPT_TIMEOUT \"timeout\"\n"
"\n" "\n"
"#define CFG_OPTION_TOTAL 12\n" "#define CFG_OPTION_TOTAL 14\n"
"\n" "\n"
COMMENT_BLOCK_BEGIN "\n" COMMENT_BLOCK_BEGIN "\n"
"Option value constants\n" "Option value constants\n"
@ -443,10 +488,12 @@ testRun(void)
"typedef enum\n" "typedef enum\n"
"{\n" "{\n"
" cfgOptBackupStandby,\n" " cfgOptBackupStandby,\n"
" cfgOptBufferSize,\n"
" cfgOptCompressLevel,\n" " cfgOptCompressLevel,\n"
" cfgOptCompressLevelNetwork,\n" " cfgOptCompressLevelNetwork,\n"
" cfgOptCompressType,\n" " cfgOptCompressType,\n"
" cfgOptConfig,\n" " cfgOptConfig,\n"
" cfgOptConfigInclude,\n"
" cfgOptLogLevelConsole,\n" " cfgOptLogLevelConsole,\n"
" cfgOptLogLevelFile,\n" " cfgOptLogLevelFile,\n"
" cfgOptOnline,\n" " cfgOptOnline,\n"
@ -471,8 +518,101 @@ testRun(void)
COMMENT_BLOCK_END "\n" COMMENT_BLOCK_END "\n"
"\n" "\n"
COMMENT_BLOCK_BEGIN "\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" "Command parse data\n"
COMMENT_BLOCK_END "\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" "static const ParseRuleCommand parseRuleCommand[CFG_COMMAND_TOTAL] =\n"
"{\n" "{\n"
COMMENT_SEPARATOR "\n" COMMENT_SEPARATOR "\n"
@ -560,6 +700,8 @@ testRun(void)
COMMENT_BLOCK_BEGIN "\n" COMMENT_BLOCK_BEGIN "\n"
"Option parse data\n" "Option parse data\n"
COMMENT_BLOCK_END "\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" "static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =\n"
"{\n" "{\n"
COMMENT_SEPARATOR "\n" COMMENT_SEPARATOR "\n"
@ -577,10 +719,68 @@ testRun(void)
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n" " PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
" ),\n" " ),\n"
"\n" "\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n" " PARSE_RULE_OPTIONAL\n"
" (\n" " (\n"
" PARSE_RULE_OPTION_OPTIONAL_DEPEND(cfgOptOnline),\n" " PARSE_RULE_OPTIONAL_GROUP\n"
" PARSE_RULE_OPTION_OPTIONAL_DEFAULT(\"0\"),\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" " ),\n"
"\n" "\n"
@ -616,16 +816,35 @@ testRun(void)
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n" " PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
" ),\n" " ),\n"
"\n" "\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n" " PARSE_RULE_OPTIONAL\n"
" (\n" " (\n"
" PARSE_RULE_OPTION_OPTIONAL_ALLOW_RANGE(0, 9),\n" " PARSE_RULE_OPTIONAL_GROUP\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_COMMAND_OVERRIDE\n"
" (\n" " (\n"
" PARSE_RULE_OPTION_OPTIONAL_COMMAND(cfgCmdArchiveGet),\n" " PARSE_RULE_FILTER_CMD\n"
" (\n"
" PARSE_RULE_VAL_CMD(cfgCmdArchiveGet),\n"
" ),\n"
"\n" "\n"
" PARSE_RULE_OPTION_OPTIONAL_DEPEND(cfgOptConfig),\n" " PARSE_RULE_OPTIONAL_DEPEND\n"
" )\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" " ),\n"
"\n" "\n"
@ -662,22 +881,42 @@ testRun(void)
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n" " PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
" ),\n" " ),\n"
"\n" "\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n" " PARSE_RULE_OPTIONAL\n"
" (\n" " (\n"
" PARSE_RULE_OPTION_OPTIONAL_ALLOW_RANGE(0, 9),\n" " PARSE_RULE_OPTIONAL_GROUP\n"
" PARSE_RULE_OPTION_OPTIONAL_DEPEND_LIST\n"
" (\n" " (\n"
" cfgOptCompressType,\n" " PARSE_RULE_FILTER_CMD\n"
" \"none\",\n" " (\n"
" \"gz\"\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"
"\n" "\n"
" PARSE_RULE_OPTION_OPTIONAL_COMMAND_OVERRIDE\n" " PARSE_RULE_OPTIONAL_GROUP\n"
" (\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" "\n"
" PARSE_RULE_OPTION_OPTIONAL_DEPEND(cfgOptConfig),\n" " PARSE_RULE_OPTIONAL_ALLOW_RANGE\n"
" )\n" " (\n"
" PARSE_RULE_VAL_INT(parseRuleValInt0),\n"
" PARSE_RULE_VAL_INT(parseRuleValInt9),\n"
" ),\n"
" ),\n"
" ),\n" " ),\n"
" ),\n" " ),\n"
"\n" "\n"
@ -690,16 +929,33 @@ testRun(void)
" PARSE_RULE_OPTION_REQUIRED(true),\n" " PARSE_RULE_OPTION_REQUIRED(true),\n"
" PARSE_RULE_OPTION_SECTION(cfgSectionGlobal),\n" " PARSE_RULE_OPTION_SECTION(cfgSectionGlobal),\n"
"\n" "\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n" " PARSE_RULE_OPTIONAL\n"
" (\n" " (\n"
" PARSE_RULE_OPTION_OPTIONAL_DEFAULT(\"gz\"),\n" " PARSE_RULE_OPTIONAL_GROUP\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_COMMAND_OVERRIDE\n"
" (\n" " (\n"
" PARSE_RULE_OPTION_OPTIONAL_COMMAND(cfgCmdArchiveGet),\n" " PARSE_RULE_FILTER_CMD\n"
" (\n"
" PARSE_RULE_VAL_CMD(cfgCmdArchiveGet),\n"
" ),\n"
"\n" "\n"
" PARSE_RULE_OPTION_OPTIONAL_DEPEND(cfgOptConfig),\n" " PARSE_RULE_OPTIONAL_DEPEND\n"
" )\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" " ),\n"
"\n" "\n"
@ -735,9 +991,55 @@ testRun(void)
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n" " PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
" ),\n" " ),\n"
"\n" "\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n" " PARSE_RULE_OPTIONAL\n"
" (\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" " ),\n"
"\n" "\n"
@ -773,17 +1075,23 @@ testRun(void)
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n" " PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
" ),\n" " ),\n"
"\n" "\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n" " PARSE_RULE_OPTIONAL\n"
" (\n" " (\n"
" PARSE_RULE_OPTION_OPTIONAL_ALLOW_LIST\n" " PARSE_RULE_OPTIONAL_GROUP\n"
" (\n" " (\n"
" \"off\",\n" " PARSE_RULE_OPTIONAL_ALLOW_LIST\n"
" \"error\",\n" " (\n"
" \"warn\",\n" " PARSE_RULE_VAL_STRID(parseRuleValStrIdOff),\n"
" \"debug1\"\n" " PARSE_RULE_VAL_STRID(parseRuleValStrIdError),\n"
" ),\n" " PARSE_RULE_VAL_STRID(parseRuleValStrIdWarn),\n"
" PARSE_RULE_VAL_STRID(parseRuleValStrIdDebug1),\n"
" ),\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" " ),\n"
"\n" "\n"
@ -818,37 +1126,50 @@ testRun(void)
" PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet)\n" " PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet)\n"
" ),\n" " ),\n"
"\n" "\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n" " PARSE_RULE_OPTIONAL\n"
" (\n" " (\n"
" PARSE_RULE_OPTION_OPTIONAL_ALLOW_LIST\n" " PARSE_RULE_OPTIONAL_GROUP\n"
" (\n" " (\n"
" \"off\",\n" " PARSE_RULE_FILTER_CMD\n"
" \"error\",\n" " (\n"
" \"warn\",\n" " PARSE_RULE_VAL_CMD(cfgCmdBackup),\n"
" \"debug1\"\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"
"\n" "\n"
" PARSE_RULE_OPTION_OPTIONAL_DEFAULT(\"info\"),\n" " PARSE_RULE_OPTIONAL_GROUP\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_COMMAND_OVERRIDE\n"
" (\n" " (\n"
" PARSE_RULE_OPTION_OPTIONAL_COMMAND(cfgCmdBackup),\n" " PARSE_RULE_OPTIONAL_ALLOW_LIST\n"
"\n"
" PARSE_RULE_OPTION_OPTIONAL_ALLOW_LIST\n"
" (\n" " (\n"
" \"off\",\n" " PARSE_RULE_VAL_STRID(parseRuleValStrIdOff),\n"
" \"warn\"\n" " PARSE_RULE_VAL_STRID(parseRuleValStrIdError),\n"
" PARSE_RULE_VAL_STRID(parseRuleValStrIdWarn),\n"
" PARSE_RULE_VAL_STRID(parseRuleValStrIdDebug1),\n"
" ),\n" " ),\n"
"\n" "\n"
" PARSE_RULE_OPTION_OPTIONAL_DEPEND_LIST\n" " PARSE_RULE_OPTIONAL_DEFAULT\n"
" (\n" " (\n"
" cfgOptLogLevelConsole,\n" " PARSE_RULE_VAL_STR(parseRuleValStrQT_info_QT),\n"
" \"warn\"\n"
" ),\n" " ),\n"
"\n" " ),\n"
" PARSE_RULE_OPTION_OPTIONAL_DEFAULT(\"warn\"),\n"
" PARSE_RULE_OPTION_OPTIONAL_REQUIRED(false),\n"
" )\n"
" ),\n" " ),\n"
" ),\n" " ),\n"
"\n" "\n"
@ -865,9 +1186,28 @@ testRun(void)
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n" " PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
" ),\n" " ),\n"
"\n" "\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n" " PARSE_RULE_OPTIONAL\n"
" (\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" " ),\n"
"\n" "\n"
@ -904,6 +1244,22 @@ testRun(void)
" PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet)\n" " PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet)\n"
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n" " PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
" ),\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"
"\n" "\n"
COMMENT_SEPARATOR "\n" COMMENT_SEPARATOR "\n"
@ -1007,10 +1363,62 @@ testRun(void)
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n" " PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
" ),\n" " ),\n"
"\n" "\n"
" PARSE_RULE_OPTION_OPTIONAL_LIST\n" " PARSE_RULE_OPTIONAL\n"
" (\n" " (\n"
" PARSE_RULE_OPTION_OPTIONAL_ALLOW_RANGE(5000, 50000),\n" " PARSE_RULE_OPTIONAL_GROUP\n"
" PARSE_RULE_OPTION_OPTIONAL_DEFAULT(\"10000\"),\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" " ),\n"
"};\n" "};\n"
@ -1079,7 +1487,9 @@ testRun(void)
"static const ConfigOption optionResolveOrder[] =\n" "static const ConfigOption optionResolveOrder[] =\n"
"{\n" "{\n"
" cfgOptStanza,\n" " cfgOptStanza,\n"
" cfgOptBufferSize,\n"
" cfgOptConfig,\n" " cfgOptConfig,\n"
" cfgOptConfigInclude,\n"
" cfgOptLogLevelConsole,\n" " cfgOptLogLevelConsole,\n"
" cfgOptLogLevelFile,\n" " cfgOptLogLevelFile,\n"
" cfgOptOnline,\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(varNewStrZ("test-string"), cfgOptTypeString), "test-string", "string");
TEST_RESULT_STR_Z(helpRenderValue(varNewInt64(1234), cfgOptTypeInteger), "1234", "int"); 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(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(ParseRuleOption), TEST_64BIT() ? 40 : 28, "ParseRuleOption size");
TEST_RESULT_UINT(sizeof(ParseRuleOptionDeprecate), TEST_64BIT() ? 16 : 12, "ParseRuleOptionDeprecate 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 // 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)); HRN_SYSTEM_FMT("mv %s/global-backup.conf %s/global-backup.confsave", strZ(configIncludePath), strZ(configIncludePath));
// Set up defaults // Set up defaults
String *backupCmdDefConfigValue = strNewZ(cfgParseOptionDefault(cfgParseCommandId(TEST_COMMAND_BACKUP), cfgOptConfig)); const String *const backupCmdDefConfigValue =
String *backupCmdDefConfigInclPathValue = strNewZ( (const String *)&parseRuleValueStr[parseRuleValStrCFGOPTDEF_CONFIG_PATH_SP_QT_FS_QT_SP_PROJECT_CONFIG_FILE];
cfgParseOptionDefault(cfgParseCommandId(TEST_COMMAND_BACKUP), cfgOptConfigIncludePath)); 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); const String *oldConfigDefault = STRDEF(TEST_PATH PGBACKREST_CONFIG_ORIG_PATH_FILE);
// Create the option structure and initialize with 0 // Create the option structure and initialize with 0
@ -1133,6 +1165,32 @@ testRun(void)
configParse(storageTest, strLstSize(argList), strLstPtr(argList), false), OptionInvalidError, configParse(storageTest, strLstSize(argList), strLstPtr(argList), false), OptionInvalidError,
"option 'recovery-option' not valid for command 'backup'"); "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"); 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(cfgOptionIdxStrNull(cfgOptPgHost, 1), NULL, "pg2-host is NULL");
TEST_RESULT_STR(cfgOptionStrNull(cfgOptPgHost), 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_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(cfgParseOptionRequired(cfgCmdBackup, cfgOptPgHost), false, "pg-host is not required for backup");
TEST_RESULT_BOOL(varBool(cfgOptionDefault(cfgOptBackupStandby)), false, "backup-standby default is false (again)"); 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_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_STR_Z(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(cfgOptPgPort), "5432", "pg-port default is 5432");
TEST_RESULT_STR_Z(cfgOptionDisplay(cfgOptPgPort), "5432", "pg-port display 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_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, 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(cfgOptionIdxStr(cfgOptPgSocketPath, 1), "/default", "pg2-socket-path is new default");
TEST_RESULT_STR_Z(cfgOptionIdxDisplay(cfgOptPgSocketPath, 1), "/default", "pg2-socket-path display"); 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(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(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"); TEST_ERROR(cfgOptionKv(cfgOptPgPath), AssertError, "option 'pg1-path' is type 4 but 3 was requested");