/* ******************************************************************************* * * Copyright (C) 2006, International Business Machines * Corporation and others. All Rights Reserved. * ******************************************************************************* */ using System; using System.Collections; using System.IO; using System.Diagnostics; using System.Text.RegularExpressions; namespace icu { /// /// This tool generates a C# wrapper around ICU4C code /// class GenICUWrapper { static string popen(string command, string arguments) { Process p = new Process(); StreamWriter sw; StreamReader sr; StreamReader err; ProcessStartInfo psI = new ProcessStartInfo(command, arguments); psI.UseShellExecute = false; psI.RedirectStandardInput = true; psI.RedirectStandardOutput = true; psI.RedirectStandardError = true; psI.CreateNoWindow = true; p.StartInfo = psI; p.Start(); sw = p.StandardInput; sr = p.StandardOutput; err = p.StandardError; sw.AutoFlush = true; sw.Close(); p.Close(); return sr.ReadToEnd(); } static private string ReplaceAll(string source, string from, string to) { Regex replacer = new Regex(from, RegexOptions.Singleline); return replacer.Replace(source, to); } //"(typedef\\s+(struct|enum)([^{]+\\{[^}]+\\}|\\s+\\w\\s+)\\w;|(\\bU_STABLE\\b|\\btypedef\\b|\\bstruct\\b|\\benum\\b)[^;]+;)", RegexOptions.Compiled | RegexOptions.Singleline); private static Regex stableHeadersRegEx = new Regex("(\\bU_STABLE\\b[^;]+;|\\benum\\b\\s*\\w+\\s*\\{[^;]*;|\\bstruct\\s+\\w+|typedef\\s+(const\\s*)?void\\s*\\*?\\s+\\w+\\s*;|typedef\\s+enum\\s*\\{[^}]+\\}[^;]*;)", RegexOptions.Compiled | RegexOptions.Singleline); private static Regex isFunctionRegEx = new Regex("\\bU_STABLE\\b[^;]+;", RegexOptions.Compiled | RegexOptions.Singleline); private static Regex endOfEnumRegEx = new Regex("\\w+\\s*;$", RegexOptions.Compiled | RegexOptions.Singleline); private static ArrayList definedStructs = new ArrayList(); private static string versionSuffix = ""; private static string[] unusableTypesCS = { "[", "...", "va_list ", "UNESCAPE_CHAR_AT ", "UMtx", "UMemAllocFn", "UTraceEntry", "u_cleanup", "UEnumCharNamesFn", "UCharEnumTypeRange", "UConverterToUCallback", "UConverterFromUCallback", "UConverterToUnicodeArgs", "UConverterFromUnicodeArgs", "UDataInfo", "UDataMemoryIsAcceptable", "u_nl_catd" }; private struct RegexToMatch { public Regex from; public string to; public RegexToMatch(string from, string to) { this.from = new Regex(from, RegexOptions.Singleline); this.to = to; } } private static RegexToMatch[] typeMap = { new RegexToMatch("\\bconst\\s?", ""), new RegexToMatch("\\(\\s*void\\s*\\)", "()"), // Remove C# keywords. new RegexToMatch("\\block\\b", "lock_"), new RegexToMatch("\\bbase\\b", "base_"), new RegexToMatch("\\bvalue\\b", "val"), new RegexToMatch("\\bstring\\b", "str"), // Convert to C# types new RegexToMatch("\\buint8_t\\b", "byte"), new RegexToMatch("\\buint16_t\\b", "ushort"), new RegexToMatch("\\buint32_t\\b", "uint"), new RegexToMatch("\\buint64_t\\b", "ulong"), new RegexToMatch("\\bint8_t\\b", "sbyte"), new RegexToMatch("\\bint16_t\\b", "short"), new RegexToMatch("\\bint32_t\\b", "int"), new RegexToMatch("\\bint64_t\\b", "long"), new RegexToMatch("\\bUBool\\b", "bool"), new RegexToMatch("\\bchar\\b", "byte"), new RegexToMatch("\\bUChar\\b", "char"), new RegexToMatch("\\bwchar_t\\b", "char"), new RegexToMatch("\\bUChar32\\b", "int"), new RegexToMatch("\\bUDate\\b", "double"), new RegexToMatch("\\s*UErrorCode\\s*\\*\\s*", " ref UErrorCode "), new RegexToMatch("\\bUBiDiLevel\\b", "byte"), new RegexToMatch("\\bUVersionInfo\\b", "int"), new RegexToMatch("\\bUCollationStrength\\b", "UColAttributeValue"), new RegexToMatch("\\s*\\(\\s*", "("), new RegexToMatch("\\s*\\)\\s*", ")"), }; static private bool ContainsUnusableType(string[] unusableTypes, string line) { for (int idx = 0; idx < unusableTypes.Length; idx++) { if (line.IndexOf(unusableTypes[idx]) >= 0) { return true; } } return false; } static private string ReplaceTypes(string lineToConvert) { for (int idx = 0; idx < typeMap.Length; idx++) { lineToConvert = typeMap[idx].from.Replace(lineToConvert, typeMap[idx].to); } return lineToConvert; } static private string GetFunctionName(string lineToParse) { int end = lineToParse.IndexOf("("); if (end < 0) { throw new FormatException("Parse Exception: Line does not contain a function name.\nline=" + lineToParse); } int beginning = lineToParse.LastIndexOf(" ", end); if (beginning < 0) { throw new FormatException("Parse Exception: Line does not contain a function name.\nline=" + lineToParse); } string retVal = lineToParse.Substring(beginning + 1, end - beginning - 1); return retVal; } static private string GenerateCS(string processedHeader, string libName) { string parsedHeader; string functionName = ""; if (processedHeader.IndexOf("class") >= 0) { // We can't use unmanaged C++ classes in C# right now. return "//C++\n"; } else { parsedHeader = "//C\n"; } foreach (Match matchedItem in stableHeadersRegEx.Matches(processedHeader)) { string matchedString = matchedItem.Value; string unsafeKeyword; if (isFunctionRegEx.IsMatch(matchedString)) { matchedString = ReplaceAll(matchedString, "\\bU_EXPORT2\\b", ""); matchedString = ReplaceAll(matchedString, "\\s\\s+", " "); if (ContainsUnusableType(unusableTypesCS, matchedString)) { parsedHeader += "// " + matchedString + "\n"; } else { matchedString = ReplaceTypes(matchedString); if (matchedString.IndexOf("*") >= 0 || matchedString.IndexOf("u_cleanup") >= 0) { unsafeKeyword = " unsafe"; } else { unsafeKeyword = ""; } if (versionSuffix.Length > 0) { functionName = ", EntryPoint=\""+GetFunctionName(matchedString)+versionSuffix+"\""; } matchedString = ReplaceAll(matchedString, "\\bU_STABLE\\b", "public static extern" + unsafeKeyword); parsedHeader += "[DllImport(\""+libName+"\""+functionName+")]\n" + matchedString + "\n"; } } else if (matchedString.IndexOf("typedef enum ") >= 0) { matchedString = ReplaceAll(matchedString, "typedef ", ""); matchedString = ReplaceAll(matchedString, ",\\s*", ",\n"); matchedString = ReplaceAll(matchedString, "\\{\\s*", "{\n"); string enumName = endOfEnumRegEx.Match(matchedString).Value; enumName = ReplaceAll(enumName, "\\s*;", ""); matchedString = ReplaceAll(matchedString, "enum", "enum " + enumName); matchedString = endOfEnumRegEx.Replace(matchedString, ""); parsedHeader += "public " + matchedString + "\n"; } else if (matchedString.IndexOf("enum ") >= 0) { matchedString = ReplaceAll(matchedString, "\\}\\s*\\w+\\s*;", "};"); matchedString = ReplaceAll(matchedString, ",\\s*", ",\n"); matchedString = ReplaceAll(matchedString, "\\{\\s*", "{\n"); matchedString = ReplaceAll(matchedString, "\\bINT32_MAX\\b", "int.MaxValue"); parsedHeader += "public " + matchedString + "\n"; } else if (matchedString.IndexOf("struct ") >= 0) { if (definedStructs.Contains(matchedString)) { continue; } definedStructs.Add(matchedString); parsedHeader += "[StructLayout(LayoutKind.Sequential)]\npublic " + matchedString + " {}\n"; } else if (matchedString.IndexOf("typedef ") >= 0) { definedStructs.Add(matchedString); matchedString = ReplaceAll(matchedString, "typedef\\s+(const\\s*)?void\\s*\\*?\\s+", "struct "); matchedString = ReplaceAll(matchedString, ";", ""); if (definedStructs.Contains(matchedString)) { continue; } definedStructs.Add(matchedString); parsedHeader += "[StructLayout(LayoutKind.Sequential)]\npublic " + matchedString + " {}\n"; } else { Console.WriteLine("{0} is an unknown match", matchedString); Environment.Exit(-1); } } return parsedHeader; } static private void printHelp() { Console.WriteLine("\t--classname Name of class"); Console.WriteLine("\t--libname Name of library"); Console.WriteLine("\t--suffix Suffix used on each function"); Console.WriteLine("\t--include The directory containing the ICU headers"); } /// /// The main entry point for the application. /// [STAThread] static int Main(string[] args) { string includePath = null; string className = null; string libName = null; for (int idx = 0; idx < args.Length; idx++) { if (args[idx].Equals("--classname")) { if (idx + 1 >= args.Length) { Console.WriteLine("--classname is missing required argument"); Environment.Exit(-1); } className = args[++idx]; } else if (args[idx].Equals("--libname")) { if (idx + 1 >= args.Length) { Console.WriteLine("--libname is missing required argument"); Environment.Exit(-1); } libName = args[++idx]; } else if (args[idx].Equals("--include")) { if (idx + 1 >= args.Length) { Console.WriteLine("--include is missing required argument"); Environment.Exit(-1); } includePath = args[++idx]; } else if (args[idx].Equals("--suffix")) { if (idx + 1 >= args.Length) { Console.WriteLine("--suffix is missing required argument"); Environment.Exit(-1); } versionSuffix = args[++idx]; } } if (className == null || includePath == null || libName == null) { Console.WriteLine("Required arguments are missing."); printHelp(); Environment.Exit(-1); } if (!Directory.Exists(includePath)) { Console.WriteLine("{0} is not a directory.", includePath); return -1; } bool fileParsed = false; Console.WriteLine("using System.Runtime.InteropServices;"); Console.WriteLine("namespace icu {"); Console.WriteLine("class "+className+" {"); foreach (string header in Directory.GetFiles(includePath, "*.h")) { string processedHeader; fileParsed = true; Console.WriteLine("#region {0}", header); try { processedHeader = popen("cpp", "-P -nostdinc -x c " + header); } catch (SystemException) { Console.WriteLine("Can't preprocess {0}.\nIs the Cygwin preprocessor in your PATH?", header); return -1; } Console.Write(GenerateCS(processedHeader, libName)); Console.WriteLine("#endregion"); } if (!fileParsed) { Console.WriteLine("//No headers found."); } Console.WriteLine("}"); Console.WriteLine("}"); return 0; } } }