New test.
[mono.git] / mcs / tools / resgen / monoresgen.cs
1 /*
2  * resgen: convert between the resource formats (.txt, .resources, .resx).
3  *
4  * Copyright (c) 2002 Ximian, Inc
5  *
6  * Authors:
7  *      Paolo Molaro (lupus@ximian.com)
8  *      Gonzalo Paniagua Javier (gonzalo@ximian.com)
9  */
10
11 using System;
12 using System.Text;
13 using System.IO;
14 using System.Collections;
15 using System.Resources;
16 using System.Reflection;
17
18 class ResGen {
19
20         static Assembly swf;
21         static Type resxr;
22         static Type resxw;
23
24         /*
25          * We load the ResX format stuff on demand, since the classes are in 
26          * System.Windows.Forms (!!!) and we can't depend on that assembly in mono, yet.
27          */
28         static void LoadResX () {
29                 if (swf != null)
30                         return;
31                 try {
32                         swf = Assembly.Load (Consts.AssemblySystem_Windows_Forms);
33                         resxr = swf.GetType ("System.Resources.ResXResourceReader");
34                         resxw = swf.GetType ("System.Resources.ResXResourceWriter");
35                 } catch (Exception e) {
36                         throw new Exception ("Cannot load support for ResX format: " + e.Message);
37                 }
38         }
39
40         static void Usage () {
41                 string Usage = @"Mono Resource Generator version 0.1
42 Usage:
43                 resgen source.ext [dest.ext]
44                 resgen /compile source.ext[,dest.resources] [...]
45
46 Convert a resource file from one format to another.
47 The currently supported formats are: '.txt' '.resources' '.resx' '.po'.
48 If the destination file is not specified, source.resources will be used.
49 The /compile option takes a list of .resX or .txt files to convert to
50 .resources files in one bulk operation, replacing .ext with .resources for
51 the output file name.
52 ";
53                 Console.WriteLine( Usage );
54         }
55         
56         static IResourceReader GetReader (Stream stream, string name) {
57                 string format = Path.GetExtension (name);
58                 switch (format.ToLower ()) {
59                 case ".po":
60                         return new PoResourceReader (stream);
61                 case ".txt":
62                 case ".text":
63                         return new TxtResourceReader (stream);
64                 case ".resources":
65                         return new ResourceReader (stream);
66                 case ".resx":
67                         LoadResX ();
68                         return (IResourceReader)Activator.CreateInstance (resxr, new object[] {stream});
69                 default:
70                         throw new Exception ("Unknown format in file " + name);
71                 }
72         }
73         
74         static IResourceWriter GetWriter (Stream stream, string name) {
75                 string format = Path.GetExtension (name);
76                 switch (format.ToLower ()) {
77                 case ".po":
78                         return new PoResourceWriter (stream);
79                 case ".txt":
80                 case ".text":
81                         return new TxtResourceWriter (stream);
82                 case ".resources":
83                         return new ResourceWriter (stream);
84                 case ".resx":
85                         LoadResX ();
86                         return (IResourceWriter)Activator.CreateInstance (resxw, new object[] {stream});
87                 default:
88                         throw new Exception ("Unknown format in file " + name);
89                 }
90         }
91         
92         static int CompileResourceFile(string sname, string dname ) {
93                 FileStream source, dest;
94                 IResourceReader reader;
95                 IResourceWriter writer;
96
97                 try {
98                         source = new FileStream (sname, FileMode.Open, FileAccess.Read);
99
100                         reader = GetReader (source, sname);
101
102                         dest = new FileStream (dname, FileMode.Create, FileAccess.Write);
103                         writer = GetWriter (dest, dname);
104
105                         int rescount = 0;
106                         foreach (DictionaryEntry e in reader) {
107                                 rescount++;
108                                 object val = e.Value;
109                                 if (val is string)
110                                         writer.AddResource ((string)e.Key, (string)e.Value);
111                                 else
112                                         writer.AddResource ((string)e.Key, e.Value);
113                         }
114                         Console.WriteLine( "Read in {0} resources from '{1}'", rescount, sname );
115
116                         reader.Close ();
117                         writer.Close ();
118                         Console.WriteLine("Writing resource file...  Done.");
119                 } catch (Exception e) {
120                         Console.WriteLine ("Error: {0}", e.Message);
121                         Exception inner = e.InnerException;
122                         if (inner != null)
123                                 Console.WriteLine ("Inner exception: {0}", inner.Message);
124                         return 1;
125                 }
126                 return 0;
127         }
128         
129         static int Main (string[] args) {
130                 string sname = "", dname = ""; 
131                 if ((int) args.Length < 1 || args[0] == "-h" || args[0] == "-?" || args[0] == "/h" || args[0] == "/?") {
132                           Usage();
133                           return 1;
134                 }               
135                 if (args[0] == "/compile" || args[0] == "-compile") {
136                         for ( int i=1; i< args.Length; i++ ) {                          
137                                 if ( args[i].IndexOf(",") != -1 ){
138                                         string[] pair =  args[i].Split(',');
139                                         sname = pair[0]; 
140                                         dname = pair[1];
141                                         if (dname == ""){
142                                                 Console.WriteLine(@"error: You must specify an input & outfile file name like this:");
143                                                 Console.WriteLine("inFile.txt,outFile.resources." );
144                                                 Console.WriteLine("You passed in '{0}'.", args[i] );
145                                                 return 1;
146                                         }
147                                 } else {
148                                         sname = args[i]; 
149                                         dname = Path.ChangeExtension (sname, "resources");
150                                 }
151                                 int ret = CompileResourceFile( sname, dname );
152                                 if (ret != 0 ) {
153                                         return ret;
154                                 }
155                         }
156                         return 0;
157                 
158                 }
159                 else if (args.Length == 1) {
160                         sname = args [0];
161                         dname = Path.ChangeExtension (sname, "resources");
162                 } else if (args.Length != 2) {
163                         Usage ();
164                         return 1;
165                 } else {
166                         sname = args [0];
167                         dname = args [1];                       
168                 }               
169                 return CompileResourceFile( sname, dname );
170         }
171 }
172
173 class TxtResourceWriter : IResourceWriter {
174         StreamWriter s;
175         
176         public TxtResourceWriter (Stream stream) {
177                 s = new StreamWriter (stream);
178         }
179         
180         public void AddResource (string name, byte[] value) {
181                 throw new Exception ("Binary data not valid in a text resource file");
182         }
183         
184         public void AddResource (string name, object value) {
185                 if (value is string) {
186                         AddResource (name, (string)value);
187                         return;
188                 }
189                 throw new Exception ("Objects not valid in a text resource file");
190         }
191         
192         public void AddResource (string name, string value) {
193                 s.WriteLine ("{0}={1}", name, Escape (value));
194         }
195
196         // \n -> \\n ...
197         static string Escape (string value)
198         {
199                 StringBuilder b = new StringBuilder ();
200                 for (int i = 0; i < value.Length; i++) {
201                         switch (value [i]) {
202                         case '\n':
203                                 b.Append ("\\n");
204                                 break;
205                         case '\r':
206                                 b.Append ("\\r");
207                                 break;
208                         case '\t':
209                                 b.Append ("\\t");
210                                 break;
211                         case '\\':
212                                 b.Append ("\\\\");
213                                 break;
214                         default:
215                                 b.Append (value [i]);
216                                 break;
217                         }
218                 }
219                 return b.ToString ();
220         }
221         
222         public void Close () {
223                 s.Close ();
224         }
225         
226         public void Dispose () {}
227         
228         public void Generate () {}
229 }
230
231 class TxtResourceReader : IResourceReader {
232         Hashtable data;
233         Stream s;
234         
235         public TxtResourceReader (Stream stream) {
236                 data = new Hashtable ();
237                 s = stream;
238                 Load ();
239         }
240         
241         public virtual void Close () {
242         }
243         
244         public IDictionaryEnumerator GetEnumerator() {
245                 return data.GetEnumerator ();
246         }
247         
248         void Load () {
249                 StreamReader reader = new StreamReader (s);
250                 string line, key, val;
251                 int epos, line_num = 0;
252                 while ((line = reader.ReadLine ()) != null) {
253                         line_num++;
254                         line = line.Trim ();
255                         if (line.Length == 0 || line [0] == '#' ||
256                             line [0] == ';')
257                                 continue;
258                         epos = line.IndexOf ('=');
259                         if (epos < 0) 
260                                 throw new Exception ("Invalid format at line " + line_num);
261                         key = line.Substring (0, epos);
262                         val = line.Substring (epos + 1);
263                         key = key.Trim ();
264                         val = val.Trim ();
265                         if (key.Length == 0) 
266                                 throw new Exception ("Key is empty at line " + line_num);
267
268                         val = Unescape (val);
269                         if (val == null)
270                                 throw new Exception (String.Format ("Unsupported escape character in value of key '{0}'.", key));
271
272
273                         data.Add (key, val);
274                 }
275         }
276
277         // \\n -> \n ...
278         static string Unescape (string value)
279         {
280                 StringBuilder b = new StringBuilder ();
281
282                 for (int i = 0; i < value.Length; i++) {
283                         if (value [i] == '\\') {
284                                 if (i == value.Length - 1)
285                                         return null;
286
287                                 i++;
288                                 switch (value [i]) {
289                                 case 'n':
290                                         b.Append ('\n');
291                                         break;
292                                 case 'r':
293                                         b.Append ('\r');
294                                         break;
295                                 case 't':
296                                         b.Append ('\t');
297                                         break;
298                                 case '\\':
299                                         b.Append ('\\');
300                                         break;
301                                 default:
302                                         return null;
303                                 }
304
305                         } else {
306                                 b.Append (value [i]);
307                         }
308                 }
309
310                 return b.ToString ();
311         }
312         
313         IEnumerator IEnumerable.GetEnumerator () {
314                 return ((IResourceReader) this).GetEnumerator();
315         }
316
317         void IDisposable.Dispose () {}
318 }
319
320 class PoResourceReader : IResourceReader {
321         Hashtable data;
322         Stream s;
323         int line_num;
324         
325         public PoResourceReader (Stream stream)
326         {
327                 data = new Hashtable ();
328                 s = stream;
329                 Load ();
330         }
331         
332         public virtual void Close ()
333         {
334                 s.Close ();
335         }
336         
337         public IDictionaryEnumerator GetEnumerator()
338         {
339                 return data.GetEnumerator ();
340         }
341         
342         string GetValue (string line)
343         {
344                 int begin = line.IndexOf ('"');
345                 if (begin == -1)
346                         throw new FormatException (String.Format ("No begin quote at line {0}: {1}", line_num, line));
347
348                 int end = line.LastIndexOf ('"');
349                 if (end == -1)
350                         throw new FormatException (String.Format ("No closing quote at line {0}: {1}", line_num, line));
351
352                 return line.Substring (begin + 1, end - begin - 1);
353         }
354         
355         void Load ()
356         {
357                 StreamReader reader = new StreamReader (s);
358                 string line;
359                 string msgid = null;
360                 string msgstr = null;
361                 bool ignoreNext = false;
362
363                 while ((line = reader.ReadLine ()) != null) {
364                         line_num++;
365                         line = line.Trim ();
366                         if (line.Length == 0)
367                                 continue;
368                                 
369                         if (line [0] == '#') {
370                                 if (line.Length == 1 || line [1] != ',')
371                                         continue;
372
373                                 if (line.IndexOf ("fuzzy") != -1) {
374                                         ignoreNext = true;
375                                         if (msgid != null) {
376                                                 if (msgstr == null)
377                                                         throw new FormatException ("Error. Line: " + line_num);
378                                                 data.Add (msgid, msgstr);
379                                                 msgid = null;
380                                                 msgstr = null;
381                                         }
382                                 }
383                                 continue;
384                         }
385                         
386                         if (line.StartsWith ("msgid ")) {
387                                 if (msgid == null && msgstr != null)
388                                         throw new FormatException ("Found 2 consecutive msgid. Line: " + line_num);
389
390                                 if (msgstr != null) {
391                                         if (!ignoreNext)
392                                                 data.Add (msgid, msgstr);
393
394                                         ignoreNext = false;
395                                         msgid = null;
396                                         msgstr = null;
397                                 }
398
399                                 msgid = GetValue (line);
400                                 continue;
401                         }
402
403                         if (line.StartsWith ("msgstr ")) {
404                                 if (msgid == null)
405                                         throw new FormatException ("msgstr with no msgid. Line: " + line_num);
406
407                                 msgstr = GetValue (line);
408                                 continue;
409                         }
410
411                         if (line [0] == '"') {
412                                 if (msgid == null || msgstr == null)
413                                         throw new FormatException ("Invalid format. Line: " + line_num);
414
415                                 msgstr += GetValue (line);
416                                 continue;
417                         }
418
419                         throw new FormatException ("Unexpected data. Line: " + line_num);
420                 }
421
422                 if (msgid != null) {
423                         if (msgstr == null)
424                                 throw new FormatException ("Expecting msgstr. Line: " + line_num);
425
426                         if (!ignoreNext)
427                                 data.Add (msgid, msgstr);
428                 }
429         }
430         
431         IEnumerator IEnumerable.GetEnumerator ()
432         {
433                 return GetEnumerator();
434         }
435
436         void IDisposable.Dispose ()
437         {
438                 if (data != null)
439                         data = null;
440
441                 if (s != null) {
442                         s.Close ();
443                         s = null;
444                 }
445         }
446 }
447
448 class PoResourceWriter : IResourceWriter
449 {
450         TextWriter s;
451         bool headerWritten;
452         
453         public PoResourceWriter (Stream stream)
454         {
455                 s = new StreamWriter (stream);
456         }
457         
458         public void AddResource (string name, byte [] value)
459         {
460                 throw new InvalidOperationException ("Binary data not valid in a po resource file");
461         }
462         
463         public void AddResource (string name, object value)
464         {
465                 if (value is string) {
466                         AddResource (name, (string) value);
467                         return;
468                 }
469                 throw new InvalidOperationException ("Objects not valid in a po resource file");
470         }
471
472         StringBuilder ebuilder = new StringBuilder ();
473         
474         public string Escape (string ns)
475         {
476                 ebuilder.Length = 0;
477
478                 foreach (char c in ns){
479                         switch (c){
480                         case '"':
481                         case '\\':
482                                 ebuilder.Append ('\\');
483                                 ebuilder.Append (c);
484                                 break;
485                         case '\a':
486                                 ebuilder.Append ("\\a");
487                                 break;
488                         case '\n':
489                                 ebuilder.Append ("\\n");
490                                 break;
491                         case '\r':
492                                 ebuilder.Append ("\\r");
493                                 break;
494                         default:
495                                 ebuilder.Append (c);
496                                 break;
497                         }
498                 }
499                 return ebuilder.ToString ();
500         }
501         
502         public void AddResource (string name, string value)
503         {
504                 if (!headerWritten) {
505                         headerWritten = true;
506                         WriteHeader ();
507                 }
508                 
509                 s.WriteLine ("msgid \"{0}\"", Escape (name));
510                 s.WriteLine ("msgstr \"{0}\"", Escape (value));
511                 s.WriteLine ("");
512         }
513         
514         void WriteHeader ()
515         {
516                 s.WriteLine ("msgid \"\"");
517                 s.WriteLine ("msgstr \"\"");
518                 s.WriteLine ("\"MIME-Version: 1.0\\n\"");
519                 s.WriteLine ("\"Content-Type: text/plain; charset=UTF-8\\n\"");
520                 s.WriteLine ("\"Content-Transfer-Encoding: 8bit\\n\"");
521                 s.WriteLine ("\"X-Generator: Mono resgen 0.1\\n\"");
522                 s.WriteLine ("#\"Project-Id-Version: FILLME\\n\"");
523                 s.WriteLine ("#\"POT-Creation-Date: yyyy-MM-dd HH:MM+zzzz\\n\"");
524                 s.WriteLine ("#\"PO-Revision-Date: yyyy-MM-dd HH:MM+zzzz\\n\"");
525                 s.WriteLine ("#\"Last-Translator: FILLME\\n\"");
526                 s.WriteLine ("#\"Language-Team: FILLME\\n\"");
527                 s.WriteLine ("#\"Report-Msgid-Bugs-To: \\n\"");
528                 s.WriteLine ();
529         }
530
531         public void Close ()
532         {
533                 s.Close ();
534         }
535         
536         public void Dispose () { }
537         
538         public void Generate () {}
539 }
540