##// END OF EJS Templates
windows: use upper() instead of lower() or os.path.normcase()...
FUJIWARA Katsunori -
r15671:3c5e818a stable
parent child Browse files
Show More
@@ -1,160 +1,162 b''
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
47
48 import os, sys
48 import os, sys
49 from mercurial.i18n import _
49 from mercurial.i18n import _
50 from mercurial import util, encoding
50 from mercurial import util, encoding
51
51
52 _encoding = None # see extsetup
52 _encoding = None # see extsetup
53
53
54 def decode(arg):
54 def decode(arg):
55 if isinstance(arg, str):
55 if isinstance(arg, str):
56 uarg = arg.decode(_encoding)
56 uarg = arg.decode(_encoding)
57 if arg == uarg.encode(_encoding):
57 if arg == uarg.encode(_encoding):
58 return uarg
58 return uarg
59 raise UnicodeError("Not local encoding")
59 raise UnicodeError("Not local encoding")
60 elif isinstance(arg, tuple):
60 elif isinstance(arg, tuple):
61 return tuple(map(decode, arg))
61 return tuple(map(decode, arg))
62 elif isinstance(arg, list):
62 elif isinstance(arg, list):
63 return map(decode, arg)
63 return map(decode, arg)
64 elif isinstance(arg, dict):
64 elif isinstance(arg, dict):
65 for k, v in arg.items():
65 for k, v in arg.items():
66 arg[k] = decode(v)
66 arg[k] = decode(v)
67 return arg
67 return arg
68
68
69 def encode(arg):
69 def encode(arg):
70 if isinstance(arg, unicode):
70 if isinstance(arg, unicode):
71 return arg.encode(_encoding)
71 return arg.encode(_encoding)
72 elif isinstance(arg, tuple):
72 elif isinstance(arg, tuple):
73 return tuple(map(encode, arg))
73 return tuple(map(encode, arg))
74 elif isinstance(arg, list):
74 elif isinstance(arg, list):
75 return map(encode, arg)
75 return map(encode, arg)
76 elif isinstance(arg, dict):
76 elif isinstance(arg, dict):
77 for k, v in arg.items():
77 for k, v in arg.items():
78 arg[k] = encode(v)
78 arg[k] = encode(v)
79 return arg
79 return arg
80
80
81 def appendsep(s):
81 def appendsep(s):
82 # ensure the path ends with os.sep, appending it if necessary.
82 # ensure the path ends with os.sep, appending it if necessary.
83 try:
83 try:
84 us = decode(s)
84 us = decode(s)
85 except UnicodeError:
85 except UnicodeError:
86 us = s
86 us = s
87 if us and us[-1] not in ':/\\':
87 if us and us[-1] not in ':/\\':
88 s += os.sep
88 s += os.sep
89 return s
89 return s
90
90
91 def wrapper(func, args, kwds):
91 def wrapper(func, args, kwds):
92 # check argument is unicode, then call original
92 # check argument is unicode, then call original
93 for arg in args:
93 for arg in args:
94 if isinstance(arg, unicode):
94 if isinstance(arg, unicode):
95 return func(*args, **kwds)
95 return func(*args, **kwds)
96
96
97 try:
97 try:
98 # convert arguments to unicode, call func, then convert back
98 # convert arguments to unicode, call func, then convert back
99 return encode(func(*decode(args), **decode(kwds)))
99 return encode(func(*decode(args), **decode(kwds)))
100 except UnicodeError:
100 except UnicodeError:
101 raise util.Abort(_("[win32mbcs] filename conversion failed with"
101 raise util.Abort(_("[win32mbcs] filename conversion failed with"
102 " %s encoding\n") % (_encoding))
102 " %s encoding\n") % (_encoding))
103
103
104 def wrapperforlistdir(func, args, kwds):
104 def wrapperforlistdir(func, args, kwds):
105 # Ensure 'path' argument ends with os.sep to avoids
105 # Ensure 'path' argument ends with os.sep to avoids
106 # misinterpreting last 0x5c of MBCS 2nd byte as path separator.
106 # misinterpreting last 0x5c of MBCS 2nd byte as path separator.
107 if args:
107 if args:
108 args = list(args)
108 args = list(args)
109 args[0] = appendsep(args[0])
109 args[0] = appendsep(args[0])
110 if 'path' in kwds:
110 if 'path' in kwds:
111 kwds['path'] = appendsep(kwds['path'])
111 kwds['path'] = appendsep(kwds['path'])
112 return func(*args, **kwds)
112 return func(*args, **kwds)
113
113
114 def wrapname(name, wrapper):
114 def wrapname(name, wrapper):
115 module, name = name.rsplit('.', 1)
115 module, name = name.rsplit('.', 1)
116 module = sys.modules[module]
116 module = sys.modules[module]
117 func = getattr(module, name)
117 func = getattr(module, name)
118 def f(*args, **kwds):
118 def f(*args, **kwds):
119 return wrapper(func, args, kwds)
119 return wrapper(func, args, kwds)
120 try:
120 try:
121 f.__name__ = func.__name__ # fail with python23
121 f.__name__ = func.__name__ # fail with python23
122 except Exception:
122 except Exception:
123 pass
123 pass
124 setattr(module, name, f)
124 setattr(module, name, f)
125
125
126 # List of functions to be wrapped.
126 # List of functions to be wrapped.
127 # NOTE: os.path.dirname() and os.path.basename() are safe because
127 # NOTE: os.path.dirname() and os.path.basename() are safe because
128 # they use result of os.path.split()
128 # they use result of os.path.split()
129 funcs = '''os.path.join os.path.split os.path.splitext
129 funcs = '''os.path.join os.path.split os.path.splitext
130 os.path.splitunc os.path.normpath os.path.normcase os.makedirs
130 os.path.splitunc os.path.normpath os.makedirs
131 mercurial.windows.normcase
132 mercurial.util.normcase
131 mercurial.util.endswithsep mercurial.util.splitpath mercurial.util.checkcase
133 mercurial.util.endswithsep mercurial.util.splitpath mercurial.util.checkcase
132 mercurial.util.fspath mercurial.util.pconvert mercurial.util.normpath
134 mercurial.util.fspath mercurial.util.pconvert mercurial.util.normpath
133 mercurial.util.checkwinfilename mercurial.util.checkosfilename'''
135 mercurial.util.checkwinfilename mercurial.util.checkosfilename'''
134
136
135 # codec and alias names of sjis and big5 to be faked.
137 # codec and alias names of sjis and big5 to be faked.
136 problematic_encodings = '''big5 big5-tw csbig5 big5hkscs big5-hkscs
138 problematic_encodings = '''big5 big5-tw csbig5 big5hkscs big5-hkscs
137 hkscs cp932 932 ms932 mskanji ms-kanji shift_jis csshiftjis shiftjis
139 hkscs cp932 932 ms932 mskanji ms-kanji shift_jis csshiftjis shiftjis
138 sjis s_jis shift_jis_2004 shiftjis2004 sjis_2004 sjis2004
140 sjis s_jis shift_jis_2004 shiftjis2004 sjis_2004 sjis2004
139 shift_jisx0213 shiftjisx0213 sjisx0213 s_jisx0213 950 cp950 ms950 '''
141 shift_jisx0213 shiftjisx0213 sjisx0213 s_jisx0213 950 cp950 ms950 '''
140
142
141 def extsetup(ui):
143 def extsetup(ui):
142 # TODO: decide use of config section for this extension
144 # TODO: decide use of config section for this extension
143 if not os.path.supports_unicode_filenames:
145 if not os.path.supports_unicode_filenames:
144 ui.warn(_("[win32mbcs] cannot activate on this platform.\n"))
146 ui.warn(_("[win32mbcs] cannot activate on this platform.\n"))
145 return
147 return
146 # determine encoding for filename
148 # determine encoding for filename
147 global _encoding
149 global _encoding
148 _encoding = ui.config('win32mbcs', 'encoding', encoding.encoding)
150 _encoding = ui.config('win32mbcs', 'encoding', encoding.encoding)
149 # fake is only for relevant environment.
151 # fake is only for relevant environment.
150 if _encoding.lower() in problematic_encodings.split():
152 if _encoding.lower() in problematic_encodings.split():
151 for f in funcs.split():
153 for f in funcs.split():
152 wrapname(f, wrapper)
154 wrapname(f, wrapper)
153 wrapname("mercurial.osutil.listdir", wrapperforlistdir)
155 wrapname("mercurial.osutil.listdir", wrapperforlistdir)
154 # Check sys.args manually instead of using ui.debug() because
156 # Check sys.args manually instead of using ui.debug() because
155 # command line options is not yet applied when
157 # command line options is not yet applied when
156 # extensions.loadall() is called.
158 # extensions.loadall() is called.
157 if '--debug' in sys.argv:
159 if '--debug' in sys.argv:
158 ui.write("[win32mbcs] activated with encoding: %s\n"
160 ui.write("[win32mbcs] activated with encoding: %s\n"
159 % _encoding)
161 % _encoding)
160
162
@@ -1,315 +1,316 b''
1 # windows.py - Windows utility function implementations for Mercurial
1 # windows.py - Windows utility function implementations for Mercurial
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import osutil
9 import osutil
10 import errno, msvcrt, os, re, sys
10 import errno, msvcrt, os, re, sys
11
11
12 import win32
12 import win32
13 executablepath = win32.executablepath
13 executablepath = win32.executablepath
14 getuser = win32.getuser
14 getuser = win32.getuser
15 hidewindow = win32.hidewindow
15 hidewindow = win32.hidewindow
16 lookupreg = win32.lookupreg
16 lookupreg = win32.lookupreg
17 makedir = win32.makedir
17 makedir = win32.makedir
18 nlinks = win32.nlinks
18 nlinks = win32.nlinks
19 oslink = win32.oslink
19 oslink = win32.oslink
20 samedevice = win32.samedevice
20 samedevice = win32.samedevice
21 samefile = win32.samefile
21 samefile = win32.samefile
22 setsignalhandler = win32.setsignalhandler
22 setsignalhandler = win32.setsignalhandler
23 spawndetached = win32.spawndetached
23 spawndetached = win32.spawndetached
24 termwidth = win32.termwidth
24 termwidth = win32.termwidth
25 testpid = win32.testpid
25 testpid = win32.testpid
26 unlink = win32.unlink
26 unlink = win32.unlink
27
27
28 nulldev = 'NUL:'
28 nulldev = 'NUL:'
29 umask = 002
29 umask = 002
30
30
31 # wrap osutil.posixfile to provide friendlier exceptions
31 # wrap osutil.posixfile to provide friendlier exceptions
32 def posixfile(name, mode='r', buffering=-1):
32 def posixfile(name, mode='r', buffering=-1):
33 try:
33 try:
34 return osutil.posixfile(name, mode, buffering)
34 return osutil.posixfile(name, mode, buffering)
35 except WindowsError, err:
35 except WindowsError, err:
36 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
36 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
37 posixfile.__doc__ = osutil.posixfile.__doc__
37 posixfile.__doc__ = osutil.posixfile.__doc__
38
38
39 class winstdout(object):
39 class winstdout(object):
40 '''stdout on windows misbehaves if sent through a pipe'''
40 '''stdout on windows misbehaves if sent through a pipe'''
41
41
42 def __init__(self, fp):
42 def __init__(self, fp):
43 self.fp = fp
43 self.fp = fp
44
44
45 def __getattr__(self, key):
45 def __getattr__(self, key):
46 return getattr(self.fp, key)
46 return getattr(self.fp, key)
47
47
48 def close(self):
48 def close(self):
49 try:
49 try:
50 self.fp.close()
50 self.fp.close()
51 except IOError:
51 except IOError:
52 pass
52 pass
53
53
54 def write(self, s):
54 def write(self, s):
55 try:
55 try:
56 # This is workaround for "Not enough space" error on
56 # This is workaround for "Not enough space" error on
57 # writing large size of data to console.
57 # writing large size of data to console.
58 limit = 16000
58 limit = 16000
59 l = len(s)
59 l = len(s)
60 start = 0
60 start = 0
61 self.softspace = 0
61 self.softspace = 0
62 while start < l:
62 while start < l:
63 end = start + limit
63 end = start + limit
64 self.fp.write(s[start:end])
64 self.fp.write(s[start:end])
65 start = end
65 start = end
66 except IOError, inst:
66 except IOError, inst:
67 if inst.errno != 0:
67 if inst.errno != 0:
68 raise
68 raise
69 self.close()
69 self.close()
70 raise IOError(errno.EPIPE, 'Broken pipe')
70 raise IOError(errno.EPIPE, 'Broken pipe')
71
71
72 def flush(self):
72 def flush(self):
73 try:
73 try:
74 return self.fp.flush()
74 return self.fp.flush()
75 except IOError, inst:
75 except IOError, inst:
76 if inst.errno != errno.EINVAL:
76 if inst.errno != errno.EINVAL:
77 raise
77 raise
78 self.close()
78 self.close()
79 raise IOError(errno.EPIPE, 'Broken pipe')
79 raise IOError(errno.EPIPE, 'Broken pipe')
80
80
81 sys.__stdout__ = sys.stdout = winstdout(sys.stdout)
81 sys.__stdout__ = sys.stdout = winstdout(sys.stdout)
82
82
83 def _is_win_9x():
83 def _is_win_9x():
84 '''return true if run on windows 95, 98 or me.'''
84 '''return true if run on windows 95, 98 or me.'''
85 try:
85 try:
86 return sys.getwindowsversion()[3] == 1
86 return sys.getwindowsversion()[3] == 1
87 except AttributeError:
87 except AttributeError:
88 return 'command' in os.environ.get('comspec', '')
88 return 'command' in os.environ.get('comspec', '')
89
89
90 def openhardlinks():
90 def openhardlinks():
91 return not _is_win_9x()
91 return not _is_win_9x()
92
92
93 def parsepatchoutput(output_line):
93 def parsepatchoutput(output_line):
94 """parses the output produced by patch and returns the filename"""
94 """parses the output produced by patch and returns the filename"""
95 pf = output_line[14:]
95 pf = output_line[14:]
96 if pf[0] == '`':
96 if pf[0] == '`':
97 pf = pf[1:-1] # Remove the quotes
97 pf = pf[1:-1] # Remove the quotes
98 return pf
98 return pf
99
99
100 def sshargs(sshcmd, host, user, port):
100 def sshargs(sshcmd, host, user, port):
101 '''Build argument list for ssh or Plink'''
101 '''Build argument list for ssh or Plink'''
102 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
102 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
103 args = user and ("%s@%s" % (user, host)) or host
103 args = user and ("%s@%s" % (user, host)) or host
104 return port and ("%s %s %s" % (args, pflag, port)) or args
104 return port and ("%s %s %s" % (args, pflag, port)) or args
105
105
106 def setflags(f, l, x):
106 def setflags(f, l, x):
107 pass
107 pass
108
108
109 def copymode(src, dst, mode=None):
109 def copymode(src, dst, mode=None):
110 pass
110 pass
111
111
112 def checkexec(path):
112 def checkexec(path):
113 return False
113 return False
114
114
115 def checklink(path):
115 def checklink(path):
116 return False
116 return False
117
117
118 def setbinary(fd):
118 def setbinary(fd):
119 # When run without console, pipes may expose invalid
119 # When run without console, pipes may expose invalid
120 # fileno(), usually set to -1.
120 # fileno(), usually set to -1.
121 fno = getattr(fd, 'fileno', None)
121 fno = getattr(fd, 'fileno', None)
122 if fno is not None and fno() >= 0:
122 if fno is not None and fno() >= 0:
123 msvcrt.setmode(fno(), os.O_BINARY)
123 msvcrt.setmode(fno(), os.O_BINARY)
124
124
125 def pconvert(path):
125 def pconvert(path):
126 return '/'.join(path.split(os.sep))
126 return '/'.join(path.split(os.sep))
127
127
128 def localpath(path):
128 def localpath(path):
129 return path.replace('/', '\\')
129 return path.replace('/', '\\')
130
130
131 def normpath(path):
131 def normpath(path):
132 return pconvert(os.path.normpath(path))
132 return pconvert(os.path.normpath(path))
133
133
134 normcase = os.path.normcase
134 def normcase(path):
135 return path.upper()
135
136
136 def realpath(path):
137 def realpath(path):
137 '''
138 '''
138 Returns the true, canonical file system path equivalent to the given
139 Returns the true, canonical file system path equivalent to the given
139 path.
140 path.
140 '''
141 '''
141 # TODO: There may be a more clever way to do this that also handles other,
142 # TODO: There may be a more clever way to do this that also handles other,
142 # less common file systems.
143 # less common file systems.
143 return os.path.normpath(normcase(os.path.realpath(path)))
144 return os.path.normpath(normcase(os.path.realpath(path)))
144
145
145 def samestat(s1, s2):
146 def samestat(s1, s2):
146 return False
147 return False
147
148
148 # A sequence of backslashes is special iff it precedes a double quote:
149 # A sequence of backslashes is special iff it precedes a double quote:
149 # - if there's an even number of backslashes, the double quote is not
150 # - if there's an even number of backslashes, the double quote is not
150 # quoted (i.e. it ends the quoted region)
151 # quoted (i.e. it ends the quoted region)
151 # - if there's an odd number of backslashes, the double quote is quoted
152 # - if there's an odd number of backslashes, the double quote is quoted
152 # - in both cases, every pair of backslashes is unquoted into a single
153 # - in both cases, every pair of backslashes is unquoted into a single
153 # backslash
154 # backslash
154 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
155 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
155 # So, to quote a string, we must surround it in double quotes, double
156 # So, to quote a string, we must surround it in double quotes, double
156 # the number of backslashes that preceed double quotes and add another
157 # the number of backslashes that preceed double quotes and add another
157 # backslash before every double quote (being careful with the double
158 # backslash before every double quote (being careful with the double
158 # quote we've appended to the end)
159 # quote we've appended to the end)
159 _quotere = None
160 _quotere = None
160 def shellquote(s):
161 def shellquote(s):
161 global _quotere
162 global _quotere
162 if _quotere is None:
163 if _quotere is None:
163 _quotere = re.compile(r'(\\*)("|\\$)')
164 _quotere = re.compile(r'(\\*)("|\\$)')
164 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
165 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
165
166
166 def quotecommand(cmd):
167 def quotecommand(cmd):
167 """Build a command string suitable for os.popen* calls."""
168 """Build a command string suitable for os.popen* calls."""
168 if sys.version_info < (2, 7, 1):
169 if sys.version_info < (2, 7, 1):
169 # Python versions since 2.7.1 do this extra quoting themselves
170 # Python versions since 2.7.1 do this extra quoting themselves
170 return '"' + cmd + '"'
171 return '"' + cmd + '"'
171 return cmd
172 return cmd
172
173
173 def popen(command, mode='r'):
174 def popen(command, mode='r'):
174 # Work around "popen spawned process may not write to stdout
175 # Work around "popen spawned process may not write to stdout
175 # under windows"
176 # under windows"
176 # http://bugs.python.org/issue1366
177 # http://bugs.python.org/issue1366
177 command += " 2> %s" % nulldev
178 command += " 2> %s" % nulldev
178 return os.popen(quotecommand(command), mode)
179 return os.popen(quotecommand(command), mode)
179
180
180 def explainexit(code):
181 def explainexit(code):
181 return _("exited with status %d") % code, code
182 return _("exited with status %d") % code, code
182
183
183 # if you change this stub into a real check, please try to implement the
184 # if you change this stub into a real check, please try to implement the
184 # username and groupname functions above, too.
185 # username and groupname functions above, too.
185 def isowner(st):
186 def isowner(st):
186 return True
187 return True
187
188
188 def findexe(command):
189 def findexe(command):
189 '''Find executable for command searching like cmd.exe does.
190 '''Find executable for command searching like cmd.exe does.
190 If command is a basename then PATH is searched for command.
191 If command is a basename then PATH is searched for command.
191 PATH isn't searched if command is an absolute or relative path.
192 PATH isn't searched if command is an absolute or relative path.
192 An extension from PATHEXT is found and added if not present.
193 An extension from PATHEXT is found and added if not present.
193 If command isn't found None is returned.'''
194 If command isn't found None is returned.'''
194 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
195 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
195 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
196 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
196 if os.path.splitext(command)[1].lower() in pathexts:
197 if os.path.splitext(command)[1].lower() in pathexts:
197 pathexts = ['']
198 pathexts = ['']
198
199
199 def findexisting(pathcommand):
200 def findexisting(pathcommand):
200 'Will append extension (if needed) and return existing file'
201 'Will append extension (if needed) and return existing file'
201 for ext in pathexts:
202 for ext in pathexts:
202 executable = pathcommand + ext
203 executable = pathcommand + ext
203 if os.path.exists(executable):
204 if os.path.exists(executable):
204 return executable
205 return executable
205 return None
206 return None
206
207
207 if os.sep in command:
208 if os.sep in command:
208 return findexisting(command)
209 return findexisting(command)
209
210
210 for path in os.environ.get('PATH', '').split(os.pathsep):
211 for path in os.environ.get('PATH', '').split(os.pathsep):
211 executable = findexisting(os.path.join(path, command))
212 executable = findexisting(os.path.join(path, command))
212 if executable is not None:
213 if executable is not None:
213 return executable
214 return executable
214 return findexisting(os.path.expanduser(os.path.expandvars(command)))
215 return findexisting(os.path.expanduser(os.path.expandvars(command)))
215
216
216 def statfiles(files):
217 def statfiles(files):
217 '''Stat each file in files and yield stat or None if file does not exist.
218 '''Stat each file in files and yield stat or None if file does not exist.
218 Cluster and cache stat per directory to minimize number of OS stat calls.'''
219 Cluster and cache stat per directory to minimize number of OS stat calls.'''
219 dircache = {} # dirname -> filename -> status | None if file does not exist
220 dircache = {} # dirname -> filename -> status | None if file does not exist
220 for nf in files:
221 for nf in files:
221 nf = normcase(nf)
222 nf = normcase(nf)
222 dir, base = os.path.split(nf)
223 dir, base = os.path.split(nf)
223 if not dir:
224 if not dir:
224 dir = '.'
225 dir = '.'
225 cache = dircache.get(dir, None)
226 cache = dircache.get(dir, None)
226 if cache is None:
227 if cache is None:
227 try:
228 try:
228 dmap = dict([(normcase(n), s)
229 dmap = dict([(normcase(n), s)
229 for n, k, s in osutil.listdir(dir, True)])
230 for n, k, s in osutil.listdir(dir, True)])
230 except OSError, err:
231 except OSError, err:
231 # handle directory not found in Python version prior to 2.5
232 # handle directory not found in Python version prior to 2.5
232 # Python <= 2.4 returns native Windows code 3 in errno
233 # Python <= 2.4 returns native Windows code 3 in errno
233 # Python >= 2.5 returns ENOENT and adds winerror field
234 # Python >= 2.5 returns ENOENT and adds winerror field
234 # EINVAL is raised if dir is not a directory.
235 # EINVAL is raised if dir is not a directory.
235 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
236 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
236 errno.ENOTDIR):
237 errno.ENOTDIR):
237 raise
238 raise
238 dmap = {}
239 dmap = {}
239 cache = dircache.setdefault(dir, dmap)
240 cache = dircache.setdefault(dir, dmap)
240 yield cache.get(base, None)
241 yield cache.get(base, None)
241
242
242 def username(uid=None):
243 def username(uid=None):
243 """Return the name of the user with the given uid.
244 """Return the name of the user with the given uid.
244
245
245 If uid is None, return the name of the current user."""
246 If uid is None, return the name of the current user."""
246 return None
247 return None
247
248
248 def groupname(gid=None):
249 def groupname(gid=None):
249 """Return the name of the group with the given gid.
250 """Return the name of the group with the given gid.
250
251
251 If gid is None, return the name of the current group."""
252 If gid is None, return the name of the current group."""
252 return None
253 return None
253
254
254 def _removedirs(name):
255 def _removedirs(name):
255 """special version of os.removedirs that does not remove symlinked
256 """special version of os.removedirs that does not remove symlinked
256 directories or junction points if they actually contain files"""
257 directories or junction points if they actually contain files"""
257 if osutil.listdir(name):
258 if osutil.listdir(name):
258 return
259 return
259 os.rmdir(name)
260 os.rmdir(name)
260 head, tail = os.path.split(name)
261 head, tail = os.path.split(name)
261 if not tail:
262 if not tail:
262 head, tail = os.path.split(head)
263 head, tail = os.path.split(head)
263 while head and tail:
264 while head and tail:
264 try:
265 try:
265 if osutil.listdir(head):
266 if osutil.listdir(head):
266 return
267 return
267 os.rmdir(head)
268 os.rmdir(head)
268 except (ValueError, OSError):
269 except (ValueError, OSError):
269 break
270 break
270 head, tail = os.path.split(head)
271 head, tail = os.path.split(head)
271
272
272 def unlinkpath(f):
273 def unlinkpath(f):
273 """unlink and remove the directory if it is empty"""
274 """unlink and remove the directory if it is empty"""
274 unlink(f)
275 unlink(f)
275 # try removing directories that might now be empty
276 # try removing directories that might now be empty
276 try:
277 try:
277 _removedirs(os.path.dirname(f))
278 _removedirs(os.path.dirname(f))
278 except OSError:
279 except OSError:
279 pass
280 pass
280
281
281 def rename(src, dst):
282 def rename(src, dst):
282 '''atomically rename file src to dst, replacing dst if it exists'''
283 '''atomically rename file src to dst, replacing dst if it exists'''
283 try:
284 try:
284 os.rename(src, dst)
285 os.rename(src, dst)
285 except OSError, e:
286 except OSError, e:
286 if e.errno != errno.EEXIST:
287 if e.errno != errno.EEXIST:
287 raise
288 raise
288 unlink(dst)
289 unlink(dst)
289 os.rename(src, dst)
290 os.rename(src, dst)
290
291
291 def gethgcmd():
292 def gethgcmd():
292 return [sys.executable] + sys.argv[:1]
293 return [sys.executable] + sys.argv[:1]
293
294
294 def termwidth():
295 def termwidth():
295 # cmd.exe does not handle CR like a unix console, the CR is
296 # cmd.exe does not handle CR like a unix console, the CR is
296 # counted in the line length. On 80 columns consoles, if 80
297 # counted in the line length. On 80 columns consoles, if 80
297 # characters are written, the following CR won't apply on the
298 # characters are written, the following CR won't apply on the
298 # current line but on the new one. Keep room for it.
299 # current line but on the new one. Keep room for it.
299 return 79
300 return 79
300
301
301 def groupmembers(name):
302 def groupmembers(name):
302 # Don't support groups on Windows for now
303 # Don't support groups on Windows for now
303 raise KeyError()
304 raise KeyError()
304
305
305 def isexec(f):
306 def isexec(f):
306 return False
307 return False
307
308
308 class cachestat(object):
309 class cachestat(object):
309 def __init__(self, path):
310 def __init__(self, path):
310 pass
311 pass
311
312
312 def cacheable(self):
313 def cacheable(self):
313 return False
314 return False
314
315
315 expandglobs = True
316 expandglobs = True
General Comments 0
You need to be logged in to leave comments. Login now