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