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