1*946379e7Schristos /* Writing C# satellite assemblies.
2*946379e7Schristos Copyright (C) 2003-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 #include <alloca.h>
23*946379e7Schristos
24*946379e7Schristos /* Specification. */
25*946379e7Schristos #include "write-csharp.h"
26*946379e7Schristos
27*946379e7Schristos #include <errno.h>
28*946379e7Schristos #include <stdbool.h>
29*946379e7Schristos #include <stdlib.h>
30*946379e7Schristos #include <stdio.h>
31*946379e7Schristos #include <string.h>
32*946379e7Schristos
33*946379e7Schristos #include <sys/stat.h>
34*946379e7Schristos #if !defined S_ISDIR && defined S_IFDIR
35*946379e7Schristos # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
36*946379e7Schristos #endif
37*946379e7Schristos #if !S_IRUSR && S_IREAD
38*946379e7Schristos # define S_IRUSR S_IREAD
39*946379e7Schristos #endif
40*946379e7Schristos #if !S_IRUSR
41*946379e7Schristos # define S_IRUSR 00400
42*946379e7Schristos #endif
43*946379e7Schristos #if !S_IWUSR && S_IWRITE
44*946379e7Schristos # define S_IWUSR S_IWRITE
45*946379e7Schristos #endif
46*946379e7Schristos #if !S_IWUSR
47*946379e7Schristos # define S_IWUSR 00200
48*946379e7Schristos #endif
49*946379e7Schristos #if !S_IXUSR && S_IEXEC
50*946379e7Schristos # define S_IXUSR S_IEXEC
51*946379e7Schristos #endif
52*946379e7Schristos #if !S_IXUSR
53*946379e7Schristos # define S_IXUSR 00100
54*946379e7Schristos #endif
55*946379e7Schristos #if !S_IRGRP
56*946379e7Schristos # define S_IRGRP (S_IRUSR >> 3)
57*946379e7Schristos #endif
58*946379e7Schristos #if !S_IWGRP
59*946379e7Schristos # define S_IWGRP (S_IWUSR >> 3)
60*946379e7Schristos #endif
61*946379e7Schristos #if !S_IXGRP
62*946379e7Schristos # define S_IXGRP (S_IXUSR >> 3)
63*946379e7Schristos #endif
64*946379e7Schristos #if !S_IROTH
65*946379e7Schristos # define S_IROTH (S_IRUSR >> 6)
66*946379e7Schristos #endif
67*946379e7Schristos #if !S_IWOTH
68*946379e7Schristos # define S_IWOTH (S_IWUSR >> 6)
69*946379e7Schristos #endif
70*946379e7Schristos #if !S_IXOTH
71*946379e7Schristos # define S_IXOTH (S_IXUSR >> 6)
72*946379e7Schristos #endif
73*946379e7Schristos
74*946379e7Schristos #ifdef __MINGW32__
75*946379e7Schristos # include <io.h>
76*946379e7Schristos /* mingw's _mkdir() function has 1 argument, but we pass 2 arguments.
77*946379e7Schristos Therefore we have to disable the argument count checking. */
78*946379e7Schristos # define mkdir ((int (*)()) _mkdir)
79*946379e7Schristos #endif
80*946379e7Schristos
81*946379e7Schristos #include "c-ctype.h"
82*946379e7Schristos #include "relocatable.h"
83*946379e7Schristos #include "error.h"
84*946379e7Schristos #include "xerror.h"
85*946379e7Schristos #include "csharpcomp.h"
86*946379e7Schristos #include "message.h"
87*946379e7Schristos #include "msgfmt.h"
88*946379e7Schristos #include "msgl-iconv.h"
89*946379e7Schristos #include "plural-exp.h"
90*946379e7Schristos #include "po-charset.h"
91*946379e7Schristos #include "xalloc.h"
92*946379e7Schristos #include "pathname.h"
93*946379e7Schristos #include "fwriteerror.h"
94*946379e7Schristos #include "clean-temp.h"
95*946379e7Schristos #include "utf8-ucs4.h"
96*946379e7Schristos #include "gettext.h"
97*946379e7Schristos
98*946379e7Schristos #define _(str) gettext (str)
99*946379e7Schristos
100*946379e7Schristos
101*946379e7Schristos /* Convert a resource name to a class name.
102*946379e7Schristos Return a nonempty string consisting of alphanumerics and underscores
103*946379e7Schristos and starting with a letter or underscore. */
104*946379e7Schristos static char *
construct_class_name(const char * resource_name)105*946379e7Schristos construct_class_name (const char *resource_name)
106*946379e7Schristos {
107*946379e7Schristos /* This code must be kept consistent with intl.cs, function
108*946379e7Schristos GettextResourceManager.ConstructClassName. */
109*946379e7Schristos /* We could just return an arbitrary fixed class name, like "Messages",
110*946379e7Schristos assuming that every assembly will only ever contain one
111*946379e7Schristos GettextResourceSet subclass, but this assumption would break the day
112*946379e7Schristos we want to support multi-domain PO files in the same format... */
113*946379e7Schristos bool valid;
114*946379e7Schristos const char *p;
115*946379e7Schristos
116*946379e7Schristos /* Test for a valid ASCII identifier:
117*946379e7Schristos - nonempty,
118*946379e7Schristos - first character is A..Za..z_ - see x-csharp.c:is_identifier_start.
119*946379e7Schristos - next characters are A..Za..z_0..9 - see x-csharp.c:is_identifier_part.
120*946379e7Schristos */
121*946379e7Schristos valid = (resource_name[0] != '\0');
122*946379e7Schristos for (p = resource_name; valid && *p != '\0'; p++)
123*946379e7Schristos {
124*946379e7Schristos char c = *p;
125*946379e7Schristos if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_')
126*946379e7Schristos || (p > resource_name && c >= '0' && c <= '9')))
127*946379e7Schristos valid = false;
128*946379e7Schristos }
129*946379e7Schristos if (valid)
130*946379e7Schristos return xstrdup (resource_name);
131*946379e7Schristos else
132*946379e7Schristos {
133*946379e7Schristos static const char hexdigit[] = "0123456789abcdef";
134*946379e7Schristos const char *str = resource_name;
135*946379e7Schristos const char *str_limit = str + strlen (str);
136*946379e7Schristos char *class_name = (char *) xmalloc (12 + 6 * (str_limit - str) + 1);
137*946379e7Schristos char *b;
138*946379e7Schristos
139*946379e7Schristos b = class_name;
140*946379e7Schristos memcpy (b, "__UESCAPED__", 12); b += 12;
141*946379e7Schristos while (str < str_limit)
142*946379e7Schristos {
143*946379e7Schristos unsigned int uc;
144*946379e7Schristos str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
145*946379e7Schristos if (uc >= 0x10000)
146*946379e7Schristos {
147*946379e7Schristos *b++ = '_';
148*946379e7Schristos *b++ = 'U';
149*946379e7Schristos *b++ = hexdigit[(uc >> 28) & 0x0f];
150*946379e7Schristos *b++ = hexdigit[(uc >> 24) & 0x0f];
151*946379e7Schristos *b++ = hexdigit[(uc >> 20) & 0x0f];
152*946379e7Schristos *b++ = hexdigit[(uc >> 16) & 0x0f];
153*946379e7Schristos *b++ = hexdigit[(uc >> 12) & 0x0f];
154*946379e7Schristos *b++ = hexdigit[(uc >> 8) & 0x0f];
155*946379e7Schristos *b++ = hexdigit[(uc >> 4) & 0x0f];
156*946379e7Schristos *b++ = hexdigit[uc & 0x0f];
157*946379e7Schristos }
158*946379e7Schristos else if (!((uc >= 'A' && uc <= 'Z') || (uc >= 'a' && uc <= 'z')
159*946379e7Schristos || (uc >= '0' && uc <= '9')))
160*946379e7Schristos {
161*946379e7Schristos *b++ = '_';
162*946379e7Schristos *b++ = 'u';
163*946379e7Schristos *b++ = hexdigit[(uc >> 12) & 0x0f];
164*946379e7Schristos *b++ = hexdigit[(uc >> 8) & 0x0f];
165*946379e7Schristos *b++ = hexdigit[(uc >> 4) & 0x0f];
166*946379e7Schristos *b++ = hexdigit[uc & 0x0f];
167*946379e7Schristos }
168*946379e7Schristos else
169*946379e7Schristos *b++ = uc;
170*946379e7Schristos }
171*946379e7Schristos *b++ = '\0';
172*946379e7Schristos return (char *) xrealloc (class_name, b - class_name);
173*946379e7Schristos }
174*946379e7Schristos }
175*946379e7Schristos
176*946379e7Schristos
177*946379e7Schristos /* Write a string in C# Unicode notation to the given stream. */
178*946379e7Schristos static void
write_csharp_string(FILE * stream,const char * str)179*946379e7Schristos write_csharp_string (FILE *stream, const char *str)
180*946379e7Schristos {
181*946379e7Schristos static const char hexdigit[] = "0123456789abcdef";
182*946379e7Schristos const char *str_limit = str + strlen (str);
183*946379e7Schristos
184*946379e7Schristos fprintf (stream, "\"");
185*946379e7Schristos while (str < str_limit)
186*946379e7Schristos {
187*946379e7Schristos unsigned int uc;
188*946379e7Schristos str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
189*946379e7Schristos if (uc == 0x0000)
190*946379e7Schristos fprintf (stream, "\\0");
191*946379e7Schristos else if (uc == 0x0007)
192*946379e7Schristos fprintf (stream, "\\a");
193*946379e7Schristos else if (uc == 0x0008)
194*946379e7Schristos fprintf (stream, "\\b");
195*946379e7Schristos else if (uc == 0x0009)
196*946379e7Schristos fprintf (stream, "\\t");
197*946379e7Schristos else if (uc == 0x000a)
198*946379e7Schristos fprintf (stream, "\\n");
199*946379e7Schristos else if (uc == 0x000b)
200*946379e7Schristos fprintf (stream, "\\v");
201*946379e7Schristos else if (uc == 0x000c)
202*946379e7Schristos fprintf (stream, "\\f");
203*946379e7Schristos else if (uc == 0x000d)
204*946379e7Schristos fprintf (stream, "\\r");
205*946379e7Schristos else if (uc == 0x0022)
206*946379e7Schristos fprintf (stream, "\\\"");
207*946379e7Schristos else if (uc == 0x005c)
208*946379e7Schristos fprintf (stream, "\\\\");
209*946379e7Schristos else if (uc >= 0x0020 && uc < 0x007f)
210*946379e7Schristos fprintf (stream, "%c", uc);
211*946379e7Schristos else if (uc < 0x10000)
212*946379e7Schristos fprintf (stream, "\\u%c%c%c%c",
213*946379e7Schristos hexdigit[(uc >> 12) & 0x0f], hexdigit[(uc >> 8) & 0x0f],
214*946379e7Schristos hexdigit[(uc >> 4) & 0x0f], hexdigit[uc & 0x0f]);
215*946379e7Schristos else
216*946379e7Schristos fprintf (stream, "\\U%c%c%c%c%c%c%c%c",
217*946379e7Schristos hexdigit[(uc >> 28) & 0x0f], hexdigit[(uc >> 24) & 0x0f],
218*946379e7Schristos hexdigit[(uc >> 20) & 0x0f], hexdigit[(uc >> 16) & 0x0f],
219*946379e7Schristos hexdigit[(uc >> 12) & 0x0f], hexdigit[(uc >> 8) & 0x0f],
220*946379e7Schristos hexdigit[(uc >> 4) & 0x0f], hexdigit[uc & 0x0f]);
221*946379e7Schristos }
222*946379e7Schristos fprintf (stream, "\"");
223*946379e7Schristos }
224*946379e7Schristos
225*946379e7Schristos
226*946379e7Schristos /* Write C# code that returns the value for a message. If the message
227*946379e7Schristos has plural forms, it is an expression of type System.String[], otherwise it
228*946379e7Schristos is an expression of type System.String. */
229*946379e7Schristos static void
write_csharp_msgstr(FILE * stream,message_ty * mp)230*946379e7Schristos write_csharp_msgstr (FILE *stream, message_ty *mp)
231*946379e7Schristos {
232*946379e7Schristos if (mp->msgid_plural != NULL)
233*946379e7Schristos {
234*946379e7Schristos bool first;
235*946379e7Schristos const char *p;
236*946379e7Schristos
237*946379e7Schristos fprintf (stream, "new System.String[] { ");
238*946379e7Schristos for (p = mp->msgstr, first = true;
239*946379e7Schristos p < mp->msgstr + mp->msgstr_len;
240*946379e7Schristos p += strlen (p) + 1, first = false)
241*946379e7Schristos {
242*946379e7Schristos if (!first)
243*946379e7Schristos fprintf (stream, ", ");
244*946379e7Schristos write_csharp_string (stream, p);
245*946379e7Schristos }
246*946379e7Schristos fprintf (stream, " }");
247*946379e7Schristos }
248*946379e7Schristos else
249*946379e7Schristos {
250*946379e7Schristos if (mp->msgstr_len != strlen (mp->msgstr) + 1)
251*946379e7Schristos abort ();
252*946379e7Schristos
253*946379e7Schristos write_csharp_string (stream, mp->msgstr);
254*946379e7Schristos }
255*946379e7Schristos }
256*946379e7Schristos
257*946379e7Schristos
258*946379e7Schristos /* Tests whether a plural expression, evaluated according to the C rules,
259*946379e7Schristos can only produce the values 0 and 1. */
260*946379e7Schristos static bool
is_expression_boolean(struct expression * exp)261*946379e7Schristos is_expression_boolean (struct expression *exp)
262*946379e7Schristos {
263*946379e7Schristos switch (exp->operation)
264*946379e7Schristos {
265*946379e7Schristos case var:
266*946379e7Schristos case mult:
267*946379e7Schristos case divide:
268*946379e7Schristos case module:
269*946379e7Schristos case plus:
270*946379e7Schristos case minus:
271*946379e7Schristos return false;
272*946379e7Schristos case lnot:
273*946379e7Schristos case less_than:
274*946379e7Schristos case greater_than:
275*946379e7Schristos case less_or_equal:
276*946379e7Schristos case greater_or_equal:
277*946379e7Schristos case equal:
278*946379e7Schristos case not_equal:
279*946379e7Schristos case land:
280*946379e7Schristos case lor:
281*946379e7Schristos return true;
282*946379e7Schristos case num:
283*946379e7Schristos return (exp->val.num == 0 || exp->val.num == 1);
284*946379e7Schristos case qmop:
285*946379e7Schristos return is_expression_boolean (exp->val.args[1])
286*946379e7Schristos && is_expression_boolean (exp->val.args[2]);
287*946379e7Schristos default:
288*946379e7Schristos abort ();
289*946379e7Schristos }
290*946379e7Schristos }
291*946379e7Schristos
292*946379e7Schristos
293*946379e7Schristos /* Write C# code that evaluates a plural expression according to the C rules.
294*946379e7Schristos The variable is called 'n'. */
295*946379e7Schristos static void
write_csharp_expression(FILE * stream,struct expression * exp,bool as_boolean)296*946379e7Schristos write_csharp_expression (FILE *stream, struct expression *exp, bool as_boolean)
297*946379e7Schristos {
298*946379e7Schristos /* We use parentheses everywhere. This frees us from tracking the priority
299*946379e7Schristos of arithmetic operators. */
300*946379e7Schristos if (as_boolean)
301*946379e7Schristos {
302*946379e7Schristos /* Emit a C# expression of type 'bool'. */
303*946379e7Schristos switch (exp->operation)
304*946379e7Schristos {
305*946379e7Schristos case num:
306*946379e7Schristos fprintf (stream, "%s", exp->val.num ? "true" : "false");
307*946379e7Schristos return;
308*946379e7Schristos case lnot:
309*946379e7Schristos fprintf (stream, "(!");
310*946379e7Schristos write_csharp_expression (stream, exp->val.args[0], true);
311*946379e7Schristos fprintf (stream, ")");
312*946379e7Schristos return;
313*946379e7Schristos case less_than:
314*946379e7Schristos fprintf (stream, "(");
315*946379e7Schristos write_csharp_expression (stream, exp->val.args[0], false);
316*946379e7Schristos fprintf (stream, " < ");
317*946379e7Schristos write_csharp_expression (stream, exp->val.args[1], false);
318*946379e7Schristos fprintf (stream, ")");
319*946379e7Schristos return;
320*946379e7Schristos case greater_than:
321*946379e7Schristos fprintf (stream, "(");
322*946379e7Schristos write_csharp_expression (stream, exp->val.args[0], false);
323*946379e7Schristos fprintf (stream, " > ");
324*946379e7Schristos write_csharp_expression (stream, exp->val.args[1], false);
325*946379e7Schristos fprintf (stream, ")");
326*946379e7Schristos return;
327*946379e7Schristos case less_or_equal:
328*946379e7Schristos fprintf (stream, "(");
329*946379e7Schristos write_csharp_expression (stream, exp->val.args[0], false);
330*946379e7Schristos fprintf (stream, " <= ");
331*946379e7Schristos write_csharp_expression (stream, exp->val.args[1], false);
332*946379e7Schristos fprintf (stream, ")");
333*946379e7Schristos return;
334*946379e7Schristos case greater_or_equal:
335*946379e7Schristos fprintf (stream, "(");
336*946379e7Schristos write_csharp_expression (stream, exp->val.args[0], false);
337*946379e7Schristos fprintf (stream, " >= ");
338*946379e7Schristos write_csharp_expression (stream, exp->val.args[1], false);
339*946379e7Schristos fprintf (stream, ")");
340*946379e7Schristos return;
341*946379e7Schristos case equal:
342*946379e7Schristos fprintf (stream, "(");
343*946379e7Schristos write_csharp_expression (stream, exp->val.args[0], false);
344*946379e7Schristos fprintf (stream, " == ");
345*946379e7Schristos write_csharp_expression (stream, exp->val.args[1], false);
346*946379e7Schristos fprintf (stream, ")");
347*946379e7Schristos return;
348*946379e7Schristos case not_equal:
349*946379e7Schristos fprintf (stream, "(");
350*946379e7Schristos write_csharp_expression (stream, exp->val.args[0], false);
351*946379e7Schristos fprintf (stream, " != ");
352*946379e7Schristos write_csharp_expression (stream, exp->val.args[1], false);
353*946379e7Schristos fprintf (stream, ")");
354*946379e7Schristos return;
355*946379e7Schristos case land:
356*946379e7Schristos fprintf (stream, "(");
357*946379e7Schristos write_csharp_expression (stream, exp->val.args[0], true);
358*946379e7Schristos fprintf (stream, " && ");
359*946379e7Schristos write_csharp_expression (stream, exp->val.args[1], true);
360*946379e7Schristos fprintf (stream, ")");
361*946379e7Schristos return;
362*946379e7Schristos case lor:
363*946379e7Schristos fprintf (stream, "(");
364*946379e7Schristos write_csharp_expression (stream, exp->val.args[0], true);
365*946379e7Schristos fprintf (stream, " || ");
366*946379e7Schristos write_csharp_expression (stream, exp->val.args[1], true);
367*946379e7Schristos fprintf (stream, ")");
368*946379e7Schristos return;
369*946379e7Schristos case qmop:
370*946379e7Schristos if (is_expression_boolean (exp->val.args[1])
371*946379e7Schristos && is_expression_boolean (exp->val.args[2]))
372*946379e7Schristos {
373*946379e7Schristos fprintf (stream, "(");
374*946379e7Schristos write_csharp_expression (stream, exp->val.args[0], true);
375*946379e7Schristos fprintf (stream, " ? ");
376*946379e7Schristos write_csharp_expression (stream, exp->val.args[1], true);
377*946379e7Schristos fprintf (stream, " : ");
378*946379e7Schristos write_csharp_expression (stream, exp->val.args[2], true);
379*946379e7Schristos fprintf (stream, ")");
380*946379e7Schristos return;
381*946379e7Schristos }
382*946379e7Schristos /*FALLTHROUGH*/
383*946379e7Schristos case var:
384*946379e7Schristos case mult:
385*946379e7Schristos case divide:
386*946379e7Schristos case module:
387*946379e7Schristos case plus:
388*946379e7Schristos case minus:
389*946379e7Schristos fprintf (stream, "(");
390*946379e7Schristos write_csharp_expression (stream, exp, false);
391*946379e7Schristos fprintf (stream, " != 0)");
392*946379e7Schristos return;
393*946379e7Schristos default:
394*946379e7Schristos abort ();
395*946379e7Schristos }
396*946379e7Schristos }
397*946379e7Schristos else
398*946379e7Schristos {
399*946379e7Schristos /* Emit a C# expression of type 'long'. */
400*946379e7Schristos switch (exp->operation)
401*946379e7Schristos {
402*946379e7Schristos case var:
403*946379e7Schristos fprintf (stream, "n");
404*946379e7Schristos return;
405*946379e7Schristos case num:
406*946379e7Schristos fprintf (stream, "%lu", exp->val.num);
407*946379e7Schristos return;
408*946379e7Schristos case mult:
409*946379e7Schristos fprintf (stream, "(");
410*946379e7Schristos write_csharp_expression (stream, exp->val.args[0], false);
411*946379e7Schristos fprintf (stream, " * ");
412*946379e7Schristos write_csharp_expression (stream, exp->val.args[1], false);
413*946379e7Schristos fprintf (stream, ")");
414*946379e7Schristos return;
415*946379e7Schristos case divide:
416*946379e7Schristos fprintf (stream, "(");
417*946379e7Schristos write_csharp_expression (stream, exp->val.args[0], false);
418*946379e7Schristos fprintf (stream, " / ");
419*946379e7Schristos write_csharp_expression (stream, exp->val.args[1], false);
420*946379e7Schristos fprintf (stream, ")");
421*946379e7Schristos return;
422*946379e7Schristos case module:
423*946379e7Schristos fprintf (stream, "(");
424*946379e7Schristos write_csharp_expression (stream, exp->val.args[0], false);
425*946379e7Schristos fprintf (stream, " %% ");
426*946379e7Schristos write_csharp_expression (stream, exp->val.args[1], false);
427*946379e7Schristos fprintf (stream, ")");
428*946379e7Schristos return;
429*946379e7Schristos case plus:
430*946379e7Schristos fprintf (stream, "(");
431*946379e7Schristos write_csharp_expression (stream, exp->val.args[0], false);
432*946379e7Schristos fprintf (stream, " + ");
433*946379e7Schristos write_csharp_expression (stream, exp->val.args[1], false);
434*946379e7Schristos fprintf (stream, ")");
435*946379e7Schristos return;
436*946379e7Schristos case minus:
437*946379e7Schristos fprintf (stream, "(");
438*946379e7Schristos write_csharp_expression (stream, exp->val.args[0], false);
439*946379e7Schristos fprintf (stream, " - ");
440*946379e7Schristos write_csharp_expression (stream, exp->val.args[1], false);
441*946379e7Schristos fprintf (stream, ")");
442*946379e7Schristos return;
443*946379e7Schristos case qmop:
444*946379e7Schristos fprintf (stream, "(");
445*946379e7Schristos write_csharp_expression (stream, exp->val.args[0], true);
446*946379e7Schristos fprintf (stream, " ? ");
447*946379e7Schristos write_csharp_expression (stream, exp->val.args[1], false);
448*946379e7Schristos fprintf (stream, " : ");
449*946379e7Schristos write_csharp_expression (stream, exp->val.args[2], false);
450*946379e7Schristos fprintf (stream, ")");
451*946379e7Schristos return;
452*946379e7Schristos case lnot:
453*946379e7Schristos case less_than:
454*946379e7Schristos case greater_than:
455*946379e7Schristos case less_or_equal:
456*946379e7Schristos case greater_or_equal:
457*946379e7Schristos case equal:
458*946379e7Schristos case not_equal:
459*946379e7Schristos case land:
460*946379e7Schristos case lor:
461*946379e7Schristos fprintf (stream, "(");
462*946379e7Schristos write_csharp_expression (stream, exp, true);
463*946379e7Schristos fprintf (stream, " ? 1 : 0)");
464*946379e7Schristos return;
465*946379e7Schristos default:
466*946379e7Schristos abort ();
467*946379e7Schristos }
468*946379e7Schristos }
469*946379e7Schristos }
470*946379e7Schristos
471*946379e7Schristos
472*946379e7Schristos /* Write the C# code for the GettextResourceSet subclass to the given stream.
473*946379e7Schristos Note that we use fully qualified class names and no "using" statements,
474*946379e7Schristos because applications can have their own classes called X.Y.Hashtable or
475*946379e7Schristos X.Y.String. */
476*946379e7Schristos static void
write_csharp_code(FILE * stream,const char * culture_name,const char * class_name,message_list_ty * mlp)477*946379e7Schristos write_csharp_code (FILE *stream, const char *culture_name, const char *class_name, message_list_ty *mlp)
478*946379e7Schristos {
479*946379e7Schristos const char *last_dot;
480*946379e7Schristos const char *class_name_last_part;
481*946379e7Schristos unsigned int plurals;
482*946379e7Schristos size_t j;
483*946379e7Schristos
484*946379e7Schristos fprintf (stream,
485*946379e7Schristos "/* Automatically generated by GNU msgfmt. Do not modify! */\n");
486*946379e7Schristos
487*946379e7Schristos /* We have to use a "using" statement here, to avoid a bug in the pnet-0.6.0
488*946379e7Schristos compiler. */
489*946379e7Schristos fprintf (stream, "using GNU.Gettext;\n");
490*946379e7Schristos
491*946379e7Schristos /* Assign a strong name to the assembly, so that two different localizations
492*946379e7Schristos of the same domain can be loaded one after the other. This strong name
493*946379e7Schristos tells the Global Assembly Cache that they are meant to be different. */
494*946379e7Schristos fprintf (stream, "[assembly: System.Reflection.AssemblyCulture(");
495*946379e7Schristos write_csharp_string (stream, culture_name);
496*946379e7Schristos fprintf (stream, ")]\n");
497*946379e7Schristos
498*946379e7Schristos last_dot = strrchr (class_name, '.');
499*946379e7Schristos if (last_dot != NULL)
500*946379e7Schristos {
501*946379e7Schristos fprintf (stream, "namespace ");
502*946379e7Schristos fwrite (class_name, 1, last_dot - class_name, stream);
503*946379e7Schristos fprintf (stream, " {\n");
504*946379e7Schristos class_name_last_part = last_dot + 1;
505*946379e7Schristos }
506*946379e7Schristos else
507*946379e7Schristos class_name_last_part = class_name;
508*946379e7Schristos fprintf (stream, "public class %s : GettextResourceSet {\n",
509*946379e7Schristos class_name_last_part);
510*946379e7Schristos
511*946379e7Schristos /* Determine whether there are plural messages. */
512*946379e7Schristos plurals = 0;
513*946379e7Schristos for (j = 0; j < mlp->nitems; j++)
514*946379e7Schristos if (mlp->item[j]->msgid_plural != NULL)
515*946379e7Schristos plurals++;
516*946379e7Schristos
517*946379e7Schristos /* Emit the constructor. */
518*946379e7Schristos fprintf (stream, " public %s ()\n", class_name_last_part);
519*946379e7Schristos fprintf (stream, " : base () {\n");
520*946379e7Schristos fprintf (stream, " }\n");
521*946379e7Schristos
522*946379e7Schristos /* Emit the ReadResources method. */
523*946379e7Schristos fprintf (stream, " protected override void ReadResources () {\n");
524*946379e7Schristos /* In some implementations, the ResourceSet constructor initializes Table
525*946379e7Schristos before calling ReadResources(). In other implementations, the
526*946379e7Schristos ReadResources() method is expected to initialize the Table. */
527*946379e7Schristos fprintf (stream, " if (Table == null)\n");
528*946379e7Schristos fprintf (stream, " Table = new System.Collections.Hashtable();\n");
529*946379e7Schristos fprintf (stream, " System.Collections.Hashtable t = Table;\n");
530*946379e7Schristos for (j = 0; j < mlp->nitems; j++)
531*946379e7Schristos {
532*946379e7Schristos fprintf (stream, " t.Add(");
533*946379e7Schristos write_csharp_string (stream, mlp->item[j]->msgid);
534*946379e7Schristos fprintf (stream, ",");
535*946379e7Schristos write_csharp_msgstr (stream, mlp->item[j]);
536*946379e7Schristos fprintf (stream, ");\n");
537*946379e7Schristos }
538*946379e7Schristos fprintf (stream, " }\n");
539*946379e7Schristos
540*946379e7Schristos /* Emit the msgid_plural strings. Only used by msgunfmt. */
541*946379e7Schristos if (plurals)
542*946379e7Schristos {
543*946379e7Schristos fprintf (stream, " public static System.Collections.Hashtable GetMsgidPluralTable () {\n");
544*946379e7Schristos fprintf (stream, " System.Collections.Hashtable t = new System.Collections.Hashtable();\n");
545*946379e7Schristos for (j = 0; j < mlp->nitems; j++)
546*946379e7Schristos if (mlp->item[j]->msgid_plural != NULL)
547*946379e7Schristos {
548*946379e7Schristos fprintf (stream, " t.Add(");
549*946379e7Schristos write_csharp_string (stream, mlp->item[j]->msgid);
550*946379e7Schristos fprintf (stream, ",");
551*946379e7Schristos write_csharp_string (stream, mlp->item[j]->msgid_plural);
552*946379e7Schristos fprintf (stream, ");\n");
553*946379e7Schristos }
554*946379e7Schristos fprintf (stream, " return t;\n");
555*946379e7Schristos fprintf (stream, " }\n");
556*946379e7Schristos }
557*946379e7Schristos
558*946379e7Schristos /* Emit the PluralEval function. It is a subroutine for GetPluralString. */
559*946379e7Schristos if (plurals)
560*946379e7Schristos {
561*946379e7Schristos message_ty *header_entry;
562*946379e7Schristos struct expression *plural;
563*946379e7Schristos unsigned long int nplurals;
564*946379e7Schristos
565*946379e7Schristos header_entry = message_list_search (mlp, NULL, "");
566*946379e7Schristos extract_plural_expression (header_entry ? header_entry->msgstr : NULL,
567*946379e7Schristos &plural, &nplurals);
568*946379e7Schristos
569*946379e7Schristos fprintf (stream, " protected override long PluralEval (long n) {\n");
570*946379e7Schristos fprintf (stream, " return ");
571*946379e7Schristos write_csharp_expression (stream, plural, false);
572*946379e7Schristos fprintf (stream, ";\n");
573*946379e7Schristos fprintf (stream, " }\n");
574*946379e7Schristos }
575*946379e7Schristos
576*946379e7Schristos /* Terminate the class. */
577*946379e7Schristos fprintf (stream, "}\n");
578*946379e7Schristos
579*946379e7Schristos if (last_dot != NULL)
580*946379e7Schristos /* Terminate the namespace. */
581*946379e7Schristos fprintf (stream, "}\n");
582*946379e7Schristos }
583*946379e7Schristos
584*946379e7Schristos
585*946379e7Schristos int
msgdomain_write_csharp(message_list_ty * mlp,const char * canon_encoding,const char * resource_name,const char * locale_name,const char * directory)586*946379e7Schristos msgdomain_write_csharp (message_list_ty *mlp, const char *canon_encoding,
587*946379e7Schristos const char *resource_name, const char *locale_name,
588*946379e7Schristos const char *directory)
589*946379e7Schristos {
590*946379e7Schristos int retval;
591*946379e7Schristos struct temp_dir *tmpdir;
592*946379e7Schristos char *culture_name;
593*946379e7Schristos char *output_file;
594*946379e7Schristos char *class_name;
595*946379e7Schristos char *csharp_file_name;
596*946379e7Schristos FILE *csharp_file;
597*946379e7Schristos const char *gettextlibdir;
598*946379e7Schristos const char *csharp_sources[1];
599*946379e7Schristos const char *libdirs[1];
600*946379e7Schristos const char *libraries[1];
601*946379e7Schristos
602*946379e7Schristos /* If no entry for this resource/domain, don't even create the file. */
603*946379e7Schristos if (mlp->nitems == 0)
604*946379e7Schristos return 0;
605*946379e7Schristos
606*946379e7Schristos /* Determine whether mlp has entries with context. */
607*946379e7Schristos {
608*946379e7Schristos bool has_context;
609*946379e7Schristos size_t j;
610*946379e7Schristos
611*946379e7Schristos has_context = false;
612*946379e7Schristos for (j = 0; j < mlp->nitems; j++)
613*946379e7Schristos if (mlp->item[j]->msgctxt != NULL)
614*946379e7Schristos has_context = true;
615*946379e7Schristos if (has_context)
616*946379e7Schristos {
617*946379e7Schristos multiline_error (xstrdup (""),
618*946379e7Schristos xstrdup (_("\
619*946379e7Schristos message catalog has context dependent translations\n\
620*946379e7Schristos but the C# .dll format doesn't support contexts\n")));
621*946379e7Schristos return 1;
622*946379e7Schristos }
623*946379e7Schristos }
624*946379e7Schristos
625*946379e7Schristos retval = 1;
626*946379e7Schristos
627*946379e7Schristos /* Convert the messages to Unicode. */
628*946379e7Schristos iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
629*946379e7Schristos
630*946379e7Schristos /* Create a temporary directory where we can put the C# file.
631*946379e7Schristos A simple temporary file would also be possible but would require us to
632*946379e7Schristos define our own variant of mkstemp(): On one hand the functions mktemp(),
633*946379e7Schristos tmpnam(), tempnam() present a security risk, and on the other hand the
634*946379e7Schristos function mkstemp() doesn't allow to specify a fixed suffix of the file.
635*946379e7Schristos It is simpler to create a temporary directory. */
636*946379e7Schristos tmpdir = create_temp_dir ("msg", NULL, false);
637*946379e7Schristos if (tmpdir == NULL)
638*946379e7Schristos goto quit1;
639*946379e7Schristos
640*946379e7Schristos /* Assign a default value to the resource name. */
641*946379e7Schristos if (resource_name == NULL)
642*946379e7Schristos resource_name = "Messages";
643*946379e7Schristos
644*946379e7Schristos /* Convert the locale name to a .NET specific culture name. */
645*946379e7Schristos culture_name = xstrdup (locale_name);
646*946379e7Schristos {
647*946379e7Schristos char *p;
648*946379e7Schristos for (p = culture_name; *p != '\0'; p++)
649*946379e7Schristos if (*p == '_')
650*946379e7Schristos *p = '-';
651*946379e7Schristos if (strncmp (culture_name, "sr-CS", 5) == 0)
652*946379e7Schristos memcpy (culture_name, "sr-SP", 5);
653*946379e7Schristos p = strchr (culture_name, '@');
654*946379e7Schristos if (p != NULL)
655*946379e7Schristos {
656*946379e7Schristos if (strcmp (p, "@latin") == 0)
657*946379e7Schristos strcpy (p, "-Latn");
658*946379e7Schristos else if (strcmp (p, "@cyrillic") == 0)
659*946379e7Schristos strcpy (p, "-Cyrl");
660*946379e7Schristos }
661*946379e7Schristos if (strcmp (culture_name, "sr-SP") == 0)
662*946379e7Schristos {
663*946379e7Schristos free (culture_name);
664*946379e7Schristos culture_name = xstrdup ("sr-SP-Latn");
665*946379e7Schristos }
666*946379e7Schristos else if (strcmp (culture_name, "uz-UZ") == 0)
667*946379e7Schristos {
668*946379e7Schristos free (culture_name);
669*946379e7Schristos culture_name = xstrdup ("uz-UZ-Latn");
670*946379e7Schristos }
671*946379e7Schristos }
672*946379e7Schristos
673*946379e7Schristos /* Compute the output file name. This code must be kept consistent with
674*946379e7Schristos intl.cs, function GetSatelliteAssembly(). */
675*946379e7Schristos {
676*946379e7Schristos char *output_dir = concatenated_pathname (directory, culture_name, NULL);
677*946379e7Schristos struct stat statbuf;
678*946379e7Schristos
679*946379e7Schristos /* Try to create the output directory if it does not yet exist. */
680*946379e7Schristos if (stat (output_dir, &statbuf) < 0 && errno == ENOENT)
681*946379e7Schristos if (mkdir (output_dir, S_IRUSR | S_IWUSR | S_IXUSR
682*946379e7Schristos | S_IRGRP | S_IWGRP | S_IXGRP
683*946379e7Schristos | S_IROTH | S_IWOTH | S_IXOTH) < 0)
684*946379e7Schristos {
685*946379e7Schristos error (0, errno, _("failed to create directory \"%s\""), output_dir);
686*946379e7Schristos free (output_dir);
687*946379e7Schristos goto quit2;
688*946379e7Schristos }
689*946379e7Schristos
690*946379e7Schristos output_file =
691*946379e7Schristos concatenated_pathname (output_dir, resource_name, ".resources.dll");
692*946379e7Schristos
693*946379e7Schristos free (output_dir);
694*946379e7Schristos }
695*946379e7Schristos
696*946379e7Schristos /* Compute the class name. This code must be kept consistent with intl.cs,
697*946379e7Schristos function InstantiateResourceSet(). */
698*946379e7Schristos {
699*946379e7Schristos char *class_name_part1 = construct_class_name (resource_name);
700*946379e7Schristos char *p;
701*946379e7Schristos
702*946379e7Schristos class_name =
703*946379e7Schristos (char *) xmalloc (strlen (class_name_part1) + 1 + strlen (culture_name) + 1);
704*946379e7Schristos sprintf (class_name, "%s_%s", class_name_part1, culture_name);
705*946379e7Schristos for (p = class_name + strlen (class_name_part1) + 1; *p != '\0'; p++)
706*946379e7Schristos if (*p == '-')
707*946379e7Schristos *p = '_';
708*946379e7Schristos free (class_name_part1);
709*946379e7Schristos }
710*946379e7Schristos
711*946379e7Schristos /* Compute the temporary C# file name. It must end in ".cs", so that
712*946379e7Schristos the C# compiler recognizes that it is C# source code. */
713*946379e7Schristos csharp_file_name =
714*946379e7Schristos concatenated_pathname (tmpdir->dir_name, "resset.cs", NULL);
715*946379e7Schristos
716*946379e7Schristos /* Create the C# file. */
717*946379e7Schristos register_temp_file (tmpdir, csharp_file_name);
718*946379e7Schristos csharp_file = fopen_temp (csharp_file_name, "w");
719*946379e7Schristos if (csharp_file == NULL)
720*946379e7Schristos {
721*946379e7Schristos error (0, errno, _("failed to create \"%s\""), csharp_file_name);
722*946379e7Schristos unregister_temp_file (tmpdir, csharp_file_name);
723*946379e7Schristos goto quit3;
724*946379e7Schristos }
725*946379e7Schristos
726*946379e7Schristos write_csharp_code (csharp_file, culture_name, class_name, mlp);
727*946379e7Schristos
728*946379e7Schristos if (fwriteerror_temp (csharp_file))
729*946379e7Schristos {
730*946379e7Schristos error (0, errno, _("error while writing \"%s\" file"), csharp_file_name);
731*946379e7Schristos goto quit3;
732*946379e7Schristos }
733*946379e7Schristos
734*946379e7Schristos /* Make it possible to override the .dll location. This is
735*946379e7Schristos necessary for running the testsuite before "make install". */
736*946379e7Schristos gettextlibdir = getenv ("GETTEXTCSHARPLIBDIR");
737*946379e7Schristos if (gettextlibdir == NULL || gettextlibdir[0] == '\0')
738*946379e7Schristos gettextlibdir = relocate (LIBDIR);
739*946379e7Schristos
740*946379e7Schristos /* Compile the C# file to a .dll file. */
741*946379e7Schristos csharp_sources[0] = csharp_file_name;
742*946379e7Schristos libdirs[0] = gettextlibdir;
743*946379e7Schristos libraries[0] = "GNU.Gettext";
744*946379e7Schristos if (compile_csharp_class (csharp_sources, 1, libdirs, 1, libraries, 1,
745*946379e7Schristos output_file, true, false, verbose))
746*946379e7Schristos {
747*946379e7Schristos error (0, 0, _("compilation of C# class failed, please try --verbose"));
748*946379e7Schristos goto quit3;
749*946379e7Schristos }
750*946379e7Schristos
751*946379e7Schristos retval = 0;
752*946379e7Schristos
753*946379e7Schristos quit3:
754*946379e7Schristos free (csharp_file_name);
755*946379e7Schristos free (class_name);
756*946379e7Schristos free (output_file);
757*946379e7Schristos quit2:
758*946379e7Schristos free (culture_name);
759*946379e7Schristos cleanup_temp_dir (tmpdir);
760*946379e7Schristos quit1:
761*946379e7Schristos return retval;
762*946379e7Schristos }
763