Skip to content

Commit

Permalink
tests/nutlogtest.c, common/common.c: validate_formatting_string(): to…
Browse files Browse the repository at this point in the history
…lerate dynamic formats that are sub-strings and beginnings of reference (wasteful but survivable) [networkupstools#2450]

Signed-off-by: Jim Klimov <[email protected]>
  • Loading branch information
jimklimov committed Jun 2, 2024
1 parent c18ccdb commit 900d64d
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 9 deletions.
46 changes: 45 additions & 1 deletion common/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -1463,7 +1463,10 @@ int validate_formatting_string(const char *fmt_dynamic, const char *fmt_referenc
* is in position to statically check that the actual varargs match
* that reference during build.
* Returns 0 if the two reference strings minimize to the same value,
* or a negative value (and sets errno) in case of errors:
* a positive value if they are sufficiently compatible (but not equal):
* 1 dynamic format is the beginning of reference format
* (and there are some ignored left-over arguments)
* ...or a negative value (and sets errno) in case of errors:
* -1 for memory-related errors
* -2 for minimize_formatting_string() returning NULL
* (also likely memory errors)
Expand All @@ -1480,6 +1483,7 @@ int validate_formatting_string(const char *fmt_dynamic, const char *fmt_referenc
size_t lenD = strlen(fmt_dynamic) + 1;
size_t lenR = strlen(fmt_reference) + 1;
char *bufD = xcalloc(lenD, sizeof(char)), *bufR = xcalloc(lenR, sizeof(char));
size_t lenBufD;

if (!bufD || !bufR) {
if (bufD)
Expand All @@ -1506,6 +1510,46 @@ int validate_formatting_string(const char *fmt_dynamic, const char *fmt_referenc
return 0;
}

/* Does the reference format start with the complete
* value of the dynamic format? (so bufR is same or
* longer than bufD, and with operation just ignoring
* extra passed arguments, if any)
*/

/* First, strip dangling non-conversion characters */
lenBufD = strlen(bufD);
while (lenBufD > 0) {
switch (bufD[lenBufD-1]) {
case '*':
case 'i':
case 'u':
case 'f':
case 'c':
case 's':
case 'p':
case 'n':
break;

default:
lenBufD--;
bufD[lenBufD] = '\0';
continue;
}
break;
}

if (!strncmp(bufD, bufR, strlen(bufD))) {
if (verbosity >= 0)
upsdebugx(verbosity,
"%s: dynamic formatting string '%s' (normalized as '%s') "
"is a subset of expected '%s' (normalized as '%s'); "
"ignoring some passed arguments but okay",
__func__, fmt_dynamic, bufD, fmt_reference, bufR);
free(bufD);
free(bufR);
return 1;
}

/* This be should not be fatal right here, but may be in the caller logic */
if (verbosity >= 0)
upsdebugx(verbosity,
Expand Down
75 changes: 67 additions & 8 deletions tests/nutlogtest.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,26 @@ int main(void) {
ret++;
}

if (snprintf_dynamic(buf, sizeof(buf), dynfmt, "%s%d", "Single string via dynamic format", 1) < 0) {
#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_OVERFLOW)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wformat"
#endif
if (snprintf_dynamic(buf, sizeof(buf), dynfmt, "%d", "Single string via dynamic format", 1) < 0) {
upsdebugx(0, "D: snprintf_dynamic() correctly reports mis-matched formats");
} else {
upsdebugx(0, "E: snprintf_dynamic() wrongly reports well-matched formats");
ret++;
}
#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_OVERFLOW)
# pragma GCC diagnostic pop
#endif

if (snprintf_dynamic(buf, sizeof(buf), dynfmt, "%s%d", "Single string via dynamic format, plus ignored garbage", 1) < 0) {
upsdebugx(0, "E: snprintf_dynamic() wrongly reports well-matched formats");
ret++;
} else {
upsdebugx(0, "D: snprintf_dynamic() correctly reports mis-matched formats");
}

/* Note extra non-type chars in "expected" format are stripped */
p = mkstr_dynamic(dynfmt, " %.4s %%", "Single string inlined by mkstr_dynamic()");
Expand All @@ -103,24 +117,69 @@ int main(void) {
}

if (1) { /* scoping */
int res;
char **p,
*fmtFloat[] = { "%f", " %A", " %0.1E%% ", NULL },
*fmtNotFloat[] = { "%f%", "%m", "$f", NULL };
*fmtNotFloat[] = { "%s", NULL },
/* TODO: Add conversion? More methods? */
*fmtConvertFloatOKAY[] = { "<empty>", "%f%", "%m", "$f", NULL },
*fmtConvertFloatTODO[] = { "%lf", "%d", NULL },
*fmtConvertIntOKAY[] = { "<empty>", NULL },
*fmtConvertIntTODO[] = { "%ld", "%lld", "%hd", NULL }
;

for (p = &(fmtFloat[0]); *p; p++) {
if (validate_formatting_string(*p, "Voltage: %G is not %%d", 1) < 0) {
upsdebugx(0, "E: validate_formatting_string() expecting %%f equivalent failed for: '%s'", *p);
if ((res = validate_formatting_string(*p, "Voltage: %G is not %%d", 1)) < 0) {
upsdebugx(0, "E: validate_formatting_string() expecting %%f equivalent failed (%i) for: '%s'", res, *p);
ret++;
} else {
upsdebugx(0, "D: validate_formatting_string() expecting %%f equivalent passed for: '%s'", *p);
upsdebugx(0, "D: validate_formatting_string() expecting %%f equivalent passed (%i) for: '%s'", res, *p);
}
}

for (p = &(fmtNotFloat[0]); *p; p++) {
if (validate_formatting_string("%f", *p, 1) < 0) {
upsdebugx(0, "D: validate_formatting_string() expecting %%f failed (as it should have) for: '%s'", *p);
if ((res = validate_formatting_string(*p, "%f", 1)) < 0) {
upsdebugx(0, "D: validate_formatting_string() expecting %%f failed (%i) (as it should have) for: '%s'", res, *p);
} else {
upsdebugx(0, "E: validate_formatting_string() expecting %%f passed (%i) (but should not have) for: '%s'", res, *p);
ret++;
}
}

/* Auto-conversion or other non-exact equivalence */
for (p = &(fmtConvertFloatOKAY[0]); *p; p++) {
if ((res = validate_formatting_string(*p, "%f", 1)) > 0) {
upsdebugx(0, "D: validate_formatting_string() expecting %%f passed (%i) (non-exactly) for: '%s'", res, *p);
} else {
upsdebugx(0, "E: validate_formatting_string() expecting %%f failed (%i) for: '%s'", res, *p);
ret++;
}
}

for (p = &(fmtConvertIntOKAY[0]); *p; p++) {
if ((res = validate_formatting_string(*p, "%d", 1)) > 0) {
upsdebugx(0, "D: validate_formatting_string() expecting %%f passed (%i) (non-exactly) for: '%s'", res, *p);
} else {
upsdebugx(0, "E: validate_formatting_string() expecting %%f failed (%i) for: '%s'", res, *p);
ret++;
}
}

/* TODO: Make such cases safely fit */
for (p = &(fmtConvertFloatTODO[0]); *p; p++) {
if ((res = validate_formatting_string(*p, "%f", 1)) < 0) {
upsdebugx(0, "D: validate_formatting_string() expecting %%f failed (%i) (as it should have) for: '%s'", res, *p);
} else {
upsdebugx(0, "E: validate_formatting_string() expecting %%f passed (%i) (but should not have) for: '%s'", res, *p);
ret++;
}
}

for (p = &(fmtConvertIntTODO[0]); *p; p++) {
if ((res = validate_formatting_string(*p, "%d", 1)) < 0) {
upsdebugx(0, "D: validate_formatting_string() expecting %%d failed (%i) (as it should have) for: '%s'", res, *p);
} else {
upsdebugx(0, "E: validate_formatting_string() expecting %%f passed (but should not have) for: '%s'", *p);
upsdebugx(0, "E: validate_formatting_string() expecting %%d passed (%i) (but should not have) for: '%s'", res, *p);
ret++;
}
}
Expand Down

0 comments on commit 900d64d

Please sign in to comment.