win32mbcs.py
144 lines
| 4.9 KiB
| text/x-python
|
PythonLexer
/ hgext / win32mbcs.py
|
r6887 | # win32mbcs.py -- MBCS filename support for Mercurial | ||
|
r5846 | # | ||
# Copyright (c) 2008 Shun-ichi Goto <shunichi.goto@gmail.com> | ||||
# | ||||
|
r6887 | # Version: 0.2 | ||
|
r5846 | # Author: Shun-ichi Goto <shunichi.goto@gmail.com> | ||
# | ||||
|
r8225 | # This software may be used and distributed according to the terms of the | ||
# GNU General Public License version 2, incorporated herein by reference. | ||||
|
r5846 | # | ||
|
r8228 | |||
|
r8932 | '''allow the use of MBCS paths with problematic encodings | ||
|
r5846 | |||
|
r8001 | Some MBCS encodings are not good for some path operations (i.e. | ||
splitting path, case conversion, etc.) with its encoded bytes. We call | ||||
such a encoding (i.e. shift_jis and big5) as "problematic encoding". | ||||
This extension can be used to fix the issue with those encodings by | ||||
|
r8665 | wrapping some functions to convert to Unicode string before path | ||
|
r8001 | operation. | ||
|
r5846 | |||
|
r8668 | This extension is useful for: | ||
|
r6887 | * Japanese Windows users using shift_jis encoding. | ||
* Chinese Windows users using big5 encoding. | ||||
|
r8001 | * All users who use a repository with one of problematic encodings on | ||
case-insensitive file system. | ||||
|
r6887 | |||
This extension is not needed for: | ||||
|
r8667 | * Any user who use only ASCII chars in path. | ||
|
r6887 | * Any user who do not use any of problematic encodings. | ||
|
r5846 | |||
|
r6887 | Note that there are some limitations on using this extension: | ||
* You should use single encoding in one repository. | ||||
|
r8001 | * You should set same encoding for the repository by locale or | ||
HGENCODING. | ||||
|
r6887 | |||
|
r8665 | Path encoding conversion are done between Unicode and | ||
|
r8760 | encoding.encoding which is decided by Mercurial from current locale | ||
|
r8001 | setting or HGENCODING. | ||
|
r8894 | ''' | ||
|
r5846 | |||
|
r9098 | import os, sys | ||
|
r5846 | from mercurial.i18n import _ | ||
|
r7948 | from mercurial import util, encoding | ||
|
r5846 | |||
|
r6887 | def decode(arg): | ||
|
r7877 | if isinstance(arg, str): | ||
|
r7948 | uarg = arg.decode(encoding.encoding) | ||
if arg == uarg.encode(encoding.encoding): | ||||
|
r7877 | return uarg | ||
raise UnicodeError("Not local encoding") | ||||
elif isinstance(arg, tuple): | ||||
return tuple(map(decode, arg)) | ||||
elif isinstance(arg, list): | ||||
return map(decode, arg) | ||||
|
r9131 | elif isinstance(arg, dict): | ||
for k, v in arg.items(): | ||||
arg[k] = decode(v) | ||||
|
r7877 | return arg | ||
|
r6887 | |||
def encode(arg): | ||||
|
r7877 | if isinstance(arg, unicode): | ||
|
r7948 | return arg.encode(encoding.encoding) | ||
|
r7877 | elif isinstance(arg, tuple): | ||
return tuple(map(encode, arg)) | ||||
elif isinstance(arg, list): | ||||
return map(encode, arg) | ||||
|
r9131 | elif isinstance(arg, dict): | ||
for k, v in arg.items(): | ||||
arg[k] = encode(v) | ||||
|
r7877 | return arg | ||
|
r6887 | |||
|
r9132 | def appendsep(s): | ||
# ensure the path ends with os.sep, appending it if necessary. | ||||
try: | ||||
us = decode(s) | ||||
except UnicodeError: | ||||
us = s | ||||
if us and us[-1] not in ':/\\': | ||||
s += os.sep | ||||
return s | ||||
|
r9131 | def wrapper(func, args, kwds): | ||
|
r7877 | # check argument is unicode, then call original | ||
for arg in args: | ||||
if isinstance(arg, unicode): | ||||
|
r9131 | return func(*args, **kwds) | ||
|
r5846 | |||
|
r7877 | try: | ||
# convert arguments to unicode, call func, then convert back | ||||
|
r9131 | return encode(func(*decode(args), **decode(kwds))) | ||
|
r7877 | except UnicodeError: | ||
|
r9132 | raise util.Abort(_("[win32mbcs] filename conversion failed with" | ||
|
r7948 | " %s encoding\n") % (encoding.encoding)) | ||
|
r6887 | |||
|
r9132 | def wrapperforlistdir(func, args, kwds): | ||
# Ensure 'path' argument ends with os.sep to avoids | ||||
# misinterpreting last 0x5c of MBCS 2nd byte as path separator. | ||||
if args: | ||||
args = list(args) | ||||
args[0] = appendsep(args[0]) | ||||
if kwds.has_key('path'): | ||||
kwds['path'] = appendsep(kwds['path']) | ||||
return func(*args, **kwds) | ||||
def wrapname(name, wrapper): | ||||
|
r9098 | module, name = name.rsplit('.', 1) | ||
module = sys.modules[module] | ||||
|
r7877 | func = getattr(module, name) | ||
|
r9131 | def f(*args, **kwds): | ||
return wrapper(func, args, kwds) | ||||
|
r7877 | try: | ||
f.__name__ = func.__name__ # fail with python23 | ||||
except Exception: | ||||
pass | ||||
setattr(module, name, f) | ||||
|
r6887 | |||
# List of functions to be wrapped. | ||||
# NOTE: os.path.dirname() and os.path.basename() are safe because | ||||
# they use result of os.path.split() | ||||
funcs = '''os.path.join os.path.split os.path.splitext | ||||
os.path.splitunc os.path.normpath os.path.normcase os.makedirs | ||||
|
r9098 | mercurial.util.endswithsep mercurial.util.splitpath mercurial.util.checkcase | ||
|
r9465 | mercurial.util.fspath mercurial.util.pconvert mercurial.util.normpath''' | ||
|
r5846 | |||
# codec and alias names of sjis and big5 to be faked. | ||||
|
r6887 | problematic_encodings = '''big5 big5-tw csbig5 big5hkscs big5-hkscs | ||
hkscs cp932 932 ms932 mskanji ms-kanji shift_jis csshiftjis shiftjis | ||||
sjis s_jis shift_jis_2004 shiftjis2004 sjis_2004 sjis2004 | ||||
|
r8714 | shift_jisx0213 shiftjisx0213 sjisx0213 s_jisx0213 950 cp950 ms950 ''' | ||
|
r5846 | |||
def reposetup(ui, repo): | ||||
|
r7877 | # TODO: decide use of config section for this extension | ||
if not os.path.supports_unicode_filenames: | ||||
ui.warn(_("[win32mbcs] cannot activate on this platform.\n")) | ||||
return | ||||
|
r5846 | |||
|
r7877 | # fake is only for relevant environment. | ||
|
r7948 | if encoding.encoding.lower() in problematic_encodings.split(): | ||
|
r7877 | for f in funcs.split(): | ||
|
r9132 | wrapname(f, wrapper) | ||
wrapname("mercurial.osutil.listdir", wrapperforlistdir) | ||||
|
r7948 | ui.debug(_("[win32mbcs] activated with encoding: %s\n") | ||
% encoding.encoding) | ||||
|
r6887 | |||