1 /* C# format strings.
2 Copyright (C) 2003-2004, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 "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 *
format_parse(const char * format,bool translated,char ** invalid_reason)60 format_parse (const char *format, bool translated, char **invalid_reason)
61 {
62 struct spec spec;
63 struct spec *result;
64
65 spec.directives = 0;
66 spec.numbered_arg_count = 0;
67
68 for (; *format != '\0';)
69 {
70 char c = *format++;
71
72 if (c == '{')
73 {
74 if (*format == '{')
75 format++;
76 else
77 {
78 /* A directive. */
79 unsigned int number;
80
81 spec.directives++;
82
83 if (!c_isdigit (*format))
84 {
85 *invalid_reason =
86 xasprintf (_("In the directive number %u, '{' is not followed by an argument number."), spec.directives);
87 return NULL;
88 }
89 number = 0;
90 do
91 {
92 number = 10 * number + (*format - '0');
93 format++;
94 }
95 while (c_isdigit (*format));
96
97 if (*format == ',')
98 {
99 /* Parse width. */
100 format++;
101 if (*format == '-')
102 format++;
103 if (!c_isdigit (*format))
104 {
105 *invalid_reason =
106 xasprintf (_("In the directive number %u, ',' is not followed by a number."), spec.directives);
107 return NULL;
108 }
109 do
110 format++;
111 while (c_isdigit (*format));
112 }
113
114 if (*format == ':')
115 {
116 /* Parse format specifiers. */
117 do
118 format++;
119 while (*format != '\0' && *format != '}');
120 }
121
122 if (*format == '\0')
123 {
124 *invalid_reason =
125 xstrdup (_("The string ends in the middle of a directive: found '{' without matching '}'."));
126 return NULL;
127 }
128
129 if (*format != '}')
130 {
131 *invalid_reason =
132 (c_isprint (*format)
133 ? xasprintf (_("The directive number %u ends with an invalid character '%c' instead of '}'."), spec.directives, *format)
134 : xasprintf (_("The directive number %u ends with an invalid character instead of '}'."), spec.directives));
135 return NULL;
136 }
137
138 format++;
139
140 if (spec.numbered_arg_count <= number)
141 spec.numbered_arg_count = number + 1;
142 }
143 }
144 else if (c == '}')
145 {
146 if (*format == '}')
147 format++;
148 else
149 {
150 *invalid_reason =
151 (spec.directives == 0
152 ? xstrdup (_("The string starts in the middle of a directive: found '}' without matching '{'."))
153 : xasprintf (_("The string contains a lone '}' after directive number %u."), spec.directives));
154 return NULL;
155 }
156 }
157 }
158
159 result = (struct spec *) xmalloc (sizeof (struct spec));
160 *result = spec;
161 return result;
162 }
163
164 static void
format_free(void * descr)165 format_free (void *descr)
166 {
167 struct spec *spec = (struct spec *) descr;
168
169 free (spec);
170 }
171
172 static int
format_get_number_of_directives(void * descr)173 format_get_number_of_directives (void *descr)
174 {
175 struct spec *spec = (struct spec *) descr;
176
177 return spec->directives;
178 }
179
180 static bool
format_check(void * msgid_descr,void * msgstr_descr,bool equality,formatstring_error_logger_t error_logger,const char * pretty_msgstr)181 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
182 formatstring_error_logger_t error_logger,
183 const char *pretty_msgstr)
184 {
185 struct spec *spec1 = (struct spec *) msgid_descr;
186 struct spec *spec2 = (struct spec *) msgstr_descr;
187 bool err = false;
188
189 /* Check that the argument counts are the same. */
190 if (equality
191 ? spec1->numbered_arg_count != spec2->numbered_arg_count
192 : spec1->numbered_arg_count < spec2->numbered_arg_count)
193 {
194 if (error_logger)
195 error_logger (_("number of format specifications in 'msgid' and '%s' does not match"),
196 pretty_msgstr);
197 err = true;
198 }
199
200 return err;
201 }
202
203
204 struct formatstring_parser formatstring_csharp =
205 {
206 format_parse,
207 format_free,
208 format_get_number_of_directives,
209 NULL,
210 format_check
211 };
212
213
214 #ifdef TEST
215
216 /* Test program: Print the argument list specification returned by
217 format_parse for strings read from standard input. */
218
219 #include <stdio.h>
220 #include "getline.h"
221
222 static void
format_print(void * descr)223 format_print (void *descr)
224 {
225 struct spec *spec = (struct spec *) descr;
226 unsigned int i;
227
228 if (spec == NULL)
229 {
230 printf ("INVALID");
231 return;
232 }
233
234 printf ("(");
235 for (i = 0; i < spec->numbered_arg_count; i++)
236 {
237 if (i > 0)
238 printf (" ");
239 printf ("*");
240 }
241 printf (")");
242 }
243
244 int
main()245 main ()
246 {
247 for (;;)
248 {
249 char *line = NULL;
250 size_t line_size = 0;
251 int line_len;
252 char *invalid_reason;
253 void *descr;
254
255 line_len = getline (&line, &line_size, stdin);
256 if (line_len < 0)
257 break;
258 if (line_len > 0 && line[line_len - 1] == '\n')
259 line[--line_len] = '\0';
260
261 invalid_reason = NULL;
262 descr = format_parse (line, false, &invalid_reason);
263
264 format_print (descr);
265 printf ("\n");
266 if (descr == NULL)
267 printf ("%s\n", invalid_reason);
268
269 free (invalid_reason);
270 free (line);
271 }
272
273 return 0;
274 }
275
276 /*
277 * For Emacs M-x compile
278 * Local Variables:
279 * 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-csharp.c ../lib/libgettextlib.la"
280 * End:
281 */
282
283 #endif /* TEST */
284