##// END OF EJS Templates
configitems: register the 'win32mbcs.encoding' config
Boris Feld -
r34181:37513324 default
parent child Browse files
Show More
@@ -1,196 +1,206
1 # win32mbcs.py -- MBCS filename support for Mercurial
1 # win32mbcs.py -- MBCS filename support for Mercurial
2 #
2 #
3 # Copyright (c) 2008 Shun-ichi Goto <shunichi.goto@gmail.com>
3 # Copyright (c) 2008 Shun-ichi Goto <shunichi.goto@gmail.com>
4 #
4 #
5 # Version: 0.3
5 # Version: 0.3
6 # Author: Shun-ichi Goto <shunichi.goto@gmail.com>
6 # Author: Shun-ichi Goto <shunichi.goto@gmail.com>
7 #
7 #
8 # This software may be used and distributed according to the terms of the
8 # This software may be used and distributed according to the terms of the
9 # GNU General Public License version 2 or any later version.
9 # GNU General Public License version 2 or any later version.
10 #
10 #
11
11
12 '''allow the use of MBCS paths with problematic encodings
12 '''allow the use of MBCS paths with problematic encodings
13
13
14 Some MBCS encodings are not good for some path operations (i.e.
14 Some MBCS encodings are not good for some path operations (i.e.
15 splitting path, case conversion, etc.) with its encoded bytes. We call
15 splitting path, case conversion, etc.) with its encoded bytes. We call
16 such a encoding (i.e. shift_jis and big5) as "problematic encoding".
16 such a encoding (i.e. shift_jis and big5) as "problematic encoding".
17 This extension can be used to fix the issue with those encodings by
17 This extension can be used to fix the issue with those encodings by
18 wrapping some functions to convert to Unicode string before path
18 wrapping some functions to convert to Unicode string before path
19 operation.
19 operation.
20
20
21 This extension is useful for:
21 This extension is useful for:
22
22
23 - Japanese Windows users using shift_jis encoding.
23 - Japanese Windows users using shift_jis encoding.
24 - Chinese Windows users using big5 encoding.
24 - Chinese Windows users using big5 encoding.
25 - All users who use a repository with one of problematic encodings on
25 - All users who use a repository with one of problematic encodings on
26 case-insensitive file system.
26 case-insensitive file system.
27
27
28 This extension is not needed for:
28 This extension is not needed for:
29
29
30 - Any user who use only ASCII chars in path.
30 - Any user who use only ASCII chars in path.
31 - Any user who do not use any of problematic encodings.
31 - Any user who do not use any of problematic encodings.
32
32
33 Note that there are some limitations on using this extension:
33 Note that there are some limitations on using this extension:
34
34
35 - You should use single encoding in one repository.
35 - You should use single encoding in one repository.
36 - If the repository path ends with 0x5c, .hg/hgrc cannot be read.
36 - If the repository path ends with 0x5c, .hg/hgrc cannot be read.
37 - win32mbcs is not compatible with fixutf8 extension.
37 - win32mbcs is not compatible with fixutf8 extension.
38
38
39 By default, win32mbcs uses encoding.encoding decided by Mercurial.
39 By default, win32mbcs uses encoding.encoding decided by Mercurial.
40 You can specify the encoding by config option::
40 You can specify the encoding by config option::
41
41
42 [win32mbcs]
42 [win32mbcs]
43 encoding = sjis
43 encoding = sjis
44
44
45 It is useful for the users who want to commit with UTF-8 log message.
45 It is useful for the users who want to commit with UTF-8 log message.
46 '''
46 '''
47 from __future__ import absolute_import
47 from __future__ import absolute_import
48
48
49 import os
49 import os
50 import sys
50 import sys
51
51
52 from mercurial.i18n import _
52 from mercurial.i18n import _
53 from mercurial import (
53 from mercurial import (
54 encoding,
54 encoding,
55 error,
55 error,
56 pycompat,
56 pycompat,
57 registrar,
57 )
58 )
58
59
59 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
60 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
60 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
61 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
61 # be specifying the version(s) of Mercurial they are tested with, or
62 # be specifying the version(s) of Mercurial they are tested with, or
62 # leave the attribute unspecified.
63 # leave the attribute unspecified.
63 testedwith = 'ships-with-hg-core'
64 testedwith = 'ships-with-hg-core'
64
65
66 configtable = {}
67 configitem = registrar.configitem(configtable)
68
69 # Encoding.encoding may be updated by --encoding option.
70 # Use a lambda do delay the resolution.
71 configitem('win32mbcs', 'encoding',
72 default=lambda: encoding.encoding,
73 )
74
65 _encoding = None # see extsetup
75 _encoding = None # see extsetup
66
76
67 def decode(arg):
77 def decode(arg):
68 if isinstance(arg, str):
78 if isinstance(arg, str):
69 uarg = arg.decode(_encoding)
79 uarg = arg.decode(_encoding)
70 if arg == uarg.encode(_encoding):
80 if arg == uarg.encode(_encoding):
71 return uarg
81 return uarg
72 raise UnicodeError("Not local encoding")
82 raise UnicodeError("Not local encoding")
73 elif isinstance(arg, tuple):
83 elif isinstance(arg, tuple):
74 return tuple(map(decode, arg))
84 return tuple(map(decode, arg))
75 elif isinstance(arg, list):
85 elif isinstance(arg, list):
76 return map(decode, arg)
86 return map(decode, arg)
77 elif isinstance(arg, dict):
87 elif isinstance(arg, dict):
78 for k, v in arg.items():
88 for k, v in arg.items():
79 arg[k] = decode(v)
89 arg[k] = decode(v)
80 return arg
90 return arg
81
91
82 def encode(arg):
92 def encode(arg):
83 if isinstance(arg, unicode):
93 if isinstance(arg, unicode):
84 return arg.encode(_encoding)
94 return arg.encode(_encoding)
85 elif isinstance(arg, tuple):
95 elif isinstance(arg, tuple):
86 return tuple(map(encode, arg))
96 return tuple(map(encode, arg))
87 elif isinstance(arg, list):
97 elif isinstance(arg, list):
88 return map(encode, arg)
98 return map(encode, arg)
89 elif isinstance(arg, dict):
99 elif isinstance(arg, dict):
90 for k, v in arg.items():
100 for k, v in arg.items():
91 arg[k] = encode(v)
101 arg[k] = encode(v)
92 return arg
102 return arg
93
103
94 def appendsep(s):
104 def appendsep(s):
95 # ensure the path ends with os.sep, appending it if necessary.
105 # ensure the path ends with os.sep, appending it if necessary.
96 try:
106 try:
97 us = decode(s)
107 us = decode(s)
98 except UnicodeError:
108 except UnicodeError:
99 us = s
109 us = s
100 if us and us[-1] not in ':/\\':
110 if us and us[-1] not in ':/\\':
101 s += pycompat.ossep
111 s += pycompat.ossep
102 return s
112 return s
103
113
104
114
105 def basewrapper(func, argtype, enc, dec, args, kwds):
115 def basewrapper(func, argtype, enc, dec, args, kwds):
106 # check check already converted, then call original
116 # check check already converted, then call original
107 for arg in args:
117 for arg in args:
108 if isinstance(arg, argtype):
118 if isinstance(arg, argtype):
109 return func(*args, **kwds)
119 return func(*args, **kwds)
110
120
111 try:
121 try:
112 # convert string arguments, call func, then convert back the
122 # convert string arguments, call func, then convert back the
113 # return value.
123 # return value.
114 return enc(func(*dec(args), **dec(kwds)))
124 return enc(func(*dec(args), **dec(kwds)))
115 except UnicodeError:
125 except UnicodeError:
116 raise error.Abort(_("[win32mbcs] filename conversion failed with"
126 raise error.Abort(_("[win32mbcs] filename conversion failed with"
117 " %s encoding\n") % (_encoding))
127 " %s encoding\n") % (_encoding))
118
128
119 def wrapper(func, args, kwds):
129 def wrapper(func, args, kwds):
120 return basewrapper(func, unicode, encode, decode, args, kwds)
130 return basewrapper(func, unicode, encode, decode, args, kwds)
121
131
122
132
123 def reversewrapper(func, args, kwds):
133 def reversewrapper(func, args, kwds):
124 return basewrapper(func, str, decode, encode, args, kwds)
134 return basewrapper(func, str, decode, encode, args, kwds)
125
135
126 def wrapperforlistdir(func, args, kwds):
136 def wrapperforlistdir(func, args, kwds):
127 # Ensure 'path' argument ends with os.sep to avoids
137 # Ensure 'path' argument ends with os.sep to avoids
128 # misinterpreting last 0x5c of MBCS 2nd byte as path separator.
138 # misinterpreting last 0x5c of MBCS 2nd byte as path separator.
129 if args:
139 if args:
130 args = list(args)
140 args = list(args)
131 args[0] = appendsep(args[0])
141 args[0] = appendsep(args[0])
132 if 'path' in kwds:
142 if 'path' in kwds:
133 kwds['path'] = appendsep(kwds['path'])
143 kwds['path'] = appendsep(kwds['path'])
134 return func(*args, **kwds)
144 return func(*args, **kwds)
135
145
136 def wrapname(name, wrapper):
146 def wrapname(name, wrapper):
137 module, name = name.rsplit('.', 1)
147 module, name = name.rsplit('.', 1)
138 module = sys.modules[module]
148 module = sys.modules[module]
139 func = getattr(module, name)
149 func = getattr(module, name)
140 def f(*args, **kwds):
150 def f(*args, **kwds):
141 return wrapper(func, args, kwds)
151 return wrapper(func, args, kwds)
142 f.__name__ = func.__name__
152 f.__name__ = func.__name__
143 setattr(module, name, f)
153 setattr(module, name, f)
144
154
145 # List of functions to be wrapped.
155 # List of functions to be wrapped.
146 # NOTE: os.path.dirname() and os.path.basename() are safe because
156 # NOTE: os.path.dirname() and os.path.basename() are safe because
147 # they use result of os.path.split()
157 # they use result of os.path.split()
148 funcs = '''os.path.join os.path.split os.path.splitext
158 funcs = '''os.path.join os.path.split os.path.splitext
149 os.path.normpath os.makedirs mercurial.util.endswithsep
159 os.path.normpath os.makedirs mercurial.util.endswithsep
150 mercurial.util.splitpath mercurial.util.fscasesensitive
160 mercurial.util.splitpath mercurial.util.fscasesensitive
151 mercurial.util.fspath mercurial.util.pconvert mercurial.util.normpath
161 mercurial.util.fspath mercurial.util.pconvert mercurial.util.normpath
152 mercurial.util.checkwinfilename mercurial.util.checkosfilename
162 mercurial.util.checkwinfilename mercurial.util.checkosfilename
153 mercurial.util.split'''
163 mercurial.util.split'''
154
164
155 # These functions are required to be called with local encoded string
165 # These functions are required to be called with local encoded string
156 # because they expects argument is local encoded string and cause
166 # because they expects argument is local encoded string and cause
157 # problem with unicode string.
167 # problem with unicode string.
158 rfuncs = '''mercurial.encoding.upper mercurial.encoding.lower
168 rfuncs = '''mercurial.encoding.upper mercurial.encoding.lower
159 mercurial.util._filenamebytestr'''
169 mercurial.util._filenamebytestr'''
160
170
161 # List of Windows specific functions to be wrapped.
171 # List of Windows specific functions to be wrapped.
162 winfuncs = '''os.path.splitunc'''
172 winfuncs = '''os.path.splitunc'''
163
173
164 # codec and alias names of sjis and big5 to be faked.
174 # codec and alias names of sjis and big5 to be faked.
165 problematic_encodings = '''big5 big5-tw csbig5 big5hkscs big5-hkscs
175 problematic_encodings = '''big5 big5-tw csbig5 big5hkscs big5-hkscs
166 hkscs cp932 932 ms932 mskanji ms-kanji shift_jis csshiftjis shiftjis
176 hkscs cp932 932 ms932 mskanji ms-kanji shift_jis csshiftjis shiftjis
167 sjis s_jis shift_jis_2004 shiftjis2004 sjis_2004 sjis2004
177 sjis s_jis shift_jis_2004 shiftjis2004 sjis_2004 sjis2004
168 shift_jisx0213 shiftjisx0213 sjisx0213 s_jisx0213 950 cp950 ms950 '''
178 shift_jisx0213 shiftjisx0213 sjisx0213 s_jisx0213 950 cp950 ms950 '''
169
179
170 def extsetup(ui):
180 def extsetup(ui):
171 # TODO: decide use of config section for this extension
181 # TODO: decide use of config section for this extension
172 if ((not os.path.supports_unicode_filenames) and
182 if ((not os.path.supports_unicode_filenames) and
173 (pycompat.sysplatform != 'cygwin')):
183 (pycompat.sysplatform != 'cygwin')):
174 ui.warn(_("[win32mbcs] cannot activate on this platform.\n"))
184 ui.warn(_("[win32mbcs] cannot activate on this platform.\n"))
175 return
185 return
176 # determine encoding for filename
186 # determine encoding for filename
177 global _encoding
187 global _encoding
178 _encoding = ui.config('win32mbcs', 'encoding', encoding.encoding)
188 _encoding = ui.config('win32mbcs', 'encoding')
179 # fake is only for relevant environment.
189 # fake is only for relevant environment.
180 if _encoding.lower() in problematic_encodings.split():
190 if _encoding.lower() in problematic_encodings.split():
181 for f in funcs.split():
191 for f in funcs.split():
182 wrapname(f, wrapper)
192 wrapname(f, wrapper)
183 if pycompat.osname == 'nt':
193 if pycompat.osname == 'nt':
184 for f in winfuncs.split():
194 for f in winfuncs.split():
185 wrapname(f, wrapper)
195 wrapname(f, wrapper)
186 wrapname("mercurial.util.listdir", wrapperforlistdir)
196 wrapname("mercurial.util.listdir", wrapperforlistdir)
187 wrapname("mercurial.windows.listdir", wrapperforlistdir)
197 wrapname("mercurial.windows.listdir", wrapperforlistdir)
188 # wrap functions to be called with local byte string arguments
198 # wrap functions to be called with local byte string arguments
189 for f in rfuncs.split():
199 for f in rfuncs.split():
190 wrapname(f, reversewrapper)
200 wrapname(f, reversewrapper)
191 # Check sys.args manually instead of using ui.debug() because
201 # Check sys.args manually instead of using ui.debug() because
192 # command line options is not yet applied when
202 # command line options is not yet applied when
193 # extensions.loadall() is called.
203 # extensions.loadall() is called.
194 if '--debug' in sys.argv:
204 if '--debug' in sys.argv:
195 ui.write(("[win32mbcs] activated with encoding: %s\n")
205 ui.write(("[win32mbcs] activated with encoding: %s\n")
196 % _encoding)
206 % _encoding)
General Comments 0
You need to be logged in to leave comments. Login now