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