Imported Upstream version 0.19.7
[platform/upstream/gettext.git] / gettext-tools / src / format-csharp.c
1 /* C# format strings.
2    Copyright (C) 2003-2004, 2006-2007, 2009, 2015 Free Software
3    Foundation, Inc.
4    Written by Bruno Haible <bruno@clisp.org>, 2003.
5
6    This program is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
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 "c-ctype.h"
28 #include "xalloc.h"
29 #include "xvasprintf.h"
30 #include "gettext.h"
31
32 #define _(str) gettext (str)
33
34 /* C# format strings are described in the description of the .NET System.String
35    class and implemented in
36      pnetlib-0.5.6/runtime/System/String.cs
37    and
38      mcs-0.28/class/corlib/System/String.cs
39    A format string consists of literal text (that is output verbatim), doubled
40    braces ('{{' and '}}', that lead to a single brace when output), and
41    directives.
42    A directive
43    - starts with '{',
44    - is followed by a nonnegative integer m,
45    - is optionally followed by ',' and an integer denoting a width,
46    - is optionally followed by ':' and a sequence of format specifiers.
47      (But the interpretation of the format specifiers is up to the IFormattable
48      implementation, depending on the argument's runtime value. New classes
49      implementing IFormattable can be defined by the user.)
50    - is finished with '}'.
51  */
52
53 struct spec
54 {
55   unsigned int directives;
56   unsigned int numbered_arg_count;
57 };
58
59 static void *
60 format_parse (const char *format, bool translated, char *fdi,
61               char **invalid_reason)
62 {
63   const char *const format_start = format;
64   struct spec spec;
65   struct spec *result;
66
67   spec.directives = 0;
68   spec.numbered_arg_count = 0;
69
70   for (; *format != '\0';)
71     {
72       char c = *format++;
73
74       if (c == '{')
75         {
76           FDI_SET (format - 1, FMTDIR_START);
77           if (*format == '{')
78             format++;
79           else
80             {
81               /* A directive.  */
82               unsigned int number;
83
84               spec.directives++;
85
86               if (!c_isdigit (*format))
87                 {
88                   *invalid_reason =
89                     xasprintf (_("In the directive number %u, '{' is not followed by an argument number."), spec.directives);
90                   FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR);
91                   return NULL;
92                 }
93               number = 0;
94               do
95                 {
96                   number = 10 * number + (*format - '0');
97                   format++;
98                 }
99               while (c_isdigit (*format));
100
101               if (*format == ',')
102                 {
103                   /* Parse width.  */
104                   format++;
105                   if (*format == '-')
106                     format++;
107                   if (!c_isdigit (*format))
108                     {
109                       *invalid_reason =
110                         xasprintf (_("In the directive number %u, ',' is not followed by a number."), spec.directives);
111                       FDI_SET (*format == '\0' ? format - 1 : format,
112                                FMTDIR_ERROR);
113                       return NULL;
114                     }
115                   do
116                     format++;
117                   while (c_isdigit (*format));
118                 }
119
120               if (*format == ':')
121                 {
122                   /* Parse format specifiers.  */
123                   do
124                     format++;
125                   while (*format != '\0' && *format != '}');
126                 }
127
128               if (*format == '\0')
129                 {
130                   *invalid_reason =
131                     xstrdup (_("The string ends in the middle of a directive: found '{' without matching '}'."));
132                   FDI_SET (format - 1, FMTDIR_ERROR);
133                   return NULL;
134                 }
135
136               if (*format != '}')
137                 {
138                   *invalid_reason =
139                     (c_isprint (*format)
140                      ? xasprintf (_("The directive number %u ends with an invalid character '%c' instead of '}'."), spec.directives, *format)
141                      : xasprintf (_("The directive number %u ends with an invalid character instead of '}'."), spec.directives));
142                   FDI_SET (format, FMTDIR_ERROR);
143                   return NULL;
144                 }
145
146               format++;
147
148               if (spec.numbered_arg_count <= number)
149                 spec.numbered_arg_count = number + 1;
150             }
151           FDI_SET (format - 1, FMTDIR_END);
152         }
153       else if (c == '}')
154         {
155           FDI_SET (format - 1, FMTDIR_START);
156           if (*format == '}')
157             format++;
158           else
159             {
160               *invalid_reason =
161                 (spec.directives == 0
162                  ? xstrdup (_("The string starts in the middle of a directive: found '}' without matching '{'."))
163                  : xasprintf (_("The string contains a lone '}' after directive number %u."), spec.directives));
164               FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR);
165               return NULL;
166             }
167           FDI_SET (format - 1, FMTDIR_END);
168         }
169     }
170
171   result = XMALLOC (struct spec);
172   *result = spec;
173   return result;
174 }
175
176 static void
177 format_free (void *descr)
178 {
179   struct spec *spec = (struct spec *) descr;
180
181   free (spec);
182 }
183
184 static int
185 format_get_number_of_directives (void *descr)
186 {
187   struct spec *spec = (struct spec *) descr;
188
189   return spec->directives;
190 }
191
192 static bool
193 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
194               formatstring_error_logger_t error_logger,
195               const char *pretty_msgid, const char *pretty_msgstr)
196 {
197   struct spec *spec1 = (struct spec *) msgid_descr;
198   struct spec *spec2 = (struct spec *) msgstr_descr;
199   bool err = false;
200
201   /* Check that the argument counts are the same.  */
202   if (equality
203       ? spec1->numbered_arg_count != spec2->numbered_arg_count
204       : spec1->numbered_arg_count < spec2->numbered_arg_count)
205     {
206       if (error_logger)
207         error_logger (_("number of format specifications in '%s' and '%s' does not match"),
208                       pretty_msgid, pretty_msgstr);
209       err = true;
210     }
211
212   return err;
213 }
214
215
216 struct formatstring_parser formatstring_csharp =
217 {
218   format_parse,
219   format_free,
220   format_get_number_of_directives,
221   NULL,
222   format_check
223 };
224
225
226 #ifdef TEST
227
228 /* Test program: Print the argument list specification returned by
229    format_parse for strings read from standard input.  */
230
231 #include <stdio.h>
232
233 static void
234 format_print (void *descr)
235 {
236   struct spec *spec = (struct spec *) descr;
237   unsigned int i;
238
239   if (spec == NULL)
240     {
241       printf ("INVALID");
242       return;
243     }
244
245   printf ("(");
246   for (i = 0; i < spec->numbered_arg_count; i++)
247     {
248       if (i > 0)
249         printf (" ");
250       printf ("*");
251     }
252   printf (")");
253 }
254
255 int
256 main ()
257 {
258   for (;;)
259     {
260       char *line = NULL;
261       size_t line_size = 0;
262       int line_len;
263       char *invalid_reason;
264       void *descr;
265
266       line_len = getline (&line, &line_size, stdin);
267       if (line_len < 0)
268         break;
269       if (line_len > 0 && line[line_len - 1] == '\n')
270         line[--line_len] = '\0';
271
272       invalid_reason = NULL;
273       descr = format_parse (line, false, NULL, &invalid_reason);
274
275       format_print (descr);
276       printf ("\n");
277       if (descr == NULL)
278         printf ("%s\n", invalid_reason);
279
280       free (invalid_reason);
281       free (line);
282     }
283
284   return 0;
285 }
286
287 /*
288  * For Emacs M-x compile
289  * Local Variables:
290  * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-csharp.c ../gnulib-lib/libgettextlib.la"
291  * End:
292  */
293
294 #endif /* TEST */