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