struct spec spec;
unsigned int numbered_allocated;
unsigned int unnumbered_arg_count;
+ unsigned int in_quote_group;
+ unsigned int in_color_group;
+ unsigned int in_url_group;
struct spec *result;
spec.directives = 0;
spec.uses_current_locus = false;
numbered_allocated = 0;
unnumbered_arg_count = 0;
+ in_quote_group = 0;
+ in_color_group = 0;
+ in_url_group = 0;
for (; *format != '\0';)
/* Invariant: spec.numbered_arg_count == 0 || unnumbered_arg_count == 0. */
FDI_SET (format - 1, FMTDIR_START);
spec.directives++;
- if (*format == '%' || *format == '<' || *format == '>'
- || *format == '}' || *format == '\'' || *format == 'R')
+ if (*format == '%' || *format == '\'')
;
+ else if (*format == '<')
+ {
+ if (in_quote_group)
+ {
+ *invalid_reason = xasprintf (_("The directive number %u opens a quote group, but the previous one is not terminated."), spec.directives);
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ in_quote_group = spec.directives;
+ }
+ else if (*format == '>')
+ {
+ if (!in_quote_group)
+ {
+ *invalid_reason = xasprintf (_("The directive number %u does not match a preceding '%%%c'."), spec.directives, '<');
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ in_quote_group = 0;
+ }
+ else if (*format == 'R')
+ {
+ if (!in_color_group)
+ {
+ *invalid_reason = xasprintf (_("The directive number %u does not match a preceding '%%%c'."), spec.directives, 'r');
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ in_color_group = 0;
+ }
+ else if (*format == '}')
+ {
+ if (!in_url_group)
+ {
+ *invalid_reason = xasprintf (_("The directive number %u does not match a preceding '%%%c'."), spec.directives, '{');
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ in_url_group = 0;
+ }
else if (*format == 'm')
spec.uses_err_no = true;
else if (*format == 'C')
else if (*format == 'Z')
type = FAT_INT_ARRAY_PART1;
else if (*format == 'r')
- type = FAT_COLOR;
+ {
+ if (in_color_group)
+ {
+ *invalid_reason = xasprintf (_("The directive number %u opens a color group, but the previous one is not terminated."), spec.directives);
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ in_color_group = spec.directives;
+ type = FAT_COLOR;
+ }
else if (*format == '{')
- type = FAT_URL;
+ {
+ if (in_url_group)
+ {
+ *invalid_reason = xasprintf (_("The directive number %u opens a URL group, but the previous one is not terminated."), spec.directives);
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ in_url_group = spec.directives;
+ type = FAT_URL;
+ }
else if (*format == 'D')
type = FAT_TREE | FAT_TREE_DECL;
else if (*format == 'F')
format++;
}
+ if (in_quote_group)
+ {
+ *invalid_reason = xasprintf (_("The quote group opened by the directive number %u is not terminated."), in_quote_group);
+ goto bad_format;
+ }
+ if (in_color_group)
+ {
+ *invalid_reason = xasprintf (_("The color group opened by the directive number %u is not terminated."), in_color_group);
+ goto bad_format;
+ }
+ if (in_url_group)
+ {
+ *invalid_reason = xasprintf (_("The URL group opened by the directive number %u is not terminated."), in_url_group);
+ goto bad_format;
+ }
+
/* Convert the unnumbered argument array to numbered arguments. */
if (unnumbered_arg_count > 0)
spec.numbered_arg_count = unnumbered_arg_count;
# Valid: %% doesn't count
msgid "abc%%def"
msgstr "xyz"
-# Valid: %< doesn't count
-msgid "abc%<def"
-msgstr "xyz"
-# Valid: %> doesn't count
-msgid "abc%>def"
+# Valid: %< %> doesn't count
+msgid "abc%<def%>ghi"
msgstr "xyz"
+# Invalid: use of %< without %>
+msgid "abc%<def%>ghi"
+msgstr "xyz%<"
+# Invalid: use of %> without %<
+msgid "abc%<def%>ghi"
+msgstr "xyz%>"
+# Invalid: use of %r without %R
+msgid "abc%rdef%Rghi"
+msgstr "xyz%r"
+# Invalid: use of %{ without %}
+msgid "abc%{def%}ghi"
+msgstr "xyz%{"
# Valid: %' doesn't count
msgid "abc%'def"
msgstr "xyz"
msgid "abc%u"
msgstr "xyz%x"
# Invalid: type incompatibility
-msgid "abc%r"
-msgstr "xyz%{"
+msgid "abc%r%R"
+msgstr "xyz%{%}"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%c"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%s"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%i"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%u"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%f"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%p"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%@"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%e"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%Z"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%D"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%E"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%F"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%T"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%V"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%v"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%A"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%H"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%I"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%O"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%P"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%Q"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%S"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%X"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%C"
# Invalid: type incompatibility
-msgid "abc%r"
+msgid "abc%r%R"
msgstr "xyz%L"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%c"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%s"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%i"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%u"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%f"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%p"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%@"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%e"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%Z"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%D"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%E"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%F"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%T"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%V"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%v"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%A"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%H"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%I"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%O"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%P"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%Q"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%S"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%X"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%C"
# Invalid: type incompatibility
-msgid "abc%{"
+msgid "abc%{%}"
msgstr "xyz%L"
# Invalid: type incompatibility
msgid "abc%c"