1/* Qt format strings.
2   Copyright (C) 2003-2004 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 "xerror.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  format_check
149};
150
151
152#ifdef TEST
153
154/* Test program: Print the argument list specification returned by
155   format_parse for strings read from standard input.  */
156
157#include <stdio.h>
158#include "getline.h"
159
160static void
161format_print (void *descr)
162{
163  struct spec *spec = (struct spec *) descr;
164  unsigned int i;
165
166  if (spec == NULL)
167    {
168      printf ("INVALID");
169      return;
170    }
171
172  printf ("(");
173  for (i = 0; i < spec->arg_count; i++)
174    {
175      if (i > 0)
176	printf (" ");
177      if (spec->args_used[i])
178	printf ("*");
179      else
180	printf ("_");
181    }
182  printf (")");
183}
184
185int
186main ()
187{
188  for (;;)
189    {
190      char *line = NULL;
191      size_t line_size = 0;
192      int line_len;
193      char *invalid_reason;
194      void *descr;
195
196      line_len = getline (&line, &line_size, stdin);
197      if (line_len < 0)
198	break;
199      if (line_len > 0 && line[line_len - 1] == '\n')
200	line[--line_len] = '\0';
201
202      invalid_reason = NULL;
203      descr = format_parse (line, false, &invalid_reason);
204
205      format_print (descr);
206      printf ("\n");
207      if (descr == NULL)
208	printf ("%s\n", invalid_reason);
209
210      free (invalid_reason);
211      free (line);
212    }
213
214  return 0;
215}
216
217/*
218 * For Emacs M-x compile
219 * Local Variables:
220 * 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"
221 * End:
222 */
223
224#endif /* TEST */
225