No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / gettext / gettext-tools / src / msgunfmt.cs
blob7243c23e5ffee1eb05b45f4e60e75f5417d35f56
1 /* GNU gettext for C#
2 * Copyright (C) 2003-2004 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.
21 * This program dumps a GettextResourceSet subclass (in a satellite assembly)
22 * or a .resources file as a PO file.
25 using System; /* Object, String, Type, Console, Exception */
26 using System.Reflection; /* Assembly, MethodInfo, ConstructorInfo */
27 using System.Collections; /* Hashtable, DictionaryEntry */
28 using System.IO; /* BufferedStream, StreamWriter, TextWriter, FileNotFoundException, Path */
29 using System.Text; /* StringBuilder, UTF8Encoding */
30 using System.Resources; /* ResourceReader */
31 using GNU.Gettext; /* GettextResourceSet */
33 namespace GNU.Gettext {
34 public class DumpResource {
35 private TextWriter Out;
36 private void DumpString (String str) {
37 int n = str.Length;
38 Out.Write('"');
39 for (int i = 0; i < n; i++) {
40 char c = str[i];
41 if (c == 0x0008) {
42 Out.Write('\\'); Out.Write('b');
43 } else if (c == 0x000c) {
44 Out.Write('\\'); Out.Write('f');
45 } else if (c == 0x000a) {
46 Out.Write('\\'); Out.Write('n');
47 } else if (c == 0x000d) {
48 Out.Write('\\'); Out.Write('r');
49 } else if (c == 0x0009) {
50 Out.Write('\\'); Out.Write('t');
51 } else if (c == '\\' || c == '"') {
52 Out.Write('\\'); Out.Write(c);
53 } else
54 Out.Write(c);
56 Out.Write('"');
58 private void DumpMessage (String msgid, String msgid_plural, Object msgstr) {
59 Out.Write("msgid "); DumpString(msgid); Out.Write('\n');
60 if (msgid_plural != null) {
61 Out.Write("msgid_plural "); DumpString(msgid_plural); Out.Write('\n');
62 for (int i = 0; i < (msgstr as String[]).Length; i++) {
63 Out.Write("msgstr[" + i + "] ");
64 DumpString((msgstr as String[])[i]);
65 Out.Write('\n');
67 } else {
68 Out.Write("msgstr "); DumpString(msgstr as String); Out.Write('\n');
70 Out.Write('\n');
73 // ---------------- Dumping a GettextResourceSet ----------------
75 private void Dump (GettextResourceSet catalog) {
76 MethodInfo pluralMethod =
77 catalog.GetType().GetMethod("GetMsgidPluralTable", Type.EmptyTypes);
78 // Search for the header entry.
80 Object header_entry = catalog.GetObject("");
81 // If there is no header entry, fake one.
82 // FIXME: This is not needed; right after po_lex_charset_init set
83 // the PO charset to UTF-8.
84 if (header_entry == null)
85 header_entry = "Content-Type: text/plain; charset=UTF-8\n";
86 DumpMessage("", null, header_entry);
88 // Now the other messages.
90 Hashtable plural = null;
91 if (pluralMethod != null)
92 plural = pluralMethod.Invoke(catalog, new Object[0]) as Hashtable;
93 foreach (String key in catalog.Keys)
94 if (!"".Equals(key)) {
95 Object value = catalog.GetObject(key);
96 String key_plural =
97 (plural != null && value is String[] ? plural[key] as String : null);
98 DumpMessage(key, key_plural, value);
102 // Essentially taken from class GettextResourceManager.
103 private static Assembly GetSatelliteAssembly (String baseDirectory, String resourceName, String cultureName) {
104 String satelliteExpectedLocation =
105 baseDirectory
106 + Path.DirectorySeparatorChar + cultureName
107 + Path.DirectorySeparatorChar + resourceName + ".resources.dll";
108 return Assembly.LoadFrom(satelliteExpectedLocation);
110 // Taken from class GettextResourceManager.
111 private static String ConstructClassName (String resourceName) {
112 // We could just return an arbitrary fixed class name, like "Messages",
113 // assuming that every assembly will only ever contain one
114 // GettextResourceSet subclass, but this assumption would break the day
115 // we want to support multi-domain PO files in the same format...
116 bool valid = (resourceName.Length > 0);
117 for (int i = 0; valid && i < resourceName.Length; i++) {
118 char c = resourceName[i];
119 if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_')
120 || (i > 0 && c >= '0' && c <= '9')))
121 valid = false;
123 if (valid)
124 return resourceName;
125 else {
126 // Use hexadecimal escapes, using the underscore as escape character.
127 String hexdigit = "0123456789abcdef";
128 StringBuilder b = new StringBuilder();
129 b.Append("__UESCAPED__");
130 for (int i = 0; i < resourceName.Length; i++) {
131 char c = resourceName[i];
132 if (c >= 0xd800 && c < 0xdc00
133 && i+1 < resourceName.Length
134 && resourceName[i+1] >= 0xdc00 && resourceName[i+1] < 0xe000) {
135 // Combine two UTF-16 words to a character.
136 char c2 = resourceName[i+1];
137 int uc = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
138 b.Append('_');
139 b.Append('U');
140 b.Append(hexdigit[(uc >> 28) & 0x0f]);
141 b.Append(hexdigit[(uc >> 24) & 0x0f]);
142 b.Append(hexdigit[(uc >> 20) & 0x0f]);
143 b.Append(hexdigit[(uc >> 16) & 0x0f]);
144 b.Append(hexdigit[(uc >> 12) & 0x0f]);
145 b.Append(hexdigit[(uc >> 8) & 0x0f]);
146 b.Append(hexdigit[(uc >> 4) & 0x0f]);
147 b.Append(hexdigit[uc & 0x0f]);
148 i++;
149 } else if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
150 || (c >= '0' && c <= '9'))) {
151 int uc = c;
152 b.Append('_');
153 b.Append('u');
154 b.Append(hexdigit[(uc >> 12) & 0x0f]);
155 b.Append(hexdigit[(uc >> 8) & 0x0f]);
156 b.Append(hexdigit[(uc >> 4) & 0x0f]);
157 b.Append(hexdigit[uc & 0x0f]);
158 } else
159 b.Append(c);
161 return b.ToString();
164 // Essentially taken from class GettextResourceManager.
165 private static GettextResourceSet InstantiateResourceSet (Assembly satelliteAssembly, String resourceName, String cultureName) {
166 Type clazz = satelliteAssembly.GetType(ConstructClassName(resourceName)+"_"+cultureName.Replace('-','_'));
167 ConstructorInfo constructor = clazz.GetConstructor(Type.EmptyTypes);
168 return constructor.Invoke(null) as GettextResourceSet;
170 public DumpResource (String baseDirectory, String resourceName, String cultureName) {
171 // We are only interested in the messages belonging to the locale
172 // itself, not in the inherited messages. Therefore we instantiate just
173 // the GettextResourceSet, not a GettextResourceManager.
174 Assembly satelliteAssembly =
175 GetSatelliteAssembly(baseDirectory, resourceName, cultureName);
176 GettextResourceSet catalog =
177 InstantiateResourceSet(satelliteAssembly, resourceName, cultureName);
178 BufferedStream stream = new BufferedStream(Console.OpenStandardOutput());
179 Out = new StreamWriter(stream, new UTF8Encoding());
180 Dump(catalog);
181 Out.Close();
182 stream.Close();
185 // ----------------- Dumping a .resources file ------------------
187 public DumpResource (String filename) {
188 BufferedStream stream = new BufferedStream(Console.OpenStandardOutput());
189 Out = new StreamWriter(stream, new UTF8Encoding());
190 ResourceReader rr;
191 if (filename.Equals("-")) {
192 BufferedStream input = new BufferedStream(Console.OpenStandardInput());
193 // A temporary output stream is needed because ResourceReader expects
194 // to be able to seek in the Stream.
195 byte[] contents;
197 MemoryStream tmpstream = new MemoryStream();
198 byte[] buf = new byte[1024];
199 for (;;) {
200 int n = input.Read(buf, 0, 1024);
201 if (n == 0)
202 break;
203 tmpstream.Write(buf, 0, n);
205 contents = tmpstream.ToArray();
206 tmpstream.Close();
208 MemoryStream tmpinput = new MemoryStream(contents);
209 rr = new ResourceReader(tmpinput);
210 } else {
211 rr = new ResourceReader(filename);
213 foreach (DictionaryEntry entry in rr) // uses rr.GetEnumerator()
214 DumpMessage(entry.Key as String, null, entry.Value as String);
215 rr.Close();
216 Out.Close();
217 stream.Close();
220 // --------------------------------------------------------------
222 public static int Main (String[] args) {
223 try {
224 if (args.Length > 1)
225 new DumpResource(args[0], args[1], args[2]);
226 else
227 new DumpResource(args[0]);
228 } catch (Exception e) {
229 Console.Error.WriteLine(e);
230 Console.Error.WriteLine(e.StackTrace);
231 return 1;
233 return 0;