From: Bruno Haible Date: Mon, 27 Aug 2001 12:05:20 +0000 (+0000) Subject: YCP format string checking. X-Git-Tag: v0.11~531 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b5b4015fe61a8edc125a020e138a6c88ebd7c9db;p=thirdparty%2Fgettext.git YCP format string checking. --- diff --git a/src/format-ycp.c b/src/format-ycp.c new file mode 100644 index 000000000..61bd74a24 --- /dev/null +++ b/src/format-ycp.c @@ -0,0 +1,221 @@ +/* YCP format strings. + Copyright (C) 2001 Free Software Foundation, Inc. + Written by Bruno Haible , 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "format.h" +#include "system.h" +#include "error.h" +#include "progname.h" +#include "libgettext.h" + +#define _(str) gettext (str) + +/* YCP sformat strings are described in libycp documentation YCP-builtins.html. + A directive starts with '%' and is followed by '%' or a nonzero digit ('1' + to '9'). + */ + +struct spec +{ + unsigned int directives; + unsigned int arg_count; + bool args_used[9]; +}; + + +/* Prototypes for local functions. Needed to ensure compiler checking of + function argument counts despite of K&R C function definition syntax. */ +static void *format_parse PARAMS ((const char *format)); +static void format_free PARAMS ((void *descr)); +static int format_get_number_of_directives PARAMS ((void *descr)); +static bool format_check PARAMS ((const lex_pos_ty *pos, + void *msgid_descr, void *msgstr_descr)); + + +static void * +format_parse (format) + const char *format; +{ + struct spec spec; + struct spec *result; + + spec.directives = 0; + spec.arg_count = 0; + + for (; *format != '\0';) + if (*format++ == '%') + { + /* A directive. */ + spec.directives++; + + if (*format == '%') + format++; + else if (*format >= '1' && *format <= '9') + { + unsigned int number = *format - '1'; + + while (spec.arg_count <= number) + spec.args_used[spec.arg_count++] = false; + spec.args_used[number] = true; + + format++; + } + else + goto bad_format; + } + + result = (struct spec *) xmalloc (sizeof (struct spec)); + *result = spec; + return result; + + bad_format: + return NULL; +} + +static void +format_free (descr) + void *descr; +{ + struct spec *spec = (struct spec *) descr; + + free (spec); +} + +static int +format_get_number_of_directives (descr) + void *descr; +{ + struct spec *spec = (struct spec *) descr; + + return spec->directives; +} + +static bool +format_check (pos, msgid_descr, msgstr_descr) + const lex_pos_ty *pos; + void *msgid_descr; + void *msgstr_descr; +{ + struct spec *spec1 = (struct spec *) msgid_descr; + struct spec *spec2 = (struct spec *) msgstr_descr; + bool err = false; + unsigned int i; + + for (i = 0; i < spec1->arg_count || i < spec2->arg_count; i++) + { + bool arg_used1 = (i < spec1->arg_count && spec1->args_used[i]); + bool arg_used2 = (i < spec2->arg_count && spec2->args_used[i]); + + if (arg_used1 != arg_used2) + { + error_with_progname = false; + error_at_line (0, 0, pos->file_name, pos->line_number, + arg_used1 + ? _("a format specification for argument %u doesn't exist in 'msgstr'") + : _("a format specification for argument %u doesn't exist in 'msgid'"), + i + 1); + error_with_progname = true; + err = true; + break; + } + } + + return err; +} + + +struct formatstring_parser formatstring_ycp = +{ + format_parse, + format_free, + format_get_number_of_directives, + format_check +}; + + +#ifdef TEST + +/* Test program: Print the argument list specification returned by + format_parse for strings read from standard input. */ + +#include +#include "getline.h" + +static void +format_print (descr) + void *descr; +{ + struct spec *spec = (struct spec *) descr; + unsigned int i; + + if (spec == NULL) + { + printf ("INVALID"); + return; + } + + printf ("("); + for (i = 0; i < spec->arg_count; i++) + { + if (i > 0) + printf (" "); + if (spec->args_used[i]) + printf ("*"); + else + printf ("_"); + } + printf (")"); +} + +int +main () +{ + for (;;) + { + char *line = NULL; + size_t line_len = 0; + void *descr; + + if (getline (&line, &line_len, stdin) < 0) + break; + + descr = format_parse (line); + + format_print (descr); + printf ("\n"); + + free (line); + } + + return 0; +} + +/* + * For Emacs M-x compile + * Local Variables: + * compile-command: "gcc -O -g -Wall -I.. -I../lib -I../intl -DHAVE_CONFIG_H -DTEST format-ycp.c ../lib/libnlsut.a" + * End: + */ + +#endif /* TEST */ diff --git a/tests/format-ycp-1 b/tests/format-ycp-1 new file mode 100755 index 000000000..ba4eac2ba --- /dev/null +++ b/tests/format-ycp-1 @@ -0,0 +1,64 @@ +#! /bin/sh + +# Test recognition of YCP format strings. + +tmpfiles="" +trap 'rm -fr $tmpfiles' 1 2 3 15 + +tmpfiles="$tmpfiles f-y-1.data" +cat <<\EOF > f-y-1.data +# Valid: no argument +"abc%%def" +# Valid: one argument +"abc%1def" +# Valid: nine arguments +"abc%9def" +# Invalid: unterminated +"abc%%def%" +# Invalid: non-digit +"abc%%def%x" +# Invalid: zero +"abc%%def%0" +# Valid: permutation +"abc%2def%1" +# Valid: multiple uses of same argument +"abc%2def%1ghi%2" +EOF + +: ${XGETTEXT=xgettext} +n=0 +while read comment; do + read string + n=`expr $n + 1` + tmpfiles="$tmpfiles f-y-1-$n.in f-y-1-$n.po" + cat < f-y-1-$n.in +gettext(${string}); +EOF + ${XGETTEXT} -L YCP -o f-y-1-$n.po f-y-1-$n.in || exit 1 + test -f f-y-1-$n.po || exit 1 + fail= + if echo "$comment" | grep 'Valid:' > /dev/null; then + if grep ycp-format f-y-1-$n.po > /dev/null; then + : + else + fail=yes + fi + else + if grep ycp-format f-y-1-$n.po > /dev/null; then + fail=yes + else + : + fi + fi + if test -n "$fail"; then + echo "Format string recognition error:" 1>&2 + cat f-y-1-$n.in 1>&2 + echo "Got:" 1>&2 + cat f-y-1-$n.po 1>&2 + exit 1 + fi +done < f-y-1.data + +rm -fr $tmpfiles + +exit 0 diff --git a/tests/format-ycp-2 b/tests/format-ycp-2 new file mode 100755 index 000000000..05b81385a --- /dev/null +++ b/tests/format-ycp-2 @@ -0,0 +1,72 @@ +#! /bin/sh + +# Test checking of YCP format strings. + +tmpfiles="" +trap 'rm -fr $tmpfiles' 1 2 3 15 + +tmpfiles="$tmpfiles f-y-2.data" +cat <<\EOF > f-y-2.data +# Valid: %% doesn't count +msgid "abc%%def" +msgstr "xyz" +# Invalid: invalid msgstr +msgid "abc%%def" +msgstr "xyz%" +# Valid: same arguments +msgid "abc%2def" +msgstr "xyz%2" +# Valid: permutation +msgid "abc%3%1%2def" +msgstr "xyz%2%1%3" +# Invalid: too few arguments +msgid "abc%2def%1" +msgstr "xyz%1" +# Invalid: too many arguments +msgid "abc%1def" +msgstr "xyz%1uvw%2" +# Invalid: missing non-final argument +msgid "abc%2def%1" +msgstr "xyz%2" +# Invalid: added non-final argument +msgid "abc%2def" +msgstr "xyz%1%2" +EOF + +: ${MSGFMT=msgfmt} +n=0 +while read comment; do + read msgid_line + read msgstr_line + n=`expr $n + 1` + tmpfiles="$tmpfiles f-y-2-$n.po f-y-2-$n.mo" + cat < f-y-2-$n.po +#, ycp-format +${msgid_line} +${msgstr_line} +EOF + fail= + if echo "$comment" | grep 'Valid:' > /dev/null; then + if ${MSGFMT} -c -o f-y-2-$n.mo f-y-2-$n.po; then + : + else + fail=yes + fi + else + ${MSGFMT} -c -o f-y-2-$n.mo f-y-2-$n.po 2> /dev/null + if test $? = 1; then + : + else + fail=yes + fi + fi + if test -n "$fail"; then + echo "Format string checking error:" 1>&2 + cat f-y-2-$n.po 1>&2 + exit 1 + fi +done < f-y-2.data + +rm -fr $tmpfiles + +exit 0