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