1/* Writing C# satellite assemblies. 2 Copyright (C) 2003-2005 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 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 76 77#if HAVE_UNISTD_H 78# include <unistd.h> 79#endif 80 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 86 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" 106 107#define _(str) gettext (str) 108 109 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. */ 113static char * 114construct_class_name (const char *resource_name) 115{ 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; 124 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. 129 */ 130 valid = (resource_name[0] != '\0'); 131 for (p = resource_name; valid && *p != '\0'; p++) 132 { 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; 137 } 138 if (valid) 139 return xstrdup (resource_name); 140 else 141 { 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; 147 148 b = class_name; 149 memcpy (b, "__UESCAPED__", 12); b += 12; 150 while (str < str_limit) 151 { 152 unsigned int uc; 153 str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str); 154 if (uc >= 0x10000) 155 { 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]; 166 } 167 else if (!((uc >= 'A' && uc <= 'Z') || (uc >= 'a' && uc <= 'z') 168 || (uc >= '0' && uc <= '9'))) 169 { 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]; 176 } 177 else 178 *b++ = uc; 179 } 180 *b++ = '\0'; 181 return (char *) xrealloc (class_name, b - class_name); 182 } 183} 184 185 186/* Write a string in C# Unicode notation to the given stream. */ 187static void 188write_csharp_string (FILE *stream, const char *str) 189{ 190 static const char hexdigit[] = "0123456789abcdef"; 191 const char *str_limit = str + strlen (str); 192 193 fprintf (stream, "\""); 194 while (str < str_limit) 195 { 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]); 230 } 231 fprintf (stream, "\""); 232} 233 234 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. */ 238static void 239write_csharp_msgstr (FILE *stream, message_ty *mp) 240{ 241 if (mp->msgid_plural != NULL) 242 { 243 bool first; 244 const char *p; 245 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) 250 { 251 if (!first) 252 fprintf (stream, ", "); 253 write_csharp_string (stream, p); 254 } 255 fprintf (stream, " }"); 256 } 257 else 258 { 259 if (mp->msgstr_len != strlen (mp->msgstr) + 1) 260 abort (); 261 262 write_csharp_string (stream, mp->msgstr); 263 } 264} 265 266 267/* Tests whether a plural expression, evaluated according to the C rules, 268 can only produce the values 0 and 1. */ 269static bool 270is_expression_boolean (struct expression *exp) 271{ 272 switch (exp->operation) 273 { 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 (); 298 } 299} 300 301 302/* Write C# code that evaluates a plural expression according to the C rules. 303 The variable is called 'n'. */ 304static void 305write_csharp_expression (FILE *stream, struct expression *exp, bool as_boolean) 306{ 307 /* We use parentheses everywhere. This frees us from tracking the priority 308 of arithmetic operators. */ 309 if (as_boolean) 310 { 311 /* Emit a C# expression of type 'bool'. */ 312 switch (exp->operation) 313 { 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])) 381 { 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; 390 } 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 (); 404 } 405 } 406 else 407 { 408 /* Emit a C# expression of type 'long'. */ 409 switch (exp->operation) 410 { 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 (); 476 } 477 } 478} 479 480 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. */ 485static void 486write_csharp_code (FILE *stream, const char *class_name, message_list_ty *mlp) 487{ 488 const char *last_dot; 489 const char *class_name_last_part; 490 unsigned int plurals; 491 size_t j; 492 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) 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, ""); 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/* Asynchronously cleaning up temporary files, when we receive any of the 586 usually occurring signals whose default action is to terminate the 587 program. */ 588 589static struct 590{ 591 const char *tmpdir; 592 const char *file_name; 593} cleanup_list; 594 595/* The signal handler. It gets called asynchronously. */ 596static void 597cleanup () 598{ 599 /* First cleanup the files in the subdirectory. */ 600 { 601 const char *filename = cleanup_list.file_name; 602 603 if (filename != NULL) 604 unlink (filename); 605 } 606 607 /* Then cleanup the main temporary directory. */ 608 { 609 const char *filename = cleanup_list.tmpdir; 610 611 if (filename != NULL) 612 rmdir (filename); 613 } 614} 615 616 617int 618msgdomain_write_csharp (message_list_ty *mlp, const char *canon_encoding, 619 const char *resource_name, const char *locale_name, 620 const char *directory) 621{ 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]; 634 635 /* If no entry for this resource/domain, don't even create the file. */ 636 if (mlp->nitems == 0) 637 return 0; 638 639 retval = 1; 640 641 /* Convert the messages to Unicode. */ 642 iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL); 643 644 cleanup_list.tmpdir = NULL; 645 cleanup_list.file_name = NULL; 646 { 647 static bool cleanup_already_registered = false; 648 if (!cleanup_already_registered) 649 { 650 at_fatal_signal (&cleanup); 651 cleanup_already_registered = true; 652 } 653 } 654 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)) 663 { 664 error (0, errno, 665 _("cannot find a temporary directory, try setting $TMPDIR")); 666 goto quit1; 667 } 668 block_fatal_signals (); 669 tmpdir = mkdtemp (template); 670 cleanup_list.tmpdir = tmpdir; 671 unblock_fatal_signals (); 672 if (tmpdir == NULL) 673 { 674 error (0, errno, 675 _("cannot create a temporary directory using template \"%s\""), 676 template); 677 goto quit1; 678 } 679 680 /* Assign a default value to the resource name. */ 681 if (resource_name == NULL) 682 resource_name = "Messages"; 683 684 /* Convert the locale name to a .NET specific culture name. */ 685 culture_name = xstrdup (locale_name); 686 { 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) 695 { 696 if (strcmp (p, "@latin") == 0) 697 strcpy (p, "-Latn"); 698 else if (strcmp (p, "@cyrillic") == 0) 699 strcpy (p, "-Cyrl"); 700 } 701 if (strcmp (culture_name, "sr-SP") == 0) 702 { 703 free (culture_name); 704 culture_name = xstrdup ("sr-SP-Latn"); 705 } 706 else if (strcmp (culture_name, "uz-UZ") == 0) 707 { 708 free (culture_name); 709 culture_name = xstrdup ("uz-UZ-Latn"); 710 } 711 } 712 713 714 /* Compute the output file name. This code must be kept consistent with 715 intl.cs, function GetSatelliteAssembly(). */ 716 { 717 char *output_dir = concatenated_pathname (directory, culture_name, NULL); 718 struct stat statbuf; 719 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) 725 { 726 error (0, errno, _("failed to create directory \"%s\""), output_dir); 727 free (output_dir); 728 goto quit3; 729 } 730 731 output_file = 732 concatenated_pathname (output_dir, resource_name, ".resources.dll"); 733 734 free (output_dir); 735 } 736 737 /* Compute the class name. This code must be kept consistent with intl.cs, 738 function InstantiateResourceSet(). */ 739 { 740 char *class_name_part1 = construct_class_name (resource_name); 741 char *p; 742 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); 750 } 751 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); 755 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) 760 { 761 error (0, errno, _("failed to create \"%s\""), csharp_file_name); 762 goto quit4; 763 } 764 765 write_csharp_code (csharp_file, class_name, mlp); 766 767 if (fwriteerror (csharp_file)) 768 { 769 error (0, errno, _("error while writing \"%s\" file"), csharp_file_name); 770 fclose (csharp_file); 771 goto quit5; 772 } 773 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); 779 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)) 786 { 787 error (0, 0, _("compilation of C# class failed, please try --verbose")); 788 goto quit5; 789 } 790 791 retval = 0; 792 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; 808} 809