*/
using System;
+using System.Globalization;
using System.Text;
using System.IO;
using System.Collections;
using System.Resources;
using System.Reflection;
+using System.Xml;
class ResGen {
}
static void Usage () {
- string Usage = @"Mono Resource Generator version 0.1
+
+ string Usage = @"Mono Resource Generator version " + Consts.MonoVersion +
+ @"
Usage:
resgen source.ext [dest.ext]
- resgen /compile source.ext[,dest.resources] [...]
+ resgen [options] /compile source.ext[,dest.resources] [...]";
+ Usage += @"
Convert a resource file from one format to another.
The currently supported formats are: '.txt' '.resources' '.resx' '.po'.
-If the destination file is not specified, source.resources will be used.
-The /compile option takes a list of .resX or .txt files to convert to
-.resources files in one bulk operation, replacing .ext with .resources for
-the output file name.
+If the destination file is not specified, source.resources will be used.";
+
+ Usage += @"
+
+Options:
+-compile, /compile
+ takes a list of .resX or .txt files to convert to .resources files
+ in one bulk operation, replacing .ext with .resources for the
+ output file name (if not set).
+-usesourcepath, /useSourcePath
+ to resolve relative file paths, use the directory of the resource
+ file as current directory.";
+ Usage += @"
";
Console.WriteLine( Usage );
}
- static IResourceReader GetReader (Stream stream, string name) {
+ static IResourceReader GetReader (Stream stream, string name, bool useSourcePath) {
string format = Path.GetExtension (name);
- switch (format.ToLower ()) {
+ switch (format.ToLower (System.Globalization.CultureInfo.InvariantCulture)) {
case ".po":
return new PoResourceReader (stream);
case ".txt":
return new ResourceReader (stream);
case ".resx":
LoadResX ();
- return (IResourceReader)Activator.CreateInstance (resxr, new object[] {stream});
+ IResourceReader reader = (IResourceReader) Activator.CreateInstance (
+ resxr, new object[] {stream});
+ if (useSourcePath) { // only possible on 2.0 profile, or higher
+ PropertyInfo p = reader.GetType ().GetProperty ("BasePath",
+ BindingFlags.Public | BindingFlags.Instance);
+ if (p != null && p.CanWrite) {
+ p.SetValue (reader, Path.GetDirectoryName (name), null);
+ }
+ }
+ return reader;
default:
throw new Exception ("Unknown format in file " + name);
}
}
}
- static int CompileResourceFile(string sname, string dname ) {
- FileStream source, dest;
- IResourceReader reader;
- IResourceWriter writer;
+ static int CompileResourceFile (string sname, string dname, bool useSourcePath) {
+ FileStream source = null;
+ FileStream dest = null;
+ IResourceReader reader = null;
+ IResourceWriter writer = null;
try {
source = new FileStream (sname, FileMode.Open, FileAccess.Read);
-
- reader = GetReader (source, sname);
+ reader = GetReader (source, sname, useSourcePath);
dest = new FileStream (dname, FileMode.Create, FileAccess.Write);
writer = GetWriter (dest, dname);
} catch (Exception e) {
Console.WriteLine ("Error: {0}", e.Message);
Exception inner = e.InnerException;
+
+ // under 2.0 ResXResourceReader can wrap an exception into an XmlException
+ // and this hides some helpful message from the original exception
+ XmlException xex = (inner as XmlException);
+ if (xex != null) {
+ // message is identical to the inner exception (from MWF ResXResourceReader)
+ Console.WriteLine ("Position: Line {0}, Column {1}.", xex.LineNumber, xex.LinePosition);
+ inner = inner.InnerException;
+ }
+
+ if (inner is TargetInvocationException && inner.InnerException != null)
+ inner = inner.InnerException;
if (inner != null)
Console.WriteLine ("Inner exception: {0}", inner.Message);
+
+ if (reader != null)
+ reader.Dispose ();
+ if (source != null)
+ source.Close ();
+ if (writer != null)
+ writer.Dispose ();
+ if (dest != null)
+ dest.Close ();
+
+ // since we're not first reading all entries in source, we may get a
+ // read failure after we're started writing to the destination file
+ // and leave behind a broken resources file, so remove it here
+ try {
+ File.Delete (dname);
+ } catch {
+ }
return 1;
}
return 0;
}
static int Main (string[] args) {
- string sname = "", dname = "";
- if ((int) args.Length < 1 || args[0] == "-h" || args[0] == "-?" || args[0] == "/h" || args[0] == "/?") {
- Usage();
- return 1;
- }
- if (args[0] == "/compile" || args[0] == "-compile") {
- for ( int i=1; i< args.Length; i++ ) {
- if ( args[i].IndexOf(",") != -1 ){
- string[] pair = args[i].Split(',');
- sname = pair[0];
- dname = pair[1];
- if (dname == ""){
- Console.WriteLine(@"error: You must specify an input & outfile file name like this:");
- Console.WriteLine("inFile.txt,outFile.resources." );
- Console.WriteLine("You passed in '{0}'.", args[i] );
+ bool compileMultiple = false;
+ bool useSourcePath = false;
+ ArrayList inputFiles = new ArrayList ();
+
+ for (int i = 0; i < args.Length; i++) {
+ switch (args [i].ToLower ()) {
+ case "-h":
+ case "/h":
+ case "-?":
+ case "/?":
+ Usage ();
+ return 1;
+ case "/compile":
+ case "-compile":
+ if (inputFiles.Count > 0) {
+ // the /compile option should be specified before any files
+ Usage ();
+ return 1;
+ }
+ compileMultiple = true;
+ break;
+
+ case "/usesourcepath":
+ case "-usesourcepath":
+ if (compileMultiple) {
+ // the /usesourcepath option should not appear after the
+ // /compile switch on the command-line
+ Console.WriteLine ("ResGen : error RG0000: Invalid "
+ + "command line syntax. Switch: \"/compile\" Bad value: "
+ + args [i] + ". Use ResGen /? for usage information.");
+ return 1;
+ }
+ useSourcePath = true;
+ break;
+
+ default:
+ if (!IsFileArgument (args [i])) {
+ Usage ();
+ return 1;
+ }
+
+ ResourceInfo resInf = new ResourceInfo ();
+ if (compileMultiple) {
+ string [] pair = args [i].Split (',');
+ switch (pair.Length) {
+ case 1:
+ resInf.InputFile = Path.GetFullPath (pair [0]);
+ resInf.OutputFile = Path.ChangeExtension (resInf.InputFile,
+ "resources");
+ break;
+ case 2:
+ if (pair [1].Length == 0) {
+ Console.WriteLine (@"error: You must specify an input & outfile file name like this:");
+ Console.WriteLine ("inFile.txt,outFile.resources.");
+ Console.WriteLine ("You passed in '{0}'.", args [i]);
+ return 1;
+ }
+ resInf.InputFile = Path.GetFullPath (pair [0]);
+ resInf.OutputFile = Path.GetFullPath (pair [1]);
+ break;
+ default:
+ Usage ();
return 1;
}
} else {
- sname = args[i];
- dname = Path.ChangeExtension (sname, "resources");
- }
- int ret = CompileResourceFile( sname, dname );
- if (ret != 0 ) {
- return ret;
+ if ((i + 1) < args.Length) {
+ resInf.InputFile = Path.GetFullPath (args [i]);
+ // move to next arg, since we assume that one holds
+ // the name of the output file
+ i++;
+ resInf.OutputFile = Path.GetFullPath (args [i]);
+ } else {
+ resInf.InputFile = Path.GetFullPath (args [i]);
+ resInf.OutputFile = Path.ChangeExtension (resInf.InputFile,
+ "resources");
+ }
}
+ inputFiles.Add (resInf);
+ break;
}
- return 0;
-
}
- else if (args.Length == 1) {
- sname = args [0];
- dname = Path.ChangeExtension (sname, "resources");
- } else if (args.Length != 2) {
+
+ if (inputFiles.Count == 0) {
Usage ();
return 1;
- } else {
- sname = args [0];
- dname = args [1];
- }
- return CompileResourceFile( sname, dname );
+ }
+
+ foreach (ResourceInfo res in inputFiles) {
+ int ret = CompileResourceFile (res.InputFile, res.OutputFile, useSourcePath);
+ if (ret != 0 )
+ return ret;
+ }
+ return 0;
+ }
+
+ private static bool RunningOnUnix {
+ get {
+ // check for Unix platforms - see FAQ for more details
+ // http://www.mono-project.com/FAQ:_Technical#How_to_detect_the_execution_platform_.3F
+ int platform = (int) Environment.OSVersion.Platform;
+ return ((platform == 4) || (platform == 128) || (platform == 6));
+ }
+ }
+
+ private static bool IsFileArgument (string arg)
+ {
+ if ((arg [0] != '-') && (arg [0] != '/'))
+ return true;
+
+ // cope with absolute filenames for resx files on unix, as
+ // they also match the option pattern
+ //
+ // `/home/test.resx' is considered as a resx file, however
+ // '/test.resx' is considered as error
+ return (RunningOnUnix && arg.Length > 2 && arg.IndexOf ('/', 2) != -1);
}
}
case 't':
b.Append ('\t');
break;
+ case 'u':
+ int ch = int.Parse (value.Substring (++i, 4), NumberStyles.HexNumber);
+ b.Append (char.ConvertFromUtf32 (ch));
+ i += 3;
+ break;
case '\\':
b.Append ('\\');
break;
public void Generate () {}
}
+class ResourceInfo
+{
+ public string InputFile;
+ public string OutputFile;
+}