##// END OF EJS Templates
fix issue 217....
Vadim Gelfer -
r2117:760339cc default
parent child Browse files
Show More
@@ -1,842 +1,845
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
6 6 This software may be used and distributed according to the terms
7 7 of the GNU General Public License, incorporated herein by reference.
8 8
9 9 This contains helper routines that are independent of the SCM core and hide
10 10 platform-specific details from the core.
11 11 """
12 12
13 13 import os, errno
14 14 from i18n import gettext as _
15 15 from demandload import *
16 16 demandload(globals(), "cStringIO errno popen2 re shutil sys tempfile")
17 17 demandload(globals(), "threading time")
18 18
19 19 def pipefilter(s, cmd):
20 20 '''filter string S through command CMD, returning its output'''
21 21 (pout, pin) = popen2.popen2(cmd, -1, 'b')
22 22 def writer():
23 23 try:
24 24 pin.write(s)
25 25 pin.close()
26 26 except IOError, inst:
27 27 if inst.errno != errno.EPIPE:
28 28 raise
29 29
30 30 # we should use select instead on UNIX, but this will work on most
31 31 # systems, including Windows
32 32 w = threading.Thread(target=writer)
33 33 w.start()
34 34 f = pout.read()
35 35 pout.close()
36 36 w.join()
37 37 return f
38 38
39 39 def tempfilter(s, cmd):
40 40 '''filter string S through a pair of temporary files with CMD.
41 41 CMD is used as a template to create the real command to be run,
42 42 with the strings INFILE and OUTFILE replaced by the real names of
43 43 the temporary files generated.'''
44 44 inname, outname = None, None
45 45 try:
46 46 infd, inname = tempfile.mkstemp(prefix='hgfin')
47 47 fp = os.fdopen(infd, 'wb')
48 48 fp.write(s)
49 49 fp.close()
50 50 outfd, outname = tempfile.mkstemp(prefix='hgfout')
51 51 os.close(outfd)
52 52 cmd = cmd.replace('INFILE', inname)
53 53 cmd = cmd.replace('OUTFILE', outname)
54 54 code = os.system(cmd)
55 55 if code: raise Abort(_("command '%s' failed: %s") %
56 56 (cmd, explain_exit(code)))
57 57 return open(outname, 'rb').read()
58 58 finally:
59 59 try:
60 60 if inname: os.unlink(inname)
61 61 except: pass
62 62 try:
63 63 if outname: os.unlink(outname)
64 64 except: pass
65 65
66 66 filtertable = {
67 67 'tempfile:': tempfilter,
68 68 'pipe:': pipefilter,
69 69 }
70 70
71 71 def filter(s, cmd):
72 72 "filter a string through a command that transforms its input to its output"
73 73 for name, fn in filtertable.iteritems():
74 74 if cmd.startswith(name):
75 75 return fn(s, cmd[len(name):].lstrip())
76 76 return pipefilter(s, cmd)
77 77
78 78 def find_in_path(name, path, default=None):
79 79 '''find name in search path. path can be string (will be split
80 80 with os.pathsep), or iterable thing that returns strings. if name
81 81 found, return path to name. else return default.'''
82 82 if isinstance(path, str):
83 83 path = path.split(os.pathsep)
84 84 for p in path:
85 85 p_name = os.path.join(p, name)
86 86 if os.path.exists(p_name):
87 87 return p_name
88 88 return default
89 89
90 90 def patch(strip, patchname, ui):
91 91 """apply the patch <patchname> to the working directory.
92 92 a list of patched files is returned"""
93 93 patcher = find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
94 94 fp = os.popen('"%s" -p%d < "%s"' % (patcher, strip, patchname))
95 95 files = {}
96 96 for line in fp:
97 97 line = line.rstrip()
98 98 ui.status("%s\n" % line)
99 99 if line.startswith('patching file '):
100 100 pf = parse_patch_output(line)
101 101 files.setdefault(pf, 1)
102 102 code = fp.close()
103 103 if code:
104 104 raise Abort(_("patch command failed: %s") % explain_exit(code)[0])
105 105 return files.keys()
106 106
107 107 def binary(s):
108 108 """return true if a string is binary data using diff's heuristic"""
109 109 if s and '\0' in s[:4096]:
110 110 return True
111 111 return False
112 112
113 113 def unique(g):
114 114 """return the uniq elements of iterable g"""
115 115 seen = {}
116 116 for f in g:
117 117 if f not in seen:
118 118 seen[f] = 1
119 119 yield f
120 120
121 121 class Abort(Exception):
122 122 """Raised if a command needs to print an error and exit."""
123 123
124 124 def always(fn): return True
125 125 def never(fn): return False
126 126
127 127 def patkind(name, dflt_pat='glob'):
128 128 """Split a string into an optional pattern kind prefix and the
129 129 actual pattern."""
130 130 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
131 131 if name.startswith(prefix + ':'): return name.split(':', 1)
132 132 return dflt_pat, name
133 133
134 134 def globre(pat, head='^', tail='$'):
135 135 "convert a glob pattern into a regexp"
136 136 i, n = 0, len(pat)
137 137 res = ''
138 138 group = False
139 139 def peek(): return i < n and pat[i]
140 140 while i < n:
141 141 c = pat[i]
142 142 i = i+1
143 143 if c == '*':
144 144 if peek() == '*':
145 145 i += 1
146 146 res += '.*'
147 147 else:
148 148 res += '[^/]*'
149 149 elif c == '?':
150 150 res += '.'
151 151 elif c == '[':
152 152 j = i
153 153 if j < n and pat[j] in '!]':
154 154 j += 1
155 155 while j < n and pat[j] != ']':
156 156 j += 1
157 157 if j >= n:
158 158 res += '\\['
159 159 else:
160 160 stuff = pat[i:j].replace('\\','\\\\')
161 161 i = j + 1
162 162 if stuff[0] == '!':
163 163 stuff = '^' + stuff[1:]
164 164 elif stuff[0] == '^':
165 165 stuff = '\\' + stuff
166 166 res = '%s[%s]' % (res, stuff)
167 167 elif c == '{':
168 168 group = True
169 169 res += '(?:'
170 170 elif c == '}' and group:
171 171 res += ')'
172 172 group = False
173 173 elif c == ',' and group:
174 174 res += '|'
175 175 elif c == '\\':
176 176 p = peek()
177 177 if p:
178 178 i += 1
179 179 res += re.escape(p)
180 180 else:
181 181 res += re.escape(c)
182 182 else:
183 183 res += re.escape(c)
184 184 return head + res + tail
185 185
186 186 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
187 187
188 188 def pathto(n1, n2):
189 189 '''return the relative path from one place to another.
190 190 this returns a path in the form used by the local filesystem, not hg.'''
191 191 if not n1: return localpath(n2)
192 192 a, b = n1.split('/'), n2.split('/')
193 193 a.reverse()
194 194 b.reverse()
195 195 while a and b and a[-1] == b[-1]:
196 196 a.pop()
197 197 b.pop()
198 198 b.reverse()
199 199 return os.sep.join((['..'] * len(a)) + b)
200 200
201 201 def canonpath(root, cwd, myname):
202 202 """return the canonical path of myname, given cwd and root"""
203 203 if root == os.sep:
204 204 rootsep = os.sep
205 205 else:
206 206 rootsep = root + os.sep
207 207 name = myname
208 208 if not os.path.isabs(name):
209 209 name = os.path.join(root, cwd, name)
210 210 name = os.path.normpath(name)
211 211 if name.startswith(rootsep):
212 212 name = name[len(rootsep):]
213 213 audit_path(name)
214 214 return pconvert(name)
215 215 elif name == root:
216 216 return ''
217 217 else:
218 218 # Determine whether `name' is in the hierarchy at or beneath `root',
219 219 # by iterating name=dirname(name) until that causes no change (can't
220 220 # check name == '/', because that doesn't work on windows). For each
221 221 # `name', compare dev/inode numbers. If they match, the list `rel'
222 222 # holds the reversed list of components making up the relative file
223 223 # name we want.
224 224 root_st = os.stat(root)
225 225 rel = []
226 226 while True:
227 227 try:
228 228 name_st = os.stat(name)
229 229 except OSError:
230 230 break
231 231 if os.path.samestat(name_st, root_st):
232 232 rel.reverse()
233 233 name = os.path.join(*rel)
234 234 audit_path(name)
235 235 return pconvert(name)
236 236 dirname, basename = os.path.split(name)
237 237 rel.append(basename)
238 238 if dirname == name:
239 239 break
240 240 name = dirname
241 241
242 242 raise Abort('%s not under root' % myname)
243 243
244 244 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
245 245 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
246 246
247 247 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
248 248 if os.name == 'nt':
249 249 dflt_pat = 'glob'
250 250 else:
251 251 dflt_pat = 'relpath'
252 252 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
253 253
254 254 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
255 255 """build a function to match a set of file patterns
256 256
257 257 arguments:
258 258 canonroot - the canonical root of the tree you're matching against
259 259 cwd - the current working directory, if relevant
260 260 names - patterns to find
261 261 inc - patterns to include
262 262 exc - patterns to exclude
263 263 head - a regex to prepend to patterns to control whether a match is rooted
264 264
265 265 a pattern is one of:
266 266 'glob:<rooted glob>'
267 267 're:<rooted regexp>'
268 268 'path:<rooted path>'
269 269 'relglob:<relative glob>'
270 270 'relpath:<relative path>'
271 271 'relre:<relative regexp>'
272 272 '<rooted path or regexp>'
273 273
274 274 returns:
275 275 a 3-tuple containing
276 276 - list of explicit non-pattern names passed in
277 277 - a bool match(filename) function
278 278 - a bool indicating if any patterns were passed in
279 279
280 280 todo:
281 281 make head regex a rooted bool
282 282 """
283 283
284 284 def contains_glob(name):
285 285 for c in name:
286 286 if c in _globchars: return True
287 287 return False
288 288
289 289 def regex(kind, name, tail):
290 290 '''convert a pattern into a regular expression'''
291 291 if kind == 're':
292 292 return name
293 293 elif kind == 'path':
294 294 return '^' + re.escape(name) + '(?:/|$)'
295 295 elif kind == 'relglob':
296 296 return head + globre(name, '(?:|.*/)', tail)
297 297 elif kind == 'relpath':
298 298 return head + re.escape(name) + tail
299 299 elif kind == 'relre':
300 300 if name.startswith('^'):
301 301 return name
302 302 return '.*' + name
303 303 return head + globre(name, '', tail)
304 304
305 305 def matchfn(pats, tail):
306 306 """build a matching function from a set of patterns"""
307 307 if not pats:
308 308 return
309 309 matches = []
310 310 for k, p in pats:
311 311 try:
312 312 pat = '(?:%s)' % regex(k, p, tail)
313 313 matches.append(re.compile(pat).match)
314 314 except re.error:
315 315 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
316 316 else: raise Abort("invalid pattern (%s): %s" % (k, p))
317 317
318 318 def buildfn(text):
319 319 for m in matches:
320 320 r = m(text)
321 321 if r:
322 322 return r
323 323
324 324 return buildfn
325 325
326 326 def globprefix(pat):
327 327 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
328 328 root = []
329 329 for p in pat.split(os.sep):
330 330 if contains_glob(p): break
331 331 root.append(p)
332 332 return '/'.join(root)
333 333
334 334 pats = []
335 335 files = []
336 336 roots = []
337 337 for kind, name in [patkind(p, dflt_pat) for p in names]:
338 338 if kind in ('glob', 'relpath'):
339 339 name = canonpath(canonroot, cwd, name)
340 340 if name == '':
341 341 kind, name = 'glob', '**'
342 342 if kind in ('glob', 'path', 're'):
343 343 pats.append((kind, name))
344 344 if kind == 'glob':
345 345 root = globprefix(name)
346 346 if root: roots.append(root)
347 347 elif kind == 'relpath':
348 348 files.append((kind, name))
349 349 roots.append(name)
350 350
351 351 patmatch = matchfn(pats, '$') or always
352 352 filematch = matchfn(files, '(?:/|$)') or always
353 353 incmatch = always
354 354 if inc:
355 355 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
356 356 excmatch = lambda fn: False
357 357 if exc:
358 358 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
359 359
360 360 return (roots,
361 361 lambda fn: (incmatch(fn) and not excmatch(fn) and
362 362 (fn.endswith('/') or
363 363 (not pats and not files) or
364 364 (pats and patmatch(fn)) or
365 365 (files and filematch(fn)))),
366 366 (inc or exc or (pats and pats != [('glob', '**')])) and True)
367 367
368 368 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
369 369 '''enhanced shell command execution.
370 370 run with environment maybe modified, maybe in different dir.
371 371
372 372 if command fails and onerr is None, return status. if ui object,
373 373 print error message and return status, else raise onerr object as
374 374 exception.'''
375 375 oldenv = {}
376 376 for k in environ:
377 377 oldenv[k] = os.environ.get(k)
378 378 if cwd is not None:
379 379 oldcwd = os.getcwd()
380 380 try:
381 381 for k, v in environ.iteritems():
382 382 os.environ[k] = str(v)
383 383 if cwd is not None and oldcwd != cwd:
384 384 os.chdir(cwd)
385 385 rc = os.system(cmd)
386 386 if rc and onerr:
387 387 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
388 388 explain_exit(rc)[0])
389 389 if errprefix:
390 390 errmsg = '%s: %s' % (errprefix, errmsg)
391 391 try:
392 392 onerr.warn(errmsg + '\n')
393 393 except AttributeError:
394 394 raise onerr(errmsg)
395 395 return rc
396 396 finally:
397 397 for k, v in oldenv.iteritems():
398 398 if v is None:
399 399 del os.environ[k]
400 400 else:
401 401 os.environ[k] = v
402 402 if cwd is not None and oldcwd != cwd:
403 403 os.chdir(oldcwd)
404 404
405 405 def rename(src, dst):
406 406 """forcibly rename a file"""
407 407 try:
408 408 os.rename(src, dst)
409 409 except:
410 410 os.unlink(dst)
411 411 os.rename(src, dst)
412 412
413 413 def unlink(f):
414 414 """unlink and remove the directory if it is empty"""
415 415 os.unlink(f)
416 416 # try removing directories that might now be empty
417 417 try:
418 418 os.removedirs(os.path.dirname(f))
419 419 except OSError:
420 420 pass
421 421
422 422 def copyfiles(src, dst, hardlink=None):
423 423 """Copy a directory tree using hardlinks if possible"""
424 424
425 425 if hardlink is None:
426 426 hardlink = (os.stat(src).st_dev ==
427 427 os.stat(os.path.dirname(dst)).st_dev)
428 428
429 429 if os.path.isdir(src):
430 430 os.mkdir(dst)
431 431 for name in os.listdir(src):
432 432 srcname = os.path.join(src, name)
433 433 dstname = os.path.join(dst, name)
434 434 copyfiles(srcname, dstname, hardlink)
435 435 else:
436 436 if hardlink:
437 437 try:
438 438 os_link(src, dst)
439 439 except (IOError, OSError):
440 440 hardlink = False
441 441 shutil.copy(src, dst)
442 442 else:
443 443 shutil.copy(src, dst)
444 444
445 445 def audit_path(path):
446 446 """Abort if path contains dangerous components"""
447 447 parts = os.path.normcase(path).split(os.sep)
448 448 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
449 449 or os.pardir in parts):
450 450 raise Abort(_("path contains illegal component: %s\n") % path)
451 451
452 452 def opener(base, audit=True):
453 453 """
454 454 return a function that opens files relative to base
455 455
456 456 this function is used to hide the details of COW semantics and
457 457 remote file access from higher level code.
458 458 """
459 459 p = base
460 460 audit_p = audit
461 461
462 462 def mktempcopy(name):
463 463 d, fn = os.path.split(name)
464 464 fd, temp = tempfile.mkstemp(prefix=fn, dir=d)
465 465 fp = os.fdopen(fd, "wb")
466 466 try:
467 467 fp.write(file(name, "rb").read())
468 468 except:
469 469 try: os.unlink(temp)
470 470 except: pass
471 471 raise
472 472 fp.close()
473 473 st = os.lstat(name)
474 474 os.chmod(temp, st.st_mode)
475 475 return temp
476 476
477 477 class atomictempfile(file):
478 478 """the file will only be copied when rename is called"""
479 479 def __init__(self, name, mode):
480 480 self.__name = name
481 481 self.temp = mktempcopy(name)
482 482 file.__init__(self, self.temp, mode)
483 483 def rename(self):
484 484 if not self.closed:
485 485 file.close(self)
486 486 rename(self.temp, self.__name)
487 487 def __del__(self):
488 488 if not self.closed:
489 489 try:
490 490 os.unlink(self.temp)
491 491 except: pass
492 492 file.close(self)
493 493
494 494 class atomicfile(atomictempfile):
495 495 """the file will only be copied on close"""
496 496 def __init__(self, name, mode):
497 497 atomictempfile.__init__(self, name, mode)
498 498 def close(self):
499 499 self.rename()
500 500 def __del__(self):
501 501 self.rename()
502 502
503 503 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
504 504 if audit_p:
505 505 audit_path(path)
506 506 f = os.path.join(p, path)
507 507
508 508 if not text:
509 509 mode += "b" # for that other OS
510 510
511 511 if mode[0] != "r":
512 512 try:
513 513 nlink = nlinks(f)
514 514 except OSError:
515 515 d = os.path.dirname(f)
516 516 if not os.path.isdir(d):
517 517 os.makedirs(d)
518 518 else:
519 519 if atomic:
520 520 return atomicfile(f, mode)
521 521 elif atomictemp:
522 522 return atomictempfile(f, mode)
523 523 if nlink > 1:
524 524 rename(mktempcopy(f), f)
525 525 return file(f, mode)
526 526
527 527 return o
528 528
529 529 def _makelock_file(info, pathname):
530 530 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
531 531 os.write(ld, info)
532 532 os.close(ld)
533 533
534 534 def _readlock_file(pathname):
535 535 return file(pathname).read()
536 536
537 537 def nlinks(pathname):
538 538 """Return number of hardlinks for the given file."""
539 539 return os.stat(pathname).st_nlink
540 540
541 541 if hasattr(os, 'link'):
542 542 os_link = os.link
543 543 else:
544 544 def os_link(src, dst):
545 545 raise OSError(0, _("Hardlinks not supported"))
546 546
547 547 # Platform specific variants
548 548 if os.name == 'nt':
549 549 demandload(globals(), "msvcrt")
550 550 nulldev = 'NUL:'
551 551
552 552 class winstdout:
553 553 '''stdout on windows misbehaves if sent through a pipe'''
554 554
555 555 def __init__(self, fp):
556 556 self.fp = fp
557 557
558 558 def __getattr__(self, key):
559 559 return getattr(self.fp, key)
560 560
561 561 def close(self):
562 562 try:
563 563 self.fp.close()
564 564 except: pass
565 565
566 566 def write(self, s):
567 567 try:
568 568 return self.fp.write(s)
569 569 except IOError, inst:
570 570 if inst.errno != 0: raise
571 571 self.close()
572 572 raise IOError(errno.EPIPE, 'Broken pipe')
573 573
574 574 sys.stdout = winstdout(sys.stdout)
575 575
576 576 def system_rcpath():
577 try:
578 return system_rcpath_win32()
579 except:
577 580 return [r'c:\mercurial\mercurial.ini']
578 581
579 582 def os_rcpath():
580 583 '''return default os-specific hgrc search path'''
581 584 return system_rcpath() + [os.path.join(os.path.expanduser('~'),
582 585 'mercurial.ini')]
583 586
584 587 def parse_patch_output(output_line):
585 588 """parses the output produced by patch and returns the file name"""
586 589 pf = output_line[14:]
587 590 if pf[0] == '`':
588 591 pf = pf[1:-1] # Remove the quotes
589 592 return pf
590 593
591 594 def testpid(pid):
592 595 '''return False if pid dead, True if running or not known'''
593 596 return True
594 597
595 598 def is_exec(f, last):
596 599 return last
597 600
598 601 def set_exec(f, mode):
599 602 pass
600 603
601 604 def set_binary(fd):
602 605 msvcrt.setmode(fd.fileno(), os.O_BINARY)
603 606
604 607 def pconvert(path):
605 608 return path.replace("\\", "/")
606 609
607 610 def localpath(path):
608 611 return path.replace('/', '\\')
609 612
610 613 def normpath(path):
611 614 return pconvert(os.path.normpath(path))
612 615
613 616 makelock = _makelock_file
614 617 readlock = _readlock_file
615 618
616 619 def explain_exit(code):
617 620 return _("exited with status %d") % code, code
618 621
619 622 try:
620 623 # override functions with win32 versions if possible
621 624 from util_win32 import *
622 625 except ImportError:
623 626 pass
624 627
625 628 else:
626 629 nulldev = '/dev/null'
627 630
628 631 def rcfiles(path):
629 632 rcs = [os.path.join(path, 'hgrc')]
630 633 rcdir = os.path.join(path, 'hgrc.d')
631 634 try:
632 635 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
633 636 if f.endswith(".rc")])
634 637 except OSError, inst: pass
635 638 return rcs
636 639
637 640 def os_rcpath():
638 641 '''return default os-specific hgrc search path'''
639 642 path = []
640 643 if len(sys.argv) > 0:
641 644 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
642 645 '/../etc/mercurial'))
643 646 path.extend(rcfiles('/etc/mercurial'))
644 647 path.append(os.path.expanduser('~/.hgrc'))
645 648 path = [os.path.normpath(f) for f in path]
646 649 return path
647 650
648 651 def parse_patch_output(output_line):
649 652 """parses the output produced by patch and returns the file name"""
650 653 pf = output_line[14:]
651 654 if pf.startswith("'") and pf.endswith("'") and pf.find(" ") >= 0:
652 655 pf = pf[1:-1] # Remove the quotes
653 656 return pf
654 657
655 658 def is_exec(f, last):
656 659 """check whether a file is executable"""
657 660 return (os.stat(f).st_mode & 0100 != 0)
658 661
659 662 def set_exec(f, mode):
660 663 s = os.stat(f).st_mode
661 664 if (s & 0100 != 0) == mode:
662 665 return
663 666 if mode:
664 667 # Turn on +x for every +r bit when making a file executable
665 668 # and obey umask.
666 669 umask = os.umask(0)
667 670 os.umask(umask)
668 671 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
669 672 else:
670 673 os.chmod(f, s & 0666)
671 674
672 675 def set_binary(fd):
673 676 pass
674 677
675 678 def pconvert(path):
676 679 return path
677 680
678 681 def localpath(path):
679 682 return path
680 683
681 684 normpath = os.path.normpath
682 685
683 686 def makelock(info, pathname):
684 687 try:
685 688 os.symlink(info, pathname)
686 689 except OSError, why:
687 690 if why.errno == errno.EEXIST:
688 691 raise
689 692 else:
690 693 _makelock_file(info, pathname)
691 694
692 695 def readlock(pathname):
693 696 try:
694 697 return os.readlink(pathname)
695 698 except OSError, why:
696 699 if why.errno == errno.EINVAL:
697 700 return _readlock_file(pathname)
698 701 else:
699 702 raise
700 703
701 704 def testpid(pid):
702 705 '''return False if pid dead, True if running or not sure'''
703 706 try:
704 707 os.kill(pid, 0)
705 708 return True
706 709 except OSError, inst:
707 710 return inst.errno != errno.ESRCH
708 711
709 712 def explain_exit(code):
710 713 """return a 2-tuple (desc, code) describing a process's status"""
711 714 if os.WIFEXITED(code):
712 715 val = os.WEXITSTATUS(code)
713 716 return _("exited with status %d") % val, val
714 717 elif os.WIFSIGNALED(code):
715 718 val = os.WTERMSIG(code)
716 719 return _("killed by signal %d") % val, val
717 720 elif os.WIFSTOPPED(code):
718 721 val = os.WSTOPSIG(code)
719 722 return _("stopped by signal %d") % val, val
720 723 raise ValueError(_("invalid exit code"))
721 724
722 725 class chunkbuffer(object):
723 726 """Allow arbitrary sized chunks of data to be efficiently read from an
724 727 iterator over chunks of arbitrary size."""
725 728
726 729 def __init__(self, in_iter, targetsize = 2**16):
727 730 """in_iter is the iterator that's iterating over the input chunks.
728 731 targetsize is how big a buffer to try to maintain."""
729 732 self.in_iter = iter(in_iter)
730 733 self.buf = ''
731 734 self.targetsize = int(targetsize)
732 735 if self.targetsize <= 0:
733 736 raise ValueError(_("targetsize must be greater than 0, was %d") %
734 737 targetsize)
735 738 self.iterempty = False
736 739
737 740 def fillbuf(self):
738 741 """Ignore target size; read every chunk from iterator until empty."""
739 742 if not self.iterempty:
740 743 collector = cStringIO.StringIO()
741 744 collector.write(self.buf)
742 745 for ch in self.in_iter:
743 746 collector.write(ch)
744 747 self.buf = collector.getvalue()
745 748 self.iterempty = True
746 749
747 750 def read(self, l):
748 751 """Read L bytes of data from the iterator of chunks of data.
749 752 Returns less than L bytes if the iterator runs dry."""
750 753 if l > len(self.buf) and not self.iterempty:
751 754 # Clamp to a multiple of self.targetsize
752 755 targetsize = self.targetsize * ((l // self.targetsize) + 1)
753 756 collector = cStringIO.StringIO()
754 757 collector.write(self.buf)
755 758 collected = len(self.buf)
756 759 for chunk in self.in_iter:
757 760 collector.write(chunk)
758 761 collected += len(chunk)
759 762 if collected >= targetsize:
760 763 break
761 764 if collected < targetsize:
762 765 self.iterempty = True
763 766 self.buf = collector.getvalue()
764 767 s, self.buf = self.buf[:l], buffer(self.buf, l)
765 768 return s
766 769
767 770 def filechunkiter(f, size = 65536):
768 771 """Create a generator that produces all the data in the file size
769 772 (default 65536) bytes at a time. Chunks may be less than size
770 773 bytes if the chunk is the last chunk in the file, or the file is a
771 774 socket or some other type of file that sometimes reads less data
772 775 than is requested."""
773 776 s = f.read(size)
774 777 while len(s) > 0:
775 778 yield s
776 779 s = f.read(size)
777 780
778 781 def makedate():
779 782 lt = time.localtime()
780 783 if lt[8] == 1 and time.daylight:
781 784 tz = time.altzone
782 785 else:
783 786 tz = time.timezone
784 787 return time.mktime(lt), tz
785 788
786 789 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
787 790 """represent a (unixtime, offset) tuple as a localized time.
788 791 unixtime is seconds since the epoch, and offset is the time zone's
789 792 number of seconds away from UTC. if timezone is false, do not
790 793 append time zone to string."""
791 794 t, tz = date or makedate()
792 795 s = time.strftime(format, time.gmtime(float(t) - tz))
793 796 if timezone:
794 797 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
795 798 return s
796 799
797 800 def shortuser(user):
798 801 """Return a short representation of a user name or email address."""
799 802 f = user.find('@')
800 803 if f >= 0:
801 804 user = user[:f]
802 805 f = user.find('<')
803 806 if f >= 0:
804 807 user = user[f+1:]
805 808 return user
806 809
807 810 def walkrepos(path):
808 811 '''yield every hg repository under path, recursively.'''
809 812 def errhandler(err):
810 813 if err.filename == path:
811 814 raise err
812 815
813 816 for root, dirs, files in os.walk(path, onerror=errhandler):
814 817 for d in dirs:
815 818 if d == '.hg':
816 819 yield root
817 820 dirs[:] = []
818 821 break
819 822
820 823 _rcpath = None
821 824
822 825 def rcpath():
823 826 '''return hgrc search path. if env var HGRCPATH is set, use it.
824 827 for each item in path, if directory, use files ending in .rc,
825 828 else use item.
826 829 make HGRCPATH empty to only look in .hg/hgrc of current repo.
827 830 if no HGRCPATH, use default os-specific path.'''
828 831 global _rcpath
829 832 if _rcpath is None:
830 833 if 'HGRCPATH' in os.environ:
831 834 _rcpath = []
832 835 for p in os.environ['HGRCPATH'].split(os.pathsep):
833 836 if not p: continue
834 837 if os.path.isdir(p):
835 838 for f in os.listdir(p):
836 839 if f.endswith('.rc'):
837 840 _rcpath.append(os.path.join(p, f))
838 841 else:
839 842 _rcpath.append(p)
840 843 else:
841 844 _rcpath = os_rcpath()
842 845 return _rcpath
@@ -1,171 +1,171
1 1 # util_win32.py - utility functions that use win32 API
2 2 #
3 3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 5 #
6 6 # This software may be used and distributed according to the terms of
7 7 # the GNU General Public License, incorporated herein by reference.
8 8
9 9 # Mark Hammond's win32all package allows better functionality on
10 10 # Windows. this module overrides definitions in util.py. if not
11 11 # available, import of this module will fail, and generic code will be
12 12 # used.
13 13
14 14 import win32api
15 15
16 16 from demandload import *
17 17 from i18n import gettext as _
18 18 demandload(globals(), 'errno os pywintypes win32con win32file win32process')
19 19 demandload(globals(), 'winerror')
20 20
21 21 class WinError(OSError):
22 22 winerror_map = {
23 23 winerror.ERROR_ACCESS_DENIED: errno.EACCES,
24 24 winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES,
25 25 winerror.ERROR_ACCOUNT_RESTRICTION: errno.EACCES,
26 26 winerror.ERROR_ALREADY_ASSIGNED: errno.EBUSY,
27 27 winerror.ERROR_ALREADY_EXISTS: errno.EEXIST,
28 28 winerror.ERROR_ARITHMETIC_OVERFLOW: errno.ERANGE,
29 29 winerror.ERROR_BAD_COMMAND: errno.EIO,
30 30 winerror.ERROR_BAD_DEVICE: errno.ENODEV,
31 31 winerror.ERROR_BAD_DRIVER_LEVEL: errno.ENXIO,
32 32 winerror.ERROR_BAD_EXE_FORMAT: errno.ENOEXEC,
33 33 winerror.ERROR_BAD_FORMAT: errno.ENOEXEC,
34 34 winerror.ERROR_BAD_LENGTH: errno.EINVAL,
35 35 winerror.ERROR_BAD_PATHNAME: errno.ENOENT,
36 36 winerror.ERROR_BAD_PIPE: errno.EPIPE,
37 37 winerror.ERROR_BAD_UNIT: errno.ENODEV,
38 38 winerror.ERROR_BAD_USERNAME: errno.EINVAL,
39 39 winerror.ERROR_BROKEN_PIPE: errno.EPIPE,
40 40 winerror.ERROR_BUFFER_OVERFLOW: errno.ENAMETOOLONG,
41 41 winerror.ERROR_BUSY: errno.EBUSY,
42 42 winerror.ERROR_BUSY_DRIVE: errno.EBUSY,
43 43 winerror.ERROR_CALL_NOT_IMPLEMENTED: errno.ENOSYS,
44 44 winerror.ERROR_CANNOT_MAKE: errno.EACCES,
45 45 winerror.ERROR_CANTOPEN: errno.EIO,
46 46 winerror.ERROR_CANTREAD: errno.EIO,
47 47 winerror.ERROR_CANTWRITE: errno.EIO,
48 48 winerror.ERROR_CRC: errno.EIO,
49 49 winerror.ERROR_CURRENT_DIRECTORY: errno.EACCES,
50 50 winerror.ERROR_DEVICE_IN_USE: errno.EBUSY,
51 51 winerror.ERROR_DEV_NOT_EXIST: errno.ENODEV,
52 52 winerror.ERROR_DIRECTORY: errno.EINVAL,
53 53 winerror.ERROR_DIR_NOT_EMPTY: errno.ENOTEMPTY,
54 54 winerror.ERROR_DISK_CHANGE: errno.EIO,
55 55 winerror.ERROR_DISK_FULL: errno.ENOSPC,
56 56 winerror.ERROR_DRIVE_LOCKED: errno.EBUSY,
57 57 winerror.ERROR_ENVVAR_NOT_FOUND: errno.EINVAL,
58 58 winerror.ERROR_EXE_MARKED_INVALID: errno.ENOEXEC,
59 59 winerror.ERROR_FILENAME_EXCED_RANGE: errno.ENAMETOOLONG,
60 60 winerror.ERROR_FILE_EXISTS: errno.EEXIST,
61 61 winerror.ERROR_FILE_INVALID: errno.ENODEV,
62 62 winerror.ERROR_FILE_NOT_FOUND: errno.ENOENT,
63 63 winerror.ERROR_GEN_FAILURE: errno.EIO,
64 64 winerror.ERROR_HANDLE_DISK_FULL: errno.ENOSPC,
65 65 winerror.ERROR_INSUFFICIENT_BUFFER: errno.ENOMEM,
66 66 winerror.ERROR_INVALID_ACCESS: errno.EACCES,
67 67 winerror.ERROR_INVALID_ADDRESS: errno.EFAULT,
68 68 winerror.ERROR_INVALID_BLOCK: errno.EFAULT,
69 69 winerror.ERROR_INVALID_DATA: errno.EINVAL,
70 70 winerror.ERROR_INVALID_DRIVE: errno.ENODEV,
71 71 winerror.ERROR_INVALID_EXE_SIGNATURE: errno.ENOEXEC,
72 72 winerror.ERROR_INVALID_FLAGS: errno.EINVAL,
73 73 winerror.ERROR_INVALID_FUNCTION: errno.ENOSYS,
74 74 winerror.ERROR_INVALID_HANDLE: errno.EBADF,
75 75 winerror.ERROR_INVALID_LOGON_HOURS: errno.EACCES,
76 76 winerror.ERROR_INVALID_NAME: errno.EINVAL,
77 77 winerror.ERROR_INVALID_OWNER: errno.EINVAL,
78 78 winerror.ERROR_INVALID_PARAMETER: errno.EINVAL,
79 79 winerror.ERROR_INVALID_PASSWORD: errno.EPERM,
80 80 winerror.ERROR_INVALID_PRIMARY_GROUP: errno.EINVAL,
81 81 winerror.ERROR_INVALID_SIGNAL_NUMBER: errno.EINVAL,
82 82 winerror.ERROR_INVALID_TARGET_HANDLE: errno.EIO,
83 83 winerror.ERROR_INVALID_WORKSTATION: errno.EACCES,
84 84 winerror.ERROR_IO_DEVICE: errno.EIO,
85 85 winerror.ERROR_IO_INCOMPLETE: errno.EINTR,
86 86 winerror.ERROR_LOCKED: errno.EBUSY,
87 87 winerror.ERROR_LOCK_VIOLATION: errno.EACCES,
88 88 winerror.ERROR_LOGON_FAILURE: errno.EACCES,
89 89 winerror.ERROR_MAPPED_ALIGNMENT: errno.EINVAL,
90 90 winerror.ERROR_META_EXPANSION_TOO_LONG: errno.E2BIG,
91 91 winerror.ERROR_MORE_DATA: errno.EPIPE,
92 92 winerror.ERROR_NEGATIVE_SEEK: errno.ESPIPE,
93 93 winerror.ERROR_NOACCESS: errno.EFAULT,
94 94 winerror.ERROR_NONE_MAPPED: errno.EINVAL,
95 95 winerror.ERROR_NOT_ENOUGH_MEMORY: errno.ENOMEM,
96 96 winerror.ERROR_NOT_READY: errno.EAGAIN,
97 97 winerror.ERROR_NOT_SAME_DEVICE: errno.EXDEV,
98 98 winerror.ERROR_NO_DATA: errno.EPIPE,
99 99 winerror.ERROR_NO_MORE_SEARCH_HANDLES: errno.EIO,
100 100 winerror.ERROR_NO_PROC_SLOTS: errno.EAGAIN,
101 101 winerror.ERROR_NO_SUCH_PRIVILEGE: errno.EACCES,
102 102 winerror.ERROR_OPEN_FAILED: errno.EIO,
103 103 winerror.ERROR_OPEN_FILES: errno.EBUSY,
104 104 winerror.ERROR_OPERATION_ABORTED: errno.EINTR,
105 105 winerror.ERROR_OUTOFMEMORY: errno.ENOMEM,
106 106 winerror.ERROR_PASSWORD_EXPIRED: errno.EACCES,
107 107 winerror.ERROR_PATH_BUSY: errno.EBUSY,
108 108 winerror.ERROR_PATH_NOT_FOUND: errno.ENOTDIR,
109 109 winerror.ERROR_PIPE_BUSY: errno.EBUSY,
110 110 winerror.ERROR_PIPE_CONNECTED: errno.EPIPE,
111 111 winerror.ERROR_PIPE_LISTENING: errno.EPIPE,
112 112 winerror.ERROR_PIPE_NOT_CONNECTED: errno.EPIPE,
113 113 winerror.ERROR_PRIVILEGE_NOT_HELD: errno.EACCES,
114 114 winerror.ERROR_READ_FAULT: errno.EIO,
115 115 winerror.ERROR_SEEK: errno.EIO,
116 116 winerror.ERROR_SEEK_ON_DEVICE: errno.ESPIPE,
117 117 winerror.ERROR_SHARING_BUFFER_EXCEEDED: errno.ENFILE,
118 118 winerror.ERROR_SHARING_VIOLATION: errno.EACCES,
119 119 winerror.ERROR_STACK_OVERFLOW: errno.ENOMEM,
120 120 winerror.ERROR_SWAPERROR: errno.ENOENT,
121 121 winerror.ERROR_TOO_MANY_MODULES: errno.EMFILE,
122 122 winerror.ERROR_TOO_MANY_OPEN_FILES: errno.EMFILE,
123 123 winerror.ERROR_UNRECOGNIZED_MEDIA: errno.ENXIO,
124 124 winerror.ERROR_UNRECOGNIZED_VOLUME: errno.ENODEV,
125 125 winerror.ERROR_WAIT_NO_CHILDREN: errno.ECHILD,
126 126 winerror.ERROR_WRITE_FAULT: errno.EIO,
127 127 winerror.ERROR_WRITE_PROTECT: errno.EROFS,
128 128 }
129 129
130 130 def __init__(self, err):
131 131 self.win_errno, self.win_function, self.win_strerror = err
132 132 OSError.__init__(self, self.winerror_map.get(self.win_errno, 0),
133 133 self.win_strerror)
134 134
135 135 def os_link(src, dst):
136 136 # NB will only succeed on NTFS
137 137 try:
138 138 win32file.CreateHardLink(dst, src)
139 139 except pywintypes.error, details:
140 140 raise WinError(details)
141 141
142 142 def nlinks(pathname):
143 143 """Return number of hardlinks for the given file."""
144 144 try:
145 145 fh = win32file.CreateFile(pathname,
146 146 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
147 147 None, win32file.OPEN_EXISTING, 0, None)
148 148 res = win32file.GetFileInformationByHandle(fh)
149 149 fh.Close()
150 150 return res[7]
151 151 except pywintypes.error:
152 152 return os.stat(pathname).st_nlink
153 153
154 154 def testpid(pid):
155 155 '''return True if pid is still running or unable to
156 156 determine, False otherwise'''
157 157 try:
158 158 handle = win32api.OpenProcess(
159 159 win32con.PROCESS_QUERY_INFORMATION, False, pid)
160 160 if handle:
161 161 status = win32process.GetExitCodeProcess(handle)
162 162 return status == win32con.STILL_ACTIVE
163 163 except pywintypes.error, details:
164 164 return details[0] != winerror.ERROR_INVALID_PARAMETER
165 165 return True
166 166
167 def system_rcpath():
167 def system_rcpath_win32():
168 168 '''return default os-specific hgrc search path'''
169 169 proc = win32api.GetCurrentProcess()
170 170 filename = win32process.GetModuleFileNameEx(proc, 0)
171 171 return [os.path.join(os.path.dirname(filename), 'mercurial.ini')]
General Comments 0
You need to be logged in to leave comments. Login now