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