From: Bruno Haible Date: Thu, 8 Jan 2004 11:47:38 +0000 (+0000) Subject: Writing C# satellite assemblies. X-Git-Tag: v0.14~86 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d47e14606147ecccaedfa2101a153b38b4d2438c;p=thirdparty%2Fgettext.git Writing C# satellite assemblies. --- diff --git a/gettext-tools/src/write-csharp.c b/gettext-tools/src/write-csharp.c new file mode 100644 index 000000000..6e4e8030f --- /dev/null +++ b/gettext-tools/src/write-csharp.c @@ -0,0 +1,788 @@ +/* Writing C# satellite assemblies. + Copyright (C) 2003 Free Software Foundation, Inc. + Written by Bruno Haible , 2003. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include + +/* Specification. */ +#include "write-csharp.h" + +#include +#include +#include +#include +#include + +#include +#if STAT_MACROS_BROKEN +# undef S_ISDIR +#endif +#if !defined S_ISDIR && defined S_IFDIR +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !S_IRUSR && S_IREAD +# define S_IRUSR S_IREAD +#endif +#if !S_IRUSR +# define S_IRUSR 00400 +#endif +#if !S_IWUSR && S_IWRITE +# define S_IWUSR S_IWRITE +#endif +#if !S_IWUSR +# define S_IWUSR 00200 +#endif +#if !S_IXUSR && S_IEXEC +# define S_IXUSR S_IEXEC +#endif +#if !S_IXUSR +# define S_IXUSR 00100 +#endif +#if !S_IRGRP +# define S_IRGRP (S_IRUSR >> 3) +#endif +#if !S_IWGRP +# define S_IWGRP (S_IWUSR >> 3) +#endif +#if !S_IXGRP +# define S_IXGRP (S_IXUSR >> 3) +#endif +#if !S_IROTH +# define S_IROTH (S_IRUSR >> 6) +#endif +#if !S_IWOTH +# define S_IWOTH (S_IWUSR >> 6) +#endif +#if !S_IXOTH +# define S_IXOTH (S_IXUSR >> 6) +#endif + +#if HAVE_UNISTD_H +# include +#endif + +#ifdef __MINGW32__ +/* mingw's mkdir() function has 1 argument, but we pass 2 arguments. + Therefore we have to disable the argument count checking. */ +# define mkdir ((int (*)()) mkdir) +#endif + +#include "c-ctype.h" +#include "error.h" +#include "relocatable.h" +#include "csharpcomp.h" +#include "message.h" +#include "mkdtemp.h" +#include "msgfmt.h" +#include "msgl-iconv.h" +#include "pathmax.h" +#include "plural-exp.h" +#include "po-charset.h" +#include "xalloc.h" +#include "xallocsa.h" +#include "pathname.h" +#include "fatal-signal.h" +#include "fwriteerror.h" +#include "tmpdir.h" +#include "utf8-ucs4.h" +#include "gettext.h" + +#define _(str) gettext (str) + + +/* Convert a resource name to a class name. + Return a nonempty string consisting of alphanumerics and underscores + and starting with a letter or underscore. */ +static char * +construct_class_name (const char *resource_name) +{ + /* This code must be kept consistent with intl.cs, function + GettextResourceManager.ConstructClassName. */ + /* We could just return an arbitrary fixed class name, like "Messages", + assuming that every assembly will only ever contain one + GettextResourceSet subclass, but this assumption would break the day + we want to support multi-domain PO files in the same format... */ + bool valid; + const char *p; + + /* Test for a valid ASCII identifier: + - nonempty, + - first character is A..Za..z_ - see x-csharp.c:is_identifier_start. + - next characters are A..Za..z_0..9 - see x-csharp.c:is_identifier_part. + */ + valid = (resource_name[0] != '\0'); + for (p = resource_name; valid && *p != '\0'; p++) + { + char c = *p; + if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_') + || (p > resource_name && c >= '0' && c <= '9'))) + valid = false; + } + if (valid) + return xstrdup (resource_name); + else + { + static const char hexdigit[] = "0123456789abcdef"; + const char *str = resource_name; + const char *str_limit = str + strlen (str); + char *class_name = (char *) xmalloc (12 + 6 * (str_limit - str) + 1); + char *b; + + b = class_name; + memcpy (b, "__UESCAPED__", 12); b += 12; + while (str < str_limit) + { + unsigned int uc; + str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str); + if (uc >= 0x10000) + { + *b++ = '_'; + *b++ = 'U'; + *b++ = hexdigit[(uc >> 28) & 0x0f]; + *b++ = hexdigit[(uc >> 24) & 0x0f]; + *b++ = hexdigit[(uc >> 20) & 0x0f]; + *b++ = hexdigit[(uc >> 16) & 0x0f]; + *b++ = hexdigit[(uc >> 12) & 0x0f]; + *b++ = hexdigit[(uc >> 8) & 0x0f]; + *b++ = hexdigit[(uc >> 4) & 0x0f]; + *b++ = hexdigit[uc & 0x0f]; + } + else if (!((uc >= 'A' && uc <= 'Z') || (uc >= 'a' && uc <= 'z') + || (uc >= '0' && uc <= '9'))) + { + *b++ = '_'; + *b++ = 'u'; + *b++ = hexdigit[(uc >> 12) & 0x0f]; + *b++ = hexdigit[(uc >> 8) & 0x0f]; + *b++ = hexdigit[(uc >> 4) & 0x0f]; + *b++ = hexdigit[uc & 0x0f]; + } + else + *b++ = uc; + } + *b++ = '\0'; + return (char *) xrealloc (class_name, b - class_name); + } +} + + +/* Write a string in C# Unicode notation to the given stream. */ +static void +write_csharp_string (FILE *stream, const char *str) +{ + static const char hexdigit[] = "0123456789abcdef"; + const char *str_limit = str + strlen (str); + + fprintf (stream, "\""); + while (str < str_limit) + { + unsigned int uc; + str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str); + if (uc == 0x0000) + fprintf (stream, "\\0"); + else if (uc == 0x0007) + fprintf (stream, "\\a"); + else if (uc == 0x0008) + fprintf (stream, "\\b"); + else if (uc == 0x0009) + fprintf (stream, "\\t"); + else if (uc == 0x000a) + fprintf (stream, "\\n"); + else if (uc == 0x000b) + fprintf (stream, "\\v"); + else if (uc == 0x000c) + fprintf (stream, "\\f"); + else if (uc == 0x000d) + fprintf (stream, "\\r"); + else if (uc == 0x0022) + fprintf (stream, "\\\""); + else if (uc == 0x005c) + fprintf (stream, "\\\\"); + else if (uc >= 0x0020 && uc < 0x007f) + fprintf (stream, "%c", uc); + else if (uc < 0x10000) + fprintf (stream, "\\u%c%c%c%c", + hexdigit[(uc >> 12) & 0x0f], hexdigit[(uc >> 8) & 0x0f], + hexdigit[(uc >> 4) & 0x0f], hexdigit[uc & 0x0f]); + else + fprintf (stream, "\\U%c%c%c%c%c%c%c%c", + hexdigit[(uc >> 28) & 0x0f], hexdigit[(uc >> 24) & 0x0f], + hexdigit[(uc >> 20) & 0x0f], hexdigit[(uc >> 16) & 0x0f], + hexdigit[(uc >> 12) & 0x0f], hexdigit[(uc >> 8) & 0x0f], + hexdigit[(uc >> 4) & 0x0f], hexdigit[uc & 0x0f]); + } + fprintf (stream, "\""); +} + + +/* Write C# code that returns the value for a message. If the message + has plural forms, it is an expression of type System.String[], otherwise it + is an expression of type System.String. */ +static void +write_csharp_msgstr (FILE *stream, message_ty *mp) +{ + if (mp->msgid_plural != NULL) + { + bool first; + const char *p; + + fprintf (stream, "new System.String[] { "); + for (p = mp->msgstr, first = true; + p < mp->msgstr + mp->msgstr_len; + p += strlen (p) + 1, first = false) + { + if (!first) + fprintf (stream, ", "); + write_csharp_string (stream, p); + } + fprintf (stream, " }"); + } + else + { + if (mp->msgstr_len != strlen (mp->msgstr) + 1) + abort (); + + write_csharp_string (stream, mp->msgstr); + } +} + + +/* Tests whether a plural expression, evaluated according to the C rules, + can only produce the values 0 and 1. */ +static bool +is_expression_boolean (struct expression *exp) +{ + switch (exp->operation) + { + case var: + case mult: + case divide: + case module: + case plus: + case minus: + return false; + case lnot: + case less_than: + case greater_than: + case less_or_equal: + case greater_or_equal: + case equal: + case not_equal: + case land: + case lor: + return true; + case num: + return (exp->val.num == 0 || exp->val.num == 1); + case qmop: + return is_expression_boolean (exp->val.args[1]) + && is_expression_boolean (exp->val.args[2]); + default: + abort (); + } +} + + +/* Write C# code that evaluates a plural expression according to the C rules. + The variable is called 'n'. */ +static void +write_csharp_expression (FILE *stream, struct expression *exp, bool as_boolean) +{ + /* We use parentheses everywhere. This frees us from tracking the priority + of arithmetic operators. */ + if (as_boolean) + { + /* Emit a C# expression of type 'bool'. */ + switch (exp->operation) + { + case num: + fprintf (stream, "%s", exp->val.num ? "true" : "false"); + return; + case lnot: + fprintf (stream, "(!"); + write_csharp_expression (stream, exp->val.args[0], true); + fprintf (stream, ")"); + return; + case less_than: + fprintf (stream, "("); + write_csharp_expression (stream, exp->val.args[0], false); + fprintf (stream, " < "); + write_csharp_expression (stream, exp->val.args[1], false); + fprintf (stream, ")"); + return; + case greater_than: + fprintf (stream, "("); + write_csharp_expression (stream, exp->val.args[0], false); + fprintf (stream, " > "); + write_csharp_expression (stream, exp->val.args[1], false); + fprintf (stream, ")"); + return; + case less_or_equal: + fprintf (stream, "("); + write_csharp_expression (stream, exp->val.args[0], false); + fprintf (stream, " <= "); + write_csharp_expression (stream, exp->val.args[1], false); + fprintf (stream, ")"); + return; + case greater_or_equal: + fprintf (stream, "("); + write_csharp_expression (stream, exp->val.args[0], false); + fprintf (stream, " >= "); + write_csharp_expression (stream, exp->val.args[1], false); + fprintf (stream, ")"); + return; + case equal: + fprintf (stream, "("); + write_csharp_expression (stream, exp->val.args[0], false); + fprintf (stream, " == "); + write_csharp_expression (stream, exp->val.args[1], false); + fprintf (stream, ")"); + return; + case not_equal: + fprintf (stream, "("); + write_csharp_expression (stream, exp->val.args[0], false); + fprintf (stream, " != "); + write_csharp_expression (stream, exp->val.args[1], false); + fprintf (stream, ")"); + return; + case land: + fprintf (stream, "("); + write_csharp_expression (stream, exp->val.args[0], true); + fprintf (stream, " && "); + write_csharp_expression (stream, exp->val.args[1], true); + fprintf (stream, ")"); + return; + case lor: + fprintf (stream, "("); + write_csharp_expression (stream, exp->val.args[0], true); + fprintf (stream, " || "); + write_csharp_expression (stream, exp->val.args[1], true); + fprintf (stream, ")"); + return; + case qmop: + if (is_expression_boolean (exp->val.args[1]) + && is_expression_boolean (exp->val.args[2])) + { + fprintf (stream, "("); + write_csharp_expression (stream, exp->val.args[0], true); + fprintf (stream, " ? "); + write_csharp_expression (stream, exp->val.args[1], true); + fprintf (stream, " : "); + write_csharp_expression (stream, exp->val.args[2], true); + fprintf (stream, ")"); + return; + } + /*FALLTHROUGH*/ + case var: + case mult: + case divide: + case module: + case plus: + case minus: + fprintf (stream, "("); + write_csharp_expression (stream, exp, false); + fprintf (stream, " != 0)"); + return; + default: + abort (); + } + } + else + { + /* Emit a C# expression of type 'long'. */ + switch (exp->operation) + { + case var: + fprintf (stream, "n"); + return; + case num: + fprintf (stream, "%lu", exp->val.num); + return; + case mult: + fprintf (stream, "("); + write_csharp_expression (stream, exp->val.args[0], false); + fprintf (stream, " * "); + write_csharp_expression (stream, exp->val.args[1], false); + fprintf (stream, ")"); + return; + case divide: + fprintf (stream, "("); + write_csharp_expression (stream, exp->val.args[0], false); + fprintf (stream, " / "); + write_csharp_expression (stream, exp->val.args[1], false); + fprintf (stream, ")"); + return; + case module: + fprintf (stream, "("); + write_csharp_expression (stream, exp->val.args[0], false); + fprintf (stream, " %% "); + write_csharp_expression (stream, exp->val.args[1], false); + fprintf (stream, ")"); + return; + case plus: + fprintf (stream, "("); + write_csharp_expression (stream, exp->val.args[0], false); + fprintf (stream, " + "); + write_csharp_expression (stream, exp->val.args[1], false); + fprintf (stream, ")"); + return; + case minus: + fprintf (stream, "("); + write_csharp_expression (stream, exp->val.args[0], false); + fprintf (stream, " - "); + write_csharp_expression (stream, exp->val.args[1], false); + fprintf (stream, ")"); + return; + case qmop: + fprintf (stream, "("); + write_csharp_expression (stream, exp->val.args[0], true); + fprintf (stream, " ? "); + write_csharp_expression (stream, exp->val.args[1], false); + fprintf (stream, " : "); + write_csharp_expression (stream, exp->val.args[2], false); + fprintf (stream, ")"); + return; + case lnot: + case less_than: + case greater_than: + case less_or_equal: + case greater_or_equal: + case equal: + case not_equal: + case land: + case lor: + fprintf (stream, "("); + write_csharp_expression (stream, exp, true); + fprintf (stream, " ? 1 : 0)"); + return; + default: + abort (); + } + } +} + + +/* Write the C# code for the GettextResourceSet subclass to the given stream. + Note that we use fully qualified class names and no "using" statements, + because applications can have their own classes called X.Y.Hashtable or + X.Y.String. */ +static void +write_csharp_code (FILE *stream, const char *class_name, message_list_ty *mlp) +{ + const char *last_dot; + const char *class_name_last_part; + unsigned int plurals; + size_t j; + + fprintf (stream, + "/* Automatically generated by GNU msgfmt. Do not modify! */\n"); + /* We have to use a "using" statement here, to avoid a bug in the pnet-0.6.0 + compiler. */ + fprintf (stream, "using GNU.Gettext;\n"); + last_dot = strrchr (class_name, '.'); + if (last_dot != NULL) + { + fprintf (stream, "namespace "); + fwrite (class_name, 1, last_dot - class_name, stream); + fprintf (stream, " {\n"); + class_name_last_part = last_dot + 1; + } + else + class_name_last_part = class_name; + fprintf (stream, "public class %s : GettextResourceSet {\n", + class_name_last_part); + + /* Determine whether there are plural messages. */ + plurals = 0; + for (j = 0; j < mlp->nitems; j++) + if (mlp->item[j]->msgid_plural != NULL) + plurals++; + + /* Emit the constructor. */ + fprintf (stream, " public %s ()\n", class_name_last_part); + fprintf (stream, " : base () {\n"); + fprintf (stream, " }\n"); + + /* Emit the ReadResources method. */ + fprintf (stream, " protected override void ReadResources () {\n"); + /* In some implementations, the ResourceSet constructor initializes Table + before calling ReadResources(). In other implementations, the + ReadResources() method is expected to initialize the Table. */ + fprintf (stream, " if (Table == null)\n"); + fprintf (stream, " Table = new System.Collections.Hashtable();\n"); + fprintf (stream, " System.Collections.Hashtable t = Table;\n"); + for (j = 0; j < mlp->nitems; j++) + { + fprintf (stream, " t.Add("); + write_csharp_string (stream, mlp->item[j]->msgid); + fprintf (stream, ","); + write_csharp_msgstr (stream, mlp->item[j]); + fprintf (stream, ");\n"); + } + fprintf (stream, " }\n"); + + /* Emit the msgid_plural strings. Only used by msgunfmt. */ + if (plurals) + { + fprintf (stream, " public static System.Collections.Hashtable GetMsgidPluralTable () {\n"); + fprintf (stream, " System.Collections.Hashtable t = new System.Collections.Hashtable();\n"); + for (j = 0; j < mlp->nitems; j++) + if (mlp->item[j]->msgid_plural != NULL) + { + fprintf (stream, " t.Add("); + write_csharp_string (stream, mlp->item[j]->msgid); + fprintf (stream, ","); + write_csharp_string (stream, mlp->item[j]->msgid_plural); + fprintf (stream, ");\n"); + } + fprintf (stream, " return t;\n"); + fprintf (stream, " }\n"); + } + + /* Emit the PluralEval function. It is a subroutine for GetPluralString. */ + if (plurals) + { + message_ty *header_entry; + struct expression *plural; + unsigned long int nplurals; + + header_entry = message_list_search (mlp, ""); + extract_plural_expression (header_entry ? header_entry->msgstr : NULL, + &plural, &nplurals); + + fprintf (stream, " protected override long PluralEval (long n) {\n"); + fprintf (stream, " return "); + write_csharp_expression (stream, plural, false); + fprintf (stream, ";\n"); + fprintf (stream, " }\n"); + } + + /* Terminate the class. */ + fprintf (stream, "}\n"); + + if (last_dot != NULL) + /* Terminate the namespace. */ + fprintf (stream, "}\n"); +} + + +/* Asynchronously cleaning up temporary files, when we receive any of the + usually occurring signals whose default action is to terminate the + program. */ + +static struct +{ + const char *tmpdir; + const char *file_name; +} cleanup_list; + +/* The signal handler. It gets called asynchronously. */ +static void +cleanup () +{ + /* First cleanup the files in the subdirectory. */ + { + const char *filename = cleanup_list.file_name; + + if (filename != NULL) + unlink (filename); + } + + /* Then cleanup the main temporary directory. */ + { + const char *filename = cleanup_list.tmpdir; + + if (filename != NULL) + rmdir (filename); + } +} + + +int +msgdomain_write_csharp (message_list_ty *mlp, const char *canon_encoding, + const char *resource_name, const char *locale_name, + const char *directory) +{ + int retval; + char *template; + char *tmpdir; + char *culture_name; + char *output_file; + char *class_name; + char *csharp_file_name; + FILE *csharp_file; + const char *gettextlibdir; + const char *csharp_sources[1]; + const char *libdirs[1]; + const char *libraries[1]; + + /* If no entry for this resource/domain, don't even create the file. */ + if (mlp->nitems == 0) + return 0; + + retval = 1; + + /* Convert the messages to Unicode. */ + iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL); + + cleanup_list.tmpdir = NULL; + cleanup_list.file_name = NULL; + { + static bool cleanup_already_registered = false; + if (!cleanup_already_registered) + { + at_fatal_signal (&cleanup); + cleanup_already_registered = true; + } + } + + /* Create a temporary directory where we can put the C# file. + A simple temporary file would also be possible but would require us to + define our own variant of mkstemp(): On one hand the functions mktemp(), + tmpnam(), tempnam() present a security risk, and on the other hand the + function mkstemp() doesn't allow to specify a fixed suffix of the file. + It is simpler to create a temporary directory. */ + template = (char *) xallocsa (PATH_MAX); + if (path_search (template, PATH_MAX, NULL, "msg", 1)) + { + error (0, errno, + _("cannot find a temporary directory, try setting $TMPDIR")); + goto quit1; + } + block_fatal_signals (); + tmpdir = mkdtemp (template); + cleanup_list.tmpdir = tmpdir; + unblock_fatal_signals (); + if (tmpdir == NULL) + { + error (0, errno, + _("cannot create a temporary directory using template \"%s\""), + template); + goto quit1; + } + + /* Assign a default value to the resource name. */ + if (resource_name == NULL) + resource_name = "Messages"; + + /* Convert the locale name to a .NET specific culture name. */ + culture_name = xstrdup (locale_name); + { + char *p; + for (p = culture_name; *p != '\0'; p++) + if (*p == '_') + *p = '-'; + } + + /* Compute the output file name. This code must be kept consistent with + intl.cs, function GetSatelliteAssembly(). */ + { + char *output_dir = concatenated_pathname (directory, culture_name, NULL); + struct stat statbuf; + + /* Try to create the output directory if it does not yet exist. */ + if (stat (output_dir, &statbuf) < 0 && errno == ENOENT) + if (mkdir (output_dir, S_IRUSR | S_IWUSR | S_IXUSR + | S_IRGRP | S_IWGRP | S_IXGRP + | S_IROTH | S_IWOTH | S_IXOTH) < 0) + { + error (0, errno, _("failed to create directory \"%s\""), output_dir); + free (output_dir); + goto quit3; + } + + output_file = + concatenated_pathname (output_dir, resource_name, ".resources.dll"); + + free (output_dir); + } + + /* Compute the class name. This code must be kept consistent with intl.cs, + function InstantiateResourceSet(). */ + { + char *class_name_part1 = construct_class_name (resource_name); + char *p; + + class_name = + (char *) xmalloc (strlen (class_name_part1) + 1 + strlen (culture_name) + 1); + sprintf (class_name, "%s_%s", class_name_part1, culture_name); + for (p = class_name + strlen (class_name_part1) + 1; *p != '\0'; p++) + if (*p == '-') + *p = '_'; + free (class_name_part1); + } + + /* Compute the temporary C# file name. It must end in ".cs", so that + the C# compiler recognizes that it is C# source code. */ + csharp_file_name = concatenated_pathname (tmpdir, "resset.cs", NULL); + + /* Create the C# file. */ + cleanup_list.file_name = csharp_file_name; + csharp_file = fopen (csharp_file_name, "w"); + if (csharp_file == NULL) + { + error (0, errno, _("failed to create \"%s\""), csharp_file_name); + goto quit4; + } + + write_csharp_code (csharp_file, class_name, mlp); + + if (fwriteerror (csharp_file)) + { + error (0, errno, _("error while writing \"%s\" file"), csharp_file_name); + fclose (csharp_file); + goto quit5; + } + fclose (csharp_file); + + /* Make it possible to override the .dll location. This is + necessary for running the testsuite before "make install". */ + gettextlibdir = getenv ("GETTEXTCSHARPLIBDIR"); + if (gettextlibdir == NULL || gettextlibdir[0] == '\0') + gettextlibdir = relocate (LIBDIR); + + /* Compile the C# file to a .dll file. */ + csharp_sources[0] = csharp_file_name; + libdirs[0] = gettextlibdir; + libraries[0] = "GNU.Gettext"; + if (compile_csharp_class (csharp_sources, 1, libdirs, 1, libraries, 1, + output_file, true, false, verbose)) + { + error (0, 0, _("compilation of C# class failed, please try --verbose")); + goto quit5; + } + + retval = 0; + + quit5: + unlink (csharp_file_name); + quit4: + cleanup_list.file_name = NULL; + free (csharp_file_name); + free (class_name); + free (output_file); + quit3: + free (culture_name); + rmdir (tmpdir); + quit1: + cleanup_list.tmpdir = NULL; + freesa (template); + /* Here we could unregister the cleanup() handler. */ + return retval; +} diff --git a/gettext-tools/src/write-csharp.h b/gettext-tools/src/write-csharp.h new file mode 100644 index 000000000..a56ef6ab0 --- /dev/null +++ b/gettext-tools/src/write-csharp.h @@ -0,0 +1,36 @@ +/* Writing C# satellite assemblies. + Copyright (C) 2003 Free Software Foundation, Inc. + Written by Bruno Haible , 2003. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _WRITE_CSHARP_H +#define _WRITE_CSHARP_H + +#include "message.h" + +/* Write a C# satellite assembly DLL file. mlp is a list containing the + messages to be output. resource_name is the name of the resource + (with dot separators), locale_name is the locale name (with underscore + separators) or NULL, directory is the base directory. + Return 0 if ok, nonzero on error. */ +extern int + msgdomain_write_csharp (message_list_ty *mlp, + const char *canon_encoding, + const char *resource_name, + const char *locale_name, + const char *directory); + +#endif /* _WRITE_CSHARP_H */