##// END OF EJS Templates
Fix util._statfiles_clustered() failing at root of a windows drive...
Patrick Mezard -
r7301:00d76fa3 default
parent child Browse files
Show More
@@ -1,40 +1,42 b''
1 1 import os
2 2 import stat as _stat
3 3
4 4 def _mode_to_kind(mode):
5 5 if _stat.S_ISREG(mode): return _stat.S_IFREG
6 6 if _stat.S_ISDIR(mode): return _stat.S_IFDIR
7 7 if _stat.S_ISLNK(mode): return _stat.S_IFLNK
8 8 if _stat.S_ISBLK(mode): return _stat.S_IFBLK
9 9 if _stat.S_ISCHR(mode): return _stat.S_IFCHR
10 10 if _stat.S_ISFIFO(mode): return _stat.S_IFIFO
11 11 if _stat.S_ISSOCK(mode): return _stat.S_IFSOCK
12 12 return mode
13 13
14 14 def listdir(path, stat=False, skip=None):
15 15 '''listdir(path, stat=False) -> list_of_tuples
16 16
17 17 Return a sorted list containing information about the entries
18 18 in the directory.
19 19
20 20 If stat is True, each element is a 3-tuple:
21 21
22 22 (name, type, stat object)
23 23
24 24 Otherwise, each element is a 2-tuple:
25 25
26 26 (name, type)
27 27 '''
28 28 result = []
29 prefix = path + os.sep
29 prefix = path
30 if not prefix.endswith(os.sep):
31 prefix += os.sep
30 32 names = os.listdir(path)
31 33 names.sort()
32 34 for fn in names:
33 35 st = os.lstat(prefix + fn)
34 36 if fn == skip and _stat.S_ISDIR(st.st_mode):
35 37 return []
36 38 if stat:
37 39 result.append((fn, _mode_to_kind(st.st_mode), st))
38 40 else:
39 41 result.append((fn, _mode_to_kind(st.st_mode)))
40 42 return result
@@ -1,1934 +1,1934 b''
1 1 """
2 2 util.py - Mercurial utility functions and platform specfic implementations
3 3
4 4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 5 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
6 6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
7 7
8 8 This software may be used and distributed according to the terms
9 9 of the GNU General Public License, incorporated herein by reference.
10 10
11 11 This contains helper routines that are independent of the SCM core and hide
12 12 platform-specific details from the core.
13 13 """
14 14
15 15 from i18n import _
16 16 import cStringIO, errno, getpass, re, shutil, sys, tempfile
17 17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
18 18 import imp
19 19
20 20 # Python compatibility
21 21
22 22 try:
23 23 set = set
24 24 frozenset = frozenset
25 25 except NameError:
26 26 from sets import Set as set, ImmutableSet as frozenset
27 27
28 28 _md5 = None
29 29 def md5(s):
30 30 global _md5
31 31 if _md5 is None:
32 32 try:
33 33 import hashlib
34 34 _md5 = hashlib.md5
35 35 except ImportError:
36 36 import md5
37 37 _md5 = md5.md5
38 38 return _md5(s)
39 39
40 40 _sha1 = None
41 41 def sha1(s):
42 42 global _sha1
43 43 if _sha1 is None:
44 44 try:
45 45 import hashlib
46 46 _sha1 = hashlib.sha1
47 47 except ImportError:
48 48 import sha
49 49 _sha1 = sha.sha
50 50 return _sha1(s)
51 51
52 52 try:
53 53 import subprocess
54 54 subprocess.Popen # trigger ImportError early
55 55 closefds = os.name == 'posix'
56 56 def popen2(cmd, mode='t', bufsize=-1):
57 57 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
58 58 close_fds=closefds,
59 59 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
60 60 return p.stdin, p.stdout
61 61 def popen3(cmd, mode='t', bufsize=-1):
62 62 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
63 63 close_fds=closefds,
64 64 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
65 65 stderr=subprocess.PIPE)
66 66 return p.stdin, p.stdout, p.stderr
67 67 def Popen3(cmd, capturestderr=False, bufsize=-1):
68 68 stderr = capturestderr and subprocess.PIPE or None
69 69 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
70 70 close_fds=closefds,
71 71 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
72 72 stderr=stderr)
73 73 p.fromchild = p.stdout
74 74 p.tochild = p.stdin
75 75 p.childerr = p.stderr
76 76 return p
77 77 except ImportError:
78 78 subprocess = None
79 79 from popen2 import Popen3
80 80 popen2 = os.popen2
81 81 popen3 = os.popen3
82 82
83 83
84 84 try:
85 85 _encoding = os.environ.get("HGENCODING")
86 86 if sys.platform == 'darwin' and not _encoding:
87 87 # On darwin, getpreferredencoding ignores the locale environment and
88 88 # always returns mac-roman. We override this if the environment is
89 89 # not C (has been customized by the user).
90 90 locale.setlocale(locale.LC_CTYPE, '')
91 91 _encoding = locale.getlocale()[1]
92 92 if not _encoding:
93 93 _encoding = locale.getpreferredencoding() or 'ascii'
94 94 except locale.Error:
95 95 _encoding = 'ascii'
96 96 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
97 97 _fallbackencoding = 'ISO-8859-1'
98 98
99 99 def tolocal(s):
100 100 """
101 101 Convert a string from internal UTF-8 to local encoding
102 102
103 103 All internal strings should be UTF-8 but some repos before the
104 104 implementation of locale support may contain latin1 or possibly
105 105 other character sets. We attempt to decode everything strictly
106 106 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
107 107 replace unknown characters.
108 108 """
109 109 for e in ('UTF-8', _fallbackencoding):
110 110 try:
111 111 u = s.decode(e) # attempt strict decoding
112 112 return u.encode(_encoding, "replace")
113 113 except LookupError, k:
114 114 raise Abort(_("%s, please check your locale settings") % k)
115 115 except UnicodeDecodeError:
116 116 pass
117 117 u = s.decode("utf-8", "replace") # last ditch
118 118 return u.encode(_encoding, "replace")
119 119
120 120 def fromlocal(s):
121 121 """
122 122 Convert a string from the local character encoding to UTF-8
123 123
124 124 We attempt to decode strings using the encoding mode set by
125 125 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
126 126 characters will cause an error message. Other modes include
127 127 'replace', which replaces unknown characters with a special
128 128 Unicode character, and 'ignore', which drops the character.
129 129 """
130 130 try:
131 131 return s.decode(_encoding, _encodingmode).encode("utf-8")
132 132 except UnicodeDecodeError, inst:
133 133 sub = s[max(0, inst.start-10):inst.start+10]
134 134 raise Abort("decoding near '%s': %s!" % (sub, inst))
135 135 except LookupError, k:
136 136 raise Abort(_("%s, please check your locale settings") % k)
137 137
138 138 def locallen(s):
139 139 """Find the length in characters of a local string"""
140 140 return len(s.decode(_encoding, "replace"))
141 141
142 142 # used by parsedate
143 143 defaultdateformats = (
144 144 '%Y-%m-%d %H:%M:%S',
145 145 '%Y-%m-%d %I:%M:%S%p',
146 146 '%Y-%m-%d %H:%M',
147 147 '%Y-%m-%d %I:%M%p',
148 148 '%Y-%m-%d',
149 149 '%m-%d',
150 150 '%m/%d',
151 151 '%m/%d/%y',
152 152 '%m/%d/%Y',
153 153 '%a %b %d %H:%M:%S %Y',
154 154 '%a %b %d %I:%M:%S%p %Y',
155 155 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
156 156 '%b %d %H:%M:%S %Y',
157 157 '%b %d %I:%M:%S%p %Y',
158 158 '%b %d %H:%M:%S',
159 159 '%b %d %I:%M:%S%p',
160 160 '%b %d %H:%M',
161 161 '%b %d %I:%M%p',
162 162 '%b %d %Y',
163 163 '%b %d',
164 164 '%H:%M:%S',
165 165 '%I:%M:%SP',
166 166 '%H:%M',
167 167 '%I:%M%p',
168 168 )
169 169
170 170 extendeddateformats = defaultdateformats + (
171 171 "%Y",
172 172 "%Y-%m",
173 173 "%b",
174 174 "%b %Y",
175 175 )
176 176
177 177 class SignalInterrupt(Exception):
178 178 """Exception raised on SIGTERM and SIGHUP."""
179 179
180 180 # differences from SafeConfigParser:
181 181 # - case-sensitive keys
182 182 # - allows values that are not strings (this means that you may not
183 183 # be able to save the configuration to a file)
184 184 class configparser(ConfigParser.SafeConfigParser):
185 185 def optionxform(self, optionstr):
186 186 return optionstr
187 187
188 188 def set(self, section, option, value):
189 189 return ConfigParser.ConfigParser.set(self, section, option, value)
190 190
191 191 def _interpolate(self, section, option, rawval, vars):
192 192 if not isinstance(rawval, basestring):
193 193 return rawval
194 194 return ConfigParser.SafeConfigParser._interpolate(self, section,
195 195 option, rawval, vars)
196 196
197 197 def cachefunc(func):
198 198 '''cache the result of function calls'''
199 199 # XXX doesn't handle keywords args
200 200 cache = {}
201 201 if func.func_code.co_argcount == 1:
202 202 # we gain a small amount of time because
203 203 # we don't need to pack/unpack the list
204 204 def f(arg):
205 205 if arg not in cache:
206 206 cache[arg] = func(arg)
207 207 return cache[arg]
208 208 else:
209 209 def f(*args):
210 210 if args not in cache:
211 211 cache[args] = func(*args)
212 212 return cache[args]
213 213
214 214 return f
215 215
216 216 def pipefilter(s, cmd):
217 217 '''filter string S through command CMD, returning its output'''
218 218 (pin, pout) = popen2(cmd, 'b')
219 219 def writer():
220 220 try:
221 221 pin.write(s)
222 222 pin.close()
223 223 except IOError, inst:
224 224 if inst.errno != errno.EPIPE:
225 225 raise
226 226
227 227 # we should use select instead on UNIX, but this will work on most
228 228 # systems, including Windows
229 229 w = threading.Thread(target=writer)
230 230 w.start()
231 231 f = pout.read()
232 232 pout.close()
233 233 w.join()
234 234 return f
235 235
236 236 def tempfilter(s, cmd):
237 237 '''filter string S through a pair of temporary files with CMD.
238 238 CMD is used as a template to create the real command to be run,
239 239 with the strings INFILE and OUTFILE replaced by the real names of
240 240 the temporary files generated.'''
241 241 inname, outname = None, None
242 242 try:
243 243 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
244 244 fp = os.fdopen(infd, 'wb')
245 245 fp.write(s)
246 246 fp.close()
247 247 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
248 248 os.close(outfd)
249 249 cmd = cmd.replace('INFILE', inname)
250 250 cmd = cmd.replace('OUTFILE', outname)
251 251 code = os.system(cmd)
252 252 if sys.platform == 'OpenVMS' and code & 1:
253 253 code = 0
254 254 if code: raise Abort(_("command '%s' failed: %s") %
255 255 (cmd, explain_exit(code)))
256 256 return open(outname, 'rb').read()
257 257 finally:
258 258 try:
259 259 if inname: os.unlink(inname)
260 260 except: pass
261 261 try:
262 262 if outname: os.unlink(outname)
263 263 except: pass
264 264
265 265 filtertable = {
266 266 'tempfile:': tempfilter,
267 267 'pipe:': pipefilter,
268 268 }
269 269
270 270 def filter(s, cmd):
271 271 "filter a string through a command that transforms its input to its output"
272 272 for name, fn in filtertable.iteritems():
273 273 if cmd.startswith(name):
274 274 return fn(s, cmd[len(name):].lstrip())
275 275 return pipefilter(s, cmd)
276 276
277 277 def binary(s):
278 278 """return true if a string is binary data"""
279 279 if s and '\0' in s:
280 280 return True
281 281 return False
282 282
283 283 def unique(g):
284 284 """return the uniq elements of iterable g"""
285 285 return dict.fromkeys(g).keys()
286 286
287 287 def sort(l):
288 288 if not isinstance(l, list):
289 289 l = list(l)
290 290 l.sort()
291 291 return l
292 292
293 293 class Abort(Exception):
294 294 """Raised if a command needs to print an error and exit."""
295 295
296 296 class UnexpectedOutput(Abort):
297 297 """Raised to print an error with part of output and exit."""
298 298
299 299 def always(fn): return True
300 300 def never(fn): return False
301 301
302 302 def expand_glob(pats):
303 303 '''On Windows, expand the implicit globs in a list of patterns'''
304 304 if os.name != 'nt':
305 305 return list(pats)
306 306 ret = []
307 307 for p in pats:
308 308 kind, name = patkind(p, None)
309 309 if kind is None:
310 310 globbed = glob.glob(name)
311 311 if globbed:
312 312 ret.extend(globbed)
313 313 continue
314 314 # if we couldn't expand the glob, just keep it around
315 315 ret.append(p)
316 316 return ret
317 317
318 318 def patkind(name, default):
319 319 """Split a string into an optional pattern kind prefix and the
320 320 actual pattern."""
321 321 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
322 322 if name.startswith(prefix + ':'): return name.split(':', 1)
323 323 return default, name
324 324
325 325 def globre(pat, head='^', tail='$'):
326 326 "convert a glob pattern into a regexp"
327 327 i, n = 0, len(pat)
328 328 res = ''
329 329 group = 0
330 330 def peek(): return i < n and pat[i]
331 331 while i < n:
332 332 c = pat[i]
333 333 i = i+1
334 334 if c == '*':
335 335 if peek() == '*':
336 336 i += 1
337 337 res += '.*'
338 338 else:
339 339 res += '[^/]*'
340 340 elif c == '?':
341 341 res += '.'
342 342 elif c == '[':
343 343 j = i
344 344 if j < n and pat[j] in '!]':
345 345 j += 1
346 346 while j < n and pat[j] != ']':
347 347 j += 1
348 348 if j >= n:
349 349 res += '\\['
350 350 else:
351 351 stuff = pat[i:j].replace('\\','\\\\')
352 352 i = j + 1
353 353 if stuff[0] == '!':
354 354 stuff = '^' + stuff[1:]
355 355 elif stuff[0] == '^':
356 356 stuff = '\\' + stuff
357 357 res = '%s[%s]' % (res, stuff)
358 358 elif c == '{':
359 359 group += 1
360 360 res += '(?:'
361 361 elif c == '}' and group:
362 362 res += ')'
363 363 group -= 1
364 364 elif c == ',' and group:
365 365 res += '|'
366 366 elif c == '\\':
367 367 p = peek()
368 368 if p:
369 369 i += 1
370 370 res += re.escape(p)
371 371 else:
372 372 res += re.escape(c)
373 373 else:
374 374 res += re.escape(c)
375 375 return head + res + tail
376 376
377 377 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
378 378
379 379 def pathto(root, n1, n2):
380 380 '''return the relative path from one place to another.
381 381 root should use os.sep to separate directories
382 382 n1 should use os.sep to separate directories
383 383 n2 should use "/" to separate directories
384 384 returns an os.sep-separated path.
385 385
386 386 If n1 is a relative path, it's assumed it's
387 387 relative to root.
388 388 n2 should always be relative to root.
389 389 '''
390 390 if not n1: return localpath(n2)
391 391 if os.path.isabs(n1):
392 392 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
393 393 return os.path.join(root, localpath(n2))
394 394 n2 = '/'.join((pconvert(root), n2))
395 395 a, b = splitpath(n1), n2.split('/')
396 396 a.reverse()
397 397 b.reverse()
398 398 while a and b and a[-1] == b[-1]:
399 399 a.pop()
400 400 b.pop()
401 401 b.reverse()
402 402 return os.sep.join((['..'] * len(a)) + b) or '.'
403 403
404 404 def canonpath(root, cwd, myname):
405 405 """return the canonical path of myname, given cwd and root"""
406 406 if root == os.sep:
407 407 rootsep = os.sep
408 408 elif endswithsep(root):
409 409 rootsep = root
410 410 else:
411 411 rootsep = root + os.sep
412 412 name = myname
413 413 if not os.path.isabs(name):
414 414 name = os.path.join(root, cwd, name)
415 415 name = os.path.normpath(name)
416 416 audit_path = path_auditor(root)
417 417 if name != rootsep and name.startswith(rootsep):
418 418 name = name[len(rootsep):]
419 419 audit_path(name)
420 420 return pconvert(name)
421 421 elif name == root:
422 422 return ''
423 423 else:
424 424 # Determine whether `name' is in the hierarchy at or beneath `root',
425 425 # by iterating name=dirname(name) until that causes no change (can't
426 426 # check name == '/', because that doesn't work on windows). For each
427 427 # `name', compare dev/inode numbers. If they match, the list `rel'
428 428 # holds the reversed list of components making up the relative file
429 429 # name we want.
430 430 root_st = os.stat(root)
431 431 rel = []
432 432 while True:
433 433 try:
434 434 name_st = os.stat(name)
435 435 except OSError:
436 436 break
437 437 if samestat(name_st, root_st):
438 438 if not rel:
439 439 # name was actually the same as root (maybe a symlink)
440 440 return ''
441 441 rel.reverse()
442 442 name = os.path.join(*rel)
443 443 audit_path(name)
444 444 return pconvert(name)
445 445 dirname, basename = os.path.split(name)
446 446 rel.append(basename)
447 447 if dirname == name:
448 448 break
449 449 name = dirname
450 450
451 451 raise Abort('%s not under root' % myname)
452 452
453 453 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
454 454 """build a function to match a set of file patterns
455 455
456 456 arguments:
457 457 canonroot - the canonical root of the tree you're matching against
458 458 cwd - the current working directory, if relevant
459 459 names - patterns to find
460 460 inc - patterns to include
461 461 exc - patterns to exclude
462 462 dflt_pat - if a pattern in names has no explicit type, assume this one
463 463 src - where these patterns came from (e.g. .hgignore)
464 464
465 465 a pattern is one of:
466 466 'glob:<glob>' - a glob relative to cwd
467 467 're:<regexp>' - a regular expression
468 468 'path:<path>' - a path relative to canonroot
469 469 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
470 470 'relpath:<path>' - a path relative to cwd
471 471 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
472 472 '<something>' - one of the cases above, selected by the dflt_pat argument
473 473
474 474 returns:
475 475 a 3-tuple containing
476 476 - list of roots (places where one should start a recursive walk of the fs);
477 477 this often matches the explicit non-pattern names passed in, but also
478 478 includes the initial part of glob: patterns that has no glob characters
479 479 - a bool match(filename) function
480 480 - a bool indicating if any patterns were passed in
481 481 """
482 482
483 483 # a common case: no patterns at all
484 484 if not names and not inc and not exc:
485 485 return [], always, False
486 486
487 487 def contains_glob(name):
488 488 for c in name:
489 489 if c in _globchars: return True
490 490 return False
491 491
492 492 def regex(kind, name, tail):
493 493 '''convert a pattern into a regular expression'''
494 494 if not name:
495 495 return ''
496 496 if kind == 're':
497 497 return name
498 498 elif kind == 'path':
499 499 return '^' + re.escape(name) + '(?:/|$)'
500 500 elif kind == 'relglob':
501 501 return globre(name, '(?:|.*/)', tail)
502 502 elif kind == 'relpath':
503 503 return re.escape(name) + '(?:/|$)'
504 504 elif kind == 'relre':
505 505 if name.startswith('^'):
506 506 return name
507 507 return '.*' + name
508 508 return globre(name, '', tail)
509 509
510 510 def matchfn(pats, tail):
511 511 """build a matching function from a set of patterns"""
512 512 if not pats:
513 513 return
514 514 try:
515 515 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
516 516 if len(pat) > 20000:
517 517 raise OverflowError()
518 518 return re.compile(pat).match
519 519 except OverflowError:
520 520 # We're using a Python with a tiny regex engine and we
521 521 # made it explode, so we'll divide the pattern list in two
522 522 # until it works
523 523 l = len(pats)
524 524 if l < 2:
525 525 raise
526 526 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
527 527 return lambda s: a(s) or b(s)
528 528 except re.error:
529 529 for k, p in pats:
530 530 try:
531 531 re.compile('(?:%s)' % regex(k, p, tail))
532 532 except re.error:
533 533 if src:
534 534 raise Abort("%s: invalid pattern (%s): %s" %
535 535 (src, k, p))
536 536 else:
537 537 raise Abort("invalid pattern (%s): %s" % (k, p))
538 538 raise Abort("invalid pattern")
539 539
540 540 def globprefix(pat):
541 541 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
542 542 root = []
543 543 for p in pat.split('/'):
544 544 if contains_glob(p): break
545 545 root.append(p)
546 546 return '/'.join(root) or '.'
547 547
548 548 def normalizepats(names, default):
549 549 pats = []
550 550 roots = []
551 551 anypats = False
552 552 for kind, name in [patkind(p, default) for p in names]:
553 553 if kind in ('glob', 'relpath'):
554 554 name = canonpath(canonroot, cwd, name)
555 555 elif kind in ('relglob', 'path'):
556 556 name = normpath(name)
557 557
558 558 pats.append((kind, name))
559 559
560 560 if kind in ('glob', 're', 'relglob', 'relre'):
561 561 anypats = True
562 562
563 563 if kind == 'glob':
564 564 root = globprefix(name)
565 565 roots.append(root)
566 566 elif kind in ('relpath', 'path'):
567 567 roots.append(name or '.')
568 568 elif kind == 'relglob':
569 569 roots.append('.')
570 570 return roots, pats, anypats
571 571
572 572 roots, pats, anypats = normalizepats(names, dflt_pat)
573 573
574 574 patmatch = matchfn(pats, '$') or always
575 575 incmatch = always
576 576 if inc:
577 577 dummy, inckinds, dummy = normalizepats(inc, 'glob')
578 578 incmatch = matchfn(inckinds, '(?:/|$)')
579 579 excmatch = lambda fn: False
580 580 if exc:
581 581 dummy, exckinds, dummy = normalizepats(exc, 'glob')
582 582 excmatch = matchfn(exckinds, '(?:/|$)')
583 583
584 584 if not names and inc and not exc:
585 585 # common case: hgignore patterns
586 586 match = incmatch
587 587 else:
588 588 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
589 589
590 590 return (roots, match, (inc or exc or anypats) and True)
591 591
592 592 _hgexecutable = None
593 593
594 594 def main_is_frozen():
595 595 """return True if we are a frozen executable.
596 596
597 597 The code supports py2exe (most common, Windows only) and tools/freeze
598 598 (portable, not much used).
599 599 """
600 600 return (hasattr(sys, "frozen") or # new py2exe
601 601 hasattr(sys, "importers") or # old py2exe
602 602 imp.is_frozen("__main__")) # tools/freeze
603 603
604 604 def hgexecutable():
605 605 """return location of the 'hg' executable.
606 606
607 607 Defaults to $HG or 'hg' in the search path.
608 608 """
609 609 if _hgexecutable is None:
610 610 hg = os.environ.get('HG')
611 611 if hg:
612 612 set_hgexecutable(hg)
613 613 elif main_is_frozen():
614 614 set_hgexecutable(sys.executable)
615 615 else:
616 616 set_hgexecutable(find_exe('hg', 'hg'))
617 617 return _hgexecutable
618 618
619 619 def set_hgexecutable(path):
620 620 """set location of the 'hg' executable"""
621 621 global _hgexecutable
622 622 _hgexecutable = path
623 623
624 624 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
625 625 '''enhanced shell command execution.
626 626 run with environment maybe modified, maybe in different dir.
627 627
628 628 if command fails and onerr is None, return status. if ui object,
629 629 print error message and return status, else raise onerr object as
630 630 exception.'''
631 631 def py2shell(val):
632 632 'convert python object into string that is useful to shell'
633 633 if val in (None, False):
634 634 return '0'
635 635 if val == True:
636 636 return '1'
637 637 return str(val)
638 638 oldenv = {}
639 639 for k in environ:
640 640 oldenv[k] = os.environ.get(k)
641 641 if cwd is not None:
642 642 oldcwd = os.getcwd()
643 643 origcmd = cmd
644 644 if os.name == 'nt':
645 645 cmd = '"%s"' % cmd
646 646 try:
647 647 for k, v in environ.iteritems():
648 648 os.environ[k] = py2shell(v)
649 649 os.environ['HG'] = hgexecutable()
650 650 if cwd is not None and oldcwd != cwd:
651 651 os.chdir(cwd)
652 652 rc = os.system(cmd)
653 653 if sys.platform == 'OpenVMS' and rc & 1:
654 654 rc = 0
655 655 if rc and onerr:
656 656 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
657 657 explain_exit(rc)[0])
658 658 if errprefix:
659 659 errmsg = '%s: %s' % (errprefix, errmsg)
660 660 try:
661 661 onerr.warn(errmsg + '\n')
662 662 except AttributeError:
663 663 raise onerr(errmsg)
664 664 return rc
665 665 finally:
666 666 for k, v in oldenv.iteritems():
667 667 if v is None:
668 668 del os.environ[k]
669 669 else:
670 670 os.environ[k] = v
671 671 if cwd is not None and oldcwd != cwd:
672 672 os.chdir(oldcwd)
673 673
674 674 # os.path.lexists is not available on python2.3
675 675 def lexists(filename):
676 676 "test whether a file with this name exists. does not follow symlinks"
677 677 try:
678 678 os.lstat(filename)
679 679 except:
680 680 return False
681 681 return True
682 682
683 683 def rename(src, dst):
684 684 """forcibly rename a file"""
685 685 try:
686 686 os.rename(src, dst)
687 687 except OSError, err: # FIXME: check err (EEXIST ?)
688 688 # on windows, rename to existing file is not allowed, so we
689 689 # must delete destination first. but if file is open, unlink
690 690 # schedules it for delete but does not delete it. rename
691 691 # happens immediately even for open files, so we create
692 692 # temporary file, delete it, rename destination to that name,
693 693 # then delete that. then rename is safe to do.
694 694 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
695 695 os.close(fd)
696 696 os.unlink(temp)
697 697 os.rename(dst, temp)
698 698 os.unlink(temp)
699 699 os.rename(src, dst)
700 700
701 701 def unlink(f):
702 702 """unlink and remove the directory if it is empty"""
703 703 os.unlink(f)
704 704 # try removing directories that might now be empty
705 705 try:
706 706 os.removedirs(os.path.dirname(f))
707 707 except OSError:
708 708 pass
709 709
710 710 def copyfile(src, dest):
711 711 "copy a file, preserving mode"
712 712 if os.path.islink(src):
713 713 try:
714 714 os.unlink(dest)
715 715 except:
716 716 pass
717 717 os.symlink(os.readlink(src), dest)
718 718 else:
719 719 try:
720 720 shutil.copyfile(src, dest)
721 721 shutil.copymode(src, dest)
722 722 except shutil.Error, inst:
723 723 raise Abort(str(inst))
724 724
725 725 def copyfiles(src, dst, hardlink=None):
726 726 """Copy a directory tree using hardlinks if possible"""
727 727
728 728 if hardlink is None:
729 729 hardlink = (os.stat(src).st_dev ==
730 730 os.stat(os.path.dirname(dst)).st_dev)
731 731
732 732 if os.path.isdir(src):
733 733 os.mkdir(dst)
734 734 for name, kind in osutil.listdir(src):
735 735 srcname = os.path.join(src, name)
736 736 dstname = os.path.join(dst, name)
737 737 copyfiles(srcname, dstname, hardlink)
738 738 else:
739 739 if hardlink:
740 740 try:
741 741 os_link(src, dst)
742 742 except (IOError, OSError):
743 743 hardlink = False
744 744 shutil.copy(src, dst)
745 745 else:
746 746 shutil.copy(src, dst)
747 747
748 748 class path_auditor(object):
749 749 '''ensure that a filesystem path contains no banned components.
750 750 the following properties of a path are checked:
751 751
752 752 - under top-level .hg
753 753 - starts at the root of a windows drive
754 754 - contains ".."
755 755 - traverses a symlink (e.g. a/symlink_here/b)
756 756 - inside a nested repository'''
757 757
758 758 def __init__(self, root):
759 759 self.audited = set()
760 760 self.auditeddir = set()
761 761 self.root = root
762 762
763 763 def __call__(self, path):
764 764 if path in self.audited:
765 765 return
766 766 normpath = os.path.normcase(path)
767 767 parts = splitpath(normpath)
768 768 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
769 769 or os.pardir in parts):
770 770 raise Abort(_("path contains illegal component: %s") % path)
771 771 def check(prefix):
772 772 curpath = os.path.join(self.root, prefix)
773 773 try:
774 774 st = os.lstat(curpath)
775 775 except OSError, err:
776 776 # EINVAL can be raised as invalid path syntax under win32.
777 777 # They must be ignored for patterns can be checked too.
778 778 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
779 779 raise
780 780 else:
781 781 if stat.S_ISLNK(st.st_mode):
782 782 raise Abort(_('path %r traverses symbolic link %r') %
783 783 (path, prefix))
784 784 elif (stat.S_ISDIR(st.st_mode) and
785 785 os.path.isdir(os.path.join(curpath, '.hg'))):
786 786 raise Abort(_('path %r is inside repo %r') %
787 787 (path, prefix))
788 788 parts.pop()
789 789 prefixes = []
790 790 for n in range(len(parts)):
791 791 prefix = os.sep.join(parts)
792 792 if prefix in self.auditeddir:
793 793 break
794 794 check(prefix)
795 795 prefixes.append(prefix)
796 796 parts.pop()
797 797
798 798 self.audited.add(path)
799 799 # only add prefixes to the cache after checking everything: we don't
800 800 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
801 801 self.auditeddir.update(prefixes)
802 802
803 803 def _makelock_file(info, pathname):
804 804 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
805 805 os.write(ld, info)
806 806 os.close(ld)
807 807
808 808 def _readlock_file(pathname):
809 809 return posixfile(pathname).read()
810 810
811 811 def nlinks(pathname):
812 812 """Return number of hardlinks for the given file."""
813 813 return os.lstat(pathname).st_nlink
814 814
815 815 if hasattr(os, 'link'):
816 816 os_link = os.link
817 817 else:
818 818 def os_link(src, dst):
819 819 raise OSError(0, _("Hardlinks not supported"))
820 820
821 821 def fstat(fp):
822 822 '''stat file object that may not have fileno method.'''
823 823 try:
824 824 return os.fstat(fp.fileno())
825 825 except AttributeError:
826 826 return os.stat(fp.name)
827 827
828 828 posixfile = file
829 829
830 830 def openhardlinks():
831 831 '''return true if it is safe to hold open file handles to hardlinks'''
832 832 return True
833 833
834 834 def _statfiles(files):
835 835 'Stat each file in files and yield stat or None if file does not exist.'
836 836 lstat = os.lstat
837 837 for nf in files:
838 838 try:
839 839 st = lstat(nf)
840 840 except OSError, err:
841 841 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
842 842 raise
843 843 st = None
844 844 yield st
845 845
846 846 def _statfiles_clustered(files):
847 847 '''Stat each file in files and yield stat or None if file does not exist.
848 848 Cluster and cache stat per directory to minimize number of OS stat calls.'''
849 849 lstat = os.lstat
850 850 ncase = os.path.normcase
851 851 sep = os.sep
852 852 dircache = {} # dirname -> filename -> status | None if file does not exist
853 853 for nf in files:
854 854 nf = ncase(nf)
855 855 pos = nf.rfind(sep)
856 856 if pos == -1:
857 857 dir, base = '.', nf
858 858 else:
859 dir, base = nf[:pos], nf[pos+1:]
859 dir, base = nf[:pos+1], nf[pos+1:]
860 860 cache = dircache.get(dir, None)
861 861 if cache is None:
862 862 try:
863 863 dmap = dict([(ncase(n), s)
864 864 for n, k, s in osutil.listdir(dir, True)])
865 865 except OSError, err:
866 866 # handle directory not found in Python version prior to 2.5
867 867 # Python <= 2.4 returns native Windows code 3 in errno
868 868 # Python >= 2.5 returns ENOENT and adds winerror field
869 869 # EINVAL is raised if dir is not a directory.
870 870 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
871 871 errno.ENOTDIR):
872 872 raise
873 873 dmap = {}
874 874 cache = dircache.setdefault(dir, dmap)
875 875 yield cache.get(base, None)
876 876
877 877 if sys.platform == 'win32':
878 878 statfiles = _statfiles_clustered
879 879 else:
880 880 statfiles = _statfiles
881 881
882 882 getuser_fallback = None
883 883
884 884 def getuser():
885 885 '''return name of current user'''
886 886 try:
887 887 return getpass.getuser()
888 888 except ImportError:
889 889 # import of pwd will fail on windows - try fallback
890 890 if getuser_fallback:
891 891 return getuser_fallback()
892 892 # raised if win32api not available
893 893 raise Abort(_('user name not available - set USERNAME '
894 894 'environment variable'))
895 895
896 896 def username(uid=None):
897 897 """Return the name of the user with the given uid.
898 898
899 899 If uid is None, return the name of the current user."""
900 900 try:
901 901 import pwd
902 902 if uid is None:
903 903 uid = os.getuid()
904 904 try:
905 905 return pwd.getpwuid(uid)[0]
906 906 except KeyError:
907 907 return str(uid)
908 908 except ImportError:
909 909 return None
910 910
911 911 def groupname(gid=None):
912 912 """Return the name of the group with the given gid.
913 913
914 914 If gid is None, return the name of the current group."""
915 915 try:
916 916 import grp
917 917 if gid is None:
918 918 gid = os.getgid()
919 919 try:
920 920 return grp.getgrgid(gid)[0]
921 921 except KeyError:
922 922 return str(gid)
923 923 except ImportError:
924 924 return None
925 925
926 926 # File system features
927 927
928 928 def checkcase(path):
929 929 """
930 930 Check whether the given path is on a case-sensitive filesystem
931 931
932 932 Requires a path (like /foo/.hg) ending with a foldable final
933 933 directory component.
934 934 """
935 935 s1 = os.stat(path)
936 936 d, b = os.path.split(path)
937 937 p2 = os.path.join(d, b.upper())
938 938 if path == p2:
939 939 p2 = os.path.join(d, b.lower())
940 940 try:
941 941 s2 = os.stat(p2)
942 942 if s2 == s1:
943 943 return False
944 944 return True
945 945 except:
946 946 return True
947 947
948 948 _fspathcache = {}
949 949 def fspath(name, root):
950 950 '''Get name in the case stored in the filesystem
951 951
952 952 The name is either relative to root, or it is an absolute path starting
953 953 with root. Note that this function is unnecessary, and should not be
954 954 called, for case-sensitive filesystems (simply because it's expensive).
955 955 '''
956 956 # If name is absolute, make it relative
957 957 if name.lower().startswith(root.lower()):
958 958 l = len(root)
959 959 if name[l] == os.sep or name[l] == os.altsep:
960 960 l = l + 1
961 961 name = name[l:]
962 962
963 963 if not os.path.exists(os.path.join(root, name)):
964 964 return None
965 965
966 966 seps = os.sep
967 967 if os.altsep:
968 968 seps = seps + os.altsep
969 969 # Protect backslashes. This gets silly very quickly.
970 970 seps.replace('\\','\\\\')
971 971 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
972 972 dir = os.path.normcase(os.path.normpath(root))
973 973 result = []
974 974 for part, sep in pattern.findall(name):
975 975 if sep:
976 976 result.append(sep)
977 977 continue
978 978
979 979 if dir not in _fspathcache:
980 980 _fspathcache[dir] = os.listdir(dir)
981 981 contents = _fspathcache[dir]
982 982
983 983 lpart = part.lower()
984 984 for n in contents:
985 985 if n.lower() == lpart:
986 986 result.append(n)
987 987 break
988 988 else:
989 989 # Cannot happen, as the file exists!
990 990 result.append(part)
991 991 dir = os.path.join(dir, lpart)
992 992
993 993 return ''.join(result)
994 994
995 995 def checkexec(path):
996 996 """
997 997 Check whether the given path is on a filesystem with UNIX-like exec flags
998 998
999 999 Requires a directory (like /foo/.hg)
1000 1000 """
1001 1001
1002 1002 # VFAT on some Linux versions can flip mode but it doesn't persist
1003 1003 # a FS remount. Frequently we can detect it if files are created
1004 1004 # with exec bit on.
1005 1005
1006 1006 try:
1007 1007 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
1008 1008 fh, fn = tempfile.mkstemp("", "", path)
1009 1009 try:
1010 1010 os.close(fh)
1011 1011 m = os.stat(fn).st_mode & 0777
1012 1012 new_file_has_exec = m & EXECFLAGS
1013 1013 os.chmod(fn, m ^ EXECFLAGS)
1014 1014 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
1015 1015 finally:
1016 1016 os.unlink(fn)
1017 1017 except (IOError, OSError):
1018 1018 # we don't care, the user probably won't be able to commit anyway
1019 1019 return False
1020 1020 return not (new_file_has_exec or exec_flags_cannot_flip)
1021 1021
1022 1022 def checklink(path):
1023 1023 """check whether the given path is on a symlink-capable filesystem"""
1024 1024 # mktemp is not racy because symlink creation will fail if the
1025 1025 # file already exists
1026 1026 name = tempfile.mktemp(dir=path)
1027 1027 try:
1028 1028 os.symlink(".", name)
1029 1029 os.unlink(name)
1030 1030 return True
1031 1031 except (OSError, AttributeError):
1032 1032 return False
1033 1033
1034 1034 _umask = os.umask(0)
1035 1035 os.umask(_umask)
1036 1036
1037 1037 def needbinarypatch():
1038 1038 """return True if patches should be applied in binary mode by default."""
1039 1039 return os.name == 'nt'
1040 1040
1041 1041 def endswithsep(path):
1042 1042 '''Check path ends with os.sep or os.altsep.'''
1043 1043 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1044 1044
1045 1045 def splitpath(path):
1046 1046 '''Split path by os.sep.
1047 1047 Note that this function does not use os.altsep because this is
1048 1048 an alternative of simple "xxx.split(os.sep)".
1049 1049 It is recommended to use os.path.normpath() before using this
1050 1050 function if need.'''
1051 1051 return path.split(os.sep)
1052 1052
1053 1053 def gui():
1054 1054 '''Are we running in a GUI?'''
1055 1055 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
1056 1056
1057 1057 def lookup_reg(key, name=None, scope=None):
1058 1058 return None
1059 1059
1060 1060 # Platform specific variants
1061 1061 if os.name == 'nt':
1062 1062 import msvcrt
1063 1063 nulldev = 'NUL:'
1064 1064
1065 1065 class winstdout:
1066 1066 '''stdout on windows misbehaves if sent through a pipe'''
1067 1067
1068 1068 def __init__(self, fp):
1069 1069 self.fp = fp
1070 1070
1071 1071 def __getattr__(self, key):
1072 1072 return getattr(self.fp, key)
1073 1073
1074 1074 def close(self):
1075 1075 try:
1076 1076 self.fp.close()
1077 1077 except: pass
1078 1078
1079 1079 def write(self, s):
1080 1080 try:
1081 1081 # This is workaround for "Not enough space" error on
1082 1082 # writing large size of data to console.
1083 1083 limit = 16000
1084 1084 l = len(s)
1085 1085 start = 0
1086 1086 while start < l:
1087 1087 end = start + limit
1088 1088 self.fp.write(s[start:end])
1089 1089 start = end
1090 1090 except IOError, inst:
1091 1091 if inst.errno != 0: raise
1092 1092 self.close()
1093 1093 raise IOError(errno.EPIPE, 'Broken pipe')
1094 1094
1095 1095 def flush(self):
1096 1096 try:
1097 1097 return self.fp.flush()
1098 1098 except IOError, inst:
1099 1099 if inst.errno != errno.EINVAL: raise
1100 1100 self.close()
1101 1101 raise IOError(errno.EPIPE, 'Broken pipe')
1102 1102
1103 1103 sys.stdout = winstdout(sys.stdout)
1104 1104
1105 1105 def _is_win_9x():
1106 1106 '''return true if run on windows 95, 98 or me.'''
1107 1107 try:
1108 1108 return sys.getwindowsversion()[3] == 1
1109 1109 except AttributeError:
1110 1110 return 'command' in os.environ.get('comspec', '')
1111 1111
1112 1112 def openhardlinks():
1113 1113 return not _is_win_9x and "win32api" in locals()
1114 1114
1115 1115 def system_rcpath():
1116 1116 try:
1117 1117 return system_rcpath_win32()
1118 1118 except:
1119 1119 return [r'c:\mercurial\mercurial.ini']
1120 1120
1121 1121 def user_rcpath():
1122 1122 '''return os-specific hgrc search path to the user dir'''
1123 1123 try:
1124 1124 path = user_rcpath_win32()
1125 1125 except:
1126 1126 home = os.path.expanduser('~')
1127 1127 path = [os.path.join(home, 'mercurial.ini'),
1128 1128 os.path.join(home, '.hgrc')]
1129 1129 userprofile = os.environ.get('USERPROFILE')
1130 1130 if userprofile:
1131 1131 path.append(os.path.join(userprofile, 'mercurial.ini'))
1132 1132 path.append(os.path.join(userprofile, '.hgrc'))
1133 1133 return path
1134 1134
1135 1135 def parse_patch_output(output_line):
1136 1136 """parses the output produced by patch and returns the file name"""
1137 1137 pf = output_line[14:]
1138 1138 if pf[0] == '`':
1139 1139 pf = pf[1:-1] # Remove the quotes
1140 1140 return pf
1141 1141
1142 1142 def sshargs(sshcmd, host, user, port):
1143 1143 '''Build argument list for ssh or Plink'''
1144 1144 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
1145 1145 args = user and ("%s@%s" % (user, host)) or host
1146 1146 return port and ("%s %s %s" % (args, pflag, port)) or args
1147 1147
1148 1148 def testpid(pid):
1149 1149 '''return False if pid dead, True if running or not known'''
1150 1150 return True
1151 1151
1152 1152 def set_flags(f, l, x):
1153 1153 pass
1154 1154
1155 1155 def set_binary(fd):
1156 1156 # When run without console, pipes may expose invalid
1157 1157 # fileno(), usually set to -1.
1158 1158 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
1159 1159 msvcrt.setmode(fd.fileno(), os.O_BINARY)
1160 1160
1161 1161 def pconvert(path):
1162 1162 return '/'.join(splitpath(path))
1163 1163
1164 1164 def localpath(path):
1165 1165 return path.replace('/', '\\')
1166 1166
1167 1167 def normpath(path):
1168 1168 return pconvert(os.path.normpath(path))
1169 1169
1170 1170 makelock = _makelock_file
1171 1171 readlock = _readlock_file
1172 1172
1173 1173 def samestat(s1, s2):
1174 1174 return False
1175 1175
1176 1176 # A sequence of backslashes is special iff it precedes a double quote:
1177 1177 # - if there's an even number of backslashes, the double quote is not
1178 1178 # quoted (i.e. it ends the quoted region)
1179 1179 # - if there's an odd number of backslashes, the double quote is quoted
1180 1180 # - in both cases, every pair of backslashes is unquoted into a single
1181 1181 # backslash
1182 1182 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1183 1183 # So, to quote a string, we must surround it in double quotes, double
1184 1184 # the number of backslashes that preceed double quotes and add another
1185 1185 # backslash before every double quote (being careful with the double
1186 1186 # quote we've appended to the end)
1187 1187 _quotere = None
1188 1188 def shellquote(s):
1189 1189 global _quotere
1190 1190 if _quotere is None:
1191 1191 _quotere = re.compile(r'(\\*)("|\\$)')
1192 1192 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1193 1193
1194 1194 def quotecommand(cmd):
1195 1195 """Build a command string suitable for os.popen* calls."""
1196 1196 # The extra quotes are needed because popen* runs the command
1197 1197 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1198 1198 return '"' + cmd + '"'
1199 1199
1200 1200 def popen(command, mode='r'):
1201 1201 # Work around "popen spawned process may not write to stdout
1202 1202 # under windows"
1203 1203 # http://bugs.python.org/issue1366
1204 1204 command += " 2> %s" % nulldev
1205 1205 return os.popen(quotecommand(command), mode)
1206 1206
1207 1207 def explain_exit(code):
1208 1208 return _("exited with status %d") % code, code
1209 1209
1210 1210 # if you change this stub into a real check, please try to implement the
1211 1211 # username and groupname functions above, too.
1212 1212 def isowner(fp, st=None):
1213 1213 return True
1214 1214
1215 1215 def find_in_path(name, path, default=None):
1216 1216 '''find name in search path. path can be string (will be split
1217 1217 with os.pathsep), or iterable thing that returns strings. if name
1218 1218 found, return path to name. else return default. name is looked up
1219 1219 using cmd.exe rules, using PATHEXT.'''
1220 1220 if isinstance(path, str):
1221 1221 path = path.split(os.pathsep)
1222 1222
1223 1223 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1224 1224 pathext = pathext.lower().split(os.pathsep)
1225 1225 isexec = os.path.splitext(name)[1].lower() in pathext
1226 1226
1227 1227 for p in path:
1228 1228 p_name = os.path.join(p, name)
1229 1229
1230 1230 if isexec and os.path.exists(p_name):
1231 1231 return p_name
1232 1232
1233 1233 for ext in pathext:
1234 1234 p_name_ext = p_name + ext
1235 1235 if os.path.exists(p_name_ext):
1236 1236 return p_name_ext
1237 1237 return default
1238 1238
1239 1239 def set_signal_handler():
1240 1240 try:
1241 1241 set_signal_handler_win32()
1242 1242 except NameError:
1243 1243 pass
1244 1244
1245 1245 try:
1246 1246 # override functions with win32 versions if possible
1247 1247 from util_win32 import *
1248 1248 if not _is_win_9x():
1249 1249 posixfile = posixfile_nt
1250 1250 except ImportError:
1251 1251 pass
1252 1252
1253 1253 else:
1254 1254 nulldev = '/dev/null'
1255 1255
1256 1256 def rcfiles(path):
1257 1257 rcs = [os.path.join(path, 'hgrc')]
1258 1258 rcdir = os.path.join(path, 'hgrc.d')
1259 1259 try:
1260 1260 rcs.extend([os.path.join(rcdir, f)
1261 1261 for f, kind in osutil.listdir(rcdir)
1262 1262 if f.endswith(".rc")])
1263 1263 except OSError:
1264 1264 pass
1265 1265 return rcs
1266 1266
1267 1267 def system_rcpath():
1268 1268 path = []
1269 1269 # old mod_python does not set sys.argv
1270 1270 if len(getattr(sys, 'argv', [])) > 0:
1271 1271 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1272 1272 '/../etc/mercurial'))
1273 1273 path.extend(rcfiles('/etc/mercurial'))
1274 1274 return path
1275 1275
1276 1276 def user_rcpath():
1277 1277 return [os.path.expanduser('~/.hgrc')]
1278 1278
1279 1279 def parse_patch_output(output_line):
1280 1280 """parses the output produced by patch and returns the file name"""
1281 1281 pf = output_line[14:]
1282 1282 if os.sys.platform == 'OpenVMS':
1283 1283 if pf[0] == '`':
1284 1284 pf = pf[1:-1] # Remove the quotes
1285 1285 else:
1286 1286 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1287 1287 pf = pf[1:-1] # Remove the quotes
1288 1288 return pf
1289 1289
1290 1290 def sshargs(sshcmd, host, user, port):
1291 1291 '''Build argument list for ssh'''
1292 1292 args = user and ("%s@%s" % (user, host)) or host
1293 1293 return port and ("%s -p %s" % (args, port)) or args
1294 1294
1295 1295 def is_exec(f):
1296 1296 """check whether a file is executable"""
1297 1297 return (os.lstat(f).st_mode & 0100 != 0)
1298 1298
1299 1299 def set_flags(f, l, x):
1300 1300 s = os.lstat(f).st_mode
1301 1301 if l:
1302 1302 if not stat.S_ISLNK(s):
1303 1303 # switch file to link
1304 1304 data = file(f).read()
1305 1305 os.unlink(f)
1306 1306 try:
1307 1307 os.symlink(data, f)
1308 1308 except:
1309 1309 # failed to make a link, rewrite file
1310 1310 file(f, "w").write(data)
1311 1311 # no chmod needed at this point
1312 1312 return
1313 1313 if stat.S_ISLNK(s):
1314 1314 # switch link to file
1315 1315 data = os.readlink(f)
1316 1316 os.unlink(f)
1317 1317 file(f, "w").write(data)
1318 1318 s = 0666 & ~_umask # avoid restatting for chmod
1319 1319
1320 1320 sx = s & 0100
1321 1321 if x and not sx:
1322 1322 # Turn on +x for every +r bit when making a file executable
1323 1323 # and obey umask.
1324 1324 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1325 1325 elif not x and sx:
1326 1326 # Turn off all +x bits
1327 1327 os.chmod(f, s & 0666)
1328 1328
1329 1329 def set_binary(fd):
1330 1330 pass
1331 1331
1332 1332 def pconvert(path):
1333 1333 return path
1334 1334
1335 1335 def localpath(path):
1336 1336 return path
1337 1337
1338 1338 normpath = os.path.normpath
1339 1339 samestat = os.path.samestat
1340 1340
1341 1341 def makelock(info, pathname):
1342 1342 try:
1343 1343 os.symlink(info, pathname)
1344 1344 except OSError, why:
1345 1345 if why.errno == errno.EEXIST:
1346 1346 raise
1347 1347 else:
1348 1348 _makelock_file(info, pathname)
1349 1349
1350 1350 def readlock(pathname):
1351 1351 try:
1352 1352 return os.readlink(pathname)
1353 1353 except OSError, why:
1354 1354 if why.errno in (errno.EINVAL, errno.ENOSYS):
1355 1355 return _readlock_file(pathname)
1356 1356 else:
1357 1357 raise
1358 1358
1359 1359 def shellquote(s):
1360 1360 if os.sys.platform == 'OpenVMS':
1361 1361 return '"%s"' % s
1362 1362 else:
1363 1363 return "'%s'" % s.replace("'", "'\\''")
1364 1364
1365 1365 def quotecommand(cmd):
1366 1366 return cmd
1367 1367
1368 1368 def popen(command, mode='r'):
1369 1369 return os.popen(command, mode)
1370 1370
1371 1371 def testpid(pid):
1372 1372 '''return False if pid dead, True if running or not sure'''
1373 1373 if os.sys.platform == 'OpenVMS':
1374 1374 return True
1375 1375 try:
1376 1376 os.kill(pid, 0)
1377 1377 return True
1378 1378 except OSError, inst:
1379 1379 return inst.errno != errno.ESRCH
1380 1380
1381 1381 def explain_exit(code):
1382 1382 """return a 2-tuple (desc, code) describing a process's status"""
1383 1383 if os.WIFEXITED(code):
1384 1384 val = os.WEXITSTATUS(code)
1385 1385 return _("exited with status %d") % val, val
1386 1386 elif os.WIFSIGNALED(code):
1387 1387 val = os.WTERMSIG(code)
1388 1388 return _("killed by signal %d") % val, val
1389 1389 elif os.WIFSTOPPED(code):
1390 1390 val = os.WSTOPSIG(code)
1391 1391 return _("stopped by signal %d") % val, val
1392 1392 raise ValueError(_("invalid exit code"))
1393 1393
1394 1394 def isowner(fp, st=None):
1395 1395 """Return True if the file object f belongs to the current user.
1396 1396
1397 1397 The return value of a util.fstat(f) may be passed as the st argument.
1398 1398 """
1399 1399 if st is None:
1400 1400 st = fstat(fp)
1401 1401 return st.st_uid == os.getuid()
1402 1402
1403 1403 def find_in_path(name, path, default=None):
1404 1404 '''find name in search path. path can be string (will be split
1405 1405 with os.pathsep), or iterable thing that returns strings. if name
1406 1406 found, return path to name. else return default.'''
1407 1407 if isinstance(path, str):
1408 1408 path = path.split(os.pathsep)
1409 1409 for p in path:
1410 1410 p_name = os.path.join(p, name)
1411 1411 if os.path.exists(p_name):
1412 1412 return p_name
1413 1413 return default
1414 1414
1415 1415 def set_signal_handler():
1416 1416 pass
1417 1417
1418 1418 def find_exe(name, default=None):
1419 1419 '''find path of an executable.
1420 1420 if name contains a path component, return it as is. otherwise,
1421 1421 use normal executable search path.'''
1422 1422
1423 1423 if os.sep in name or sys.platform == 'OpenVMS':
1424 1424 # don't check the executable bit. if the file isn't
1425 1425 # executable, whoever tries to actually run it will give a
1426 1426 # much more useful error message.
1427 1427 return name
1428 1428 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1429 1429
1430 1430 def mktempcopy(name, emptyok=False, createmode=None):
1431 1431 """Create a temporary file with the same contents from name
1432 1432
1433 1433 The permission bits are copied from the original file.
1434 1434
1435 1435 If the temporary file is going to be truncated immediately, you
1436 1436 can use emptyok=True as an optimization.
1437 1437
1438 1438 Returns the name of the temporary file.
1439 1439 """
1440 1440 d, fn = os.path.split(name)
1441 1441 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1442 1442 os.close(fd)
1443 1443 # Temporary files are created with mode 0600, which is usually not
1444 1444 # what we want. If the original file already exists, just copy
1445 1445 # its mode. Otherwise, manually obey umask.
1446 1446 try:
1447 1447 st_mode = os.lstat(name).st_mode & 0777
1448 1448 except OSError, inst:
1449 1449 if inst.errno != errno.ENOENT:
1450 1450 raise
1451 1451 st_mode = createmode
1452 1452 if st_mode is None:
1453 1453 st_mode = ~_umask
1454 1454 st_mode &= 0666
1455 1455 os.chmod(temp, st_mode)
1456 1456 if emptyok:
1457 1457 return temp
1458 1458 try:
1459 1459 try:
1460 1460 ifp = posixfile(name, "rb")
1461 1461 except IOError, inst:
1462 1462 if inst.errno == errno.ENOENT:
1463 1463 return temp
1464 1464 if not getattr(inst, 'filename', None):
1465 1465 inst.filename = name
1466 1466 raise
1467 1467 ofp = posixfile(temp, "wb")
1468 1468 for chunk in filechunkiter(ifp):
1469 1469 ofp.write(chunk)
1470 1470 ifp.close()
1471 1471 ofp.close()
1472 1472 except:
1473 1473 try: os.unlink(temp)
1474 1474 except: pass
1475 1475 raise
1476 1476 return temp
1477 1477
1478 1478 class atomictempfile(posixfile):
1479 1479 """file-like object that atomically updates a file
1480 1480
1481 1481 All writes will be redirected to a temporary copy of the original
1482 1482 file. When rename is called, the copy is renamed to the original
1483 1483 name, making the changes visible.
1484 1484 """
1485 1485 def __init__(self, name, mode, createmode):
1486 1486 self.__name = name
1487 1487 self.temp = mktempcopy(name, emptyok=('w' in mode),
1488 1488 createmode=createmode)
1489 1489 posixfile.__init__(self, self.temp, mode)
1490 1490
1491 1491 def rename(self):
1492 1492 if not self.closed:
1493 1493 posixfile.close(self)
1494 1494 rename(self.temp, localpath(self.__name))
1495 1495
1496 1496 def __del__(self):
1497 1497 if not self.closed:
1498 1498 try:
1499 1499 os.unlink(self.temp)
1500 1500 except: pass
1501 1501 posixfile.close(self)
1502 1502
1503 1503 def makedirs(name, mode=None):
1504 1504 """recursive directory creation with parent mode inheritance"""
1505 1505 try:
1506 1506 os.mkdir(name)
1507 1507 if mode is not None:
1508 1508 os.chmod(name, mode)
1509 1509 return
1510 1510 except OSError, err:
1511 1511 if err.errno == errno.EEXIST:
1512 1512 return
1513 1513 if err.errno != errno.ENOENT:
1514 1514 raise
1515 1515 parent = os.path.abspath(os.path.dirname(name))
1516 1516 makedirs(parent, mode)
1517 1517 makedirs(name, mode)
1518 1518
1519 1519 class opener(object):
1520 1520 """Open files relative to a base directory
1521 1521
1522 1522 This class is used to hide the details of COW semantics and
1523 1523 remote file access from higher level code.
1524 1524 """
1525 1525 def __init__(self, base, audit=True):
1526 1526 self.base = base
1527 1527 if audit:
1528 1528 self.audit_path = path_auditor(base)
1529 1529 else:
1530 1530 self.audit_path = always
1531 1531 self.createmode = None
1532 1532
1533 1533 def __getattr__(self, name):
1534 1534 if name == '_can_symlink':
1535 1535 self._can_symlink = checklink(self.base)
1536 1536 return self._can_symlink
1537 1537 raise AttributeError(name)
1538 1538
1539 1539 def _fixfilemode(self, name):
1540 1540 if self.createmode is None:
1541 1541 return
1542 1542 os.chmod(name, self.createmode & 0666)
1543 1543
1544 1544 def __call__(self, path, mode="r", text=False, atomictemp=False):
1545 1545 self.audit_path(path)
1546 1546 f = os.path.join(self.base, path)
1547 1547
1548 1548 if not text and "b" not in mode:
1549 1549 mode += "b" # for that other OS
1550 1550
1551 1551 nlink = -1
1552 1552 if mode not in ("r", "rb"):
1553 1553 try:
1554 1554 nlink = nlinks(f)
1555 1555 except OSError:
1556 1556 nlink = 0
1557 1557 d = os.path.dirname(f)
1558 1558 if not os.path.isdir(d):
1559 1559 makedirs(d, self.createmode)
1560 1560 if atomictemp:
1561 1561 return atomictempfile(f, mode, self.createmode)
1562 1562 if nlink > 1:
1563 1563 rename(mktempcopy(f), f)
1564 1564 fp = posixfile(f, mode)
1565 1565 if nlink == 0:
1566 1566 self._fixfilemode(f)
1567 1567 return fp
1568 1568
1569 1569 def symlink(self, src, dst):
1570 1570 self.audit_path(dst)
1571 1571 linkname = os.path.join(self.base, dst)
1572 1572 try:
1573 1573 os.unlink(linkname)
1574 1574 except OSError:
1575 1575 pass
1576 1576
1577 1577 dirname = os.path.dirname(linkname)
1578 1578 if not os.path.exists(dirname):
1579 1579 makedirs(dirname, self.createmode)
1580 1580
1581 1581 if self._can_symlink:
1582 1582 try:
1583 1583 os.symlink(src, linkname)
1584 1584 except OSError, err:
1585 1585 raise OSError(err.errno, _('could not symlink to %r: %s') %
1586 1586 (src, err.strerror), linkname)
1587 1587 else:
1588 1588 f = self(dst, "w")
1589 1589 f.write(src)
1590 1590 f.close()
1591 1591 self._fixfilemode(dst)
1592 1592
1593 1593 class chunkbuffer(object):
1594 1594 """Allow arbitrary sized chunks of data to be efficiently read from an
1595 1595 iterator over chunks of arbitrary size."""
1596 1596
1597 1597 def __init__(self, in_iter):
1598 1598 """in_iter is the iterator that's iterating over the input chunks.
1599 1599 targetsize is how big a buffer to try to maintain."""
1600 1600 self.iter = iter(in_iter)
1601 1601 self.buf = ''
1602 1602 self.targetsize = 2**16
1603 1603
1604 1604 def read(self, l):
1605 1605 """Read L bytes of data from the iterator of chunks of data.
1606 1606 Returns less than L bytes if the iterator runs dry."""
1607 1607 if l > len(self.buf) and self.iter:
1608 1608 # Clamp to a multiple of self.targetsize
1609 1609 targetsize = max(l, self.targetsize)
1610 1610 collector = cStringIO.StringIO()
1611 1611 collector.write(self.buf)
1612 1612 collected = len(self.buf)
1613 1613 for chunk in self.iter:
1614 1614 collector.write(chunk)
1615 1615 collected += len(chunk)
1616 1616 if collected >= targetsize:
1617 1617 break
1618 1618 if collected < targetsize:
1619 1619 self.iter = False
1620 1620 self.buf = collector.getvalue()
1621 1621 if len(self.buf) == l:
1622 1622 s, self.buf = str(self.buf), ''
1623 1623 else:
1624 1624 s, self.buf = self.buf[:l], buffer(self.buf, l)
1625 1625 return s
1626 1626
1627 1627 def filechunkiter(f, size=65536, limit=None):
1628 1628 """Create a generator that produces the data in the file size
1629 1629 (default 65536) bytes at a time, up to optional limit (default is
1630 1630 to read all data). Chunks may be less than size bytes if the
1631 1631 chunk is the last chunk in the file, or the file is a socket or
1632 1632 some other type of file that sometimes reads less data than is
1633 1633 requested."""
1634 1634 assert size >= 0
1635 1635 assert limit is None or limit >= 0
1636 1636 while True:
1637 1637 if limit is None: nbytes = size
1638 1638 else: nbytes = min(limit, size)
1639 1639 s = nbytes and f.read(nbytes)
1640 1640 if not s: break
1641 1641 if limit: limit -= len(s)
1642 1642 yield s
1643 1643
1644 1644 def makedate():
1645 1645 lt = time.localtime()
1646 1646 if lt[8] == 1 and time.daylight:
1647 1647 tz = time.altzone
1648 1648 else:
1649 1649 tz = time.timezone
1650 1650 return time.mktime(lt), tz
1651 1651
1652 1652 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1653 1653 """represent a (unixtime, offset) tuple as a localized time.
1654 1654 unixtime is seconds since the epoch, and offset is the time zone's
1655 1655 number of seconds away from UTC. if timezone is false, do not
1656 1656 append time zone to string."""
1657 1657 t, tz = date or makedate()
1658 1658 if "%1" in format or "%2" in format:
1659 1659 sign = (tz > 0) and "-" or "+"
1660 1660 minutes = abs(tz) / 60
1661 1661 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1662 1662 format = format.replace("%2", "%02d" % (minutes % 60))
1663 1663 s = time.strftime(format, time.gmtime(float(t) - tz))
1664 1664 return s
1665 1665
1666 1666 def shortdate(date=None):
1667 1667 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1668 1668 return datestr(date, format='%Y-%m-%d')
1669 1669
1670 1670 def strdate(string, format, defaults=[]):
1671 1671 """parse a localized time string and return a (unixtime, offset) tuple.
1672 1672 if the string cannot be parsed, ValueError is raised."""
1673 1673 def timezone(string):
1674 1674 tz = string.split()[-1]
1675 1675 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1676 1676 sign = (tz[0] == "+") and 1 or -1
1677 1677 hours = int(tz[1:3])
1678 1678 minutes = int(tz[3:5])
1679 1679 return -sign * (hours * 60 + minutes) * 60
1680 1680 if tz == "GMT" or tz == "UTC":
1681 1681 return 0
1682 1682 return None
1683 1683
1684 1684 # NOTE: unixtime = localunixtime + offset
1685 1685 offset, date = timezone(string), string
1686 1686 if offset != None:
1687 1687 date = " ".join(string.split()[:-1])
1688 1688
1689 1689 # add missing elements from defaults
1690 1690 for part in defaults:
1691 1691 found = [True for p in part if ("%"+p) in format]
1692 1692 if not found:
1693 1693 date += "@" + defaults[part]
1694 1694 format += "@%" + part[0]
1695 1695
1696 1696 timetuple = time.strptime(date, format)
1697 1697 localunixtime = int(calendar.timegm(timetuple))
1698 1698 if offset is None:
1699 1699 # local timezone
1700 1700 unixtime = int(time.mktime(timetuple))
1701 1701 offset = unixtime - localunixtime
1702 1702 else:
1703 1703 unixtime = localunixtime + offset
1704 1704 return unixtime, offset
1705 1705
1706 1706 def parsedate(date, formats=None, defaults=None):
1707 1707 """parse a localized date/time string and return a (unixtime, offset) tuple.
1708 1708
1709 1709 The date may be a "unixtime offset" string or in one of the specified
1710 1710 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1711 1711 """
1712 1712 if not date:
1713 1713 return 0, 0
1714 1714 if isinstance(date, tuple) and len(date) == 2:
1715 1715 return date
1716 1716 if not formats:
1717 1717 formats = defaultdateformats
1718 1718 date = date.strip()
1719 1719 try:
1720 1720 when, offset = map(int, date.split(' '))
1721 1721 except ValueError:
1722 1722 # fill out defaults
1723 1723 if not defaults:
1724 1724 defaults = {}
1725 1725 now = makedate()
1726 1726 for part in "d mb yY HI M S".split():
1727 1727 if part not in defaults:
1728 1728 if part[0] in "HMS":
1729 1729 defaults[part] = "00"
1730 1730 else:
1731 1731 defaults[part] = datestr(now, "%" + part[0])
1732 1732
1733 1733 for format in formats:
1734 1734 try:
1735 1735 when, offset = strdate(date, format, defaults)
1736 1736 except (ValueError, OverflowError):
1737 1737 pass
1738 1738 else:
1739 1739 break
1740 1740 else:
1741 1741 raise Abort(_('invalid date: %r ') % date)
1742 1742 # validate explicit (probably user-specified) date and
1743 1743 # time zone offset. values must fit in signed 32 bits for
1744 1744 # current 32-bit linux runtimes. timezones go from UTC-12
1745 1745 # to UTC+14
1746 1746 if abs(when) > 0x7fffffff:
1747 1747 raise Abort(_('date exceeds 32 bits: %d') % when)
1748 1748 if offset < -50400 or offset > 43200:
1749 1749 raise Abort(_('impossible time zone offset: %d') % offset)
1750 1750 return when, offset
1751 1751
1752 1752 def matchdate(date):
1753 1753 """Return a function that matches a given date match specifier
1754 1754
1755 1755 Formats include:
1756 1756
1757 1757 '{date}' match a given date to the accuracy provided
1758 1758
1759 1759 '<{date}' on or before a given date
1760 1760
1761 1761 '>{date}' on or after a given date
1762 1762
1763 1763 """
1764 1764
1765 1765 def lower(date):
1766 1766 d = dict(mb="1", d="1")
1767 1767 return parsedate(date, extendeddateformats, d)[0]
1768 1768
1769 1769 def upper(date):
1770 1770 d = dict(mb="12", HI="23", M="59", S="59")
1771 1771 for days in "31 30 29".split():
1772 1772 try:
1773 1773 d["d"] = days
1774 1774 return parsedate(date, extendeddateformats, d)[0]
1775 1775 except:
1776 1776 pass
1777 1777 d["d"] = "28"
1778 1778 return parsedate(date, extendeddateformats, d)[0]
1779 1779
1780 1780 if date[0] == "<":
1781 1781 when = upper(date[1:])
1782 1782 return lambda x: x <= when
1783 1783 elif date[0] == ">":
1784 1784 when = lower(date[1:])
1785 1785 return lambda x: x >= when
1786 1786 elif date[0] == "-":
1787 1787 try:
1788 1788 days = int(date[1:])
1789 1789 except ValueError:
1790 1790 raise Abort(_("invalid day spec: %s") % date[1:])
1791 1791 when = makedate()[0] - days * 3600 * 24
1792 1792 return lambda x: x >= when
1793 1793 elif " to " in date:
1794 1794 a, b = date.split(" to ")
1795 1795 start, stop = lower(a), upper(b)
1796 1796 return lambda x: x >= start and x <= stop
1797 1797 else:
1798 1798 start, stop = lower(date), upper(date)
1799 1799 return lambda x: x >= start and x <= stop
1800 1800
1801 1801 def shortuser(user):
1802 1802 """Return a short representation of a user name or email address."""
1803 1803 f = user.find('@')
1804 1804 if f >= 0:
1805 1805 user = user[:f]
1806 1806 f = user.find('<')
1807 1807 if f >= 0:
1808 1808 user = user[f+1:]
1809 1809 f = user.find(' ')
1810 1810 if f >= 0:
1811 1811 user = user[:f]
1812 1812 f = user.find('.')
1813 1813 if f >= 0:
1814 1814 user = user[:f]
1815 1815 return user
1816 1816
1817 1817 def email(author):
1818 1818 '''get email of author.'''
1819 1819 r = author.find('>')
1820 1820 if r == -1: r = None
1821 1821 return author[author.find('<')+1:r]
1822 1822
1823 1823 def ellipsis(text, maxlength=400):
1824 1824 """Trim string to at most maxlength (default: 400) characters."""
1825 1825 if len(text) <= maxlength:
1826 1826 return text
1827 1827 else:
1828 1828 return "%s..." % (text[:maxlength-3])
1829 1829
1830 1830 def walkrepos(path, followsym=False, seen_dirs=None):
1831 1831 '''yield every hg repository under path, recursively.'''
1832 1832 def errhandler(err):
1833 1833 if err.filename == path:
1834 1834 raise err
1835 1835 if followsym and hasattr(os.path, 'samestat'):
1836 1836 def _add_dir_if_not_there(dirlst, dirname):
1837 1837 match = False
1838 1838 samestat = os.path.samestat
1839 1839 dirstat = os.stat(dirname)
1840 1840 for lstdirstat in dirlst:
1841 1841 if samestat(dirstat, lstdirstat):
1842 1842 match = True
1843 1843 break
1844 1844 if not match:
1845 1845 dirlst.append(dirstat)
1846 1846 return not match
1847 1847 else:
1848 1848 followsym = False
1849 1849
1850 1850 if (seen_dirs is None) and followsym:
1851 1851 seen_dirs = []
1852 1852 _add_dir_if_not_there(seen_dirs, path)
1853 1853 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1854 1854 if '.hg' in dirs:
1855 1855 dirs.remove('.hg') # don't recurse inside the .hg directory
1856 1856 yield root # found a repository
1857 1857 qroot = os.path.join(root, '.hg', 'patches')
1858 1858 if os.path.isdir(os.path.join(qroot, '.hg')):
1859 1859 yield qroot # we have a patch queue repo here
1860 1860 elif followsym:
1861 1861 newdirs = []
1862 1862 for d in dirs:
1863 1863 fname = os.path.join(root, d)
1864 1864 if _add_dir_if_not_there(seen_dirs, fname):
1865 1865 if os.path.islink(fname):
1866 1866 for hgname in walkrepos(fname, True, seen_dirs):
1867 1867 yield hgname
1868 1868 else:
1869 1869 newdirs.append(d)
1870 1870 dirs[:] = newdirs
1871 1871
1872 1872 _rcpath = None
1873 1873
1874 1874 def os_rcpath():
1875 1875 '''return default os-specific hgrc search path'''
1876 1876 path = system_rcpath()
1877 1877 path.extend(user_rcpath())
1878 1878 path = [os.path.normpath(f) for f in path]
1879 1879 return path
1880 1880
1881 1881 def rcpath():
1882 1882 '''return hgrc search path. if env var HGRCPATH is set, use it.
1883 1883 for each item in path, if directory, use files ending in .rc,
1884 1884 else use item.
1885 1885 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1886 1886 if no HGRCPATH, use default os-specific path.'''
1887 1887 global _rcpath
1888 1888 if _rcpath is None:
1889 1889 if 'HGRCPATH' in os.environ:
1890 1890 _rcpath = []
1891 1891 for p in os.environ['HGRCPATH'].split(os.pathsep):
1892 1892 if not p: continue
1893 1893 if os.path.isdir(p):
1894 1894 for f, kind in osutil.listdir(p):
1895 1895 if f.endswith('.rc'):
1896 1896 _rcpath.append(os.path.join(p, f))
1897 1897 else:
1898 1898 _rcpath.append(p)
1899 1899 else:
1900 1900 _rcpath = os_rcpath()
1901 1901 return _rcpath
1902 1902
1903 1903 def bytecount(nbytes):
1904 1904 '''return byte count formatted as readable string, with units'''
1905 1905
1906 1906 units = (
1907 1907 (100, 1<<30, _('%.0f GB')),
1908 1908 (10, 1<<30, _('%.1f GB')),
1909 1909 (1, 1<<30, _('%.2f GB')),
1910 1910 (100, 1<<20, _('%.0f MB')),
1911 1911 (10, 1<<20, _('%.1f MB')),
1912 1912 (1, 1<<20, _('%.2f MB')),
1913 1913 (100, 1<<10, _('%.0f KB')),
1914 1914 (10, 1<<10, _('%.1f KB')),
1915 1915 (1, 1<<10, _('%.2f KB')),
1916 1916 (1, 1, _('%.0f bytes')),
1917 1917 )
1918 1918
1919 1919 for multiplier, divisor, format in units:
1920 1920 if nbytes >= divisor * multiplier:
1921 1921 return format % (nbytes / float(divisor))
1922 1922 return units[-1][2] % nbytes
1923 1923
1924 1924 def drop_scheme(scheme, path):
1925 1925 sc = scheme + ':'
1926 1926 if path.startswith(sc):
1927 1927 path = path[len(sc):]
1928 1928 if path.startswith('//'):
1929 1929 path = path[2:]
1930 1930 return path
1931 1931
1932 1932 def uirepr(s):
1933 1933 # Avoid double backslash in Windows path repr()
1934 1934 return repr(s).replace('\\\\', '\\')
General Comments 0
You need to be logged in to leave comments. Login now