##// END OF EJS Templates
windows: use 'str.replace()' instead of combination of split() and join()...
FUJIWARA Katsunori -
r16076:e7701459 default
parent child Browse files
Show More
@@ -1,319 +1,319
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 = 0022
29 umask = 0022
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 path.replace(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 encodinglower = None
134 encodinglower = None
135 encodingupper = None
135 encodingupper = None
136
136
137 def normcase(path):
137 def normcase(path):
138 return encodingupper(path)
138 return encodingupper(path)
139
139
140 def realpath(path):
140 def realpath(path):
141 '''
141 '''
142 Returns the true, canonical file system path equivalent to the given
142 Returns the true, canonical file system path equivalent to the given
143 path.
143 path.
144 '''
144 '''
145 # TODO: There may be a more clever way to do this that also handles other,
145 # TODO: There may be a more clever way to do this that also handles other,
146 # less common file systems.
146 # less common file systems.
147 return os.path.normpath(normcase(os.path.realpath(path)))
147 return os.path.normpath(normcase(os.path.realpath(path)))
148
148
149 def samestat(s1, s2):
149 def samestat(s1, s2):
150 return False
150 return False
151
151
152 # A sequence of backslashes is special iff it precedes a double quote:
152 # A sequence of backslashes is special iff it precedes a double quote:
153 # - if there's an even number of backslashes, the double quote is not
153 # - if there's an even number of backslashes, the double quote is not
154 # quoted (i.e. it ends the quoted region)
154 # quoted (i.e. it ends the quoted region)
155 # - if there's an odd number of backslashes, the double quote is quoted
155 # - if there's an odd number of backslashes, the double quote is quoted
156 # - in both cases, every pair of backslashes is unquoted into a single
156 # - in both cases, every pair of backslashes is unquoted into a single
157 # backslash
157 # backslash
158 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
158 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
159 # So, to quote a string, we must surround it in double quotes, double
159 # So, to quote a string, we must surround it in double quotes, double
160 # the number of backslashes that preceed double quotes and add another
160 # the number of backslashes that preceed double quotes and add another
161 # backslash before every double quote (being careful with the double
161 # backslash before every double quote (being careful with the double
162 # quote we've appended to the end)
162 # quote we've appended to the end)
163 _quotere = None
163 _quotere = None
164 def shellquote(s):
164 def shellquote(s):
165 global _quotere
165 global _quotere
166 if _quotere is None:
166 if _quotere is None:
167 _quotere = re.compile(r'(\\*)("|\\$)')
167 _quotere = re.compile(r'(\\*)("|\\$)')
168 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
168 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
169
169
170 def quotecommand(cmd):
170 def quotecommand(cmd):
171 """Build a command string suitable for os.popen* calls."""
171 """Build a command string suitable for os.popen* calls."""
172 if sys.version_info < (2, 7, 1):
172 if sys.version_info < (2, 7, 1):
173 # Python versions since 2.7.1 do this extra quoting themselves
173 # Python versions since 2.7.1 do this extra quoting themselves
174 return '"' + cmd + '"'
174 return '"' + cmd + '"'
175 return cmd
175 return cmd
176
176
177 def popen(command, mode='r'):
177 def popen(command, mode='r'):
178 # Work around "popen spawned process may not write to stdout
178 # Work around "popen spawned process may not write to stdout
179 # under windows"
179 # under windows"
180 # http://bugs.python.org/issue1366
180 # http://bugs.python.org/issue1366
181 command += " 2> %s" % nulldev
181 command += " 2> %s" % nulldev
182 return os.popen(quotecommand(command), mode)
182 return os.popen(quotecommand(command), mode)
183
183
184 def explainexit(code):
184 def explainexit(code):
185 return _("exited with status %d") % code, code
185 return _("exited with status %d") % code, code
186
186
187 # if you change this stub into a real check, please try to implement the
187 # if you change this stub into a real check, please try to implement the
188 # username and groupname functions above, too.
188 # username and groupname functions above, too.
189 def isowner(st):
189 def isowner(st):
190 return True
190 return True
191
191
192 def findexe(command):
192 def findexe(command):
193 '''Find executable for command searching like cmd.exe does.
193 '''Find executable for command searching like cmd.exe does.
194 If command is a basename then PATH is searched for command.
194 If command is a basename then PATH is searched for command.
195 PATH isn't searched if command is an absolute or relative path.
195 PATH isn't searched if command is an absolute or relative path.
196 An extension from PATHEXT is found and added if not present.
196 An extension from PATHEXT is found and added if not present.
197 If command isn't found None is returned.'''
197 If command isn't found None is returned.'''
198 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
198 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
199 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
199 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
200 if os.path.splitext(command)[1].lower() in pathexts:
200 if os.path.splitext(command)[1].lower() in pathexts:
201 pathexts = ['']
201 pathexts = ['']
202
202
203 def findexisting(pathcommand):
203 def findexisting(pathcommand):
204 'Will append extension (if needed) and return existing file'
204 'Will append extension (if needed) and return existing file'
205 for ext in pathexts:
205 for ext in pathexts:
206 executable = pathcommand + ext
206 executable = pathcommand + ext
207 if os.path.exists(executable):
207 if os.path.exists(executable):
208 return executable
208 return executable
209 return None
209 return None
210
210
211 if os.sep in command:
211 if os.sep in command:
212 return findexisting(command)
212 return findexisting(command)
213
213
214 for path in os.environ.get('PATH', '').split(os.pathsep):
214 for path in os.environ.get('PATH', '').split(os.pathsep):
215 executable = findexisting(os.path.join(path, command))
215 executable = findexisting(os.path.join(path, command))
216 if executable is not None:
216 if executable is not None:
217 return executable
217 return executable
218 return findexisting(os.path.expanduser(os.path.expandvars(command)))
218 return findexisting(os.path.expanduser(os.path.expandvars(command)))
219
219
220 def statfiles(files):
220 def statfiles(files):
221 '''Stat each file in files and yield stat or None if file does not exist.
221 '''Stat each file in files and yield stat or None if file does not exist.
222 Cluster and cache stat per directory to minimize number of OS stat calls.'''
222 Cluster and cache stat per directory to minimize number of OS stat calls.'''
223 dircache = {} # dirname -> filename -> status | None if file does not exist
223 dircache = {} # dirname -> filename -> status | None if file does not exist
224 for nf in files:
224 for nf in files:
225 nf = normcase(nf)
225 nf = normcase(nf)
226 dir, base = os.path.split(nf)
226 dir, base = os.path.split(nf)
227 if not dir:
227 if not dir:
228 dir = '.'
228 dir = '.'
229 cache = dircache.get(dir, None)
229 cache = dircache.get(dir, None)
230 if cache is None:
230 if cache is None:
231 try:
231 try:
232 dmap = dict([(normcase(n), s)
232 dmap = dict([(normcase(n), s)
233 for n, k, s in osutil.listdir(dir, True)])
233 for n, k, s in osutil.listdir(dir, True)])
234 except OSError, err:
234 except OSError, err:
235 # handle directory not found in Python version prior to 2.5
235 # handle directory not found in Python version prior to 2.5
236 # Python <= 2.4 returns native Windows code 3 in errno
236 # Python <= 2.4 returns native Windows code 3 in errno
237 # Python >= 2.5 returns ENOENT and adds winerror field
237 # Python >= 2.5 returns ENOENT and adds winerror field
238 # EINVAL is raised if dir is not a directory.
238 # EINVAL is raised if dir is not a directory.
239 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
239 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
240 errno.ENOTDIR):
240 errno.ENOTDIR):
241 raise
241 raise
242 dmap = {}
242 dmap = {}
243 cache = dircache.setdefault(dir, dmap)
243 cache = dircache.setdefault(dir, dmap)
244 yield cache.get(base, None)
244 yield cache.get(base, None)
245
245
246 def username(uid=None):
246 def username(uid=None):
247 """Return the name of the user with the given uid.
247 """Return the name of the user with the given uid.
248
248
249 If uid is None, return the name of the current user."""
249 If uid is None, return the name of the current user."""
250 return None
250 return None
251
251
252 def groupname(gid=None):
252 def groupname(gid=None):
253 """Return the name of the group with the given gid.
253 """Return the name of the group with the given gid.
254
254
255 If gid is None, return the name of the current group."""
255 If gid is None, return the name of the current group."""
256 return None
256 return None
257
257
258 def _removedirs(name):
258 def _removedirs(name):
259 """special version of os.removedirs that does not remove symlinked
259 """special version of os.removedirs that does not remove symlinked
260 directories or junction points if they actually contain files"""
260 directories or junction points if they actually contain files"""
261 if osutil.listdir(name):
261 if osutil.listdir(name):
262 return
262 return
263 os.rmdir(name)
263 os.rmdir(name)
264 head, tail = os.path.split(name)
264 head, tail = os.path.split(name)
265 if not tail:
265 if not tail:
266 head, tail = os.path.split(head)
266 head, tail = os.path.split(head)
267 while head and tail:
267 while head and tail:
268 try:
268 try:
269 if osutil.listdir(head):
269 if osutil.listdir(head):
270 return
270 return
271 os.rmdir(head)
271 os.rmdir(head)
272 except (ValueError, OSError):
272 except (ValueError, OSError):
273 break
273 break
274 head, tail = os.path.split(head)
274 head, tail = os.path.split(head)
275
275
276 def unlinkpath(f):
276 def unlinkpath(f):
277 """unlink and remove the directory if it is empty"""
277 """unlink and remove the directory if it is empty"""
278 unlink(f)
278 unlink(f)
279 # try removing directories that might now be empty
279 # try removing directories that might now be empty
280 try:
280 try:
281 _removedirs(os.path.dirname(f))
281 _removedirs(os.path.dirname(f))
282 except OSError:
282 except OSError:
283 pass
283 pass
284
284
285 def rename(src, dst):
285 def rename(src, dst):
286 '''atomically rename file src to dst, replacing dst if it exists'''
286 '''atomically rename file src to dst, replacing dst if it exists'''
287 try:
287 try:
288 os.rename(src, dst)
288 os.rename(src, dst)
289 except OSError, e:
289 except OSError, e:
290 if e.errno != errno.EEXIST:
290 if e.errno != errno.EEXIST:
291 raise
291 raise
292 unlink(dst)
292 unlink(dst)
293 os.rename(src, dst)
293 os.rename(src, dst)
294
294
295 def gethgcmd():
295 def gethgcmd():
296 return [sys.executable] + sys.argv[:1]
296 return [sys.executable] + sys.argv[:1]
297
297
298 def termwidth():
298 def termwidth():
299 # cmd.exe does not handle CR like a unix console, the CR is
299 # cmd.exe does not handle CR like a unix console, the CR is
300 # counted in the line length. On 80 columns consoles, if 80
300 # counted in the line length. On 80 columns consoles, if 80
301 # characters are written, the following CR won't apply on the
301 # characters are written, the following CR won't apply on the
302 # current line but on the new one. Keep room for it.
302 # current line but on the new one. Keep room for it.
303 return 79
303 return 79
304
304
305 def groupmembers(name):
305 def groupmembers(name):
306 # Don't support groups on Windows for now
306 # Don't support groups on Windows for now
307 raise KeyError()
307 raise KeyError()
308
308
309 def isexec(f):
309 def isexec(f):
310 return False
310 return False
311
311
312 class cachestat(object):
312 class cachestat(object):
313 def __init__(self, path):
313 def __init__(self, path):
314 pass
314 pass
315
315
316 def cacheable(self):
316 def cacheable(self):
317 return False
317 return False
318
318
319 expandglobs = True
319 expandglobs = True
General Comments 0
You need to be logged in to leave comments. Login now