1/* Qt format strings.
2   Copyright (C) 2003-2004, 2006 Free Software Foundation, Inc.
3   Written by Bruno Haible <bruno@clisp.org>, 2003.
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2, or (at your option)
8   any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software Foundation,
17   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19#ifdef HAVE_CONFIG_H
20# include <config.h>
21#endif
22
23#include <stdbool.h>
24#include <stdlib.h>
25
26#include "format.h"
27#include "xalloc.h"
28#include "xvasprintf.h"
29#include "gettext.h"
30
31#define _(str) gettext (str)
32
33/* Qt format strings are processed by QString::arg and are documented in
34   qt-3.0.5/doc/html/qstring.html.
35   A directive starts with '%' and is followed by a digit ('0' to '9').
36   Each %n must occur only once in the given string.
37   The first .arg() invocation replaces the %n with the lowest numbered n,
38   the next .arg() invocation then replaces the %n with the second-lowest
39   numbered n, and so on.
40   (This is inherently buggy because a '%' in the first replacement confuses
41   the second .arg() invocation.)
42   Although %0 is supported, usually %1 denotes the first argument, %2 the
43   second argument etc.  */
44
45struct spec
46{
47  unsigned int directives;
48  unsigned int arg_count;
49  bool args_used[10];
50};
51
52
53static void *
54format_parse (const char *format, bool translated, char **invalid_reason)
55{
56  struct spec spec;
57  struct spec *result;
58
59  spec.directives = 0;
60  spec.arg_count = 0;
61
62  for (; *format != '\0';)
63    if (*format++ == '%')
64      if (*format >= '0' && *format <= '9')
65	{
66	  /* A directive.  */
67	  unsigned int number;
68
69	  spec.directives++;
70
71	  number = *format - '0';
72
73	  while (spec.arg_count <= number)
74	    spec.args_used[spec.arg_count++] = false;
75	  if (spec.args_used[number])
76	    {
77	      *invalid_reason =
78		xasprintf (_("Multiple references to %%%c."), *format);
79	      goto bad_format;
80	    }
81	  spec.args_used[number] = true;
82
83	  format++;
84	}
85
86  result = (struct spec *) xmalloc (sizeof (struct spec));
87  *result = spec;
88  return result;
89
90 bad_format:
91  return NULL;
92}
93
94static void
95format_free (void *descr)
96{
97  struct spec *spec = (struct spec *) descr;
98
99  free (spec);
100}
101
102static int
103format_get_number_of_directives (void *descr)
104{
105  struct spec *spec = (struct spec *) descr;
106
107  return spec->directives;
108}
109
110static bool
111format_check (void *msgid_descr, void *msgstr_descr, bool equality,
112	      formatstring_error_logger_t error_logger,
113	      const char *pretty_msgstr)
114{
115  struct spec *spec1 = (struct spec *) msgid_descr;
116  struct spec *spec2 = (struct spec *) msgstr_descr;
117  bool err = false;
118  unsigned int i;
119
120  for (i = 0; i < spec1->arg_count || i < spec2->arg_count; i++)
121    {
122      bool arg_used1 = (i < spec1->arg_count && spec1->args_used[i]);
123      bool arg_used2 = (i < spec2->arg_count && spec2->args_used[i]);
124
125      /* The translator cannot omit a %n from the msgstr because that would
126	 yield a "Argument missing" warning at runtime.  */
127      if (arg_used1 != arg_used2)
128	{
129	  if (error_logger)
130	    error_logger (arg_used1
131			  ? _("a format specification for argument %u doesn't exist in '%s'")
132			  : _("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"),
133			  i, pretty_msgstr);
134	  err = true;
135	  break;
136	}
137    }
138
139  return err;
140}
141
142
143struct formatstring_parser formatstring_qt =
144{
145  format_parse,
146  format_free,
147  format_get_number_of_directives,
148  NULL,
149  format_check
150};
151
152
153#ifdef TEST
154
155/* Test program: Print the argument list specification returned by
156   format_parse for strings read from standard input.  */
157
158#include <stdio.h>
159#include "getline.h"
160
161static void
162format_print (void *descr)
163{
164  struct spec *spec = (struct spec *) descr;
165  unsigned int i;
166
167  if (spec == NULL)
168    {
169      printf ("INVALID");
170      return;
171    }
172
173  printf ("(");
174  for (i = 0; i < spec->arg_count; i++)
175    {
176      if (i > 0)
177	printf (" ");
178      if (spec->args_used[i])
179	printf ("*");
180      else
181	printf ("_");
182    }
183  printf (")");
184}
185
186int
187main ()
188{
189  for (;;)
190    {
191      char *line = NULL;
192      size_t line_size = 0;
193      int line_len;
194      char *invalid_reason;
195      void *descr;
196
197      line_len = getline (&line, &line_size, stdin);
198      if (line_len < 0)
199	break;
200      if (line_len > 0 && line[line_len - 1] == '\n')
201	line[--line_len] = '\0';
202
203      invalid_reason = NULL;
204      descr = format_parse (line, false, &invalid_reason);
205
206      format_print (descr);
207      printf ("\n");
208      if (descr == NULL)
209	printf ("%s\n", invalid_reason);
210
211      free (invalid_reason);
212      free (line);
213    }
214
215  return 0;
216}
217
218/*
219 * For Emacs M-x compile
220 * Local Variables:
221 * compile-command: "/bin/sh ../libtool --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../lib -I../intl -DHAVE_CONFIG_H -DTEST format-qt.c ../lib/libgettextlib.la"
222 * End:
223 */
224
225#endif /* TEST */
226