]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
YCP format string checking.
authorBruno Haible <bruno@clisp.org>
Mon, 27 Aug 2001 12:05:20 +0000 (12:05 +0000)
committerBruno Haible <bruno@clisp.org>
Mon, 27 Aug 2001 12:05:20 +0000 (12:05 +0000)
src/format-ycp.c [new file with mode: 0644]
tests/format-ycp-1 [new file with mode: 0755]
tests/format-ycp-2 [new file with mode: 0755]

diff --git a/src/format-ycp.c b/src/format-ycp.c
new file mode 100644 (file)
index 0000000..61bd74a
--- /dev/null
@@ -0,0 +1,221 @@
+/* YCP format strings.
+   Copyright (C) 2001 Free Software Foundation, Inc.
+   Written by Bruno Haible <haible@clisp.cons.org>, 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 <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#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 <stdio.h>
+#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 (executable)
index 0000000..ba4eac2
--- /dev/null
@@ -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 <<EOF > 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 (executable)
index 0000000..05b8138
--- /dev/null
@@ -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 <<EOF > 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