Skip to content

Instantly share code, notes, and snippets.

@thekid
Created June 13, 2011 00:01
Show Gist options
  • Save thekid/1022149 to your computer and use it in GitHub Desktop.
Save thekid/1022149 to your computer and use it in GitHub Desktop.
XP Framework: Unicode revamp
diff --git a/unix/src/exec.in b/unix/src/exec.in
index 05efad0..d97a586 100644
--- a/unix/src/exec.in
+++ b/unix/src/exec.in
@@ -50,6 +50,12 @@ if [ ! -d /proc/1 ] ; then
export XP_CMDLINE
fi
#endif
+LC_CONSOLE=${LANG#*\.}
+LC_CONSOLE="${LC_CONSOLE} ${LC_CONSOLE} ${LC_CONSOLE} "
+if [ -t 0 ] ; then LC_CONSOLE=${LC_CONSOLE}0 ; else LC_CONSOLE=${LC_CONSOLE}1; fi
+if [ -t 1 ] ; then LC_CONSOLE=${LC_CONSOLE}0 ; else LC_CONSOLE=${LC_CONSOLE}1; fi
+if [ -t 2 ] ; then LC_CONSOLE=${LC_CONSOLE}0 ; else LC_CONSOLE=${LC_CONSOLE}1; fi
export XP_RT
+export LC_CONSOLE
IFS="|"
${XP_RT}${ifs}${args}${ifs}$tool ${ARGS} "$@"
diff --git a/windows/src/CompositeConfigSource.cs b/windows/src/CompositeConfigSource.cs
index f204daa..68657b5 100644
--- a/windows/src/CompositeConfigSource.cs
+++ b/windows/src/CompositeConfigSource.cs
@@ -42,6 +42,19 @@ namespace Net.XpFramework.Runner
}
/// <summary>
+ /// Returns whether the PHP runtime supports wmain()
+ /// </summary>
+ public bool? GetWMain()
+ {
+ foreach (XpConfigSource source in this.sources)
+ {
+ bool? wmain = source.GetWMain();
+ if (wmain != null) return wmain;
+ }
+ return null;
+ }
+
+ /// <summary>
/// Returns the PHP runtime arguments to be used from this config source
/// </summary>
public Dictionary<string, IEnumerable<string>> GetArgs()
diff --git a/windows/src/EnvironmentConfigSource.cs b/windows/src/EnvironmentConfigSource.cs
index 56e8b24..cf9d96c 100644
--- a/windows/src/EnvironmentConfigSource.cs
+++ b/windows/src/EnvironmentConfigSource.cs
@@ -28,6 +28,14 @@ namespace Net.XpFramework.Runner
}
/// <summary>
+ /// Returns whether the PHP runtime supports wmain()
+ /// </summary>
+ public bool? GetWMain()
+ {
+ return null;
+ }
+
+ /// <summary>
/// Returns the PHP runtime arguments to be used from this config source
/// </summary>
public Dictionary<string, IEnumerable<string>> GetArgs()
diff --git a/windows/src/Executor.cs b/windows/src/Executor.cs
index 4126ed1..6322e98 100755
--- a/windows/src/Executor.cs
+++ b/windows/src/Executor.cs
@@ -3,14 +3,43 @@ using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
+using System.Runtime.InteropServices;
+
namespace Net.XpFramework.Runner
{
+ delegate string ArgProcessor(string arg);
+
class Executor
{
private static string PATH_SEPARATOR = new string(new char[] { Path.PathSeparator});
private static List<string> EMPTY_LIST = new List<string>();
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ internal static extern IntPtr GetStdHandle(int nStdHandle);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ internal static extern bool GetConsoleMode(IntPtr hConsoleHandle, out int mode);
+
+ internal enum StandardHandles
+ {
+ IN = -10,
+ OUT = -11,
+ ERR = -12
+ }
+
+ public static bool IsRedirect(StandardHandles id)
+ {
+ int mode;
+
+ if (!GetConsoleMode(GetStdHandle((int)id), out mode))
+ {
+ throw new IOException("GetConsoleMode(" + id + ") failed");
+ }
+
+ return 0 == mode;
+ }
+
/// <summary>
///
/// </summary>
@@ -33,6 +62,7 @@ namespace Net.XpFramework.Runner
);
IEnumerable<string> use_xp = configs.GetUse();
string executor = configs.GetRuntime() ?? "php";
+ bool wmain = configs.GetWMain() ?? false;
if (null == use_xp) {
throw new EntryPointNotFoundException("Cannot determine use_xp setting from " + configs);
@@ -54,19 +84,33 @@ namespace Net.XpFramework.Runner
String.Join(PATH_SEPARATOR, includes)
);
- // If input or output encoding are not equal to default, also pass their
- // names inside an LC_CONSOLE environment variable. Only do this inside
- // real Windows console windows!
+ // Pass input,output,default encodings and whether stdin,out and err are redirects
+ // or not via LC_CONSOLE. Only do this inside real Windows console windows, for
+ // Cygwin, this works quite differently!
//
// See http://msdn.microsoft.com/en-us/library/system.text.encoding.headername.aspx
// and http://msdn.microsoft.com/en-us/library/system.text.encoding.aspx
if (null == Environment.GetEnvironmentVariable("TERM"))
{
Encoding defaultEncoding = Encoding.Default;
- if (!defaultEncoding.Equals(Console.InputEncoding) || !defaultEncoding.Equals(Console.OutputEncoding))
- {
- Environment.SetEnvironmentVariable("LC_CONSOLE", Console.InputEncoding.HeaderName + "," + Console.OutputEncoding.HeaderName);
- }
+ Environment.SetEnvironmentVariable("LC_CONSOLE", String.Format(
+ "{0} {1} {2} {3}{4}{5}",
+ Console.InputEncoding.HeaderName,
+ Console.OutputEncoding.HeaderName,
+ wmain ? "utf-16" : "utf-8",
+ IsRedirect(StandardHandles.IN) ? 1 : 0,
+ IsRedirect(StandardHandles.OUT) ? 1 : 0,
+ IsRedirect(StandardHandles.ERR) ? 1 : 0
+ ));
+ }
+ else
+ {
+ string lang = Environment.GetEnvironmentVariable("LANG");
+ Environment.SetEnvironmentVariable("LC_CONSOLE", String.Format(
+ "{0} {0} {1} 000",
+ null == lang ? Encoding.Default.HeaderName : lang.Split('.')[1],
+ wmain ? "utf-16" : "utf-8"
+ ));
}
// Look for PHP configuration
@@ -77,7 +121,6 @@ namespace Net.XpFramework.Runner
argv += " -d" + kv.Key + "=\"" + value + "\"";
}
}
-
// Spawn runtime
var proc = new Process();
@@ -85,8 +128,35 @@ namespace Net.XpFramework.Runner
proc.StartInfo.Arguments = argv + " \"" + new List<string>(Paths.Locate(use_xp, "tools\\" + runner + ".php", true))[0] + "\" " + tool;
if (args.Length > 0)
{
- foreach (string arg in args) {
- proc.StartInfo.Arguments += " \"" + arg.Replace("\"", "\"\"\"") + "\"";
+ ArgProcessor process;
+
+ // Workaround problem that php itself doesn't have a wmain() method, so the OS
+ // converts the strings passed as argument to OS default encoding (CP1252, on
+ // a German Windows installation). Works fine until someone enters Japanese
+ // characters). Convert to single byte character set before passing.
+ //
+ // This workaround can be disabled by setting runtime.wmain to true once PHP
+ // declares that. See http://msdn.microsoft.com/en-us/library/88w63h9k.aspx
+ if (wmain)
+ {
+ process = delegate(string arg)
+ {
+ return arg;
+ };
+ }
+ else
+ {
+ process = delegate(string arg)
+ {
+ var mb = Encoding.UTF8;
+ var sb = Encoding.GetEncoding(1252);
+
+ return sb.GetString(mb.GetBytes(arg));
+ };
+ }
+ foreach (string arg in args)
+ {
+ proc.StartInfo.Arguments += " \"" + process(arg.Replace("\"", "\"\"\"")) + "\"";
}
}
diff --git a/windows/src/IniConfigSource.cs b/windows/src/IniConfigSource.cs
index 7561aa9..cd7f065 100644
--- a/windows/src/IniConfigSource.cs
+++ b/windows/src/IniConfigSource.cs
@@ -39,6 +39,22 @@ namespace Net.XpFramework.Runner
}
/// <summary>
+ /// Returns whether the PHP runtime supports wmain()
+ /// </summary>
+ public bool? GetWMain()
+ {
+ var wmain = this.ini.Get("runtime", "wmain");
+
+ if (null == wmain)
+ {
+ return null;
+ }
+
+ wmain = wmain.ToLower();
+ return wmain.Equals("yes") || wmain.Equals("true") || wmain.Equals("1") || wmain.Equals("on");
+ }
+
+ /// <summary>
/// Returns the PHP runtime arguments to be used from this config source
/// </summary>
public Dictionary<string, IEnumerable<string>> GetArgs()
@@ -47,7 +63,7 @@ namespace Net.XpFramework.Runner
List<string> empty= new List<string>();
foreach (string key in this.ini.Keys("runtime", empty))
{
- if (!"default".Equals(key))
+ if (!"default".Equals(key) && !"wmain".Equals(key))
{
args[key]= this.ini.GetAll("runtime", key, empty);
}
diff --git a/windows/src/XpConfigSource.cs b/windows/src/XpConfigSource.cs
index d0ba28a..60744e9 100644
--- a/windows/src/XpConfigSource.cs
+++ b/windows/src/XpConfigSource.cs
@@ -19,6 +19,12 @@ namespace Net.XpFramework.Runner
string GetRuntime();
/// <summary>
+ /// Returns whether the PHP runtime supports wmain()
+ /// See http://stackoverflow.com/questions/2627891/does-process-startinfo-arguments-support-a-utf-8-string
+ /// </summary>
+ bool? GetWMain();
+
+ /// <summary>
/// Returns the PHP runtime arguments to be used from this config source
/// </summary>
Dictionary<string, IEnumerable<string>> GetArgs();
@thekid
Copy link
Author

thekid commented Jun 13, 2011

The unittest output is that of unittest src/resources/unittest/*.ini...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment