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