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