##// END OF EJS Templates
create the encode and decode functions for the store
Benoit Boissinot -
r3852:8a9a1a7e default
parent child Browse files
Show More
@@ -1,1182 +1,1210 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 636 # File system features
637 637
638 638 def checkfolding(path):
639 639 """
640 640 Check whether the given path is on a case-sensitive filesystem
641 641
642 642 Requires a path (like /foo/.hg) ending with a foldable final
643 643 directory component.
644 644 """
645 645 s1 = os.stat(path)
646 646 d, b = os.path.split(path)
647 647 p2 = os.path.join(d, b.upper())
648 648 if path == p2:
649 649 p2 = os.path.join(d, b.lower())
650 650 try:
651 651 s2 = os.stat(p2)
652 652 if s2 == s1:
653 653 return False
654 654 return True
655 655 except:
656 656 return True
657 657
658 658 # Platform specific variants
659 659 if os.name == 'nt':
660 660 demandload(globals(), "msvcrt")
661 661 nulldev = 'NUL:'
662 662
663 663 class winstdout:
664 664 '''stdout on windows misbehaves if sent through a pipe'''
665 665
666 666 def __init__(self, fp):
667 667 self.fp = fp
668 668
669 669 def __getattr__(self, key):
670 670 return getattr(self.fp, key)
671 671
672 672 def close(self):
673 673 try:
674 674 self.fp.close()
675 675 except: pass
676 676
677 677 def write(self, s):
678 678 try:
679 679 return self.fp.write(s)
680 680 except IOError, inst:
681 681 if inst.errno != 0: raise
682 682 self.close()
683 683 raise IOError(errno.EPIPE, 'Broken pipe')
684 684
685 685 sys.stdout = winstdout(sys.stdout)
686 686
687 687 def system_rcpath():
688 688 try:
689 689 return system_rcpath_win32()
690 690 except:
691 691 return [r'c:\mercurial\mercurial.ini']
692 692
693 693 def os_rcpath():
694 694 '''return default os-specific hgrc search path'''
695 695 path = system_rcpath()
696 696 path.append(user_rcpath())
697 697 userprofile = os.environ.get('USERPROFILE')
698 698 if userprofile:
699 699 path.append(os.path.join(userprofile, 'mercurial.ini'))
700 700 return path
701 701
702 702 def user_rcpath():
703 703 '''return os-specific hgrc search path to the user dir'''
704 704 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
705 705
706 706 def parse_patch_output(output_line):
707 707 """parses the output produced by patch and returns the file name"""
708 708 pf = output_line[14:]
709 709 if pf[0] == '`':
710 710 pf = pf[1:-1] # Remove the quotes
711 711 return pf
712 712
713 713 def testpid(pid):
714 714 '''return False if pid dead, True if running or not known'''
715 715 return True
716 716
717 717 def is_exec(f, last):
718 718 return last
719 719
720 720 def set_exec(f, mode):
721 721 pass
722 722
723 723 def set_binary(fd):
724 724 msvcrt.setmode(fd.fileno(), os.O_BINARY)
725 725
726 726 def pconvert(path):
727 727 return path.replace("\\", "/")
728 728
729 729 def localpath(path):
730 730 return path.replace('/', '\\')
731 731
732 732 def normpath(path):
733 733 return pconvert(os.path.normpath(path))
734 734
735 735 makelock = _makelock_file
736 736 readlock = _readlock_file
737 737
738 738 def samestat(s1, s2):
739 739 return False
740 740
741 741 def shellquote(s):
742 742 return '"%s"' % s.replace('"', '\\"')
743 743
744 744 def explain_exit(code):
745 745 return _("exited with status %d") % code, code
746 746
747 747 # if you change this stub into a real check, please try to implement the
748 748 # username and groupname functions above, too.
749 749 def isowner(fp, st=None):
750 750 return True
751 751
752 752 try:
753 753 # override functions with win32 versions if possible
754 754 from util_win32 import *
755 755 if not is_win_9x():
756 756 posixfile = posixfile_nt
757 757 except ImportError:
758 758 pass
759 759
760 760 else:
761 761 nulldev = '/dev/null'
762 762
763 763 def rcfiles(path):
764 764 rcs = [os.path.join(path, 'hgrc')]
765 765 rcdir = os.path.join(path, 'hgrc.d')
766 766 try:
767 767 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
768 768 if f.endswith(".rc")])
769 769 except OSError:
770 770 pass
771 771 return rcs
772 772
773 773 def os_rcpath():
774 774 '''return default os-specific hgrc search path'''
775 775 path = []
776 776 # old mod_python does not set sys.argv
777 777 if len(getattr(sys, 'argv', [])) > 0:
778 778 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
779 779 '/../etc/mercurial'))
780 780 path.extend(rcfiles('/etc/mercurial'))
781 781 path.append(os.path.expanduser('~/.hgrc'))
782 782 path = [os.path.normpath(f) for f in path]
783 783 return path
784 784
785 785 def parse_patch_output(output_line):
786 786 """parses the output produced by patch and returns the file name"""
787 787 pf = output_line[14:]
788 788 if pf.startswith("'") and pf.endswith("'") and " " in pf:
789 789 pf = pf[1:-1] # Remove the quotes
790 790 return pf
791 791
792 792 def is_exec(f, last):
793 793 """check whether a file is executable"""
794 794 return (os.lstat(f).st_mode & 0100 != 0)
795 795
796 796 def set_exec(f, mode):
797 797 s = os.lstat(f).st_mode
798 798 if (s & 0100 != 0) == mode:
799 799 return
800 800 if mode:
801 801 # Turn on +x for every +r bit when making a file executable
802 802 # and obey umask.
803 803 umask = os.umask(0)
804 804 os.umask(umask)
805 805 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
806 806 else:
807 807 os.chmod(f, s & 0666)
808 808
809 809 def set_binary(fd):
810 810 pass
811 811
812 812 def pconvert(path):
813 813 return path
814 814
815 815 def localpath(path):
816 816 return path
817 817
818 818 normpath = os.path.normpath
819 819 samestat = os.path.samestat
820 820
821 821 def makelock(info, pathname):
822 822 try:
823 823 os.symlink(info, pathname)
824 824 except OSError, why:
825 825 if why.errno == errno.EEXIST:
826 826 raise
827 827 else:
828 828 _makelock_file(info, pathname)
829 829
830 830 def readlock(pathname):
831 831 try:
832 832 return os.readlink(pathname)
833 833 except OSError, why:
834 834 if why.errno == errno.EINVAL:
835 835 return _readlock_file(pathname)
836 836 else:
837 837 raise
838 838
839 839 def shellquote(s):
840 840 return "'%s'" % s.replace("'", "'\\''")
841 841
842 842 def testpid(pid):
843 843 '''return False if pid dead, True if running or not sure'''
844 844 try:
845 845 os.kill(pid, 0)
846 846 return True
847 847 except OSError, inst:
848 848 return inst.errno != errno.ESRCH
849 849
850 850 def explain_exit(code):
851 851 """return a 2-tuple (desc, code) describing a process's status"""
852 852 if os.WIFEXITED(code):
853 853 val = os.WEXITSTATUS(code)
854 854 return _("exited with status %d") % val, val
855 855 elif os.WIFSIGNALED(code):
856 856 val = os.WTERMSIG(code)
857 857 return _("killed by signal %d") % val, val
858 858 elif os.WIFSTOPPED(code):
859 859 val = os.WSTOPSIG(code)
860 860 return _("stopped by signal %d") % val, val
861 861 raise ValueError(_("invalid exit code"))
862 862
863 863 def isowner(fp, st=None):
864 864 """Return True if the file object f belongs to the current user.
865 865
866 866 The return value of a util.fstat(f) may be passed as the st argument.
867 867 """
868 868 if st is None:
869 869 st = fstat(f)
870 870 return st.st_uid == os.getuid()
871 871
872 def _buildencodefun():
873 e = '_'
874 win_reserved = [ord(x) for x in '|\?*<":>+[]']
875 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
876 for x in (range(32) + range(126, 256) + win_reserved):
877 cmap[chr(x)] = "~%02x" % x
878 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
879 cmap[chr(x)] = e + chr(x).lower()
880 dmap = {}
881 for k, v in cmap.iteritems():
882 dmap[v] = k
883 def decode(s):
884 i = 0
885 while i < len(s):
886 for l in xrange(1, 4):
887 try:
888 yield dmap[s[i:i+l]]
889 i += l
890 break
891 except KeyError:
892 pass
893 else:
894 raise KeyError
895 return (lambda s: "".join([cmap[c] for c in s]),
896 lambda s: "".join(list(decode(s))))
897
898 encodefilename, decodefilename = _buildencodefun()
899
872 900
873 901 def opener(base, audit=True):
874 902 """
875 903 return a function that opens files relative to base
876 904
877 905 this function is used to hide the details of COW semantics and
878 906 remote file access from higher level code.
879 907 """
880 908 p = base
881 909 audit_p = audit
882 910
883 911 def mktempcopy(name):
884 912 d, fn = os.path.split(name)
885 913 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
886 914 os.close(fd)
887 915 ofp = posixfile(temp, "wb")
888 916 try:
889 917 try:
890 918 ifp = posixfile(name, "rb")
891 919 except IOError, inst:
892 920 if not getattr(inst, 'filename', None):
893 921 inst.filename = name
894 922 raise
895 923 for chunk in filechunkiter(ifp):
896 924 ofp.write(chunk)
897 925 ifp.close()
898 926 ofp.close()
899 927 except:
900 928 try: os.unlink(temp)
901 929 except: pass
902 930 raise
903 931 st = os.lstat(name)
904 932 os.chmod(temp, st.st_mode)
905 933 return temp
906 934
907 935 class atomictempfile(posixfile):
908 936 """the file will only be copied when rename is called"""
909 937 def __init__(self, name, mode):
910 938 self.__name = name
911 939 self.temp = mktempcopy(name)
912 940 posixfile.__init__(self, self.temp, mode)
913 941 def rename(self):
914 942 if not self.closed:
915 943 posixfile.close(self)
916 944 rename(self.temp, localpath(self.__name))
917 945 def __del__(self):
918 946 if not self.closed:
919 947 try:
920 948 os.unlink(self.temp)
921 949 except: pass
922 950 posixfile.close(self)
923 951
924 952 class atomicfile(atomictempfile):
925 953 """the file will only be copied on close"""
926 954 def __init__(self, name, mode):
927 955 atomictempfile.__init__(self, name, mode)
928 956 def close(self):
929 957 self.rename()
930 958 def __del__(self):
931 959 self.rename()
932 960
933 961 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
934 962 if audit_p:
935 963 audit_path(path)
936 964 f = os.path.join(p, path)
937 965
938 966 if not text:
939 967 mode += "b" # for that other OS
940 968
941 969 if mode[0] != "r":
942 970 try:
943 971 nlink = nlinks(f)
944 972 except OSError:
945 973 d = os.path.dirname(f)
946 974 if not os.path.isdir(d):
947 975 os.makedirs(d)
948 976 else:
949 977 if atomic:
950 978 return atomicfile(f, mode)
951 979 elif atomictemp:
952 980 return atomictempfile(f, mode)
953 981 if nlink > 1:
954 982 rename(mktempcopy(f), f)
955 983 return posixfile(f, mode)
956 984
957 985 return o
958 986
959 987 class chunkbuffer(object):
960 988 """Allow arbitrary sized chunks of data to be efficiently read from an
961 989 iterator over chunks of arbitrary size."""
962 990
963 991 def __init__(self, in_iter, targetsize = 2**16):
964 992 """in_iter is the iterator that's iterating over the input chunks.
965 993 targetsize is how big a buffer to try to maintain."""
966 994 self.in_iter = iter(in_iter)
967 995 self.buf = ''
968 996 self.targetsize = int(targetsize)
969 997 if self.targetsize <= 0:
970 998 raise ValueError(_("targetsize must be greater than 0, was %d") %
971 999 targetsize)
972 1000 self.iterempty = False
973 1001
974 1002 def fillbuf(self):
975 1003 """Ignore target size; read every chunk from iterator until empty."""
976 1004 if not self.iterempty:
977 1005 collector = cStringIO.StringIO()
978 1006 collector.write(self.buf)
979 1007 for ch in self.in_iter:
980 1008 collector.write(ch)
981 1009 self.buf = collector.getvalue()
982 1010 self.iterempty = True
983 1011
984 1012 def read(self, l):
985 1013 """Read L bytes of data from the iterator of chunks of data.
986 1014 Returns less than L bytes if the iterator runs dry."""
987 1015 if l > len(self.buf) and not self.iterempty:
988 1016 # Clamp to a multiple of self.targetsize
989 1017 targetsize = self.targetsize * ((l // self.targetsize) + 1)
990 1018 collector = cStringIO.StringIO()
991 1019 collector.write(self.buf)
992 1020 collected = len(self.buf)
993 1021 for chunk in self.in_iter:
994 1022 collector.write(chunk)
995 1023 collected += len(chunk)
996 1024 if collected >= targetsize:
997 1025 break
998 1026 if collected < targetsize:
999 1027 self.iterempty = True
1000 1028 self.buf = collector.getvalue()
1001 1029 s, self.buf = self.buf[:l], buffer(self.buf, l)
1002 1030 return s
1003 1031
1004 1032 def filechunkiter(f, size=65536, limit=None):
1005 1033 """Create a generator that produces the data in the file size
1006 1034 (default 65536) bytes at a time, up to optional limit (default is
1007 1035 to read all data). Chunks may be less than size bytes if the
1008 1036 chunk is the last chunk in the file, or the file is a socket or
1009 1037 some other type of file that sometimes reads less data than is
1010 1038 requested."""
1011 1039 assert size >= 0
1012 1040 assert limit is None or limit >= 0
1013 1041 while True:
1014 1042 if limit is None: nbytes = size
1015 1043 else: nbytes = min(limit, size)
1016 1044 s = nbytes and f.read(nbytes)
1017 1045 if not s: break
1018 1046 if limit: limit -= len(s)
1019 1047 yield s
1020 1048
1021 1049 def makedate():
1022 1050 lt = time.localtime()
1023 1051 if lt[8] == 1 and time.daylight:
1024 1052 tz = time.altzone
1025 1053 else:
1026 1054 tz = time.timezone
1027 1055 return time.mktime(lt), tz
1028 1056
1029 1057 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1030 1058 """represent a (unixtime, offset) tuple as a localized time.
1031 1059 unixtime is seconds since the epoch, and offset is the time zone's
1032 1060 number of seconds away from UTC. if timezone is false, do not
1033 1061 append time zone to string."""
1034 1062 t, tz = date or makedate()
1035 1063 s = time.strftime(format, time.gmtime(float(t) - tz))
1036 1064 if timezone:
1037 1065 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1038 1066 return s
1039 1067
1040 1068 def strdate(string, format='%a %b %d %H:%M:%S %Y'):
1041 1069 """parse a localized time string and return a (unixtime, offset) tuple.
1042 1070 if the string cannot be parsed, ValueError is raised."""
1043 1071 def hastimezone(string):
1044 1072 return (string[-4:].isdigit() and
1045 1073 (string[-5] == '+' or string[-5] == '-') and
1046 1074 string[-6].isspace())
1047 1075
1048 1076 # NOTE: unixtime = localunixtime + offset
1049 1077 if hastimezone(string):
1050 1078 date, tz = string[:-6], string[-5:]
1051 1079 tz = int(tz)
1052 1080 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1053 1081 else:
1054 1082 date, offset = string, None
1055 1083 timetuple = time.strptime(date, format)
1056 1084 localunixtime = int(calendar.timegm(timetuple))
1057 1085 if offset is None:
1058 1086 # local timezone
1059 1087 unixtime = int(time.mktime(timetuple))
1060 1088 offset = unixtime - localunixtime
1061 1089 else:
1062 1090 unixtime = localunixtime + offset
1063 1091 return unixtime, offset
1064 1092
1065 1093 def parsedate(string, formats=None):
1066 1094 """parse a localized time string and return a (unixtime, offset) tuple.
1067 1095 The date may be a "unixtime offset" string or in one of the specified
1068 1096 formats."""
1069 1097 if not formats:
1070 1098 formats = defaultdateformats
1071 1099 try:
1072 1100 when, offset = map(int, string.split(' '))
1073 1101 except ValueError:
1074 1102 for format in formats:
1075 1103 try:
1076 1104 when, offset = strdate(string, format)
1077 1105 except ValueError:
1078 1106 pass
1079 1107 else:
1080 1108 break
1081 1109 else:
1082 1110 raise ValueError(_('invalid date: %r '
1083 1111 'see hg(1) manual page for details')
1084 1112 % string)
1085 1113 # validate explicit (probably user-specified) date and
1086 1114 # time zone offset. values must fit in signed 32 bits for
1087 1115 # current 32-bit linux runtimes. timezones go from UTC-12
1088 1116 # to UTC+14
1089 1117 if abs(when) > 0x7fffffff:
1090 1118 raise ValueError(_('date exceeds 32 bits: %d') % when)
1091 1119 if offset < -50400 or offset > 43200:
1092 1120 raise ValueError(_('impossible time zone offset: %d') % offset)
1093 1121 return when, offset
1094 1122
1095 1123 def shortuser(user):
1096 1124 """Return a short representation of a user name or email address."""
1097 1125 f = user.find('@')
1098 1126 if f >= 0:
1099 1127 user = user[:f]
1100 1128 f = user.find('<')
1101 1129 if f >= 0:
1102 1130 user = user[f+1:]
1103 1131 f = user.find(' ')
1104 1132 if f >= 0:
1105 1133 user = user[:f]
1106 1134 f = user.find('.')
1107 1135 if f >= 0:
1108 1136 user = user[:f]
1109 1137 return user
1110 1138
1111 1139 def ellipsis(text, maxlength=400):
1112 1140 """Trim string to at most maxlength (default: 400) characters."""
1113 1141 if len(text) <= maxlength:
1114 1142 return text
1115 1143 else:
1116 1144 return "%s..." % (text[:maxlength-3])
1117 1145
1118 1146 def walkrepos(path):
1119 1147 '''yield every hg repository under path, recursively.'''
1120 1148 def errhandler(err):
1121 1149 if err.filename == path:
1122 1150 raise err
1123 1151
1124 1152 for root, dirs, files in os.walk(path, onerror=errhandler):
1125 1153 for d in dirs:
1126 1154 if d == '.hg':
1127 1155 yield root
1128 1156 dirs[:] = []
1129 1157 break
1130 1158
1131 1159 _rcpath = None
1132 1160
1133 1161 def rcpath():
1134 1162 '''return hgrc search path. if env var HGRCPATH is set, use it.
1135 1163 for each item in path, if directory, use files ending in .rc,
1136 1164 else use item.
1137 1165 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1138 1166 if no HGRCPATH, use default os-specific path.'''
1139 1167 global _rcpath
1140 1168 if _rcpath is None:
1141 1169 if 'HGRCPATH' in os.environ:
1142 1170 _rcpath = []
1143 1171 for p in os.environ['HGRCPATH'].split(os.pathsep):
1144 1172 if not p: continue
1145 1173 if os.path.isdir(p):
1146 1174 for f in os.listdir(p):
1147 1175 if f.endswith('.rc'):
1148 1176 _rcpath.append(os.path.join(p, f))
1149 1177 else:
1150 1178 _rcpath.append(p)
1151 1179 else:
1152 1180 _rcpath = os_rcpath()
1153 1181 return _rcpath
1154 1182
1155 1183 def bytecount(nbytes):
1156 1184 '''return byte count formatted as readable string, with units'''
1157 1185
1158 1186 units = (
1159 1187 (100, 1<<30, _('%.0f GB')),
1160 1188 (10, 1<<30, _('%.1f GB')),
1161 1189 (1, 1<<30, _('%.2f GB')),
1162 1190 (100, 1<<20, _('%.0f MB')),
1163 1191 (10, 1<<20, _('%.1f MB')),
1164 1192 (1, 1<<20, _('%.2f MB')),
1165 1193 (100, 1<<10, _('%.0f KB')),
1166 1194 (10, 1<<10, _('%.1f KB')),
1167 1195 (1, 1<<10, _('%.2f KB')),
1168 1196 (1, 1, _('%.0f bytes')),
1169 1197 )
1170 1198
1171 1199 for multiplier, divisor, format in units:
1172 1200 if nbytes >= divisor * multiplier:
1173 1201 return format % (nbytes / float(divisor))
1174 1202 return units[-1][2] % nbytes
1175 1203
1176 1204 def drop_scheme(scheme, path):
1177 1205 sc = scheme + ':'
1178 1206 if path.startswith(sc):
1179 1207 path = path[len(sc):]
1180 1208 if path.startswith('//'):
1181 1209 path = path[2:]
1182 1210 return path
General Comments 0
You need to be logged in to leave comments. Login now