[PATCH] hgext/win32chcp: switches the console into an encoding actually used (issue2926)

Andrei Polushin polushin at gmail.com
Thu Jul 28 18:47:55 CDT 2011


# HG changeset patch
# User Andrei Polushin <polushin at gmail.com>
# Date 1311886282 -25200
# Branch stable
# Node ID 79b6058b58e93cd5b3edbd3340b1348e46e043a7
# Parent  56848e2bb0c5a43b580dd2ca7ce1e781d4e75b2b
hgext/win32chcp: switches the console into an encoding actually used (issue2926)

The encoding of the Windows console is switched temporarily, for the time the
Mercurial is running, and restored to the previous value on exit.

This solution is backward compatible, it changes almost nothing, i.e. neither
stdout/pipe encoding, nor internal string handling are affected anyway.
For maximum compatibility, it is implemented as an optional extension.

One problem still remains: `hg log | more' doesn't produce readable output on
OEM console, I've found no way to change the console encoding when redirected.

One problem possibly introduced: killing hg process may not restore previous
console encoding. I'm unable to reproduce the problem, however.

diff -r 56848e2bb0c5 -r 79b6058b58e9 hgext/win32chcp.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/win32chcp.py	Fri Jul 29 03:51:22 2011 +0700
@@ -0,0 +1,30 @@
+# Copyright 2011 Andrei Polushin <polushin at gmail.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+'''switch the Windows console into an encoding actually used on output'''
+
+import os, codecs
+from mercurial.i18n import _
+from mercurial import osutil, encoding
+
+_cpaliases = {
+    'utf-8': 'cp65001',
+}
+
+def uisetup(ui):
+    if os.name != 'nt':
+        ui.warn(_("[win32chcp] is not supported on this platform.\n"))
+        return
+
+    cp = encoding.encoding
+    try:
+        cp = codecs.lookup(cp).name
+        cp = _cpaliases.get(cp, cp)
+        if cp.startswith('cp'):
+            cp = cp[2:]
+        osutil.forceconsoleencoding(int(cp))
+    except (LookupError, ValueError):
+        ui.warn(_("[win32chcp] unrecognized encoding: %s.\n") % cp)
+        pass
diff -r 56848e2bb0c5 -r 79b6058b58e9 mercurial/osutil.c
--- a/mercurial/osutil.c	Fri Jul 22 20:31:15 2011 -0300
+++ b/mercurial/osutil.c	Fri Jul 29 03:51:22 2011 +0700
@@ -14,6 +14,7 @@
 #include <string.h>
 
 #ifdef _WIN32
+#define _WIN32_WINNT 0x0501
 #include <windows.h>
 #include <io.h>
 #else
@@ -402,6 +403,36 @@
 }
 
 #ifdef _WIN32
+static UINT inputcp;
+static UINT outputcp;
+
+static void restoreconsoleencoding(void)
+{
+	SetConsoleCP(inputcp);
+	SetConsoleOutputCP(outputcp);
+}
+
+static PyObject *forceconsoleencoding(PyObject *self, PyObject *args)
+{
+	int cp;
+
+	if (!PyArg_ParseTuple(args, "i", &cp))
+		return NULL;
+
+	if (!inputcp && !outputcp && IsValidCodePage(cp)) {
+		inputcp = GetConsoleCP();
+		outputcp = GetConsoleOutputCP();
+
+		SetConsoleCP(cp);
+		SetConsoleOutputCP(cp);
+
+		atexit(restoreconsoleencoding);
+		return Py_True;
+	}
+
+	return Py_False;
+}
+
 static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
 {
 	static char *kwlist[] = {"name", "mode", "buffering", NULL};
@@ -536,6 +567,8 @@
 	{"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
 	 "list a directory\n"},
 #ifdef _WIN32
+	{"forceconsoleencoding", forceconsoleencoding, METH_VARARGS,
+	 "Temporarily switch the console encoding, restored on exit.\n"},
 	{"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
 	 "Open a file with POSIX-like semantics.\n"
 "On error, this function may raise either a WindowsError or an IOError."},


More information about the Mercurial-devel mailing list