##// END OF EJS Templates
windows: use win32.unlink in unlinkpath()...
Adrian Buehlmann -
r13777:f6e5035d default
parent child Browse files
Show More
@@ -1,322 +1,322 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, error
9 import osutil, error
10 import errno, msvcrt, os, re, sys, subprocess
10 import errno, msvcrt, os, re, sys, subprocess
11
11
12 nulldev = 'NUL:'
12 nulldev = 'NUL:'
13 umask = 002
13 umask = 002
14
14
15 # wrap osutil.posixfile to provide friendlier exceptions
15 # wrap osutil.posixfile to provide friendlier exceptions
16 def posixfile(name, mode='r', buffering=-1):
16 def posixfile(name, mode='r', buffering=-1):
17 try:
17 try:
18 return osutil.posixfile(name, mode, buffering)
18 return osutil.posixfile(name, mode, buffering)
19 except WindowsError, err:
19 except WindowsError, err:
20 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
20 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
21 posixfile.__doc__ = osutil.posixfile.__doc__
21 posixfile.__doc__ = osutil.posixfile.__doc__
22
22
23 class winstdout(object):
23 class winstdout(object):
24 '''stdout on windows misbehaves if sent through a pipe'''
24 '''stdout on windows misbehaves if sent through a pipe'''
25
25
26 def __init__(self, fp):
26 def __init__(self, fp):
27 self.fp = fp
27 self.fp = fp
28
28
29 def __getattr__(self, key):
29 def __getattr__(self, key):
30 return getattr(self.fp, key)
30 return getattr(self.fp, key)
31
31
32 def close(self):
32 def close(self):
33 try:
33 try:
34 self.fp.close()
34 self.fp.close()
35 except: pass
35 except: pass
36
36
37 def write(self, s):
37 def write(self, s):
38 try:
38 try:
39 # This is workaround for "Not enough space" error on
39 # This is workaround for "Not enough space" error on
40 # writing large size of data to console.
40 # writing large size of data to console.
41 limit = 16000
41 limit = 16000
42 l = len(s)
42 l = len(s)
43 start = 0
43 start = 0
44 self.softspace = 0
44 self.softspace = 0
45 while start < l:
45 while start < l:
46 end = start + limit
46 end = start + limit
47 self.fp.write(s[start:end])
47 self.fp.write(s[start:end])
48 start = end
48 start = end
49 except IOError, inst:
49 except IOError, inst:
50 if inst.errno != 0:
50 if inst.errno != 0:
51 raise
51 raise
52 self.close()
52 self.close()
53 raise IOError(errno.EPIPE, 'Broken pipe')
53 raise IOError(errno.EPIPE, 'Broken pipe')
54
54
55 def flush(self):
55 def flush(self):
56 try:
56 try:
57 return self.fp.flush()
57 return self.fp.flush()
58 except IOError, inst:
58 except IOError, inst:
59 if inst.errno != errno.EINVAL:
59 if inst.errno != errno.EINVAL:
60 raise
60 raise
61 self.close()
61 self.close()
62 raise IOError(errno.EPIPE, 'Broken pipe')
62 raise IOError(errno.EPIPE, 'Broken pipe')
63
63
64 sys.stdout = winstdout(sys.stdout)
64 sys.stdout = winstdout(sys.stdout)
65
65
66 def _is_win_9x():
66 def _is_win_9x():
67 '''return true if run on windows 95, 98 or me.'''
67 '''return true if run on windows 95, 98 or me.'''
68 try:
68 try:
69 return sys.getwindowsversion()[3] == 1
69 return sys.getwindowsversion()[3] == 1
70 except AttributeError:
70 except AttributeError:
71 return 'command' in os.environ.get('comspec', '')
71 return 'command' in os.environ.get('comspec', '')
72
72
73 def openhardlinks():
73 def openhardlinks():
74 return not _is_win_9x()
74 return not _is_win_9x()
75
75
76 _HKEY_LOCAL_MACHINE = 0x80000002L
76 _HKEY_LOCAL_MACHINE = 0x80000002L
77
77
78 def system_rcpath():
78 def system_rcpath():
79 '''return default os-specific hgrc search path'''
79 '''return default os-specific hgrc search path'''
80 rcpath = []
80 rcpath = []
81 filename = executable_path()
81 filename = executable_path()
82 # Use mercurial.ini found in directory with hg.exe
82 # Use mercurial.ini found in directory with hg.exe
83 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
83 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
84 if os.path.isfile(progrc):
84 if os.path.isfile(progrc):
85 rcpath.append(progrc)
85 rcpath.append(progrc)
86 return rcpath
86 return rcpath
87 # Use hgrc.d found in directory with hg.exe
87 # Use hgrc.d found in directory with hg.exe
88 progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
88 progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
89 if os.path.isdir(progrcd):
89 if os.path.isdir(progrcd):
90 for f, kind in osutil.listdir(progrcd):
90 for f, kind in osutil.listdir(progrcd):
91 if f.endswith('.rc'):
91 if f.endswith('.rc'):
92 rcpath.append(os.path.join(progrcd, f))
92 rcpath.append(os.path.join(progrcd, f))
93 return rcpath
93 return rcpath
94 # else look for a system rcpath in the registry
94 # else look for a system rcpath in the registry
95 value = lookup_reg('SOFTWARE\\Mercurial', None, _HKEY_LOCAL_MACHINE)
95 value = lookup_reg('SOFTWARE\\Mercurial', None, _HKEY_LOCAL_MACHINE)
96 if not isinstance(value, str) or not value:
96 if not isinstance(value, str) or not value:
97 return rcpath
97 return rcpath
98 value = value.replace('/', os.sep)
98 value = value.replace('/', os.sep)
99 for p in value.split(os.pathsep):
99 for p in value.split(os.pathsep):
100 if p.lower().endswith('mercurial.ini'):
100 if p.lower().endswith('mercurial.ini'):
101 rcpath.append(p)
101 rcpath.append(p)
102 elif os.path.isdir(p):
102 elif os.path.isdir(p):
103 for f, kind in osutil.listdir(p):
103 for f, kind in osutil.listdir(p):
104 if f.endswith('.rc'):
104 if f.endswith('.rc'):
105 rcpath.append(os.path.join(p, f))
105 rcpath.append(os.path.join(p, f))
106 return rcpath
106 return rcpath
107
107
108 def user_rcpath():
108 def user_rcpath():
109 '''return os-specific hgrc search path to the user dir'''
109 '''return os-specific hgrc search path to the user dir'''
110 home = os.path.expanduser('~')
110 home = os.path.expanduser('~')
111 path = [os.path.join(home, 'mercurial.ini'),
111 path = [os.path.join(home, 'mercurial.ini'),
112 os.path.join(home, '.hgrc')]
112 os.path.join(home, '.hgrc')]
113 userprofile = os.environ.get('USERPROFILE')
113 userprofile = os.environ.get('USERPROFILE')
114 if userprofile:
114 if userprofile:
115 path.append(os.path.join(userprofile, 'mercurial.ini'))
115 path.append(os.path.join(userprofile, 'mercurial.ini'))
116 path.append(os.path.join(userprofile, '.hgrc'))
116 path.append(os.path.join(userprofile, '.hgrc'))
117 return path
117 return path
118
118
119 def parse_patch_output(output_line):
119 def parse_patch_output(output_line):
120 """parses the output produced by patch and returns the filename"""
120 """parses the output produced by patch and returns the filename"""
121 pf = output_line[14:]
121 pf = output_line[14:]
122 if pf[0] == '`':
122 if pf[0] == '`':
123 pf = pf[1:-1] # Remove the quotes
123 pf = pf[1:-1] # Remove the quotes
124 return pf
124 return pf
125
125
126 def sshargs(sshcmd, host, user, port):
126 def sshargs(sshcmd, host, user, port):
127 '''Build argument list for ssh or Plink'''
127 '''Build argument list for ssh or Plink'''
128 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
128 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
129 args = user and ("%s@%s" % (user, host)) or host
129 args = user and ("%s@%s" % (user, host)) or host
130 return port and ("%s %s %s" % (args, pflag, port)) or args
130 return port and ("%s %s %s" % (args, pflag, port)) or args
131
131
132 def set_flags(f, l, x):
132 def set_flags(f, l, x):
133 pass
133 pass
134
134
135 def set_binary(fd):
135 def set_binary(fd):
136 # When run without console, pipes may expose invalid
136 # When run without console, pipes may expose invalid
137 # fileno(), usually set to -1.
137 # fileno(), usually set to -1.
138 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
138 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
139 msvcrt.setmode(fd.fileno(), os.O_BINARY)
139 msvcrt.setmode(fd.fileno(), os.O_BINARY)
140
140
141 def pconvert(path):
141 def pconvert(path):
142 return '/'.join(path.split(os.sep))
142 return '/'.join(path.split(os.sep))
143
143
144 def localpath(path):
144 def localpath(path):
145 return path.replace('/', '\\')
145 return path.replace('/', '\\')
146
146
147 def normpath(path):
147 def normpath(path):
148 return pconvert(os.path.normpath(path))
148 return pconvert(os.path.normpath(path))
149
149
150 def realpath(path):
150 def realpath(path):
151 '''
151 '''
152 Returns the true, canonical file system path equivalent to the given
152 Returns the true, canonical file system path equivalent to the given
153 path.
153 path.
154 '''
154 '''
155 # TODO: There may be a more clever way to do this that also handles other,
155 # TODO: There may be a more clever way to do this that also handles other,
156 # less common file systems.
156 # less common file systems.
157 return os.path.normpath(os.path.normcase(os.path.realpath(path)))
157 return os.path.normpath(os.path.normcase(os.path.realpath(path)))
158
158
159 def samestat(s1, s2):
159 def samestat(s1, s2):
160 return False
160 return False
161
161
162 # A sequence of backslashes is special iff it precedes a double quote:
162 # A sequence of backslashes is special iff it precedes a double quote:
163 # - if there's an even number of backslashes, the double quote is not
163 # - if there's an even number of backslashes, the double quote is not
164 # quoted (i.e. it ends the quoted region)
164 # quoted (i.e. it ends the quoted region)
165 # - if there's an odd number of backslashes, the double quote is quoted
165 # - if there's an odd number of backslashes, the double quote is quoted
166 # - in both cases, every pair of backslashes is unquoted into a single
166 # - in both cases, every pair of backslashes is unquoted into a single
167 # backslash
167 # backslash
168 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
168 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
169 # So, to quote a string, we must surround it in double quotes, double
169 # So, to quote a string, we must surround it in double quotes, double
170 # the number of backslashes that preceed double quotes and add another
170 # the number of backslashes that preceed double quotes and add another
171 # backslash before every double quote (being careful with the double
171 # backslash before every double quote (being careful with the double
172 # quote we've appended to the end)
172 # quote we've appended to the end)
173 _quotere = None
173 _quotere = None
174 def shellquote(s):
174 def shellquote(s):
175 global _quotere
175 global _quotere
176 if _quotere is None:
176 if _quotere is None:
177 _quotere = re.compile(r'(\\*)("|\\$)')
177 _quotere = re.compile(r'(\\*)("|\\$)')
178 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
178 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
179
179
180 def quotecommand(cmd):
180 def quotecommand(cmd):
181 """Build a command string suitable for os.popen* calls."""
181 """Build a command string suitable for os.popen* calls."""
182 if sys.version_info < (2, 7, 1):
182 if sys.version_info < (2, 7, 1):
183 # Python versions since 2.7.1 do this extra quoting themselves
183 # Python versions since 2.7.1 do this extra quoting themselves
184 return '"' + cmd + '"'
184 return '"' + cmd + '"'
185 return cmd
185 return cmd
186
186
187 def popen(command, mode='r'):
187 def popen(command, mode='r'):
188 # Work around "popen spawned process may not write to stdout
188 # Work around "popen spawned process may not write to stdout
189 # under windows"
189 # under windows"
190 # http://bugs.python.org/issue1366
190 # http://bugs.python.org/issue1366
191 command += " 2> %s" % nulldev
191 command += " 2> %s" % nulldev
192 return os.popen(quotecommand(command), mode)
192 return os.popen(quotecommand(command), mode)
193
193
194 def explain_exit(code):
194 def explain_exit(code):
195 return _("exited with status %d") % code, code
195 return _("exited with status %d") % code, code
196
196
197 # if you change this stub into a real check, please try to implement the
197 # if you change this stub into a real check, please try to implement the
198 # username and groupname functions above, too.
198 # username and groupname functions above, too.
199 def isowner(st):
199 def isowner(st):
200 return True
200 return True
201
201
202 def find_exe(command):
202 def find_exe(command):
203 '''Find executable for command searching like cmd.exe does.
203 '''Find executable for command searching like cmd.exe does.
204 If command is a basename then PATH is searched for command.
204 If command is a basename then PATH is searched for command.
205 PATH isn't searched if command is an absolute or relative path.
205 PATH isn't searched if command is an absolute or relative path.
206 An extension from PATHEXT is found and added if not present.
206 An extension from PATHEXT is found and added if not present.
207 If command isn't found None is returned.'''
207 If command isn't found None is returned.'''
208 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
208 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
209 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
209 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
210 if os.path.splitext(command)[1].lower() in pathexts:
210 if os.path.splitext(command)[1].lower() in pathexts:
211 pathexts = ['']
211 pathexts = ['']
212
212
213 def findexisting(pathcommand):
213 def findexisting(pathcommand):
214 'Will append extension (if needed) and return existing file'
214 'Will append extension (if needed) and return existing file'
215 for ext in pathexts:
215 for ext in pathexts:
216 executable = pathcommand + ext
216 executable = pathcommand + ext
217 if os.path.exists(executable):
217 if os.path.exists(executable):
218 return executable
218 return executable
219 return None
219 return None
220
220
221 if os.sep in command:
221 if os.sep in command:
222 return findexisting(command)
222 return findexisting(command)
223
223
224 for path in os.environ.get('PATH', '').split(os.pathsep):
224 for path in os.environ.get('PATH', '').split(os.pathsep):
225 executable = findexisting(os.path.join(path, command))
225 executable = findexisting(os.path.join(path, command))
226 if executable is not None:
226 if executable is not None:
227 return executable
227 return executable
228 return findexisting(os.path.expanduser(os.path.expandvars(command)))
228 return findexisting(os.path.expanduser(os.path.expandvars(command)))
229
229
230 def statfiles(files):
230 def statfiles(files):
231 '''Stat each file in files and yield stat or None if file does not exist.
231 '''Stat each file in files and yield stat or None if file does not exist.
232 Cluster and cache stat per directory to minimize number of OS stat calls.'''
232 Cluster and cache stat per directory to minimize number of OS stat calls.'''
233 ncase = os.path.normcase
233 ncase = os.path.normcase
234 dircache = {} # dirname -> filename -> status | None if file does not exist
234 dircache = {} # dirname -> filename -> status | None if file does not exist
235 for nf in files:
235 for nf in files:
236 nf = ncase(nf)
236 nf = ncase(nf)
237 dir, base = os.path.split(nf)
237 dir, base = os.path.split(nf)
238 if not dir:
238 if not dir:
239 dir = '.'
239 dir = '.'
240 cache = dircache.get(dir, None)
240 cache = dircache.get(dir, None)
241 if cache is None:
241 if cache is None:
242 try:
242 try:
243 dmap = dict([(ncase(n), s)
243 dmap = dict([(ncase(n), s)
244 for n, k, s in osutil.listdir(dir, True)])
244 for n, k, s in osutil.listdir(dir, True)])
245 except OSError, err:
245 except OSError, err:
246 # handle directory not found in Python version prior to 2.5
246 # handle directory not found in Python version prior to 2.5
247 # Python <= 2.4 returns native Windows code 3 in errno
247 # Python <= 2.4 returns native Windows code 3 in errno
248 # Python >= 2.5 returns ENOENT and adds winerror field
248 # Python >= 2.5 returns ENOENT and adds winerror field
249 # EINVAL is raised if dir is not a directory.
249 # EINVAL is raised if dir is not a directory.
250 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
250 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
251 errno.ENOTDIR):
251 errno.ENOTDIR):
252 raise
252 raise
253 dmap = {}
253 dmap = {}
254 cache = dircache.setdefault(dir, dmap)
254 cache = dircache.setdefault(dir, dmap)
255 yield cache.get(base, None)
255 yield cache.get(base, None)
256
256
257 def username(uid=None):
257 def username(uid=None):
258 """Return the name of the user with the given uid.
258 """Return the name of the user with the given uid.
259
259
260 If uid is None, return the name of the current user."""
260 If uid is None, return the name of the current user."""
261 return None
261 return None
262
262
263 def groupname(gid=None):
263 def groupname(gid=None):
264 """Return the name of the group with the given gid.
264 """Return the name of the group with the given gid.
265
265
266 If gid is None, return the name of the current group."""
266 If gid is None, return the name of the current group."""
267 return None
267 return None
268
268
269 def _removedirs(name):
269 def _removedirs(name):
270 """special version of os.removedirs that does not remove symlinked
270 """special version of os.removedirs that does not remove symlinked
271 directories or junction points if they actually contain files"""
271 directories or junction points if they actually contain files"""
272 if osutil.listdir(name):
272 if osutil.listdir(name):
273 return
273 return
274 os.rmdir(name)
274 os.rmdir(name)
275 head, tail = os.path.split(name)
275 head, tail = os.path.split(name)
276 if not tail:
276 if not tail:
277 head, tail = os.path.split(head)
277 head, tail = os.path.split(head)
278 while head and tail:
278 while head and tail:
279 try:
279 try:
280 if osutil.listdir(head):
280 if osutil.listdir(head):
281 return
281 return
282 os.rmdir(head)
282 os.rmdir(head)
283 except:
283 except:
284 break
284 break
285 head, tail = os.path.split(head)
285 head, tail = os.path.split(head)
286
286
287 def unlinkpath(f):
287 def unlinkpath(f):
288 """unlink and remove the directory if it is empty"""
288 """unlink and remove the directory if it is empty"""
289 os.unlink(f)
289 unlink(f)
290 # try removing directories that might now be empty
290 # try removing directories that might now be empty
291 try:
291 try:
292 _removedirs(os.path.dirname(f))
292 _removedirs(os.path.dirname(f))
293 except OSError:
293 except OSError:
294 pass
294 pass
295
295
296 def rename(src, dst):
296 def rename(src, dst):
297 '''atomically rename file src to dst, replacing dst if it exists'''
297 '''atomically rename file src to dst, replacing dst if it exists'''
298 try:
298 try:
299 os.rename(src, dst)
299 os.rename(src, dst)
300 except OSError, e:
300 except OSError, e:
301 if e.errno != errno.EEXIST:
301 if e.errno != errno.EEXIST:
302 raise
302 raise
303 unlink(dst)
303 unlink(dst)
304 os.rename(src, dst)
304 os.rename(src, dst)
305
305
306 def gethgcmd():
306 def gethgcmd():
307 return [sys.executable] + sys.argv[:1]
307 return [sys.executable] + sys.argv[:1]
308
308
309 def termwidth():
309 def termwidth():
310 # cmd.exe does not handle CR like a unix console, the CR is
310 # cmd.exe does not handle CR like a unix console, the CR is
311 # counted in the line length. On 80 columns consoles, if 80
311 # counted in the line length. On 80 columns consoles, if 80
312 # characters are written, the following CR won't apply on the
312 # characters are written, the following CR won't apply on the
313 # current line but on the new one. Keep room for it.
313 # current line but on the new one. Keep room for it.
314 return 79
314 return 79
315
315
316 def groupmembers(name):
316 def groupmembers(name):
317 # Don't support groups on Windows for now
317 # Don't support groups on Windows for now
318 raise KeyError()
318 raise KeyError()
319
319
320 from win32 import *
320 from win32 import *
321
321
322 expandglobs = True
322 expandglobs = True
General Comments 0
You need to be logged in to leave comments. Login now