1 /* Qt plural format strings.
2 Copyright (C) 2003-2004, 2006-2007, 2009, 2019 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2009.
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 3 of the License, or
8 (at your option) 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, see <https://www.gnu.org/licenses/>. */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21
22 #include <stdbool.h>
23 #include <stdlib.h>
24
25 #include "format.h"
26 #include "xalloc.h"
27 #include "gettext.h"
28
29 #define _(str) gettext (str)
30
31 /* Qt plural format strings are processed by QObject::tr and are documented in
32 qt-x11-opensource-src-4.3.1/doc/html/qobject.html#tr.
33 A directive
34 - starts with '%',
35 - is optionally followed by 'L' (a no-op),
36 - is followed by 'n'.
37 Every directive is replaced by the numeric argument N passed to QObject::tr.
38 */
39
40 struct spec
41 {
42 /* Number of format directives. */
43 unsigned int directives;
44 };
45
46
47 static void *
format_parse(const char * format,bool translated,char * fdi,char ** invalid_reason)48 format_parse (const char *format, bool translated, char *fdi,
49 char **invalid_reason)
50 {
51 const char *const format_start = format;
52 struct spec spec;
53 struct spec *result;
54
55 spec.directives = 0;
56
57 for (; *format != '\0';)
58 if (*format++ == '%')
59 {
60 const char *dir_start = format - 1;
61
62 if (*format == 'L')
63 format++;
64 if (*format == 'n')
65 {
66 /* A directive. */
67 FDI_SET (dir_start, FMTDIR_START);
68 spec.directives++;
69 FDI_SET (format, FMTDIR_END);
70
71 format++;
72 }
73 }
74
75 result = XMALLOC (struct spec);
76 *result = spec;
77 return result;
78 }
79
80 static void
format_free(void * descr)81 format_free (void *descr)
82 {
83 struct spec *spec = (struct spec *) descr;
84
85 free (spec);
86 }
87
88 static int
format_get_number_of_directives(void * descr)89 format_get_number_of_directives (void *descr)
90 {
91 struct spec *spec = (struct spec *) descr;
92
93 return spec->directives;
94 }
95
96 static bool
format_check(void * msgid_descr,void * msgstr_descr,bool equality,formatstring_error_logger_t error_logger,const char * pretty_msgid,const char * pretty_msgstr)97 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
98 formatstring_error_logger_t error_logger,
99 const char *pretty_msgid, const char *pretty_msgstr)
100 {
101 struct spec *spec1 = (struct spec *) msgid_descr;
102 struct spec *spec2 = (struct spec *) msgstr_descr;
103 bool err = false;
104
105 /* Check the argument is used. */
106 if ((spec1->directives == 0 && spec2->directives > 0)
107 || (equality && spec1->directives > 0 && spec2->directives == 0))
108 {
109 if (error_logger)
110 error_logger (_("number of format specifications in '%s' and '%s' does not match"),
111 pretty_msgid, pretty_msgstr);
112 err = true;
113 }
114
115 return err;
116 }
117
118
119 struct formatstring_parser formatstring_qt_plural =
120 {
121 format_parse,
122 format_free,
123 format_get_number_of_directives,
124 NULL,
125 format_check
126 };
127
128
129 #ifdef TEST
130
131 /* Test program: Print the argument list specification returned by
132 format_parse for strings read from standard input. */
133
134 #include <stdio.h>
135
136 static void
format_print(void * descr)137 format_print (void *descr)
138 {
139 struct spec *spec = (struct spec *) descr;
140
141 if (spec == NULL)
142 {
143 printf ("INVALID");
144 return;
145 }
146
147 printf ("(");
148 if (spec->directives > 0)
149 printf ("*");
150 else
151 printf ("_");
152 printf (")");
153 }
154
155 int
main()156 main ()
157 {
158 for (;;)
159 {
160 char *line = NULL;
161 size_t line_size = 0;
162 int line_len;
163 char *invalid_reason;
164 void *descr;
165
166 line_len = getline (&line, &line_size, stdin);
167 if (line_len < 0)
168 break;
169 if (line_len > 0 && line[line_len - 1] == '\n')
170 line[--line_len] = '\0';
171
172 invalid_reason = NULL;
173 descr = format_parse (line, false, NULL, &invalid_reason);
174
175 format_print (descr);
176 printf ("\n");
177 if (descr == NULL)
178 printf ("%s\n", invalid_reason);
179
180 free (invalid_reason);
181 free (line);
182 }
183
184 return 0;
185 }
186
187 /*
188 * For Emacs M-x compile
189 * Local Variables:
190 * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../../gettext-runtime/intl -DHAVE_CONFIG_H -DTEST format-qt-plural.c ../gnulib-lib/libgettextlib.la"
191 * End:
192 */
193
194 #endif /* TEST */
195