966bba02bb
Former-commit-id: bb0468d0f257ff100aa895eb5fe583fb5dfbf900
750 lines
18 KiB
C#
750 lines
18 KiB
C#
/*
|
|
* resgen: convert between the resource formats (.txt, .resources, .resx).
|
|
*
|
|
* Copyright (c) 2002 Ximian, Inc
|
|
*
|
|
* Authors:
|
|
* Paolo Molaro (lupus@ximian.com)
|
|
* Gonzalo Paniagua Javier (gonzalo@ximian.com)
|
|
*/
|
|
|
|
using System;
|
|
using System.Globalization;
|
|
using System.Text;
|
|
using System.IO;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Resources;
|
|
using System.Xml;
|
|
|
|
class ResGen {
|
|
|
|
static HashSet<string> symbols = new HashSet<string> ();
|
|
|
|
static void Usage () {
|
|
|
|
string Usage = @"Mono Resource Generator version " + Consts.MonoVersion +
|
|
@"
|
|
Usage:
|
|
resgen source.ext [dest.ext]
|
|
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.";
|
|
|
|
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.
|
|
-define, /define:SYMBOL1,SYMBOL2
|
|
takes a comma-separated list of symbols that control conditional
|
|
inclusion of resources file. The source file needs to be in
|
|
the '.txt' format.
|
|
|
|
Resources enclosed with #ifdef SYMBOL1 ... #endif directives
|
|
will be included in the destination file when SYMBOL1 has
|
|
been specified using /define option.
|
|
|
|
Resources enclosed with #if ! SYMBOL2 ... #endif directives
|
|
will be included only if SYMBOL2 has NOT been specified.";
|
|
Usage += @"
|
|
";
|
|
Console.WriteLine( Usage );
|
|
}
|
|
|
|
static IResourceReader GetReader (Stream stream, string name, bool useSourcePath) {
|
|
string format = Path.GetExtension (name);
|
|
switch (format.ToLower (System.Globalization.CultureInfo.InvariantCulture)) {
|
|
case ".po":
|
|
return new PoResourceReader (stream);
|
|
case ".txt":
|
|
case ".text":
|
|
return new TxtResourceReader (stream, symbols);
|
|
case ".resources":
|
|
return new ResourceReader (stream);
|
|
case ".resx":
|
|
var reader = new ResXResourceReader (stream);
|
|
reader.BasePath = Path.GetDirectoryName (name);
|
|
return reader;
|
|
default:
|
|
throw new Exception ("Unknown format in file " + name);
|
|
}
|
|
}
|
|
|
|
static IResourceWriter GetWriter (Stream stream, string name) {
|
|
string format = Path.GetExtension (name);
|
|
switch (format.ToLower ()) {
|
|
case ".po":
|
|
return new PoResourceWriter (stream);
|
|
case ".txt":
|
|
case ".text":
|
|
return new TxtResourceWriter (stream);
|
|
case ".resources":
|
|
return new ResourceWriter (stream);
|
|
case ".resx":
|
|
return new ResXResourceWriter (stream);
|
|
default:
|
|
throw new Exception ("Unknown format in file " + name);
|
|
}
|
|
}
|
|
|
|
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, useSourcePath);
|
|
|
|
dest = new FileStream (dname, FileMode.Create, FileAccess.Write);
|
|
writer = GetWriter (dest, dname);
|
|
|
|
int rescount = 0;
|
|
foreach (DictionaryEntry e in reader) {
|
|
rescount++;
|
|
object val = e.Value;
|
|
if (val is string)
|
|
writer.AddResource ((string)e.Key, (string)e.Value);
|
|
else
|
|
writer.AddResource ((string)e.Key, e.Value);
|
|
}
|
|
Console.WriteLine( "Read in {0} resources from '{1}'", rescount, sname );
|
|
|
|
reader.Close ();
|
|
writer.Close ();
|
|
Console.WriteLine("Writing resource file... Done.");
|
|
} 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 System.Reflection.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) {
|
|
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 (args [i].StartsWith ("/d:") ||
|
|
args [i].StartsWith ("-d:") ||
|
|
args [i].StartsWith ("/define:") ||
|
|
args [i].StartsWith ("-define:") ||
|
|
args [i].StartsWith ("/D:") ||
|
|
args [i].StartsWith ("-D:") ||
|
|
args [i].StartsWith ("/DEFINE:") ||
|
|
args [i].StartsWith ("-DEFINE:")) {
|
|
|
|
string defines = args [i].Substring (args [i].IndexOf (':') + 1);
|
|
foreach (string s in defines.Split (',') ) {
|
|
symbols.Add(s);
|
|
}
|
|
break;
|
|
}
|
|
|
|
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 {
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (inputFiles.Count == 0) {
|
|
Usage ();
|
|
return 1;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
class TxtResourceWriter : IResourceWriter {
|
|
StreamWriter s;
|
|
|
|
public TxtResourceWriter (Stream stream) {
|
|
s = new StreamWriter (stream);
|
|
}
|
|
|
|
public void AddResource (string name, byte[] value) {
|
|
throw new Exception ("Binary data not valid in a text resource file");
|
|
}
|
|
|
|
public void AddResource (string name, object value) {
|
|
if (value is string) {
|
|
AddResource (name, (string)value);
|
|
return;
|
|
}
|
|
throw new Exception ("Objects not valid in a text resource file");
|
|
}
|
|
|
|
public void AddResource (string name, string value) {
|
|
s.WriteLine ("{0}={1}", name, Escape (value));
|
|
}
|
|
|
|
// \n -> \\n ...
|
|
static string Escape (string value)
|
|
{
|
|
StringBuilder b = new StringBuilder ();
|
|
for (int i = 0; i < value.Length; i++) {
|
|
switch (value [i]) {
|
|
case '\n':
|
|
b.Append ("\\n");
|
|
break;
|
|
case '\r':
|
|
b.Append ("\\r");
|
|
break;
|
|
case '\t':
|
|
b.Append ("\\t");
|
|
break;
|
|
case '\\':
|
|
b.Append ("\\\\");
|
|
break;
|
|
default:
|
|
b.Append (value [i]);
|
|
break;
|
|
}
|
|
}
|
|
return b.ToString ();
|
|
}
|
|
|
|
public void Close () {
|
|
s.Close ();
|
|
}
|
|
|
|
public void Dispose () {}
|
|
|
|
public void Generate () {}
|
|
}
|
|
|
|
class TxtResourceReader : IResourceReader {
|
|
Hashtable data;
|
|
Stream s;
|
|
HashSet <String> defines;
|
|
|
|
public TxtResourceReader (Stream stream, IEnumerable<string> symbols) {
|
|
data = new Hashtable ();
|
|
s = stream;
|
|
|
|
defines = new HashSet<String> (symbols);
|
|
Load ();
|
|
}
|
|
|
|
public virtual void Close () {
|
|
}
|
|
|
|
public IDictionaryEnumerator GetEnumerator() {
|
|
return data.GetEnumerator ();
|
|
}
|
|
|
|
static string NextWord(ref string line) {
|
|
int i, j;
|
|
string keywd;
|
|
line = line.TrimStart ();
|
|
for (i = 0; i < line.Length && !Char.IsWhiteSpace (line [i]) && line [i] != ';'; i++ );
|
|
|
|
if (i < line.Length) {
|
|
for (j = i; j < line.Length && Char.IsWhiteSpace (line [j]) && line [j] != ';'; j++ );
|
|
|
|
keywd = line.Substring (0, i);
|
|
line = line.Substring (j).TrimStart ();
|
|
} else {
|
|
keywd = line;
|
|
line = "";
|
|
}
|
|
return keywd;
|
|
}
|
|
|
|
void Load () {
|
|
StreamReader reader = new StreamReader (s);
|
|
string line, key, val;
|
|
Stack<bool> conditional = new Stack<bool> (5);
|
|
int epos, line_num = 0;
|
|
|
|
conditional.Push(true);
|
|
while ((line = reader.ReadLine ()) != null) {
|
|
line_num++;
|
|
line = line.Trim ();
|
|
|
|
if (line.Length == 0 || line [0] == ';')
|
|
continue;
|
|
|
|
if (line [0] == '#') {
|
|
bool stat;
|
|
bool neg = false;
|
|
string keywd, symbol;
|
|
|
|
line = line.Substring (1);
|
|
keywd = NextWord (ref line).ToLower ();
|
|
symbol = "";
|
|
|
|
if (line.Length > 0) {
|
|
if (line[0] == '!') {
|
|
line = line.Substring (1);
|
|
neg = true;
|
|
}
|
|
symbol = NextWord (ref line);
|
|
}
|
|
|
|
switch (keywd) {
|
|
case "endif":
|
|
case "else":
|
|
stat = conditional.Pop ();
|
|
if (conditional.Count < 1)
|
|
throw new Exception (String.Format ("Found an #{0} without matching #ifdef", keywd));
|
|
|
|
if (keywd == "else")
|
|
conditional.Push (conditional.Peek () && !stat);
|
|
break;
|
|
|
|
case "ifdef":
|
|
case "if":
|
|
if (symbol.Length == 0)
|
|
throw new Exception (String.Format ("Missing symbol after {0}", keywd));
|
|
stat = defines.Contains (symbol);
|
|
if (neg)
|
|
stat = !stat;
|
|
|
|
conditional.Push (conditional.Peek () && stat);
|
|
break;
|
|
|
|
}
|
|
continue;
|
|
}
|
|
if (conditional.Peek () == false)
|
|
continue;
|
|
|
|
epos = line.IndexOf ('=');
|
|
if (epos < 0)
|
|
throw new Exception ("Invalid format at line " + line_num);
|
|
key = line.Substring (0, epos);
|
|
val = line.Substring (epos + 1);
|
|
key = key.Trim ();
|
|
val = val.Trim ();
|
|
if (key.Length == 0)
|
|
throw new Exception ("Key is empty at line " + line_num);
|
|
|
|
val = Unescape (val);
|
|
if (val == null)
|
|
throw new Exception (String.Format ("Unsupported escape character in value of key '{0}'.", key));
|
|
|
|
|
|
data.Add (key, val);
|
|
}
|
|
if (conditional.Count > 1)
|
|
throw new Exception ("Found an #ifdef but not a matching #endif before reaching the end of the file.");
|
|
}
|
|
|
|
// \\n -> \n ...
|
|
static string Unescape (string value)
|
|
{
|
|
StringBuilder b = new StringBuilder ();
|
|
|
|
for (int i = 0; i < value.Length; i++) {
|
|
if (value [i] == '\\') {
|
|
if (i == value.Length - 1)
|
|
return null;
|
|
|
|
i++;
|
|
switch (value [i]) {
|
|
case 'n':
|
|
b.Append ('\n');
|
|
break;
|
|
case 'r':
|
|
b.Append ('\r');
|
|
break;
|
|
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;
|
|
default:
|
|
return null;
|
|
}
|
|
|
|
} else {
|
|
b.Append (value [i]);
|
|
}
|
|
}
|
|
|
|
return b.ToString ();
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator () {
|
|
return ((IResourceReader) this).GetEnumerator();
|
|
}
|
|
|
|
void IDisposable.Dispose () {}
|
|
}
|
|
|
|
class PoResourceReader : IResourceReader {
|
|
Hashtable data;
|
|
Stream s;
|
|
int line_num;
|
|
|
|
public PoResourceReader (Stream stream)
|
|
{
|
|
data = new Hashtable ();
|
|
s = stream;
|
|
Load ();
|
|
}
|
|
|
|
public virtual void Close ()
|
|
{
|
|
s.Close ();
|
|
}
|
|
|
|
public IDictionaryEnumerator GetEnumerator()
|
|
{
|
|
return data.GetEnumerator ();
|
|
}
|
|
|
|
string GetValue (string line)
|
|
{
|
|
int begin = line.IndexOf ('"');
|
|
if (begin == -1)
|
|
throw new FormatException (String.Format ("No begin quote at line {0}: {1}", line_num, line));
|
|
|
|
int end = line.LastIndexOf ('"');
|
|
if (end == -1)
|
|
throw new FormatException (String.Format ("No closing quote at line {0}: {1}", line_num, line));
|
|
|
|
return line.Substring (begin + 1, end - begin - 1);
|
|
}
|
|
|
|
void Load ()
|
|
{
|
|
StreamReader reader = new StreamReader (s);
|
|
string line;
|
|
string msgid = null;
|
|
string msgstr = null;
|
|
bool ignoreNext = false;
|
|
|
|
while ((line = reader.ReadLine ()) != null) {
|
|
line_num++;
|
|
line = line.Trim ();
|
|
if (line.Length == 0)
|
|
continue;
|
|
|
|
if (line [0] == '#') {
|
|
if (line.Length == 1 || line [1] != ',')
|
|
continue;
|
|
|
|
if (line.IndexOf ("fuzzy") != -1) {
|
|
ignoreNext = true;
|
|
if (msgid != null) {
|
|
if (msgstr == null)
|
|
throw new FormatException ("Error. Line: " + line_num);
|
|
data.Add (msgid, msgstr);
|
|
msgid = null;
|
|
msgstr = null;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (line.StartsWith ("msgid ")) {
|
|
if (msgid == null && msgstr != null)
|
|
throw new FormatException ("Found 2 consecutive msgid. Line: " + line_num);
|
|
|
|
if (msgstr != null) {
|
|
if (!ignoreNext)
|
|
data.Add (msgid, msgstr);
|
|
|
|
ignoreNext = false;
|
|
msgid = null;
|
|
msgstr = null;
|
|
}
|
|
|
|
msgid = GetValue (line);
|
|
continue;
|
|
}
|
|
|
|
if (line.StartsWith ("msgstr ")) {
|
|
if (msgid == null)
|
|
throw new FormatException ("msgstr with no msgid. Line: " + line_num);
|
|
|
|
msgstr = GetValue (line);
|
|
continue;
|
|
}
|
|
|
|
if (line [0] == '"') {
|
|
if (msgid == null || msgstr == null)
|
|
throw new FormatException ("Invalid format. Line: " + line_num);
|
|
|
|
msgstr += GetValue (line);
|
|
continue;
|
|
}
|
|
|
|
throw new FormatException ("Unexpected data. Line: " + line_num);
|
|
}
|
|
|
|
if (msgid != null) {
|
|
if (msgstr == null)
|
|
throw new FormatException ("Expecting msgstr. Line: " + line_num);
|
|
|
|
if (!ignoreNext)
|
|
data.Add (msgid, msgstr);
|
|
}
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator ()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
|
|
void IDisposable.Dispose ()
|
|
{
|
|
if (data != null)
|
|
data = null;
|
|
|
|
if (s != null) {
|
|
s.Close ();
|
|
s = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
class PoResourceWriter : IResourceWriter
|
|
{
|
|
TextWriter s;
|
|
bool headerWritten;
|
|
|
|
public PoResourceWriter (Stream stream)
|
|
{
|
|
s = new StreamWriter (stream);
|
|
}
|
|
|
|
public void AddResource (string name, byte [] value)
|
|
{
|
|
throw new InvalidOperationException ("Binary data not valid in a po resource file");
|
|
}
|
|
|
|
public void AddResource (string name, object value)
|
|
{
|
|
if (value is string) {
|
|
AddResource (name, (string) value);
|
|
return;
|
|
}
|
|
throw new InvalidOperationException ("Objects not valid in a po resource file");
|
|
}
|
|
|
|
StringBuilder ebuilder = new StringBuilder ();
|
|
|
|
public string Escape (string ns)
|
|
{
|
|
ebuilder.Length = 0;
|
|
|
|
foreach (char c in ns){
|
|
switch (c){
|
|
case '"':
|
|
case '\\':
|
|
ebuilder.Append ('\\');
|
|
ebuilder.Append (c);
|
|
break;
|
|
case '\a':
|
|
ebuilder.Append ("\\a");
|
|
break;
|
|
case '\n':
|
|
ebuilder.Append ("\\n");
|
|
break;
|
|
case '\r':
|
|
ebuilder.Append ("\\r");
|
|
break;
|
|
default:
|
|
ebuilder.Append (c);
|
|
break;
|
|
}
|
|
}
|
|
return ebuilder.ToString ();
|
|
}
|
|
|
|
public void AddResource (string name, string value)
|
|
{
|
|
if (!headerWritten) {
|
|
headerWritten = true;
|
|
WriteHeader ();
|
|
}
|
|
|
|
s.WriteLine ("msgid \"{0}\"", Escape (name));
|
|
s.WriteLine ("msgstr \"{0}\"", Escape (value));
|
|
s.WriteLine ("");
|
|
}
|
|
|
|
void WriteHeader ()
|
|
{
|
|
s.WriteLine ("msgid \"\"");
|
|
s.WriteLine ("msgstr \"\"");
|
|
s.WriteLine ("\"MIME-Version: 1.0\\n\"");
|
|
s.WriteLine ("\"Content-Type: text/plain; charset=UTF-8\\n\"");
|
|
s.WriteLine ("\"Content-Transfer-Encoding: 8bit\\n\"");
|
|
s.WriteLine ("\"X-Generator: Mono resgen 0.1\\n\"");
|
|
s.WriteLine ("#\"Project-Id-Version: FILLME\\n\"");
|
|
s.WriteLine ("#\"POT-Creation-Date: yyyy-MM-dd HH:MM+zzzz\\n\"");
|
|
s.WriteLine ("#\"PO-Revision-Date: yyyy-MM-dd HH:MM+zzzz\\n\"");
|
|
s.WriteLine ("#\"Last-Translator: FILLME\\n\"");
|
|
s.WriteLine ("#\"Language-Team: FILLME\\n\"");
|
|
s.WriteLine ("#\"Report-Msgid-Bugs-To: \\n\"");
|
|
s.WriteLine ();
|
|
}
|
|
|
|
public void Close ()
|
|
{
|
|
s.Close ();
|
|
}
|
|
|
|
public void Dispose () { }
|
|
|
|
public void Generate () {}
|
|
}
|
|
|
|
class ResourceInfo
|
|
{
|
|
public string InputFile;
|
|
public string OutputFile;
|
|
}
|