**** Merged from MCS ****
[mono-project.git] / mcs / monoresgen / monoresgen.cs
blob00aed8dcd701f7ab023bdd291e0ee34960e32408
1 /*
2 * monoresgen: convert between the resource formats (.txt, .resources, .resx).
4 * Copyright (c) 2002 Ximian, Inc
6 * Authors:
7 * Paolo Molaro (lupus@ximian.com)
8 * Gonzalo Paniagua Javier (gonzalo@ximian.com)
9 */
12 * TODO:
13 * * escape/unescape in the .txt reader/writer to be able to roundtrip values with newlines
14 * (unlike the MS ResGen utility)
17 using System;
18 using System.Text;
19 using System.IO;
20 using System.Collections;
21 using System.Resources;
22 using System.Reflection;
24 class ResGen {
26 static Assembly swf;
27 static Type resxr;
28 static Type resxw;
31 * We load the ResX format stuff on demand, since the classes are in
32 * System.Windows.Forms (!!!) and we can't depend on that assembly in mono, yet.
34 static void LoadResX () {
35 if (swf != null)
36 return;
37 try {
38 swf = Assembly.Load (Consts.AssemblySystem_Windows_Forms);
39 resxr = swf.GetType ("System.Resources.ResXResourceReader");
40 resxw = swf.GetType ("System.Resources.ResXResourceWriter");
41 } catch (Exception e) {
42 throw new Exception ("Cannot load support for ResX format: " + e.Message);
46 static void Usage () {
47 string Usage = @"Mono Resource Generator version 0.1
48 Usage:
49 monoresgen source.ext [dest.ext]
50 monoresgen /compile source.ext[,dest.resources] [...]
52 Convert a resource file from one format to another.
53 The currently supported formats are: '.txt' '.resources' '.resx' '.po'.
54 If the destination file is not specified, source.resources will be used.
55 The /compile option takes a list of .resX or .txt files to convert to
56 .resources files in one bulk operation, replacing .ext with .resources for
57 the output file name.
59 Console.WriteLine( Usage );
62 static IResourceReader GetReader (Stream stream, string name) {
63 string format = Path.GetExtension (name);
64 switch (format.ToLower ()) {
65 case ".po":
66 return new PoResourceReader (stream);
67 case ".txt":
68 case ".text":
69 return new TxtResourceReader (stream);
70 case ".resources":
71 return new ResourceReader (stream);
72 case ".resx":
73 LoadResX ();
74 return (IResourceReader)Activator.CreateInstance (resxr, new object[] {stream});
75 default:
76 throw new Exception ("Unknown format in file " + name);
80 static IResourceWriter GetWriter (Stream stream, string name) {
81 string format = Path.GetExtension (name);
82 switch (format.ToLower ()) {
83 case ".po":
84 return new PoResourceWriter (stream);
85 case ".txt":
86 case ".text":
87 return new TxtResourceWriter (stream);
88 case ".resources":
89 return new ResourceWriter (stream);
90 case ".resx":
91 LoadResX ();
92 return (IResourceWriter)Activator.CreateInstance (resxw, new object[] {stream});
93 default:
94 throw new Exception ("Unknown format in file " + name);
98 static int CompileResourceFile(string sname, string dname ) {
99 FileStream source, dest;
100 IResourceReader reader;
101 IResourceWriter writer;
103 try {
104 source = new FileStream (sname, FileMode.Open, FileAccess.Read);
106 reader = GetReader (source, sname);
108 dest = new FileStream (dname, FileMode.OpenOrCreate, FileAccess.Write);
109 writer = GetWriter (dest, dname);
111 int rescount = 0;
112 foreach (DictionaryEntry e in reader) {
113 rescount++;
114 object val = e.Value;
115 if (val is string)
116 writer.AddResource ((string)e.Key, (string)e.Value);
117 else
118 writer.AddResource ((string)e.Key, e.Value);
120 Console.WriteLine( "Read in {0} resources from '{1}'", rescount, sname );
122 reader.Close ();
123 writer.Close ();
124 Console.WriteLine("Writing resource file... Done.");
125 } catch (Exception e) {
126 Console.WriteLine ("Error: {0}", e.Message);
127 Exception inner = e.InnerException;
128 if (inner != null)
129 Console.WriteLine ("Inner exception: {0}", inner.Message);
130 return 1;
132 return 0;
135 static int Main (string[] args) {
136 string sname = "", dname = "";
137 if ((int) args.Length < 1 || args[0] == "-h" || args[0] == "-?" || args[0] == "/h" || args[0] == "/?") {
138 Usage();
139 return 1;
141 if (args[0] == "/compile" || args[0] == "-compile") {
142 for ( int i=1; i< args.Length; i++ ) {
143 if ( args[i].IndexOf(",") != -1 ){
144 string[] pair = args[i].Split(',');
145 sname = pair[0];
146 dname = pair[1];
147 if (dname == ""){
148 Console.WriteLine(@"error: You must specify an input & outfile file name like this:");
149 Console.WriteLine("inFile.txt,outFile.resources." );
150 Console.WriteLine("You passed in '{0}'.", args[i] );
151 return 1;
153 } else {
154 sname = args[i];
155 dname = Path.ChangeExtension (sname, "resources");
157 int ret = CompileResourceFile( sname, dname );
158 if (ret != 0 ) {
159 return ret;
162 return 0;
165 else if (args.Length == 1) {
166 sname = args [0];
167 dname = Path.ChangeExtension (sname, "resources");
168 } else if (args.Length != 2) {
169 Usage ();
170 return 1;
171 } else {
172 sname = args [0];
173 dname = args [1];
175 return CompileResourceFile( sname, dname );
179 class TxtResourceWriter : IResourceWriter {
180 StreamWriter s;
182 public TxtResourceWriter (Stream stream) {
183 s = new StreamWriter (stream);
186 public void AddResource (string name, byte[] value) {
187 throw new Exception ("Binary data not valid in a text resource file");
190 public void AddResource (string name, object value) {
191 if (value is string) {
192 AddResource (name, (string)value);
193 return;
195 throw new Exception ("Objects not valid in a text resource file");
198 /* FIXME: handle newlines */
199 public void AddResource (string name, string value) {
200 s.WriteLine ("{0}={1}", name, value);
203 public void Close () {
204 s.Close ();
207 public void Dispose () {}
209 public void Generate () {}
212 class TxtResourceReader : IResourceReader {
213 Hashtable data;
214 Stream s;
216 public TxtResourceReader (Stream stream) {
217 data = new Hashtable ();
218 s = stream;
219 Load ();
222 public virtual void Close () {
225 public IDictionaryEnumerator GetEnumerator() {
226 return data.GetEnumerator ();
229 void Load () {
230 StreamReader reader = new StreamReader (s);
231 string line, key, val;
232 int epos, line_num = 0;
233 while ((line = reader.ReadLine ()) != null) {
234 line_num++;
235 line = line.Trim ();
236 if (line.Length == 0 || line [0] == '#' ||
237 line [0] == ';')
238 continue;
239 epos = line.IndexOf ('=');
240 if (epos < 0)
241 throw new Exception ("Invalid format at line " + line_num);
242 key = line.Substring (0, epos);
243 val = line.Substring (epos + 1);
244 key = key.Trim ();
245 val = val.Trim ();
246 if (key.Length == 0)
247 throw new Exception ("Key is empty at line " + line_num);
248 data.Add (key, val);
252 IEnumerator IEnumerable.GetEnumerator () {
253 return ((IResourceReader) this).GetEnumerator();
256 void IDisposable.Dispose () {}
259 class PoResourceReader : IResourceReader {
260 Hashtable data;
261 Stream s;
262 int line_num;
264 public PoResourceReader (Stream stream)
266 data = new Hashtable ();
267 s = stream;
268 Load ();
271 public virtual void Close ()
273 s.Close ();
276 public IDictionaryEnumerator GetEnumerator()
278 return data.GetEnumerator ();
281 string GetValue (string line)
283 int begin = line.IndexOf ('"');
284 if (begin == -1)
285 throw new FormatException (String.Format ("No begin quote at line {0}: {1}", line_num, line));
287 int end = line.LastIndexOf ('"');
288 if (end == -1)
289 throw new FormatException (String.Format ("No closing quote at line {0}: {1}", line_num, line));
291 return line.Substring (begin + 1, end - begin - 1);
294 void Load ()
296 StreamReader reader = new StreamReader (s);
297 string line;
298 string msgid = null;
299 string msgstr = null;
300 bool ignoreNext = false;
302 while ((line = reader.ReadLine ()) != null) {
303 line_num++;
304 line = line.Trim ();
305 if (line.Length == 0)
306 continue;
308 if (line [0] == '#') {
309 if (line.Length == 1 || line [1] != ',')
310 continue;
312 if (line.IndexOf ("fuzzy") != -1) {
313 ignoreNext = true;
314 if (msgid != null) {
315 if (msgstr == null)
316 throw new FormatException ("Error. Line: " + line_num);
317 data.Add (msgid, msgstr);
318 msgid = null;
319 msgstr = null;
322 continue;
325 if (line.StartsWith ("msgid ")) {
326 if (msgid == null && msgstr != null)
327 throw new FormatException ("Found 2 consecutive msgid. Line: " + line_num);
329 if (msgstr != null) {
330 if (!ignoreNext)
331 data.Add (msgid, msgstr);
333 ignoreNext = false;
334 msgid = null;
335 msgstr = null;
338 msgid = GetValue (line);
339 continue;
342 if (line.StartsWith ("msgstr ")) {
343 if (msgid == null)
344 throw new FormatException ("msgstr with no msgid. Line: " + line_num);
346 msgstr = GetValue (line);
347 continue;
350 if (line [0] == '"') {
351 if (msgid == null || msgstr == null)
352 throw new FormatException ("Invalid format. Line: " + line_num);
354 msgstr += GetValue (line);
355 continue;
358 throw new FormatException ("Unexpected data. Line: " + line_num);
361 if (msgid != null) {
362 if (msgstr == null)
363 throw new FormatException ("Expecting msgstr. Line: " + line_num);
365 if (!ignoreNext)
366 data.Add (msgid, msgstr);
370 IEnumerator IEnumerable.GetEnumerator ()
372 return GetEnumerator();
375 void IDisposable.Dispose ()
377 if (data != null)
378 data = null;
380 if (s != null) {
381 s.Close ();
382 s = null;
387 class PoResourceWriter : IResourceWriter
389 TextWriter s;
390 bool headerWritten;
392 public PoResourceWriter (Stream stream)
394 s = new StreamWriter (stream);
397 public void AddResource (string name, byte [] value)
399 throw new InvalidOperationException ("Binary data not valid in a po resource file");
402 public void AddResource (string name, object value)
404 if (value is string) {
405 AddResource (name, (string) value);
406 return;
408 throw new InvalidOperationException ("Objects not valid in a po resource file");
411 StringBuilder ebuilder = new StringBuilder ();
413 public string Escape (string ns)
415 ebuilder.Length = 0;
417 foreach (char c in ns){
418 switch (c){
419 case '"':
420 case '\\':
421 ebuilder.Append ('\\');
422 ebuilder.Append (c);
423 break;
424 case '\a':
425 ebuilder.Append ("\\a");
426 break;
427 case '\n':
428 ebuilder.Append ("\\n");
429 break;
430 case '\r':
431 ebuilder.Append ("\\r");
432 break;
433 default:
434 ebuilder.Append (c);
435 break;
438 return ebuilder.ToString ();
441 public void AddResource (string name, string value)
443 if (!headerWritten) {
444 headerWritten = true;
445 WriteHeader ();
448 s.WriteLine ("msgid \"{0}\"", Escape (name));
449 s.WriteLine ("msgstr \"{0}\"", Escape (value));
450 s.WriteLine ("");
453 void WriteHeader ()
455 s.WriteLine ("msgid \"\"");
456 s.WriteLine ("msgstr \"\"");
457 s.WriteLine ("\"MIME-Version: 1.0\\n\"");
458 s.WriteLine ("\"Content-Type: text/plain; charset=UTF-8\\n\"");
459 s.WriteLine ("\"Content-Transfer-Encoding: 8bit\\n\"");
460 s.WriteLine ("\"X-Generator: monoresgen 0.1\\n\"");
461 s.WriteLine ("#\"Project-Id-Version: FILLME\\n\"");
462 s.WriteLine ("#\"POT-Creation-Date: yyyy-MM-dd HH:MM+zzzz\\n\"");
463 s.WriteLine ("#\"PO-Revision-Date: yyyy-MM-dd HH:MM+zzzz\\n\"");
464 s.WriteLine ("#\"Last-Translator: FILLME\\n\"");
465 s.WriteLine ("#\"Language-Team: FILLME\\n\"");
466 s.WriteLine ("#\"Report-Msgid-Bugs-To: \\n\"");
467 s.WriteLine ();
470 public void Close ()
472 s.Close ();
475 public void Dispose () { }
477 public void Generate () {}