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