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