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