xref: /netbsd-src/external/gpl2/gettext/dist/gettext-tools/src/format-csharp.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
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