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