No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / gettext / gettext-tools / src / write-csharp.c
blob3af2b74911e6a00faf30cc0e43f7a0d86df8a5f2
1 /* Writing C# satellite assemblies.
2 Copyright (C) 2003-2005 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2003.
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22 #include <alloca.h>
24 /* Specification. */
25 #include "write-csharp.h"
27 #include <errno.h>
28 #include <stdbool.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
33 #include <sys/stat.h>
34 #if STAT_MACROS_BROKEN
35 # undef S_ISDIR
36 #endif
37 #if !defined S_ISDIR && defined S_IFDIR
38 # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
39 #endif
40 #if !S_IRUSR && S_IREAD
41 # define S_IRUSR S_IREAD
42 #endif
43 #if !S_IRUSR
44 # define S_IRUSR 00400
45 #endif
46 #if !S_IWUSR && S_IWRITE
47 # define S_IWUSR S_IWRITE
48 #endif
49 #if !S_IWUSR
50 # define S_IWUSR 00200
51 #endif
52 #if !S_IXUSR && S_IEXEC
53 # define S_IXUSR S_IEXEC
54 #endif
55 #if !S_IXUSR
56 # define S_IXUSR 00100
57 #endif
58 #if !S_IRGRP
59 # define S_IRGRP (S_IRUSR >> 3)
60 #endif
61 #if !S_IWGRP
62 # define S_IWGRP (S_IWUSR >> 3)
63 #endif
64 #if !S_IXGRP
65 # define S_IXGRP (S_IXUSR >> 3)
66 #endif
67 #if !S_IROTH
68 # define S_IROTH (S_IRUSR >> 6)
69 #endif
70 #if !S_IWOTH
71 # define S_IWOTH (S_IWUSR >> 6)
72 #endif
73 #if !S_IXOTH
74 # define S_IXOTH (S_IXUSR >> 6)
75 #endif
77 #if HAVE_UNISTD_H
78 # include <unistd.h>
79 #endif
81 #ifdef __MINGW32__
82 /* mingw's mkdir() function has 1 argument, but we pass 2 arguments.
83 Therefore we have to disable the argument count checking. */
84 # define mkdir ((int (*)()) mkdir)
85 #endif
87 #include "c-ctype.h"
88 #include "error.h"
89 #include "relocatable.h"
90 #include "csharpcomp.h"
91 #include "message.h"
92 #include "mkdtemp.h"
93 #include "msgfmt.h"
94 #include "msgl-iconv.h"
95 #include "pathmax.h"
96 #include "plural-exp.h"
97 #include "po-charset.h"
98 #include "xalloc.h"
99 #include "xallocsa.h"
100 #include "pathname.h"
101 #include "fatal-signal.h"
102 #include "fwriteerror.h"
103 #include "tmpdir.h"
104 #include "utf8-ucs4.h"
105 #include "gettext.h"
107 #define _(str) gettext (str)
110 /* Convert a resource name to a class name.
111 Return a nonempty string consisting of alphanumerics and underscores
112 and starting with a letter or underscore. */
113 static char *
114 construct_class_name (const char *resource_name)
116 /* This code must be kept consistent with intl.cs, function
117 GettextResourceManager.ConstructClassName. */
118 /* We could just return an arbitrary fixed class name, like "Messages",
119 assuming that every assembly will only ever contain one
120 GettextResourceSet subclass, but this assumption would break the day
121 we want to support multi-domain PO files in the same format... */
122 bool valid;
123 const char *p;
125 /* Test for a valid ASCII identifier:
126 - nonempty,
127 - first character is A..Za..z_ - see x-csharp.c:is_identifier_start.
128 - next characters are A..Za..z_0..9 - see x-csharp.c:is_identifier_part.
130 valid = (resource_name[0] != '\0');
131 for (p = resource_name; valid && *p != '\0'; p++)
133 char c = *p;
134 if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_')
135 || (p > resource_name && c >= '0' && c <= '9')))
136 valid = false;
138 if (valid)
139 return xstrdup (resource_name);
140 else
142 static const char hexdigit[] = "0123456789abcdef";
143 const char *str = resource_name;
144 const char *str_limit = str + strlen (str);
145 char *class_name = (char *) xmalloc (12 + 6 * (str_limit - str) + 1);
146 char *b;
148 b = class_name;
149 memcpy (b, "__UESCAPED__", 12); b += 12;
150 while (str < str_limit)
152 unsigned int uc;
153 str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
154 if (uc >= 0x10000)
156 *b++ = '_';
157 *b++ = 'U';
158 *b++ = hexdigit[(uc >> 28) & 0x0f];
159 *b++ = hexdigit[(uc >> 24) & 0x0f];
160 *b++ = hexdigit[(uc >> 20) & 0x0f];
161 *b++ = hexdigit[(uc >> 16) & 0x0f];
162 *b++ = hexdigit[(uc >> 12) & 0x0f];
163 *b++ = hexdigit[(uc >> 8) & 0x0f];
164 *b++ = hexdigit[(uc >> 4) & 0x0f];
165 *b++ = hexdigit[uc & 0x0f];
167 else if (!((uc >= 'A' && uc <= 'Z') || (uc >= 'a' && uc <= 'z')
168 || (uc >= '0' && uc <= '9')))
170 *b++ = '_';
171 *b++ = 'u';
172 *b++ = hexdigit[(uc >> 12) & 0x0f];
173 *b++ = hexdigit[(uc >> 8) & 0x0f];
174 *b++ = hexdigit[(uc >> 4) & 0x0f];
175 *b++ = hexdigit[uc & 0x0f];
177 else
178 *b++ = uc;
180 *b++ = '\0';
181 return (char *) xrealloc (class_name, b - class_name);
186 /* Write a string in C# Unicode notation to the given stream. */
187 static void
188 write_csharp_string (FILE *stream, const char *str)
190 static const char hexdigit[] = "0123456789abcdef";
191 const char *str_limit = str + strlen (str);
193 fprintf (stream, "\"");
194 while (str < str_limit)
196 unsigned int uc;
197 str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
198 if (uc == 0x0000)
199 fprintf (stream, "\\0");
200 else if (uc == 0x0007)
201 fprintf (stream, "\\a");
202 else if (uc == 0x0008)
203 fprintf (stream, "\\b");
204 else if (uc == 0x0009)
205 fprintf (stream, "\\t");
206 else if (uc == 0x000a)
207 fprintf (stream, "\\n");
208 else if (uc == 0x000b)
209 fprintf (stream, "\\v");
210 else if (uc == 0x000c)
211 fprintf (stream, "\\f");
212 else if (uc == 0x000d)
213 fprintf (stream, "\\r");
214 else if (uc == 0x0022)
215 fprintf (stream, "\\\"");
216 else if (uc == 0x005c)
217 fprintf (stream, "\\\\");
218 else if (uc >= 0x0020 && uc < 0x007f)
219 fprintf (stream, "%c", uc);
220 else if (uc < 0x10000)
221 fprintf (stream, "\\u%c%c%c%c",
222 hexdigit[(uc >> 12) & 0x0f], hexdigit[(uc >> 8) & 0x0f],
223 hexdigit[(uc >> 4) & 0x0f], hexdigit[uc & 0x0f]);
224 else
225 fprintf (stream, "\\U%c%c%c%c%c%c%c%c",
226 hexdigit[(uc >> 28) & 0x0f], hexdigit[(uc >> 24) & 0x0f],
227 hexdigit[(uc >> 20) & 0x0f], hexdigit[(uc >> 16) & 0x0f],
228 hexdigit[(uc >> 12) & 0x0f], hexdigit[(uc >> 8) & 0x0f],
229 hexdigit[(uc >> 4) & 0x0f], hexdigit[uc & 0x0f]);
231 fprintf (stream, "\"");
235 /* Write C# code that returns the value for a message. If the message
236 has plural forms, it is an expression of type System.String[], otherwise it
237 is an expression of type System.String. */
238 static void
239 write_csharp_msgstr (FILE *stream, message_ty *mp)
241 if (mp->msgid_plural != NULL)
243 bool first;
244 const char *p;
246 fprintf (stream, "new System.String[] { ");
247 for (p = mp->msgstr, first = true;
248 p < mp->msgstr + mp->msgstr_len;
249 p += strlen (p) + 1, first = false)
251 if (!first)
252 fprintf (stream, ", ");
253 write_csharp_string (stream, p);
255 fprintf (stream, " }");
257 else
259 if (mp->msgstr_len != strlen (mp->msgstr) + 1)
260 abort ();
262 write_csharp_string (stream, mp->msgstr);
267 /* Tests whether a plural expression, evaluated according to the C rules,
268 can only produce the values 0 and 1. */
269 static bool
270 is_expression_boolean (struct expression *exp)
272 switch (exp->operation)
274 case var:
275 case mult:
276 case divide:
277 case module:
278 case plus:
279 case minus:
280 return false;
281 case lnot:
282 case less_than:
283 case greater_than:
284 case less_or_equal:
285 case greater_or_equal:
286 case equal:
287 case not_equal:
288 case land:
289 case lor:
290 return true;
291 case num:
292 return (exp->val.num == 0 || exp->val.num == 1);
293 case qmop:
294 return is_expression_boolean (exp->val.args[1])
295 && is_expression_boolean (exp->val.args[2]);
296 default:
297 abort ();
302 /* Write C# code that evaluates a plural expression according to the C rules.
303 The variable is called 'n'. */
304 static void
305 write_csharp_expression (FILE *stream, struct expression *exp, bool as_boolean)
307 /* We use parentheses everywhere. This frees us from tracking the priority
308 of arithmetic operators. */
309 if (as_boolean)
311 /* Emit a C# expression of type 'bool'. */
312 switch (exp->operation)
314 case num:
315 fprintf (stream, "%s", exp->val.num ? "true" : "false");
316 return;
317 case lnot:
318 fprintf (stream, "(!");
319 write_csharp_expression (stream, exp->val.args[0], true);
320 fprintf (stream, ")");
321 return;
322 case less_than:
323 fprintf (stream, "(");
324 write_csharp_expression (stream, exp->val.args[0], false);
325 fprintf (stream, " < ");
326 write_csharp_expression (stream, exp->val.args[1], false);
327 fprintf (stream, ")");
328 return;
329 case greater_than:
330 fprintf (stream, "(");
331 write_csharp_expression (stream, exp->val.args[0], false);
332 fprintf (stream, " > ");
333 write_csharp_expression (stream, exp->val.args[1], false);
334 fprintf (stream, ")");
335 return;
336 case less_or_equal:
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_or_equal:
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 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 not_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 land:
365 fprintf (stream, "(");
366 write_csharp_expression (stream, exp->val.args[0], true);
367 fprintf (stream, " && ");
368 write_csharp_expression (stream, exp->val.args[1], true);
369 fprintf (stream, ")");
370 return;
371 case lor:
372 fprintf (stream, "(");
373 write_csharp_expression (stream, exp->val.args[0], true);
374 fprintf (stream, " || ");
375 write_csharp_expression (stream, exp->val.args[1], true);
376 fprintf (stream, ")");
377 return;
378 case qmop:
379 if (is_expression_boolean (exp->val.args[1])
380 && is_expression_boolean (exp->val.args[2]))
382 fprintf (stream, "(");
383 write_csharp_expression (stream, exp->val.args[0], true);
384 fprintf (stream, " ? ");
385 write_csharp_expression (stream, exp->val.args[1], true);
386 fprintf (stream, " : ");
387 write_csharp_expression (stream, exp->val.args[2], true);
388 fprintf (stream, ")");
389 return;
391 /*FALLTHROUGH*/
392 case var:
393 case mult:
394 case divide:
395 case module:
396 case plus:
397 case minus:
398 fprintf (stream, "(");
399 write_csharp_expression (stream, exp, false);
400 fprintf (stream, " != 0)");
401 return;
402 default:
403 abort ();
406 else
408 /* Emit a C# expression of type 'long'. */
409 switch (exp->operation)
411 case var:
412 fprintf (stream, "n");
413 return;
414 case num:
415 fprintf (stream, "%lu", exp->val.num);
416 return;
417 case mult:
418 fprintf (stream, "(");
419 write_csharp_expression (stream, exp->val.args[0], false);
420 fprintf (stream, " * ");
421 write_csharp_expression (stream, exp->val.args[1], false);
422 fprintf (stream, ")");
423 return;
424 case divide:
425 fprintf (stream, "(");
426 write_csharp_expression (stream, exp->val.args[0], false);
427 fprintf (stream, " / ");
428 write_csharp_expression (stream, exp->val.args[1], false);
429 fprintf (stream, ")");
430 return;
431 case module:
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 plus:
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 minus:
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 qmop:
453 fprintf (stream, "(");
454 write_csharp_expression (stream, exp->val.args[0], true);
455 fprintf (stream, " ? ");
456 write_csharp_expression (stream, exp->val.args[1], false);
457 fprintf (stream, " : ");
458 write_csharp_expression (stream, exp->val.args[2], false);
459 fprintf (stream, ")");
460 return;
461 case lnot:
462 case less_than:
463 case greater_than:
464 case less_or_equal:
465 case greater_or_equal:
466 case equal:
467 case not_equal:
468 case land:
469 case lor:
470 fprintf (stream, "(");
471 write_csharp_expression (stream, exp, true);
472 fprintf (stream, " ? 1 : 0)");
473 return;
474 default:
475 abort ();
481 /* Write the C# code for the GettextResourceSet subclass to the given stream.
482 Note that we use fully qualified class names and no "using" statements,
483 because applications can have their own classes called X.Y.Hashtable or
484 X.Y.String. */
485 static void
486 write_csharp_code (FILE *stream, const char *class_name, message_list_ty *mlp)
488 const char *last_dot;
489 const char *class_name_last_part;
490 unsigned int plurals;
491 size_t j;
493 fprintf (stream,
494 "/* Automatically generated by GNU msgfmt. Do not modify! */\n");
495 /* We have to use a "using" statement here, to avoid a bug in the pnet-0.6.0
496 compiler. */
497 fprintf (stream, "using GNU.Gettext;\n");
498 last_dot = strrchr (class_name, '.');
499 if (last_dot != NULL)
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;
506 else
507 class_name_last_part = class_name;
508 fprintf (stream, "public class %s : GettextResourceSet {\n",
509 class_name_last_part);
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++;
517 /* Emit the constructor. */
518 fprintf (stream, " public %s ()\n", class_name_last_part);
519 fprintf (stream, " : base () {\n");
520 fprintf (stream, " }\n");
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++)
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");
538 fprintf (stream, " }\n");
540 /* Emit the msgid_plural strings. Only used by msgunfmt. */
541 if (plurals)
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)
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");
554 fprintf (stream, " return t;\n");
555 fprintf (stream, " }\n");
558 /* Emit the PluralEval function. It is a subroutine for GetPluralString. */
559 if (plurals)
561 message_ty *header_entry;
562 struct expression *plural;
563 unsigned long int nplurals;
565 header_entry = message_list_search (mlp, "");
566 extract_plural_expression (header_entry ? header_entry->msgstr : NULL,
567 &plural, &nplurals);
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");
576 /* Terminate the class. */
577 fprintf (stream, "}\n");
579 if (last_dot != NULL)
580 /* Terminate the namespace. */
581 fprintf (stream, "}\n");
585 /* Asynchronously cleaning up temporary files, when we receive any of the
586 usually occurring signals whose default action is to terminate the
587 program. */
589 static struct
591 const char *tmpdir;
592 const char *file_name;
593 } cleanup_list;
595 /* The signal handler. It gets called asynchronously. */
596 static void
597 cleanup ()
599 /* First cleanup the files in the subdirectory. */
601 const char *filename = cleanup_list.file_name;
603 if (filename != NULL)
604 unlink (filename);
607 /* Then cleanup the main temporary directory. */
609 const char *filename = cleanup_list.tmpdir;
611 if (filename != NULL)
612 rmdir (filename);
618 msgdomain_write_csharp (message_list_ty *mlp, const char *canon_encoding,
619 const char *resource_name, const char *locale_name,
620 const char *directory)
622 int retval;
623 char *template;
624 char *tmpdir;
625 char *culture_name;
626 char *output_file;
627 char *class_name;
628 char *csharp_file_name;
629 FILE *csharp_file;
630 const char *gettextlibdir;
631 const char *csharp_sources[1];
632 const char *libdirs[1];
633 const char *libraries[1];
635 /* If no entry for this resource/domain, don't even create the file. */
636 if (mlp->nitems == 0)
637 return 0;
639 retval = 1;
641 /* Convert the messages to Unicode. */
642 iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
644 cleanup_list.tmpdir = NULL;
645 cleanup_list.file_name = NULL;
647 static bool cleanup_already_registered = false;
648 if (!cleanup_already_registered)
650 at_fatal_signal (&cleanup);
651 cleanup_already_registered = true;
655 /* Create a temporary directory where we can put the C# file.
656 A simple temporary file would also be possible but would require us to
657 define our own variant of mkstemp(): On one hand the functions mktemp(),
658 tmpnam(), tempnam() present a security risk, and on the other hand the
659 function mkstemp() doesn't allow to specify a fixed suffix of the file.
660 It is simpler to create a temporary directory. */
661 template = (char *) xallocsa (PATH_MAX);
662 if (path_search (template, PATH_MAX, NULL, "msg", 1))
664 error (0, errno,
665 _("cannot find a temporary directory, try setting $TMPDIR"));
666 goto quit1;
668 block_fatal_signals ();
669 tmpdir = mkdtemp (template);
670 cleanup_list.tmpdir = tmpdir;
671 unblock_fatal_signals ();
672 if (tmpdir == NULL)
674 error (0, errno,
675 _("cannot create a temporary directory using template \"%s\""),
676 template);
677 goto quit1;
680 /* Assign a default value to the resource name. */
681 if (resource_name == NULL)
682 resource_name = "Messages";
684 /* Convert the locale name to a .NET specific culture name. */
685 culture_name = xstrdup (locale_name);
687 char *p;
688 for (p = culture_name; *p != '\0'; p++)
689 if (*p == '_')
690 *p = '-';
691 if (strncmp (culture_name, "sr-CS", 5) == 0)
692 memcpy (culture_name, "sr-SP", 5);
693 p = strchr (culture_name, '@');
694 if (p != NULL)
696 if (strcmp (p, "@latin") == 0)
697 strcpy (p, "-Latn");
698 else if (strcmp (p, "@cyrillic") == 0)
699 strcpy (p, "-Cyrl");
701 if (strcmp (culture_name, "sr-SP") == 0)
703 free (culture_name);
704 culture_name = xstrdup ("sr-SP-Latn");
706 else if (strcmp (culture_name, "uz-UZ") == 0)
708 free (culture_name);
709 culture_name = xstrdup ("uz-UZ-Latn");
714 /* Compute the output file name. This code must be kept consistent with
715 intl.cs, function GetSatelliteAssembly(). */
717 char *output_dir = concatenated_pathname (directory, culture_name, NULL);
718 struct stat statbuf;
720 /* Try to create the output directory if it does not yet exist. */
721 if (stat (output_dir, &statbuf) < 0 && errno == ENOENT)
722 if (mkdir (output_dir, S_IRUSR | S_IWUSR | S_IXUSR
723 | S_IRGRP | S_IWGRP | S_IXGRP
724 | S_IROTH | S_IWOTH | S_IXOTH) < 0)
726 error (0, errno, _("failed to create directory \"%s\""), output_dir);
727 free (output_dir);
728 goto quit3;
731 output_file =
732 concatenated_pathname (output_dir, resource_name, ".resources.dll");
734 free (output_dir);
737 /* Compute the class name. This code must be kept consistent with intl.cs,
738 function InstantiateResourceSet(). */
740 char *class_name_part1 = construct_class_name (resource_name);
741 char *p;
743 class_name =
744 (char *) xmalloc (strlen (class_name_part1) + 1 + strlen (culture_name) + 1);
745 sprintf (class_name, "%s_%s", class_name_part1, culture_name);
746 for (p = class_name + strlen (class_name_part1) + 1; *p != '\0'; p++)
747 if (*p == '-')
748 *p = '_';
749 free (class_name_part1);
752 /* Compute the temporary C# file name. It must end in ".cs", so that
753 the C# compiler recognizes that it is C# source code. */
754 csharp_file_name = concatenated_pathname (tmpdir, "resset.cs", NULL);
756 /* Create the C# file. */
757 cleanup_list.file_name = csharp_file_name;
758 csharp_file = fopen (csharp_file_name, "w");
759 if (csharp_file == NULL)
761 error (0, errno, _("failed to create \"%s\""), csharp_file_name);
762 goto quit4;
765 write_csharp_code (csharp_file, class_name, mlp);
767 if (fwriteerror (csharp_file))
769 error (0, errno, _("error while writing \"%s\" file"), csharp_file_name);
770 fclose (csharp_file);
771 goto quit5;
774 /* Make it possible to override the .dll location. This is
775 necessary for running the testsuite before "make install". */
776 gettextlibdir = getenv ("GETTEXTCSHARPLIBDIR");
777 if (gettextlibdir == NULL || gettextlibdir[0] == '\0')
778 gettextlibdir = relocate (LIBDIR);
780 /* Compile the C# file to a .dll file. */
781 csharp_sources[0] = csharp_file_name;
782 libdirs[0] = gettextlibdir;
783 libraries[0] = "GNU.Gettext";
784 if (compile_csharp_class (csharp_sources, 1, libdirs, 1, libraries, 1,
785 output_file, true, false, verbose))
787 error (0, 0, _("compilation of C# class failed, please try --verbose"));
788 goto quit5;
791 retval = 0;
793 quit5:
794 unlink (csharp_file_name);
795 quit4:
796 cleanup_list.file_name = NULL;
797 free (csharp_file_name);
798 free (class_name);
799 free (output_file);
800 quit3:
801 free (culture_name);
802 rmdir (tmpdir);
803 quit1:
804 cleanup_list.tmpdir = NULL;
805 freesa (template);
806 /* Here we could unregister the cleanup() handler. */
807 return retval;