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