test-po-files.patch
text/x-diff
Filename: test-po-files.patch
Type: text/x-diff
Part: 1
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/port/snprintf.c | 0 | 0 |
| src/port/test_po_file.c | 0 | 0 |
diff --git a/src/port/snprintf.c b/src/port/snprintf.c
index a773381..42aaea8 100644
*** a/src/port/snprintf.c
--- b/src/port/snprintf.c
***************
*** 110,115 ****
--- 110,129 ----
#undef printf
/*
+ * If this file is compiled with SNPRINTF_VALIDATE defined, it doesn't
+ * provide any of the normal *printf functions, but instead exports
+ * a function "pg_validate_printf_format()" that just detects whether a
+ * supplied format string is syntactically valid. We implement that
+ * by running the normal snprintf code with a fake output buffer, and
+ * suppressing all attempts to fetch variable arguments (since those
+ * won't be there).
+ */
+ #ifdef SNPRINTF_VALIDATE
+ #undef va_arg
+ #define va_arg(args, type) ((type) 0)
+ #endif
+
+ /*
* Info about where the formatted output is going.
*
* dopr and subroutines will not write at/past bufend, but snprintf
*************** static void dopr(PrintfTarget *target, c
*** 170,175 ****
--- 184,191 ----
* they not change the value of "errno" before reaching dopr().
*/
+ #ifndef SNPRINTF_VALIDATE
+
int
pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
{
*************** pg_printf(const char *fmt,...)
*** 290,295 ****
--- 306,335 ----
return len;
}
+ #else /* SNPRINTF_VALIDATE */
+
+ /*
+ * Returns 1 if "fmt" is syntactically valid, -1 if not.
+ * In the latter case, errno is set (but it'll always be EINVAL).
+ */
+ int
+ pg_validate_printf_format(const char *fmt)
+ {
+ /* Code is basically the same as pg_vsnprintf with NULL str argument */
+ PrintfTarget target;
+ char onebyte[1];
+
+ target.bufstart = target.bufptr = onebyte;
+ target.bufend = onebyte;
+ target.stream = NULL;
+ target.nchars = 0;
+ target.failed = false;
+ dopr(&target, fmt, NULL);
+ return target.failed ? -1 : 1;
+ }
+
+ #endif /* SNPRINTF_VALIDATE */
+
/*
* Attempt to write the entire buffer to target->stream; discard the entire
* buffer in any case. Call this only when target->stream is defined.
*************** dopr(PrintfTarget *target, const char *f
*** 438,445 ****
--- 478,489 ----
if (*format == 's')
{
format++;
+ #ifndef SNPRINTF_VALIDATE
strvalue = va_arg(args, char *);
Assert(strvalue != NULL);
+ #else
+ strvalue = "";
+ #endif
dostr(strvalue, strlen(strvalue), target);
if (target->failed)
break;
*************** nextch2:
*** 666,677 ****
--- 710,726 ----
else
fieldwidth = accum;
}
+ #ifndef SNPRINTF_VALIDATE
if (have_dollar)
strvalue = argvalues[fmtpos].cptr;
else
strvalue = va_arg(args, char *);
/* Whine if someone tries to print a NULL string */
Assert(strvalue != NULL);
+ #else
+ /* va_arg would return NULL, so inject empty string instead */
+ strvalue = "";
+ #endif
fmtstr(strvalue, leftjust, fieldwidth, precision, pointflag,
target);
break;
*************** fail:
*** 1252,1257 ****
--- 1301,1308 ----
target->failed = true;
}
+ #ifndef SNPRINTF_VALIDATE
+
/*
* Nonstandard entry point to print a double value efficiently.
*
*************** fail:
*** 1352,1357 ****
--- 1403,1410 ----
+ target.nchars);
}
+ #endif /* SNPRINTF_VALIDATE */
+
static void
dostr(const char *str, int slen, PrintfTarget *target)
diff --git a/src/port/test_po_file.c b/src/port/test_po_file.c
index ...8dc063c .
*** a/src/port/test_po_file.c
--- b/src/port/test_po_file.c
***************
*** 0 ****
--- 1,150 ----
+ /*-------------------------------------------------------------------------
+ *
+ * test_po_file.c
+ * Test messages in a .po file to see if our version of snprintf likes them.
+ *
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * src/port/test_po_file.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+ #include "c.h"
+
+ #include <gettext-po.h>
+
+ /* snprintf.c will provide this below */
+ extern int pg_validate_printf_format(const char *fmt);
+
+
+ /* Error handlers required by recent libgettextpo versions */
+ static void
+ xerror(int severity,
+ po_message_t message,
+ const char *filename, size_t lineno, size_t column,
+ int multiline_p, const char *message_text)
+ {
+ fprintf(stderr, "PO error: %s\n", message_text);
+ if (filename)
+ fprintf(stderr, "Error occurred in file \"%s\" at line %d:\n",
+ filename, (int) lineno);
+ if (severity == PO_SEVERITY_FATAL_ERROR)
+ exit(1);
+ }
+
+ static void
+ xerror2(int severity,
+ po_message_t message1,
+ const char *filename1, size_t lineno1, size_t column1,
+ int multiline_p1, const char *message_text1,
+ po_message_t message2,
+ const char *filename2, size_t lineno2, size_t column2,
+ int multiline_p2, const char *message_text2)
+ {
+ fprintf(stderr, "PO error: %s ...\n", message_text1);
+ if (filename1)
+ fprintf(stderr, "Error occurred in file \"%s\" at line %d:\n",
+ filename1, (int) lineno1);
+ fprintf(stderr, "PO error: ... %s\n", message_text2);
+ if (filename2)
+ fprintf(stderr, "Error occurred in file \"%s\" at line %d:\n",
+ filename2, (int) lineno2);
+ if (severity == PO_SEVERITY_FATAL_ERROR)
+ exit(1);
+ }
+
+ static const struct po_xerror_handler handler = {
+ xerror, xerror2
+ };
+
+
+ /*
+ * Main program.
+ *
+ * Test each .po file named on the command line.
+ */
+ int
+ main(int argc, char **argv)
+ {
+ for (int i = 1; i < argc; i++)
+ {
+ const char *filename = argv[i];
+ po_file_t file = po_file_read(filename, &handler);
+ const char *const *domains;
+ const char *const *domainp;
+
+ if (file == NULL)
+ {
+ fprintf(stderr, "could not open file \"%s\": %s\n",
+ filename, strerror(errno));
+ exit(1);
+ }
+
+ domains = po_file_domains(file);
+ for (domainp = domains; *domainp; domainp++)
+ {
+ const char *domain = *domainp;
+ po_message_iterator_t iterator = po_message_iterator(file, domain);
+
+ for (;;)
+ {
+ po_message_t message = po_next_message(iterator);
+ po_filepos_t pos;
+ char location[256];
+ const char *fmt;
+
+ if (message == NULL)
+ break;
+
+ if (!po_message_is_format(message, "c-format"))
+ continue;
+
+ pos = po_message_filepos(message, 0);
+ if (pos)
+ snprintf(location, sizeof(location), "for %s:%ld",
+ po_filepos_file(pos),
+ (long) po_filepos_start_line(pos));
+ else
+ strcpy(location, "unknown location");
+
+ fmt = po_message_msgid(message);
+ if (fmt && pg_validate_printf_format(fmt) < 0)
+ fprintf(stderr, "invalid format string in file \"%s\" %s\n\"%s\"\n",
+ filename, location, fmt);
+ fmt = po_message_msgid_plural(message);
+ if (fmt && pg_validate_printf_format(fmt) < 0)
+ fprintf(stderr, "invalid format string in file \"%s\" %s\n\"%s\"\n",
+ filename, location, fmt);
+ fmt = po_message_msgstr(message);
+ if (fmt && pg_validate_printf_format(fmt) < 0)
+ fprintf(stderr, "invalid format string in file \"%s\" %s\n\"%s\"\n",
+ filename, location, fmt);
+ for (int ndx = 0;; ndx++)
+ {
+ fmt = po_message_msgstr_plural(message, ndx);
+ if (!fmt)
+ break;
+ if (pg_validate_printf_format(fmt) < 0)
+ fprintf(stderr, "invalid format string in file \"%s\" %s\n\"%s\"\n",
+ filename, location, fmt);
+ }
+ }
+ po_message_iterator_free(iterator);
+ }
+ po_file_free(file);
+ }
+
+ return 0;
+ }
+
+ /*
+ * Now include snprintf.c with the desired variant compilation flag.
+ * We put this at the end so that its messing about with #define's
+ * won't mess up code in this file.
+ */
+ #define SNPRINTF_VALIDATE 1
+
+ #include "snprintf.c"