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