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