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