##// END OF EJS Templates
imported patch folding
Matt Mackall -
r3784:1427949b default
parent child Browse files
Show More
@@ -1,1160 +1,1182 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, 2006 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 gettext as _
16 16 from demandload import *
17 17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
18 18 demandload(globals(), "os threading time calendar ConfigParser locale")
19 19
20 20 _encoding = os.environ.get("HGENCODING") or locale.getpreferredencoding()
21 21 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
22 22
23 23 def tolocal(s):
24 24 """
25 25 Convert a string from internal UTF-8 to local encoding
26 26
27 27 All internal strings should be UTF-8 but some repos before the
28 28 implementation of locale support may contain latin1 or possibly
29 29 other character sets. We attempt to decode everything strictly
30 30 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
31 31 replace unknown characters.
32 32 """
33 33 for e in "utf-8 latin1".split():
34 34 try:
35 35 u = s.decode(e) # attempt strict decoding
36 36 return u.encode(_encoding, "replace")
37 37 except UnicodeDecodeError:
38 38 pass
39 39 u = s.decode("utf-8", "replace") # last ditch
40 40 return u.encode(_encoding, "replace")
41 41
42 42 def fromlocal(s):
43 43 """
44 44 Convert a string from the local character encoding to UTF-8
45 45
46 46 We attempt to decode strings using the encoding mode set by
47 47 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
48 48 characters will cause an error message. Other modes include
49 49 'replace', which replaces unknown characters with a special
50 50 Unicode character, and 'ignore', which drops the character.
51 51 """
52 52 try:
53 53 return s.decode(_encoding, _encodingmode).encode("utf-8")
54 54 except UnicodeDecodeError, inst:
55 55 sub = s[max(0, inst.start-10):inst.start+10]
56 56 raise Abort("decoding near '%s': %s!\n" % (sub, inst))
57 57
58 58 def locallen(s):
59 59 """Find the length in characters of a local string"""
60 60 return len(s.decode(_encoding, "replace"))
61 61
62 62 def localsub(s, a, b=None):
63 63 try:
64 64 u = s.decode(_encoding, _encodingmode)
65 65 if b is not None:
66 66 u = u[a:b]
67 67 else:
68 68 u = u[:a]
69 69 return u.encode(_encoding, _encodingmode)
70 70 except UnicodeDecodeError, inst:
71 71 sub = s[max(0, inst.start-10), inst.start+10]
72 72 raise Abort("decoding near '%s': %s!\n" % (sub, inst))
73 73
74 74 # used by parsedate
75 75 defaultdateformats = ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M',
76 76 '%a %b %d %H:%M:%S %Y')
77 77
78 78 class SignalInterrupt(Exception):
79 79 """Exception raised on SIGTERM and SIGHUP."""
80 80
81 81 # like SafeConfigParser but with case-sensitive keys
82 82 class configparser(ConfigParser.SafeConfigParser):
83 83 def optionxform(self, optionstr):
84 84 return optionstr
85 85
86 86 def cachefunc(func):
87 87 '''cache the result of function calls'''
88 88 # XXX doesn't handle keywords args
89 89 cache = {}
90 90 if func.func_code.co_argcount == 1:
91 91 # we gain a small amount of time because
92 92 # we don't need to pack/unpack the list
93 93 def f(arg):
94 94 if arg not in cache:
95 95 cache[arg] = func(arg)
96 96 return cache[arg]
97 97 else:
98 98 def f(*args):
99 99 if args not in cache:
100 100 cache[args] = func(*args)
101 101 return cache[args]
102 102
103 103 return f
104 104
105 105 def pipefilter(s, cmd):
106 106 '''filter string S through command CMD, returning its output'''
107 107 (pout, pin) = popen2.popen2(cmd, -1, 'b')
108 108 def writer():
109 109 try:
110 110 pin.write(s)
111 111 pin.close()
112 112 except IOError, inst:
113 113 if inst.errno != errno.EPIPE:
114 114 raise
115 115
116 116 # we should use select instead on UNIX, but this will work on most
117 117 # systems, including Windows
118 118 w = threading.Thread(target=writer)
119 119 w.start()
120 120 f = pout.read()
121 121 pout.close()
122 122 w.join()
123 123 return f
124 124
125 125 def tempfilter(s, cmd):
126 126 '''filter string S through a pair of temporary files with CMD.
127 127 CMD is used as a template to create the real command to be run,
128 128 with the strings INFILE and OUTFILE replaced by the real names of
129 129 the temporary files generated.'''
130 130 inname, outname = None, None
131 131 try:
132 132 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
133 133 fp = os.fdopen(infd, 'wb')
134 134 fp.write(s)
135 135 fp.close()
136 136 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
137 137 os.close(outfd)
138 138 cmd = cmd.replace('INFILE', inname)
139 139 cmd = cmd.replace('OUTFILE', outname)
140 140 code = os.system(cmd)
141 141 if code: raise Abort(_("command '%s' failed: %s") %
142 142 (cmd, explain_exit(code)))
143 143 return open(outname, 'rb').read()
144 144 finally:
145 145 try:
146 146 if inname: os.unlink(inname)
147 147 except: pass
148 148 try:
149 149 if outname: os.unlink(outname)
150 150 except: pass
151 151
152 152 filtertable = {
153 153 'tempfile:': tempfilter,
154 154 'pipe:': pipefilter,
155 155 }
156 156
157 157 def filter(s, cmd):
158 158 "filter a string through a command that transforms its input to its output"
159 159 for name, fn in filtertable.iteritems():
160 160 if cmd.startswith(name):
161 161 return fn(s, cmd[len(name):].lstrip())
162 162 return pipefilter(s, cmd)
163 163
164 164 def find_in_path(name, path, default=None):
165 165 '''find name in search path. path can be string (will be split
166 166 with os.pathsep), or iterable thing that returns strings. if name
167 167 found, return path to name. else return default.'''
168 168 if isinstance(path, str):
169 169 path = path.split(os.pathsep)
170 170 for p in path:
171 171 p_name = os.path.join(p, name)
172 172 if os.path.exists(p_name):
173 173 return p_name
174 174 return default
175 175
176 176 def binary(s):
177 177 """return true if a string is binary data using diff's heuristic"""
178 178 if s and '\0' in s[:4096]:
179 179 return True
180 180 return False
181 181
182 182 def unique(g):
183 183 """return the uniq elements of iterable g"""
184 184 seen = {}
185 185 l = []
186 186 for f in g:
187 187 if f not in seen:
188 188 seen[f] = 1
189 189 l.append(f)
190 190 return l
191 191
192 192 class Abort(Exception):
193 193 """Raised if a command needs to print an error and exit."""
194 194
195 195 class UnexpectedOutput(Abort):
196 196 """Raised to print an error with part of output and exit."""
197 197
198 198 def always(fn): return True
199 199 def never(fn): return False
200 200
201 201 def patkind(name, dflt_pat='glob'):
202 202 """Split a string into an optional pattern kind prefix and the
203 203 actual pattern."""
204 204 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
205 205 if name.startswith(prefix + ':'): return name.split(':', 1)
206 206 return dflt_pat, name
207 207
208 208 def globre(pat, head='^', tail='$'):
209 209 "convert a glob pattern into a regexp"
210 210 i, n = 0, len(pat)
211 211 res = ''
212 212 group = False
213 213 def peek(): return i < n and pat[i]
214 214 while i < n:
215 215 c = pat[i]
216 216 i = i+1
217 217 if c == '*':
218 218 if peek() == '*':
219 219 i += 1
220 220 res += '.*'
221 221 else:
222 222 res += '[^/]*'
223 223 elif c == '?':
224 224 res += '.'
225 225 elif c == '[':
226 226 j = i
227 227 if j < n and pat[j] in '!]':
228 228 j += 1
229 229 while j < n and pat[j] != ']':
230 230 j += 1
231 231 if j >= n:
232 232 res += '\\['
233 233 else:
234 234 stuff = pat[i:j].replace('\\','\\\\')
235 235 i = j + 1
236 236 if stuff[0] == '!':
237 237 stuff = '^' + stuff[1:]
238 238 elif stuff[0] == '^':
239 239 stuff = '\\' + stuff
240 240 res = '%s[%s]' % (res, stuff)
241 241 elif c == '{':
242 242 group = True
243 243 res += '(?:'
244 244 elif c == '}' and group:
245 245 res += ')'
246 246 group = False
247 247 elif c == ',' and group:
248 248 res += '|'
249 249 elif c == '\\':
250 250 p = peek()
251 251 if p:
252 252 i += 1
253 253 res += re.escape(p)
254 254 else:
255 255 res += re.escape(c)
256 256 else:
257 257 res += re.escape(c)
258 258 return head + res + tail
259 259
260 260 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
261 261
262 262 def pathto(n1, n2):
263 263 '''return the relative path from one place to another.
264 264 n1 should use os.sep to separate directories
265 265 n2 should use "/" to separate directories
266 266 returns an os.sep-separated path.
267 267 '''
268 268 if not n1: return localpath(n2)
269 269 a, b = n1.split(os.sep), n2.split('/')
270 270 a.reverse()
271 271 b.reverse()
272 272 while a and b and a[-1] == b[-1]:
273 273 a.pop()
274 274 b.pop()
275 275 b.reverse()
276 276 return os.sep.join((['..'] * len(a)) + b)
277 277
278 278 def canonpath(root, cwd, myname):
279 279 """return the canonical path of myname, given cwd and root"""
280 280 if root == os.sep:
281 281 rootsep = os.sep
282 282 elif root.endswith(os.sep):
283 283 rootsep = root
284 284 else:
285 285 rootsep = root + os.sep
286 286 name = myname
287 287 if not os.path.isabs(name):
288 288 name = os.path.join(root, cwd, name)
289 289 name = os.path.normpath(name)
290 290 if name != rootsep and name.startswith(rootsep):
291 291 name = name[len(rootsep):]
292 292 audit_path(name)
293 293 return pconvert(name)
294 294 elif name == root:
295 295 return ''
296 296 else:
297 297 # Determine whether `name' is in the hierarchy at or beneath `root',
298 298 # by iterating name=dirname(name) until that causes no change (can't
299 299 # check name == '/', because that doesn't work on windows). For each
300 300 # `name', compare dev/inode numbers. If they match, the list `rel'
301 301 # holds the reversed list of components making up the relative file
302 302 # name we want.
303 303 root_st = os.stat(root)
304 304 rel = []
305 305 while True:
306 306 try:
307 307 name_st = os.stat(name)
308 308 except OSError:
309 309 break
310 310 if samestat(name_st, root_st):
311 311 rel.reverse()
312 312 name = os.path.join(*rel)
313 313 audit_path(name)
314 314 return pconvert(name)
315 315 dirname, basename = os.path.split(name)
316 316 rel.append(basename)
317 317 if dirname == name:
318 318 break
319 319 name = dirname
320 320
321 321 raise Abort('%s not under root' % myname)
322 322
323 323 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
324 324 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
325 325
326 326 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
327 327 if os.name == 'nt':
328 328 dflt_pat = 'glob'
329 329 else:
330 330 dflt_pat = 'relpath'
331 331 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
332 332
333 333 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
334 334 """build a function to match a set of file patterns
335 335
336 336 arguments:
337 337 canonroot - the canonical root of the tree you're matching against
338 338 cwd - the current working directory, if relevant
339 339 names - patterns to find
340 340 inc - patterns to include
341 341 exc - patterns to exclude
342 342 head - a regex to prepend to patterns to control whether a match is rooted
343 343
344 344 a pattern is one of:
345 345 'glob:<rooted glob>'
346 346 're:<rooted regexp>'
347 347 'path:<rooted path>'
348 348 'relglob:<relative glob>'
349 349 'relpath:<relative path>'
350 350 'relre:<relative regexp>'
351 351 '<rooted path or regexp>'
352 352
353 353 returns:
354 354 a 3-tuple containing
355 355 - list of explicit non-pattern names passed in
356 356 - a bool match(filename) function
357 357 - a bool indicating if any patterns were passed in
358 358
359 359 todo:
360 360 make head regex a rooted bool
361 361 """
362 362
363 363 def contains_glob(name):
364 364 for c in name:
365 365 if c in _globchars: return True
366 366 return False
367 367
368 368 def regex(kind, name, tail):
369 369 '''convert a pattern into a regular expression'''
370 370 if kind == 're':
371 371 return name
372 372 elif kind == 'path':
373 373 return '^' + re.escape(name) + '(?:/|$)'
374 374 elif kind == 'relglob':
375 375 return head + globre(name, '(?:|.*/)', tail)
376 376 elif kind == 'relpath':
377 377 return head + re.escape(name) + tail
378 378 elif kind == 'relre':
379 379 if name.startswith('^'):
380 380 return name
381 381 return '.*' + name
382 382 return head + globre(name, '', tail)
383 383
384 384 def matchfn(pats, tail):
385 385 """build a matching function from a set of patterns"""
386 386 if not pats:
387 387 return
388 388 matches = []
389 389 for k, p in pats:
390 390 try:
391 391 pat = '(?:%s)' % regex(k, p, tail)
392 392 matches.append(re.compile(pat).match)
393 393 except re.error:
394 394 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
395 395 else: raise Abort("invalid pattern (%s): %s" % (k, p))
396 396
397 397 def buildfn(text):
398 398 for m in matches:
399 399 r = m(text)
400 400 if r:
401 401 return r
402 402
403 403 return buildfn
404 404
405 405 def globprefix(pat):
406 406 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
407 407 root = []
408 408 for p in pat.split(os.sep):
409 409 if contains_glob(p): break
410 410 root.append(p)
411 411 return '/'.join(root)
412 412
413 413 pats = []
414 414 files = []
415 415 roots = []
416 416 for kind, name in [patkind(p, dflt_pat) for p in names]:
417 417 if kind in ('glob', 'relpath'):
418 418 name = canonpath(canonroot, cwd, name)
419 419 if name == '':
420 420 kind, name = 'glob', '**'
421 421 if kind in ('glob', 'path', 're'):
422 422 pats.append((kind, name))
423 423 if kind == 'glob':
424 424 root = globprefix(name)
425 425 if root: roots.append(root)
426 426 elif kind == 'relpath':
427 427 files.append((kind, name))
428 428 roots.append(name)
429 429
430 430 patmatch = matchfn(pats, '$') or always
431 431 filematch = matchfn(files, '(?:/|$)') or always
432 432 incmatch = always
433 433 if inc:
434 434 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
435 435 incmatch = matchfn(inckinds, '(?:/|$)')
436 436 excmatch = lambda fn: False
437 437 if exc:
438 438 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
439 439 excmatch = matchfn(exckinds, '(?:/|$)')
440 440
441 441 return (roots,
442 442 lambda fn: (incmatch(fn) and not excmatch(fn) and
443 443 (fn.endswith('/') or
444 444 (not pats and not files) or
445 445 (pats and patmatch(fn)) or
446 446 (files and filematch(fn)))),
447 447 (inc or exc or (pats and pats != [('glob', '**')])) and True)
448 448
449 449 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
450 450 '''enhanced shell command execution.
451 451 run with environment maybe modified, maybe in different dir.
452 452
453 453 if command fails and onerr is None, return status. if ui object,
454 454 print error message and return status, else raise onerr object as
455 455 exception.'''
456 456 def py2shell(val):
457 457 'convert python object into string that is useful to shell'
458 458 if val in (None, False):
459 459 return '0'
460 460 if val == True:
461 461 return '1'
462 462 return str(val)
463 463 oldenv = {}
464 464 for k in environ:
465 465 oldenv[k] = os.environ.get(k)
466 466 if cwd is not None:
467 467 oldcwd = os.getcwd()
468 468 try:
469 469 for k, v in environ.iteritems():
470 470 os.environ[k] = py2shell(v)
471 471 if cwd is not None and oldcwd != cwd:
472 472 os.chdir(cwd)
473 473 rc = os.system(cmd)
474 474 if rc and onerr:
475 475 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
476 476 explain_exit(rc)[0])
477 477 if errprefix:
478 478 errmsg = '%s: %s' % (errprefix, errmsg)
479 479 try:
480 480 onerr.warn(errmsg + '\n')
481 481 except AttributeError:
482 482 raise onerr(errmsg)
483 483 return rc
484 484 finally:
485 485 for k, v in oldenv.iteritems():
486 486 if v is None:
487 487 del os.environ[k]
488 488 else:
489 489 os.environ[k] = v
490 490 if cwd is not None and oldcwd != cwd:
491 491 os.chdir(oldcwd)
492 492
493 493 def rename(src, dst):
494 494 """forcibly rename a file"""
495 495 try:
496 496 os.rename(src, dst)
497 497 except OSError, err:
498 498 # on windows, rename to existing file is not allowed, so we
499 499 # must delete destination first. but if file is open, unlink
500 500 # schedules it for delete but does not delete it. rename
501 501 # happens immediately even for open files, so we create
502 502 # temporary file, delete it, rename destination to that name,
503 503 # then delete that. then rename is safe to do.
504 504 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
505 505 os.close(fd)
506 506 os.unlink(temp)
507 507 os.rename(dst, temp)
508 508 os.unlink(temp)
509 509 os.rename(src, dst)
510 510
511 511 def unlink(f):
512 512 """unlink and remove the directory if it is empty"""
513 513 os.unlink(f)
514 514 # try removing directories that might now be empty
515 515 try:
516 516 os.removedirs(os.path.dirname(f))
517 517 except OSError:
518 518 pass
519 519
520 520 def copyfile(src, dest):
521 521 "copy a file, preserving mode"
522 522 try:
523 523 shutil.copyfile(src, dest)
524 524 shutil.copymode(src, dest)
525 525 except shutil.Error, inst:
526 526 raise util.Abort(str(inst))
527 527
528 528 def copyfiles(src, dst, hardlink=None):
529 529 """Copy a directory tree using hardlinks if possible"""
530 530
531 531 if hardlink is None:
532 532 hardlink = (os.stat(src).st_dev ==
533 533 os.stat(os.path.dirname(dst)).st_dev)
534 534
535 535 if os.path.isdir(src):
536 536 os.mkdir(dst)
537 537 for name in os.listdir(src):
538 538 srcname = os.path.join(src, name)
539 539 dstname = os.path.join(dst, name)
540 540 copyfiles(srcname, dstname, hardlink)
541 541 else:
542 542 if hardlink:
543 543 try:
544 544 os_link(src, dst)
545 545 except (IOError, OSError):
546 546 hardlink = False
547 547 shutil.copy(src, dst)
548 548 else:
549 549 shutil.copy(src, dst)
550 550
551 551 def audit_path(path):
552 552 """Abort if path contains dangerous components"""
553 553 parts = os.path.normcase(path).split(os.sep)
554 554 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
555 555 or os.pardir in parts):
556 556 raise Abort(_("path contains illegal component: %s\n") % path)
557 557
558 558 def _makelock_file(info, pathname):
559 559 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
560 560 os.write(ld, info)
561 561 os.close(ld)
562 562
563 563 def _readlock_file(pathname):
564 564 return posixfile(pathname).read()
565 565
566 566 def nlinks(pathname):
567 567 """Return number of hardlinks for the given file."""
568 568 return os.lstat(pathname).st_nlink
569 569
570 570 if hasattr(os, 'link'):
571 571 os_link = os.link
572 572 else:
573 573 def os_link(src, dst):
574 574 raise OSError(0, _("Hardlinks not supported"))
575 575
576 576 def fstat(fp):
577 577 '''stat file object that may not have fileno method.'''
578 578 try:
579 579 return os.fstat(fp.fileno())
580 580 except AttributeError:
581 581 return os.stat(fp.name)
582 582
583 583 posixfile = file
584 584
585 585 def is_win_9x():
586 586 '''return true if run on windows 95, 98 or me.'''
587 587 try:
588 588 return sys.getwindowsversion()[3] == 1
589 589 except AttributeError:
590 590 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
591 591
592 592 getuser_fallback = None
593 593
594 594 def getuser():
595 595 '''return name of current user'''
596 596 try:
597 597 return getpass.getuser()
598 598 except ImportError:
599 599 # import of pwd will fail on windows - try fallback
600 600 if getuser_fallback:
601 601 return getuser_fallback()
602 602 # raised if win32api not available
603 603 raise Abort(_('user name not available - set USERNAME '
604 604 'environment variable'))
605 605
606 606 def username(uid=None):
607 607 """Return the name of the user with the given uid.
608 608
609 609 If uid is None, return the name of the current user."""
610 610 try:
611 611 import pwd
612 612 if uid is None:
613 613 uid = os.getuid()
614 614 try:
615 615 return pwd.getpwuid(uid)[0]
616 616 except KeyError:
617 617 return str(uid)
618 618 except ImportError:
619 619 return None
620 620
621 621 def groupname(gid=None):
622 622 """Return the name of the group with the given gid.
623 623
624 624 If gid is None, return the name of the current group."""
625 625 try:
626 626 import grp
627 627 if gid is None:
628 628 gid = os.getgid()
629 629 try:
630 630 return grp.getgrgid(gid)[0]
631 631 except KeyError:
632 632 return str(gid)
633 633 except ImportError:
634 634 return None
635 635
636 # File system features
637
638 def checkfolding(path):
639 """
640 Check whether the given path is on a case-sensitive filesystem
641
642 Requires a path (like /foo/.hg) ending with a foldable final
643 directory component.
644 """
645 s1 = os.stat(path)
646 d, b = os.path.split(path)
647 p2 = os.path.join(d, b.upper())
648 if path == p2:
649 p2 = os.path.join(d, b.lower())
650 try:
651 s2 = os.stat(p2)
652 if s2 == s1:
653 return False
654 return True
655 except:
656 return True
657
636 658 # Platform specific variants
637 659 if os.name == 'nt':
638 660 demandload(globals(), "msvcrt")
639 661 nulldev = 'NUL:'
640 662
641 663 class winstdout:
642 664 '''stdout on windows misbehaves if sent through a pipe'''
643 665
644 666 def __init__(self, fp):
645 667 self.fp = fp
646 668
647 669 def __getattr__(self, key):
648 670 return getattr(self.fp, key)
649 671
650 672 def close(self):
651 673 try:
652 674 self.fp.close()
653 675 except: pass
654 676
655 677 def write(self, s):
656 678 try:
657 679 return self.fp.write(s)
658 680 except IOError, inst:
659 681 if inst.errno != 0: raise
660 682 self.close()
661 683 raise IOError(errno.EPIPE, 'Broken pipe')
662 684
663 685 sys.stdout = winstdout(sys.stdout)
664 686
665 687 def system_rcpath():
666 688 try:
667 689 return system_rcpath_win32()
668 690 except:
669 691 return [r'c:\mercurial\mercurial.ini']
670 692
671 693 def os_rcpath():
672 694 '''return default os-specific hgrc search path'''
673 695 path = system_rcpath()
674 696 path.append(user_rcpath())
675 697 userprofile = os.environ.get('USERPROFILE')
676 698 if userprofile:
677 699 path.append(os.path.join(userprofile, 'mercurial.ini'))
678 700 return path
679 701
680 702 def user_rcpath():
681 703 '''return os-specific hgrc search path to the user dir'''
682 704 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
683 705
684 706 def parse_patch_output(output_line):
685 707 """parses the output produced by patch and returns the file name"""
686 708 pf = output_line[14:]
687 709 if pf[0] == '`':
688 710 pf = pf[1:-1] # Remove the quotes
689 711 return pf
690 712
691 713 def testpid(pid):
692 714 '''return False if pid dead, True if running or not known'''
693 715 return True
694 716
695 717 def is_exec(f, last):
696 718 return last
697 719
698 720 def set_exec(f, mode):
699 721 pass
700 722
701 723 def set_binary(fd):
702 724 msvcrt.setmode(fd.fileno(), os.O_BINARY)
703 725
704 726 def pconvert(path):
705 727 return path.replace("\\", "/")
706 728
707 729 def localpath(path):
708 730 return path.replace('/', '\\')
709 731
710 732 def normpath(path):
711 733 return pconvert(os.path.normpath(path))
712 734
713 735 makelock = _makelock_file
714 736 readlock = _readlock_file
715 737
716 738 def samestat(s1, s2):
717 739 return False
718 740
719 741 def shellquote(s):
720 742 return '"%s"' % s.replace('"', '\\"')
721 743
722 744 def explain_exit(code):
723 745 return _("exited with status %d") % code, code
724 746
725 747 # if you change this stub into a real check, please try to implement the
726 748 # username and groupname functions above, too.
727 749 def isowner(fp, st=None):
728 750 return True
729 751
730 752 try:
731 753 # override functions with win32 versions if possible
732 754 from util_win32 import *
733 755 if not is_win_9x():
734 756 posixfile = posixfile_nt
735 757 except ImportError:
736 758 pass
737 759
738 760 else:
739 761 nulldev = '/dev/null'
740 762
741 763 def rcfiles(path):
742 764 rcs = [os.path.join(path, 'hgrc')]
743 765 rcdir = os.path.join(path, 'hgrc.d')
744 766 try:
745 767 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
746 768 if f.endswith(".rc")])
747 769 except OSError:
748 770 pass
749 771 return rcs
750 772
751 773 def os_rcpath():
752 774 '''return default os-specific hgrc search path'''
753 775 path = []
754 776 # old mod_python does not set sys.argv
755 777 if len(getattr(sys, 'argv', [])) > 0:
756 778 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
757 779 '/../etc/mercurial'))
758 780 path.extend(rcfiles('/etc/mercurial'))
759 781 path.append(os.path.expanduser('~/.hgrc'))
760 782 path = [os.path.normpath(f) for f in path]
761 783 return path
762 784
763 785 def parse_patch_output(output_line):
764 786 """parses the output produced by patch and returns the file name"""
765 787 pf = output_line[14:]
766 788 if pf.startswith("'") and pf.endswith("'") and " " in pf:
767 789 pf = pf[1:-1] # Remove the quotes
768 790 return pf
769 791
770 792 def is_exec(f, last):
771 793 """check whether a file is executable"""
772 794 return (os.lstat(f).st_mode & 0100 != 0)
773 795
774 796 def set_exec(f, mode):
775 797 s = os.lstat(f).st_mode
776 798 if (s & 0100 != 0) == mode:
777 799 return
778 800 if mode:
779 801 # Turn on +x for every +r bit when making a file executable
780 802 # and obey umask.
781 803 umask = os.umask(0)
782 804 os.umask(umask)
783 805 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
784 806 else:
785 807 os.chmod(f, s & 0666)
786 808
787 809 def set_binary(fd):
788 810 pass
789 811
790 812 def pconvert(path):
791 813 return path
792 814
793 815 def localpath(path):
794 816 return path
795 817
796 818 normpath = os.path.normpath
797 819 samestat = os.path.samestat
798 820
799 821 def makelock(info, pathname):
800 822 try:
801 823 os.symlink(info, pathname)
802 824 except OSError, why:
803 825 if why.errno == errno.EEXIST:
804 826 raise
805 827 else:
806 828 _makelock_file(info, pathname)
807 829
808 830 def readlock(pathname):
809 831 try:
810 832 return os.readlink(pathname)
811 833 except OSError, why:
812 834 if why.errno == errno.EINVAL:
813 835 return _readlock_file(pathname)
814 836 else:
815 837 raise
816 838
817 839 def shellquote(s):
818 840 return "'%s'" % s.replace("'", "'\\''")
819 841
820 842 def testpid(pid):
821 843 '''return False if pid dead, True if running or not sure'''
822 844 try:
823 845 os.kill(pid, 0)
824 846 return True
825 847 except OSError, inst:
826 848 return inst.errno != errno.ESRCH
827 849
828 850 def explain_exit(code):
829 851 """return a 2-tuple (desc, code) describing a process's status"""
830 852 if os.WIFEXITED(code):
831 853 val = os.WEXITSTATUS(code)
832 854 return _("exited with status %d") % val, val
833 855 elif os.WIFSIGNALED(code):
834 856 val = os.WTERMSIG(code)
835 857 return _("killed by signal %d") % val, val
836 858 elif os.WIFSTOPPED(code):
837 859 val = os.WSTOPSIG(code)
838 860 return _("stopped by signal %d") % val, val
839 861 raise ValueError(_("invalid exit code"))
840 862
841 863 def isowner(fp, st=None):
842 864 """Return True if the file object f belongs to the current user.
843 865
844 866 The return value of a util.fstat(f) may be passed as the st argument.
845 867 """
846 868 if st is None:
847 869 st = fstat(f)
848 870 return st.st_uid == os.getuid()
849 871
850 872
851 873 def opener(base, audit=True):
852 874 """
853 875 return a function that opens files relative to base
854 876
855 877 this function is used to hide the details of COW semantics and
856 878 remote file access from higher level code.
857 879 """
858 880 p = base
859 881 audit_p = audit
860 882
861 883 def mktempcopy(name):
862 884 d, fn = os.path.split(name)
863 885 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
864 886 os.close(fd)
865 887 ofp = posixfile(temp, "wb")
866 888 try:
867 889 try:
868 890 ifp = posixfile(name, "rb")
869 891 except IOError, inst:
870 892 if not getattr(inst, 'filename', None):
871 893 inst.filename = name
872 894 raise
873 895 for chunk in filechunkiter(ifp):
874 896 ofp.write(chunk)
875 897 ifp.close()
876 898 ofp.close()
877 899 except:
878 900 try: os.unlink(temp)
879 901 except: pass
880 902 raise
881 903 st = os.lstat(name)
882 904 os.chmod(temp, st.st_mode)
883 905 return temp
884 906
885 907 class atomictempfile(posixfile):
886 908 """the file will only be copied when rename is called"""
887 909 def __init__(self, name, mode):
888 910 self.__name = name
889 911 self.temp = mktempcopy(name)
890 912 posixfile.__init__(self, self.temp, mode)
891 913 def rename(self):
892 914 if not self.closed:
893 915 posixfile.close(self)
894 916 rename(self.temp, localpath(self.__name))
895 917 def __del__(self):
896 918 if not self.closed:
897 919 try:
898 920 os.unlink(self.temp)
899 921 except: pass
900 922 posixfile.close(self)
901 923
902 924 class atomicfile(atomictempfile):
903 925 """the file will only be copied on close"""
904 926 def __init__(self, name, mode):
905 927 atomictempfile.__init__(self, name, mode)
906 928 def close(self):
907 929 self.rename()
908 930 def __del__(self):
909 931 self.rename()
910 932
911 933 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
912 934 if audit_p:
913 935 audit_path(path)
914 936 f = os.path.join(p, path)
915 937
916 938 if not text:
917 939 mode += "b" # for that other OS
918 940
919 941 if mode[0] != "r":
920 942 try:
921 943 nlink = nlinks(f)
922 944 except OSError:
923 945 d = os.path.dirname(f)
924 946 if not os.path.isdir(d):
925 947 os.makedirs(d)
926 948 else:
927 949 if atomic:
928 950 return atomicfile(f, mode)
929 951 elif atomictemp:
930 952 return atomictempfile(f, mode)
931 953 if nlink > 1:
932 954 rename(mktempcopy(f), f)
933 955 return posixfile(f, mode)
934 956
935 957 return o
936 958
937 959 class chunkbuffer(object):
938 960 """Allow arbitrary sized chunks of data to be efficiently read from an
939 961 iterator over chunks of arbitrary size."""
940 962
941 963 def __init__(self, in_iter, targetsize = 2**16):
942 964 """in_iter is the iterator that's iterating over the input chunks.
943 965 targetsize is how big a buffer to try to maintain."""
944 966 self.in_iter = iter(in_iter)
945 967 self.buf = ''
946 968 self.targetsize = int(targetsize)
947 969 if self.targetsize <= 0:
948 970 raise ValueError(_("targetsize must be greater than 0, was %d") %
949 971 targetsize)
950 972 self.iterempty = False
951 973
952 974 def fillbuf(self):
953 975 """Ignore target size; read every chunk from iterator until empty."""
954 976 if not self.iterempty:
955 977 collector = cStringIO.StringIO()
956 978 collector.write(self.buf)
957 979 for ch in self.in_iter:
958 980 collector.write(ch)
959 981 self.buf = collector.getvalue()
960 982 self.iterempty = True
961 983
962 984 def read(self, l):
963 985 """Read L bytes of data from the iterator of chunks of data.
964 986 Returns less than L bytes if the iterator runs dry."""
965 987 if l > len(self.buf) and not self.iterempty:
966 988 # Clamp to a multiple of self.targetsize
967 989 targetsize = self.targetsize * ((l // self.targetsize) + 1)
968 990 collector = cStringIO.StringIO()
969 991 collector.write(self.buf)
970 992 collected = len(self.buf)
971 993 for chunk in self.in_iter:
972 994 collector.write(chunk)
973 995 collected += len(chunk)
974 996 if collected >= targetsize:
975 997 break
976 998 if collected < targetsize:
977 999 self.iterempty = True
978 1000 self.buf = collector.getvalue()
979 1001 s, self.buf = self.buf[:l], buffer(self.buf, l)
980 1002 return s
981 1003
982 1004 def filechunkiter(f, size=65536, limit=None):
983 1005 """Create a generator that produces the data in the file size
984 1006 (default 65536) bytes at a time, up to optional limit (default is
985 1007 to read all data). Chunks may be less than size bytes if the
986 1008 chunk is the last chunk in the file, or the file is a socket or
987 1009 some other type of file that sometimes reads less data than is
988 1010 requested."""
989 1011 assert size >= 0
990 1012 assert limit is None or limit >= 0
991 1013 while True:
992 1014 if limit is None: nbytes = size
993 1015 else: nbytes = min(limit, size)
994 1016 s = nbytes and f.read(nbytes)
995 1017 if not s: break
996 1018 if limit: limit -= len(s)
997 1019 yield s
998 1020
999 1021 def makedate():
1000 1022 lt = time.localtime()
1001 1023 if lt[8] == 1 and time.daylight:
1002 1024 tz = time.altzone
1003 1025 else:
1004 1026 tz = time.timezone
1005 1027 return time.mktime(lt), tz
1006 1028
1007 1029 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1008 1030 """represent a (unixtime, offset) tuple as a localized time.
1009 1031 unixtime is seconds since the epoch, and offset is the time zone's
1010 1032 number of seconds away from UTC. if timezone is false, do not
1011 1033 append time zone to string."""
1012 1034 t, tz = date or makedate()
1013 1035 s = time.strftime(format, time.gmtime(float(t) - tz))
1014 1036 if timezone:
1015 1037 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1016 1038 return s
1017 1039
1018 1040 def strdate(string, format='%a %b %d %H:%M:%S %Y'):
1019 1041 """parse a localized time string and return a (unixtime, offset) tuple.
1020 1042 if the string cannot be parsed, ValueError is raised."""
1021 1043 def hastimezone(string):
1022 1044 return (string[-4:].isdigit() and
1023 1045 (string[-5] == '+' or string[-5] == '-') and
1024 1046 string[-6].isspace())
1025 1047
1026 1048 # NOTE: unixtime = localunixtime + offset
1027 1049 if hastimezone(string):
1028 1050 date, tz = string[:-6], string[-5:]
1029 1051 tz = int(tz)
1030 1052 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1031 1053 else:
1032 1054 date, offset = string, None
1033 1055 timetuple = time.strptime(date, format)
1034 1056 localunixtime = int(calendar.timegm(timetuple))
1035 1057 if offset is None:
1036 1058 # local timezone
1037 1059 unixtime = int(time.mktime(timetuple))
1038 1060 offset = unixtime - localunixtime
1039 1061 else:
1040 1062 unixtime = localunixtime + offset
1041 1063 return unixtime, offset
1042 1064
1043 1065 def parsedate(string, formats=None):
1044 1066 """parse a localized time string and return a (unixtime, offset) tuple.
1045 1067 The date may be a "unixtime offset" string or in one of the specified
1046 1068 formats."""
1047 1069 if not formats:
1048 1070 formats = defaultdateformats
1049 1071 try:
1050 1072 when, offset = map(int, string.split(' '))
1051 1073 except ValueError:
1052 1074 for format in formats:
1053 1075 try:
1054 1076 when, offset = strdate(string, format)
1055 1077 except ValueError:
1056 1078 pass
1057 1079 else:
1058 1080 break
1059 1081 else:
1060 1082 raise ValueError(_('invalid date: %r '
1061 1083 'see hg(1) manual page for details')
1062 1084 % string)
1063 1085 # validate explicit (probably user-specified) date and
1064 1086 # time zone offset. values must fit in signed 32 bits for
1065 1087 # current 32-bit linux runtimes. timezones go from UTC-12
1066 1088 # to UTC+14
1067 1089 if abs(when) > 0x7fffffff:
1068 1090 raise ValueError(_('date exceeds 32 bits: %d') % when)
1069 1091 if offset < -50400 or offset > 43200:
1070 1092 raise ValueError(_('impossible time zone offset: %d') % offset)
1071 1093 return when, offset
1072 1094
1073 1095 def shortuser(user):
1074 1096 """Return a short representation of a user name or email address."""
1075 1097 f = user.find('@')
1076 1098 if f >= 0:
1077 1099 user = user[:f]
1078 1100 f = user.find('<')
1079 1101 if f >= 0:
1080 1102 user = user[f+1:]
1081 1103 f = user.find(' ')
1082 1104 if f >= 0:
1083 1105 user = user[:f]
1084 1106 f = user.find('.')
1085 1107 if f >= 0:
1086 1108 user = user[:f]
1087 1109 return user
1088 1110
1089 1111 def ellipsis(text, maxlength=400):
1090 1112 """Trim string to at most maxlength (default: 400) characters."""
1091 1113 if len(text) <= maxlength:
1092 1114 return text
1093 1115 else:
1094 1116 return "%s..." % (text[:maxlength-3])
1095 1117
1096 1118 def walkrepos(path):
1097 1119 '''yield every hg repository under path, recursively.'''
1098 1120 def errhandler(err):
1099 1121 if err.filename == path:
1100 1122 raise err
1101 1123
1102 1124 for root, dirs, files in os.walk(path, onerror=errhandler):
1103 1125 for d in dirs:
1104 1126 if d == '.hg':
1105 1127 yield root
1106 1128 dirs[:] = []
1107 1129 break
1108 1130
1109 1131 _rcpath = None
1110 1132
1111 1133 def rcpath():
1112 1134 '''return hgrc search path. if env var HGRCPATH is set, use it.
1113 1135 for each item in path, if directory, use files ending in .rc,
1114 1136 else use item.
1115 1137 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1116 1138 if no HGRCPATH, use default os-specific path.'''
1117 1139 global _rcpath
1118 1140 if _rcpath is None:
1119 1141 if 'HGRCPATH' in os.environ:
1120 1142 _rcpath = []
1121 1143 for p in os.environ['HGRCPATH'].split(os.pathsep):
1122 1144 if not p: continue
1123 1145 if os.path.isdir(p):
1124 1146 for f in os.listdir(p):
1125 1147 if f.endswith('.rc'):
1126 1148 _rcpath.append(os.path.join(p, f))
1127 1149 else:
1128 1150 _rcpath.append(p)
1129 1151 else:
1130 1152 _rcpath = os_rcpath()
1131 1153 return _rcpath
1132 1154
1133 1155 def bytecount(nbytes):
1134 1156 '''return byte count formatted as readable string, with units'''
1135 1157
1136 1158 units = (
1137 1159 (100, 1<<30, _('%.0f GB')),
1138 1160 (10, 1<<30, _('%.1f GB')),
1139 1161 (1, 1<<30, _('%.2f GB')),
1140 1162 (100, 1<<20, _('%.0f MB')),
1141 1163 (10, 1<<20, _('%.1f MB')),
1142 1164 (1, 1<<20, _('%.2f MB')),
1143 1165 (100, 1<<10, _('%.0f KB')),
1144 1166 (10, 1<<10, _('%.1f KB')),
1145 1167 (1, 1<<10, _('%.2f KB')),
1146 1168 (1, 1, _('%.0f bytes')),
1147 1169 )
1148 1170
1149 1171 for multiplier, divisor, format in units:
1150 1172 if nbytes >= divisor * multiplier:
1151 1173 return format % (nbytes / float(divisor))
1152 1174 return units[-1][2] % nbytes
1153 1175
1154 1176 def drop_scheme(scheme, path):
1155 1177 sc = scheme + ':'
1156 1178 if path.startswith(sc):
1157 1179 path = path[len(sc):]
1158 1180 if path.startswith('//'):
1159 1181 path = path[2:]
1160 1182 return path
General Comments 0
You need to be logged in to leave comments. Login now