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