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