##// END OF EJS Templates
Remove useless variable assignments in util.opener....
Thomas Arendsen Hein -
r4668:e241598e default
parent child Browse files
Show More
@@ -1,1537 +1,1534 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-2007 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 _
16 16 import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile
17 17 import os, threading, time, calendar, ConfigParser, locale, glob
18 18
19 19 try:
20 20 set = set
21 21 frozenset = frozenset
22 22 except NameError:
23 23 from sets import Set as set, ImmutableSet as frozenset
24 24
25 25 try:
26 26 _encoding = os.environ.get("HGENCODING")
27 27 if sys.platform == 'darwin' and not _encoding:
28 28 # On darwin, getpreferredencoding ignores the locale environment and
29 29 # always returns mac-roman. We override this if the environment is
30 30 # not C (has been customized by the user).
31 31 locale.setlocale(locale.LC_CTYPE, '')
32 32 _encoding = locale.getlocale()[1]
33 33 if not _encoding:
34 34 _encoding = locale.getpreferredencoding() or 'ascii'
35 35 except locale.Error:
36 36 _encoding = 'ascii'
37 37 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
38 38 _fallbackencoding = 'ISO-8859-1'
39 39
40 40 def tolocal(s):
41 41 """
42 42 Convert a string from internal UTF-8 to local encoding
43 43
44 44 All internal strings should be UTF-8 but some repos before the
45 45 implementation of locale support may contain latin1 or possibly
46 46 other character sets. We attempt to decode everything strictly
47 47 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
48 48 replace unknown characters.
49 49 """
50 50 for e in ('UTF-8', _fallbackencoding):
51 51 try:
52 52 u = s.decode(e) # attempt strict decoding
53 53 return u.encode(_encoding, "replace")
54 54 except LookupError, k:
55 55 raise Abort(_("%s, please check your locale settings") % k)
56 56 except UnicodeDecodeError:
57 57 pass
58 58 u = s.decode("utf-8", "replace") # last ditch
59 59 return u.encode(_encoding, "replace")
60 60
61 61 def fromlocal(s):
62 62 """
63 63 Convert a string from the local character encoding to UTF-8
64 64
65 65 We attempt to decode strings using the encoding mode set by
66 66 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
67 67 characters will cause an error message. Other modes include
68 68 'replace', which replaces unknown characters with a special
69 69 Unicode character, and 'ignore', which drops the character.
70 70 """
71 71 try:
72 72 return s.decode(_encoding, _encodingmode).encode("utf-8")
73 73 except UnicodeDecodeError, inst:
74 74 sub = s[max(0, inst.start-10):inst.start+10]
75 75 raise Abort("decoding near '%s': %s!" % (sub, inst))
76 76 except LookupError, k:
77 77 raise Abort(_("%s, please check your locale settings") % k)
78 78
79 79 def locallen(s):
80 80 """Find the length in characters of a local string"""
81 81 return len(s.decode(_encoding, "replace"))
82 82
83 83 def localsub(s, a, b=None):
84 84 try:
85 85 u = s.decode(_encoding, _encodingmode)
86 86 if b is not None:
87 87 u = u[a:b]
88 88 else:
89 89 u = u[:a]
90 90 return u.encode(_encoding, _encodingmode)
91 91 except UnicodeDecodeError, inst:
92 92 sub = s[max(0, inst.start-10), inst.start+10]
93 93 raise Abort(_("decoding near '%s': %s!") % (sub, inst))
94 94
95 95 # used by parsedate
96 96 defaultdateformats = (
97 97 '%Y-%m-%d %H:%M:%S',
98 98 '%Y-%m-%d %I:%M:%S%p',
99 99 '%Y-%m-%d %H:%M',
100 100 '%Y-%m-%d %I:%M%p',
101 101 '%Y-%m-%d',
102 102 '%m-%d',
103 103 '%m/%d',
104 104 '%m/%d/%y',
105 105 '%m/%d/%Y',
106 106 '%a %b %d %H:%M:%S %Y',
107 107 '%a %b %d %I:%M:%S%p %Y',
108 108 '%b %d %H:%M:%S %Y',
109 109 '%b %d %I:%M:%S%p %Y',
110 110 '%b %d %H:%M:%S',
111 111 '%b %d %I:%M:%S%p',
112 112 '%b %d %H:%M',
113 113 '%b %d %I:%M%p',
114 114 '%b %d %Y',
115 115 '%b %d',
116 116 '%H:%M:%S',
117 117 '%I:%M:%SP',
118 118 '%H:%M',
119 119 '%I:%M%p',
120 120 )
121 121
122 122 extendeddateformats = defaultdateformats + (
123 123 "%Y",
124 124 "%Y-%m",
125 125 "%b",
126 126 "%b %Y",
127 127 )
128 128
129 129 class SignalInterrupt(Exception):
130 130 """Exception raised on SIGTERM and SIGHUP."""
131 131
132 132 # differences from SafeConfigParser:
133 133 # - case-sensitive keys
134 134 # - allows values that are not strings (this means that you may not
135 135 # be able to save the configuration to a file)
136 136 class configparser(ConfigParser.SafeConfigParser):
137 137 def optionxform(self, optionstr):
138 138 return optionstr
139 139
140 140 def set(self, section, option, value):
141 141 return ConfigParser.ConfigParser.set(self, section, option, value)
142 142
143 143 def _interpolate(self, section, option, rawval, vars):
144 144 if not isinstance(rawval, basestring):
145 145 return rawval
146 146 return ConfigParser.SafeConfigParser._interpolate(self, section,
147 147 option, rawval, vars)
148 148
149 149 def cachefunc(func):
150 150 '''cache the result of function calls'''
151 151 # XXX doesn't handle keywords args
152 152 cache = {}
153 153 if func.func_code.co_argcount == 1:
154 154 # we gain a small amount of time because
155 155 # we don't need to pack/unpack the list
156 156 def f(arg):
157 157 if arg not in cache:
158 158 cache[arg] = func(arg)
159 159 return cache[arg]
160 160 else:
161 161 def f(*args):
162 162 if args not in cache:
163 163 cache[args] = func(*args)
164 164 return cache[args]
165 165
166 166 return f
167 167
168 168 def pipefilter(s, cmd):
169 169 '''filter string S through command CMD, returning its output'''
170 170 (pin, pout) = os.popen2(cmd, 'b')
171 171 def writer():
172 172 try:
173 173 pin.write(s)
174 174 pin.close()
175 175 except IOError, inst:
176 176 if inst.errno != errno.EPIPE:
177 177 raise
178 178
179 179 # we should use select instead on UNIX, but this will work on most
180 180 # systems, including Windows
181 181 w = threading.Thread(target=writer)
182 182 w.start()
183 183 f = pout.read()
184 184 pout.close()
185 185 w.join()
186 186 return f
187 187
188 188 def tempfilter(s, cmd):
189 189 '''filter string S through a pair of temporary files with CMD.
190 190 CMD is used as a template to create the real command to be run,
191 191 with the strings INFILE and OUTFILE replaced by the real names of
192 192 the temporary files generated.'''
193 193 inname, outname = None, None
194 194 try:
195 195 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
196 196 fp = os.fdopen(infd, 'wb')
197 197 fp.write(s)
198 198 fp.close()
199 199 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
200 200 os.close(outfd)
201 201 cmd = cmd.replace('INFILE', inname)
202 202 cmd = cmd.replace('OUTFILE', outname)
203 203 code = os.system(cmd)
204 204 if code: raise Abort(_("command '%s' failed: %s") %
205 205 (cmd, explain_exit(code)))
206 206 return open(outname, 'rb').read()
207 207 finally:
208 208 try:
209 209 if inname: os.unlink(inname)
210 210 except: pass
211 211 try:
212 212 if outname: os.unlink(outname)
213 213 except: pass
214 214
215 215 filtertable = {
216 216 'tempfile:': tempfilter,
217 217 'pipe:': pipefilter,
218 218 }
219 219
220 220 def filter(s, cmd):
221 221 "filter a string through a command that transforms its input to its output"
222 222 for name, fn in filtertable.iteritems():
223 223 if cmd.startswith(name):
224 224 return fn(s, cmd[len(name):].lstrip())
225 225 return pipefilter(s, cmd)
226 226
227 227 def binary(s):
228 228 """return true if a string is binary data using diff's heuristic"""
229 229 if s and '\0' in s[:4096]:
230 230 return True
231 231 return False
232 232
233 233 def unique(g):
234 234 """return the uniq elements of iterable g"""
235 235 seen = {}
236 236 l = []
237 237 for f in g:
238 238 if f not in seen:
239 239 seen[f] = 1
240 240 l.append(f)
241 241 return l
242 242
243 243 class Abort(Exception):
244 244 """Raised if a command needs to print an error and exit."""
245 245
246 246 class UnexpectedOutput(Abort):
247 247 """Raised to print an error with part of output and exit."""
248 248
249 249 def always(fn): return True
250 250 def never(fn): return False
251 251
252 252 def expand_glob(pats):
253 253 '''On Windows, expand the implicit globs in a list of patterns'''
254 254 if os.name != 'nt':
255 255 return list(pats)
256 256 ret = []
257 257 for p in pats:
258 258 kind, name = patkind(p, None)
259 259 if kind is None:
260 260 globbed = glob.glob(name)
261 261 if globbed:
262 262 ret.extend(globbed)
263 263 continue
264 264 # if we couldn't expand the glob, just keep it around
265 265 ret.append(p)
266 266 return ret
267 267
268 268 def patkind(name, dflt_pat='glob'):
269 269 """Split a string into an optional pattern kind prefix and the
270 270 actual pattern."""
271 271 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
272 272 if name.startswith(prefix + ':'): return name.split(':', 1)
273 273 return dflt_pat, name
274 274
275 275 def globre(pat, head='^', tail='$'):
276 276 "convert a glob pattern into a regexp"
277 277 i, n = 0, len(pat)
278 278 res = ''
279 279 group = False
280 280 def peek(): return i < n and pat[i]
281 281 while i < n:
282 282 c = pat[i]
283 283 i = i+1
284 284 if c == '*':
285 285 if peek() == '*':
286 286 i += 1
287 287 res += '.*'
288 288 else:
289 289 res += '[^/]*'
290 290 elif c == '?':
291 291 res += '.'
292 292 elif c == '[':
293 293 j = i
294 294 if j < n and pat[j] in '!]':
295 295 j += 1
296 296 while j < n and pat[j] != ']':
297 297 j += 1
298 298 if j >= n:
299 299 res += '\\['
300 300 else:
301 301 stuff = pat[i:j].replace('\\','\\\\')
302 302 i = j + 1
303 303 if stuff[0] == '!':
304 304 stuff = '^' + stuff[1:]
305 305 elif stuff[0] == '^':
306 306 stuff = '\\' + stuff
307 307 res = '%s[%s]' % (res, stuff)
308 308 elif c == '{':
309 309 group = True
310 310 res += '(?:'
311 311 elif c == '}' and group:
312 312 res += ')'
313 313 group = False
314 314 elif c == ',' and group:
315 315 res += '|'
316 316 elif c == '\\':
317 317 p = peek()
318 318 if p:
319 319 i += 1
320 320 res += re.escape(p)
321 321 else:
322 322 res += re.escape(c)
323 323 else:
324 324 res += re.escape(c)
325 325 return head + res + tail
326 326
327 327 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
328 328
329 329 def pathto(root, n1, n2):
330 330 '''return the relative path from one place to another.
331 331 root should use os.sep to separate directories
332 332 n1 should use os.sep to separate directories
333 333 n2 should use "/" to separate directories
334 334 returns an os.sep-separated path.
335 335
336 336 If n1 is a relative path, it's assumed it's
337 337 relative to root.
338 338 n2 should always be relative to root.
339 339 '''
340 340 if not n1: return localpath(n2)
341 341 if os.path.isabs(n1):
342 342 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
343 343 return os.path.join(root, localpath(n2))
344 344 n2 = '/'.join((pconvert(root), n2))
345 345 a, b = n1.split(os.sep), n2.split('/')
346 346 a.reverse()
347 347 b.reverse()
348 348 while a and b and a[-1] == b[-1]:
349 349 a.pop()
350 350 b.pop()
351 351 b.reverse()
352 352 return os.sep.join((['..'] * len(a)) + b)
353 353
354 354 def canonpath(root, cwd, myname):
355 355 """return the canonical path of myname, given cwd and root"""
356 356 if root == os.sep:
357 357 rootsep = os.sep
358 358 elif root.endswith(os.sep):
359 359 rootsep = root
360 360 else:
361 361 rootsep = root + os.sep
362 362 name = myname
363 363 if not os.path.isabs(name):
364 364 name = os.path.join(root, cwd, name)
365 365 name = os.path.normpath(name)
366 366 if name != rootsep and name.startswith(rootsep):
367 367 name = name[len(rootsep):]
368 368 audit_path(name)
369 369 return pconvert(name)
370 370 elif name == root:
371 371 return ''
372 372 else:
373 373 # Determine whether `name' is in the hierarchy at or beneath `root',
374 374 # by iterating name=dirname(name) until that causes no change (can't
375 375 # check name == '/', because that doesn't work on windows). For each
376 376 # `name', compare dev/inode numbers. If they match, the list `rel'
377 377 # holds the reversed list of components making up the relative file
378 378 # name we want.
379 379 root_st = os.stat(root)
380 380 rel = []
381 381 while True:
382 382 try:
383 383 name_st = os.stat(name)
384 384 except OSError:
385 385 break
386 386 if samestat(name_st, root_st):
387 387 if not rel:
388 388 # name was actually the same as root (maybe a symlink)
389 389 return ''
390 390 rel.reverse()
391 391 name = os.path.join(*rel)
392 392 audit_path(name)
393 393 return pconvert(name)
394 394 dirname, basename = os.path.split(name)
395 395 rel.append(basename)
396 396 if dirname == name:
397 397 break
398 398 name = dirname
399 399
400 400 raise Abort('%s not under root' % myname)
401 401
402 402 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None):
403 403 return _matcher(canonroot, cwd, names, inc, exc, 'glob', src)
404 404
405 405 def cmdmatcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None,
406 406 globbed=False, default=None):
407 407 default = default or 'relpath'
408 408 if default == 'relpath' and not globbed:
409 409 names = expand_glob(names)
410 410 return _matcher(canonroot, cwd, names, inc, exc, default, src)
411 411
412 412 def _matcher(canonroot, cwd, names, inc, exc, dflt_pat, src):
413 413 """build a function to match a set of file patterns
414 414
415 415 arguments:
416 416 canonroot - the canonical root of the tree you're matching against
417 417 cwd - the current working directory, if relevant
418 418 names - patterns to find
419 419 inc - patterns to include
420 420 exc - patterns to exclude
421 421 dflt_pat - if a pattern in names has no explicit type, assume this one
422 422 src - where these patterns came from (e.g. .hgignore)
423 423
424 424 a pattern is one of:
425 425 'glob:<glob>' - a glob relative to cwd
426 426 're:<regexp>' - a regular expression
427 427 'path:<path>' - a path relative to canonroot
428 428 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
429 429 'relpath:<path>' - a path relative to cwd
430 430 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
431 431 '<something>' - one of the cases above, selected by the dflt_pat argument
432 432
433 433 returns:
434 434 a 3-tuple containing
435 435 - list of roots (places where one should start a recursive walk of the fs);
436 436 this often matches the explicit non-pattern names passed in, but also
437 437 includes the initial part of glob: patterns that has no glob characters
438 438 - a bool match(filename) function
439 439 - a bool indicating if any patterns were passed in
440 440 """
441 441
442 442 # a common case: no patterns at all
443 443 if not names and not inc and not exc:
444 444 return [], always, False
445 445
446 446 def contains_glob(name):
447 447 for c in name:
448 448 if c in _globchars: return True
449 449 return False
450 450
451 451 def regex(kind, name, tail):
452 452 '''convert a pattern into a regular expression'''
453 453 if not name:
454 454 return ''
455 455 if kind == 're':
456 456 return name
457 457 elif kind == 'path':
458 458 return '^' + re.escape(name) + '(?:/|$)'
459 459 elif kind == 'relglob':
460 460 return globre(name, '(?:|.*/)', tail)
461 461 elif kind == 'relpath':
462 462 return re.escape(name) + '(?:/|$)'
463 463 elif kind == 'relre':
464 464 if name.startswith('^'):
465 465 return name
466 466 return '.*' + name
467 467 return globre(name, '', tail)
468 468
469 469 def matchfn(pats, tail):
470 470 """build a matching function from a set of patterns"""
471 471 if not pats:
472 472 return
473 473 try:
474 474 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
475 475 return re.compile(pat).match
476 476 except re.error:
477 477 for k, p in pats:
478 478 try:
479 479 re.compile('(?:%s)' % regex(k, p, tail))
480 480 except re.error:
481 481 if src:
482 482 raise Abort("%s: invalid pattern (%s): %s" %
483 483 (src, k, p))
484 484 else:
485 485 raise Abort("invalid pattern (%s): %s" % (k, p))
486 486 raise Abort("invalid pattern")
487 487
488 488 def globprefix(pat):
489 489 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
490 490 root = []
491 491 for p in pat.split('/'):
492 492 if contains_glob(p): break
493 493 root.append(p)
494 494 return '/'.join(root) or '.'
495 495
496 496 def normalizepats(names, default):
497 497 pats = []
498 498 roots = []
499 499 anypats = False
500 500 for kind, name in [patkind(p, default) for p in names]:
501 501 if kind in ('glob', 'relpath'):
502 502 name = canonpath(canonroot, cwd, name)
503 503 elif kind in ('relglob', 'path'):
504 504 name = normpath(name)
505 505
506 506 pats.append((kind, name))
507 507
508 508 if kind in ('glob', 're', 'relglob', 'relre'):
509 509 anypats = True
510 510
511 511 if kind == 'glob':
512 512 root = globprefix(name)
513 513 roots.append(root)
514 514 elif kind in ('relpath', 'path'):
515 515 roots.append(name or '.')
516 516 elif kind == 'relglob':
517 517 roots.append('.')
518 518 return roots, pats, anypats
519 519
520 520 roots, pats, anypats = normalizepats(names, dflt_pat)
521 521
522 522 patmatch = matchfn(pats, '$') or always
523 523 incmatch = always
524 524 if inc:
525 525 dummy, inckinds, dummy = normalizepats(inc, 'glob')
526 526 incmatch = matchfn(inckinds, '(?:/|$)')
527 527 excmatch = lambda fn: False
528 528 if exc:
529 529 dummy, exckinds, dummy = normalizepats(exc, 'glob')
530 530 excmatch = matchfn(exckinds, '(?:/|$)')
531 531
532 532 if not names and inc and not exc:
533 533 # common case: hgignore patterns
534 534 match = incmatch
535 535 else:
536 536 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
537 537
538 538 return (roots, match, (inc or exc or anypats) and True)
539 539
540 540 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
541 541 '''enhanced shell command execution.
542 542 run with environment maybe modified, maybe in different dir.
543 543
544 544 if command fails and onerr is None, return status. if ui object,
545 545 print error message and return status, else raise onerr object as
546 546 exception.'''
547 547 def py2shell(val):
548 548 'convert python object into string that is useful to shell'
549 549 if val in (None, False):
550 550 return '0'
551 551 if val == True:
552 552 return '1'
553 553 return str(val)
554 554 oldenv = {}
555 555 for k in environ:
556 556 oldenv[k] = os.environ.get(k)
557 557 if cwd is not None:
558 558 oldcwd = os.getcwd()
559 559 origcmd = cmd
560 560 if os.name == 'nt':
561 561 cmd = '"%s"' % cmd
562 562 try:
563 563 for k, v in environ.iteritems():
564 564 os.environ[k] = py2shell(v)
565 565 if cwd is not None and oldcwd != cwd:
566 566 os.chdir(cwd)
567 567 rc = os.system(cmd)
568 568 if rc and onerr:
569 569 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
570 570 explain_exit(rc)[0])
571 571 if errprefix:
572 572 errmsg = '%s: %s' % (errprefix, errmsg)
573 573 try:
574 574 onerr.warn(errmsg + '\n')
575 575 except AttributeError:
576 576 raise onerr(errmsg)
577 577 return rc
578 578 finally:
579 579 for k, v in oldenv.iteritems():
580 580 if v is None:
581 581 del os.environ[k]
582 582 else:
583 583 os.environ[k] = v
584 584 if cwd is not None and oldcwd != cwd:
585 585 os.chdir(oldcwd)
586 586
587 587 # os.path.lexists is not available on python2.3
588 588 def lexists(filename):
589 589 "test whether a file with this name exists. does not follow symlinks"
590 590 try:
591 591 os.lstat(filename)
592 592 except:
593 593 return False
594 594 return True
595 595
596 596 def rename(src, dst):
597 597 """forcibly rename a file"""
598 598 try:
599 599 os.rename(src, dst)
600 600 except OSError, err:
601 601 # on windows, rename to existing file is not allowed, so we
602 602 # must delete destination first. but if file is open, unlink
603 603 # schedules it for delete but does not delete it. rename
604 604 # happens immediately even for open files, so we create
605 605 # temporary file, delete it, rename destination to that name,
606 606 # then delete that. then rename is safe to do.
607 607 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
608 608 os.close(fd)
609 609 os.unlink(temp)
610 610 os.rename(dst, temp)
611 611 os.unlink(temp)
612 612 os.rename(src, dst)
613 613
614 614 def unlink(f):
615 615 """unlink and remove the directory if it is empty"""
616 616 os.unlink(f)
617 617 # try removing directories that might now be empty
618 618 try:
619 619 os.removedirs(os.path.dirname(f))
620 620 except OSError:
621 621 pass
622 622
623 623 def copyfile(src, dest):
624 624 "copy a file, preserving mode"
625 625 if os.path.islink(src):
626 626 try:
627 627 os.unlink(dest)
628 628 except:
629 629 pass
630 630 os.symlink(os.readlink(src), dest)
631 631 else:
632 632 try:
633 633 shutil.copyfile(src, dest)
634 634 shutil.copymode(src, dest)
635 635 except shutil.Error, inst:
636 636 raise Abort(str(inst))
637 637
638 638 def copyfiles(src, dst, hardlink=None):
639 639 """Copy a directory tree using hardlinks if possible"""
640 640
641 641 if hardlink is None:
642 642 hardlink = (os.stat(src).st_dev ==
643 643 os.stat(os.path.dirname(dst)).st_dev)
644 644
645 645 if os.path.isdir(src):
646 646 os.mkdir(dst)
647 647 for name in os.listdir(src):
648 648 srcname = os.path.join(src, name)
649 649 dstname = os.path.join(dst, name)
650 650 copyfiles(srcname, dstname, hardlink)
651 651 else:
652 652 if hardlink:
653 653 try:
654 654 os_link(src, dst)
655 655 except (IOError, OSError):
656 656 hardlink = False
657 657 shutil.copy(src, dst)
658 658 else:
659 659 shutil.copy(src, dst)
660 660
661 661 def audit_path(path):
662 662 """Abort if path contains dangerous components"""
663 663 parts = os.path.normcase(path).split(os.sep)
664 664 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
665 665 or os.pardir in parts):
666 666 raise Abort(_("path contains illegal component: %s") % path)
667 667
668 668 def _makelock_file(info, pathname):
669 669 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
670 670 os.write(ld, info)
671 671 os.close(ld)
672 672
673 673 def _readlock_file(pathname):
674 674 return posixfile(pathname).read()
675 675
676 676 def nlinks(pathname):
677 677 """Return number of hardlinks for the given file."""
678 678 return os.lstat(pathname).st_nlink
679 679
680 680 if hasattr(os, 'link'):
681 681 os_link = os.link
682 682 else:
683 683 def os_link(src, dst):
684 684 raise OSError(0, _("Hardlinks not supported"))
685 685
686 686 def fstat(fp):
687 687 '''stat file object that may not have fileno method.'''
688 688 try:
689 689 return os.fstat(fp.fileno())
690 690 except AttributeError:
691 691 return os.stat(fp.name)
692 692
693 693 posixfile = file
694 694
695 695 def is_win_9x():
696 696 '''return true if run on windows 95, 98 or me.'''
697 697 try:
698 698 return sys.getwindowsversion()[3] == 1
699 699 except AttributeError:
700 700 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
701 701
702 702 getuser_fallback = None
703 703
704 704 def getuser():
705 705 '''return name of current user'''
706 706 try:
707 707 return getpass.getuser()
708 708 except ImportError:
709 709 # import of pwd will fail on windows - try fallback
710 710 if getuser_fallback:
711 711 return getuser_fallback()
712 712 # raised if win32api not available
713 713 raise Abort(_('user name not available - set USERNAME '
714 714 'environment variable'))
715 715
716 716 def username(uid=None):
717 717 """Return the name of the user with the given uid.
718 718
719 719 If uid is None, return the name of the current user."""
720 720 try:
721 721 import pwd
722 722 if uid is None:
723 723 uid = os.getuid()
724 724 try:
725 725 return pwd.getpwuid(uid)[0]
726 726 except KeyError:
727 727 return str(uid)
728 728 except ImportError:
729 729 return None
730 730
731 731 def groupname(gid=None):
732 732 """Return the name of the group with the given gid.
733 733
734 734 If gid is None, return the name of the current group."""
735 735 try:
736 736 import grp
737 737 if gid is None:
738 738 gid = os.getgid()
739 739 try:
740 740 return grp.getgrgid(gid)[0]
741 741 except KeyError:
742 742 return str(gid)
743 743 except ImportError:
744 744 return None
745 745
746 746 # File system features
747 747
748 748 def checkfolding(path):
749 749 """
750 750 Check whether the given path is on a case-sensitive filesystem
751 751
752 752 Requires a path (like /foo/.hg) ending with a foldable final
753 753 directory component.
754 754 """
755 755 s1 = os.stat(path)
756 756 d, b = os.path.split(path)
757 757 p2 = os.path.join(d, b.upper())
758 758 if path == p2:
759 759 p2 = os.path.join(d, b.lower())
760 760 try:
761 761 s2 = os.stat(p2)
762 762 if s2 == s1:
763 763 return False
764 764 return True
765 765 except:
766 766 return True
767 767
768 768 def checkexec(path):
769 769 """
770 770 Check whether the given path is on a filesystem with UNIX-like exec flags
771 771
772 772 Requires a directory (like /foo/.hg)
773 773 """
774 774 fh, fn = tempfile.mkstemp("", "", path)
775 775 os.close(fh)
776 776 m = os.stat(fn).st_mode
777 777 os.chmod(fn, m ^ 0111)
778 778 r = (os.stat(fn).st_mode != m)
779 779 os.unlink(fn)
780 780 return r
781 781
782 782 def execfunc(path, fallback):
783 783 '''return an is_exec() function with default to fallback'''
784 784 if checkexec(path):
785 785 return lambda x: is_exec(os.path.join(path, x))
786 786 return fallback
787 787
788 788 def checklink(path):
789 789 """check whether the given path is on a symlink-capable filesystem"""
790 790 # mktemp is not racy because symlink creation will fail if the
791 791 # file already exists
792 792 name = tempfile.mktemp(dir=path)
793 793 try:
794 794 os.symlink(".", name)
795 795 os.unlink(name)
796 796 return True
797 797 except (OSError, AttributeError):
798 798 return False
799 799
800 800 def linkfunc(path, fallback):
801 801 '''return an is_link() function with default to fallback'''
802 802 if checklink(path):
803 803 return lambda x: os.path.islink(os.path.join(path, x))
804 804 return fallback
805 805
806 806 _umask = os.umask(0)
807 807 os.umask(_umask)
808 808
809 809 def needbinarypatch():
810 810 """return True if patches should be applied in binary mode by default."""
811 811 return os.name == 'nt'
812 812
813 813 # Platform specific variants
814 814 if os.name == 'nt':
815 815 import msvcrt
816 816 nulldev = 'NUL:'
817 817
818 818 class winstdout:
819 819 '''stdout on windows misbehaves if sent through a pipe'''
820 820
821 821 def __init__(self, fp):
822 822 self.fp = fp
823 823
824 824 def __getattr__(self, key):
825 825 return getattr(self.fp, key)
826 826
827 827 def close(self):
828 828 try:
829 829 self.fp.close()
830 830 except: pass
831 831
832 832 def write(self, s):
833 833 try:
834 834 return self.fp.write(s)
835 835 except IOError, inst:
836 836 if inst.errno != 0: raise
837 837 self.close()
838 838 raise IOError(errno.EPIPE, 'Broken pipe')
839 839
840 840 def flush(self):
841 841 try:
842 842 return self.fp.flush()
843 843 except IOError, inst:
844 844 if inst.errno != errno.EINVAL: raise
845 845 self.close()
846 846 raise IOError(errno.EPIPE, 'Broken pipe')
847 847
848 848 sys.stdout = winstdout(sys.stdout)
849 849
850 850 def system_rcpath():
851 851 try:
852 852 return system_rcpath_win32()
853 853 except:
854 854 return [r'c:\mercurial\mercurial.ini']
855 855
856 856 def user_rcpath():
857 857 '''return os-specific hgrc search path to the user dir'''
858 858 try:
859 859 userrc = user_rcpath_win32()
860 860 except:
861 861 userrc = os.path.join(os.path.expanduser('~'), 'mercurial.ini')
862 862 path = [userrc]
863 863 userprofile = os.environ.get('USERPROFILE')
864 864 if userprofile:
865 865 path.append(os.path.join(userprofile, 'mercurial.ini'))
866 866 return path
867 867
868 868 def parse_patch_output(output_line):
869 869 """parses the output produced by patch and returns the file name"""
870 870 pf = output_line[14:]
871 871 if pf[0] == '`':
872 872 pf = pf[1:-1] # Remove the quotes
873 873 return pf
874 874
875 875 def testpid(pid):
876 876 '''return False if pid dead, True if running or not known'''
877 877 return True
878 878
879 879 def set_exec(f, mode):
880 880 pass
881 881
882 882 def set_link(f, mode):
883 883 pass
884 884
885 885 def set_binary(fd):
886 886 msvcrt.setmode(fd.fileno(), os.O_BINARY)
887 887
888 888 def pconvert(path):
889 889 return path.replace("\\", "/")
890 890
891 891 def localpath(path):
892 892 return path.replace('/', '\\')
893 893
894 894 def normpath(path):
895 895 return pconvert(os.path.normpath(path))
896 896
897 897 makelock = _makelock_file
898 898 readlock = _readlock_file
899 899
900 900 def samestat(s1, s2):
901 901 return False
902 902
903 903 # A sequence of backslashes is special iff it precedes a double quote:
904 904 # - if there's an even number of backslashes, the double quote is not
905 905 # quoted (i.e. it ends the quoted region)
906 906 # - if there's an odd number of backslashes, the double quote is quoted
907 907 # - in both cases, every pair of backslashes is unquoted into a single
908 908 # backslash
909 909 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
910 910 # So, to quote a string, we must surround it in double quotes, double
911 911 # the number of backslashes that preceed double quotes and add another
912 912 # backslash before every double quote (being careful with the double
913 913 # quote we've appended to the end)
914 914 _quotere = None
915 915 def shellquote(s):
916 916 global _quotere
917 917 if _quotere is None:
918 918 _quotere = re.compile(r'(\\*)("|\\$)')
919 919 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
920 920
921 921 def explain_exit(code):
922 922 return _("exited with status %d") % code, code
923 923
924 924 # if you change this stub into a real check, please try to implement the
925 925 # username and groupname functions above, too.
926 926 def isowner(fp, st=None):
927 927 return True
928 928
929 929 def find_in_path(name, path, default=None):
930 930 '''find name in search path. path can be string (will be split
931 931 with os.pathsep), or iterable thing that returns strings. if name
932 932 found, return path to name. else return default. name is looked up
933 933 using cmd.exe rules, using PATHEXT.'''
934 934 if isinstance(path, str):
935 935 path = path.split(os.pathsep)
936 936
937 937 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
938 938 pathext = pathext.lower().split(os.pathsep)
939 939 isexec = os.path.splitext(name)[1].lower() in pathext
940 940
941 941 for p in path:
942 942 p_name = os.path.join(p, name)
943 943
944 944 if isexec and os.path.exists(p_name):
945 945 return p_name
946 946
947 947 for ext in pathext:
948 948 p_name_ext = p_name + ext
949 949 if os.path.exists(p_name_ext):
950 950 return p_name_ext
951 951 return default
952 952
953 953 try:
954 954 # override functions with win32 versions if possible
955 955 from util_win32 import *
956 956 if not is_win_9x():
957 957 posixfile = posixfile_nt
958 958 except ImportError:
959 959 pass
960 960
961 961 else:
962 962 nulldev = '/dev/null'
963 963
964 964 def rcfiles(path):
965 965 rcs = [os.path.join(path, 'hgrc')]
966 966 rcdir = os.path.join(path, 'hgrc.d')
967 967 try:
968 968 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
969 969 if f.endswith(".rc")])
970 970 except OSError:
971 971 pass
972 972 return rcs
973 973
974 974 def system_rcpath():
975 975 path = []
976 976 # old mod_python does not set sys.argv
977 977 if len(getattr(sys, 'argv', [])) > 0:
978 978 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
979 979 '/../etc/mercurial'))
980 980 path.extend(rcfiles('/etc/mercurial'))
981 981 return path
982 982
983 983 def user_rcpath():
984 984 return [os.path.expanduser('~/.hgrc')]
985 985
986 986 def parse_patch_output(output_line):
987 987 """parses the output produced by patch and returns the file name"""
988 988 pf = output_line[14:]
989 989 if pf.startswith("'") and pf.endswith("'") and " " in pf:
990 990 pf = pf[1:-1] # Remove the quotes
991 991 return pf
992 992
993 993 def is_exec(f):
994 994 """check whether a file is executable"""
995 995 return (os.lstat(f).st_mode & 0100 != 0)
996 996
997 997 def set_exec(f, mode):
998 998 s = os.lstat(f).st_mode
999 999 if (s & 0100 != 0) == mode:
1000 1000 return
1001 1001 if mode:
1002 1002 # Turn on +x for every +r bit when making a file executable
1003 1003 # and obey umask.
1004 1004 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1005 1005 else:
1006 1006 os.chmod(f, s & 0666)
1007 1007
1008 1008 def set_link(f, mode):
1009 1009 """make a file a symbolic link/regular file
1010 1010
1011 1011 if a file is changed to a link, its contents become the link data
1012 1012 if a link is changed to a file, its link data become its contents
1013 1013 """
1014 1014
1015 1015 m = os.path.islink(f)
1016 1016 if m == bool(mode):
1017 1017 return
1018 1018
1019 1019 if mode: # switch file to link
1020 1020 data = file(f).read()
1021 1021 os.unlink(f)
1022 1022 os.symlink(data, f)
1023 1023 else:
1024 1024 data = os.readlink(f)
1025 1025 os.unlink(f)
1026 1026 file(f, "w").write(data)
1027 1027
1028 1028 def set_binary(fd):
1029 1029 pass
1030 1030
1031 1031 def pconvert(path):
1032 1032 return path
1033 1033
1034 1034 def localpath(path):
1035 1035 return path
1036 1036
1037 1037 normpath = os.path.normpath
1038 1038 samestat = os.path.samestat
1039 1039
1040 1040 def makelock(info, pathname):
1041 1041 try:
1042 1042 os.symlink(info, pathname)
1043 1043 except OSError, why:
1044 1044 if why.errno == errno.EEXIST:
1045 1045 raise
1046 1046 else:
1047 1047 _makelock_file(info, pathname)
1048 1048
1049 1049 def readlock(pathname):
1050 1050 try:
1051 1051 return os.readlink(pathname)
1052 1052 except OSError, why:
1053 1053 if why.errno == errno.EINVAL:
1054 1054 return _readlock_file(pathname)
1055 1055 else:
1056 1056 raise
1057 1057
1058 1058 def shellquote(s):
1059 1059 return "'%s'" % s.replace("'", "'\\''")
1060 1060
1061 1061 def testpid(pid):
1062 1062 '''return False if pid dead, True if running or not sure'''
1063 1063 try:
1064 1064 os.kill(pid, 0)
1065 1065 return True
1066 1066 except OSError, inst:
1067 1067 return inst.errno != errno.ESRCH
1068 1068
1069 1069 def explain_exit(code):
1070 1070 """return a 2-tuple (desc, code) describing a process's status"""
1071 1071 if os.WIFEXITED(code):
1072 1072 val = os.WEXITSTATUS(code)
1073 1073 return _("exited with status %d") % val, val
1074 1074 elif os.WIFSIGNALED(code):
1075 1075 val = os.WTERMSIG(code)
1076 1076 return _("killed by signal %d") % val, val
1077 1077 elif os.WIFSTOPPED(code):
1078 1078 val = os.WSTOPSIG(code)
1079 1079 return _("stopped by signal %d") % val, val
1080 1080 raise ValueError(_("invalid exit code"))
1081 1081
1082 1082 def isowner(fp, st=None):
1083 1083 """Return True if the file object f belongs to the current user.
1084 1084
1085 1085 The return value of a util.fstat(f) may be passed as the st argument.
1086 1086 """
1087 1087 if st is None:
1088 1088 st = fstat(fp)
1089 1089 return st.st_uid == os.getuid()
1090 1090
1091 1091 def find_in_path(name, path, default=None):
1092 1092 '''find name in search path. path can be string (will be split
1093 1093 with os.pathsep), or iterable thing that returns strings. if name
1094 1094 found, return path to name. else return default.'''
1095 1095 if isinstance(path, str):
1096 1096 path = path.split(os.pathsep)
1097 1097 for p in path:
1098 1098 p_name = os.path.join(p, name)
1099 1099 if os.path.exists(p_name):
1100 1100 return p_name
1101 1101 return default
1102 1102
1103 1103 def find_exe(name, default=None):
1104 1104 '''find path of an executable.
1105 1105 if name contains a path component, return it as is. otherwise,
1106 1106 use normal executable search path.'''
1107 1107
1108 1108 if os.sep in name:
1109 1109 # don't check the executable bit. if the file isn't
1110 1110 # executable, whoever tries to actually run it will give a
1111 1111 # much more useful error message.
1112 1112 return name
1113 1113 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1114 1114
1115 1115 def _buildencodefun():
1116 1116 e = '_'
1117 1117 win_reserved = [ord(x) for x in '\\:*?"<>|']
1118 1118 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1119 1119 for x in (range(32) + range(126, 256) + win_reserved):
1120 1120 cmap[chr(x)] = "~%02x" % x
1121 1121 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1122 1122 cmap[chr(x)] = e + chr(x).lower()
1123 1123 dmap = {}
1124 1124 for k, v in cmap.iteritems():
1125 1125 dmap[v] = k
1126 1126 def decode(s):
1127 1127 i = 0
1128 1128 while i < len(s):
1129 1129 for l in xrange(1, 4):
1130 1130 try:
1131 1131 yield dmap[s[i:i+l]]
1132 1132 i += l
1133 1133 break
1134 1134 except KeyError:
1135 1135 pass
1136 1136 else:
1137 1137 raise KeyError
1138 1138 return (lambda s: "".join([cmap[c] for c in s]),
1139 1139 lambda s: "".join(list(decode(s))))
1140 1140
1141 1141 encodefilename, decodefilename = _buildencodefun()
1142 1142
1143 1143 def encodedopener(openerfn, fn):
1144 1144 def o(path, *args, **kw):
1145 1145 return openerfn(fn(path), *args, **kw)
1146 1146 return o
1147 1147
1148 1148 def opener(base, audit=True):
1149 1149 """
1150 1150 return a function that opens files relative to base
1151 1151
1152 1152 this function is used to hide the details of COW semantics and
1153 1153 remote file access from higher level code.
1154 1154 """
1155 p = base
1156 audit_p = audit
1157
1158 1155 def mktempcopy(name, emptyok=False):
1159 1156 d, fn = os.path.split(name)
1160 1157 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1161 1158 os.close(fd)
1162 1159 # Temporary files are created with mode 0600, which is usually not
1163 1160 # what we want. If the original file already exists, just copy
1164 1161 # its mode. Otherwise, manually obey umask.
1165 1162 try:
1166 1163 st_mode = os.lstat(name).st_mode
1167 1164 except OSError, inst:
1168 1165 if inst.errno != errno.ENOENT:
1169 1166 raise
1170 1167 st_mode = 0666 & ~_umask
1171 1168 os.chmod(temp, st_mode)
1172 1169 if emptyok:
1173 1170 return temp
1174 1171 try:
1175 1172 try:
1176 1173 ifp = posixfile(name, "rb")
1177 1174 except IOError, inst:
1178 1175 if inst.errno == errno.ENOENT:
1179 1176 return temp
1180 1177 if not getattr(inst, 'filename', None):
1181 1178 inst.filename = name
1182 1179 raise
1183 1180 ofp = posixfile(temp, "wb")
1184 1181 for chunk in filechunkiter(ifp):
1185 1182 ofp.write(chunk)
1186 1183 ifp.close()
1187 1184 ofp.close()
1188 1185 except:
1189 1186 try: os.unlink(temp)
1190 1187 except: pass
1191 1188 raise
1192 1189 return temp
1193 1190
1194 1191 class atomictempfile(posixfile):
1195 1192 """the file will only be copied when rename is called"""
1196 1193 def __init__(self, name, mode):
1197 1194 self.__name = name
1198 1195 self.temp = mktempcopy(name, emptyok=('w' in mode))
1199 1196 posixfile.__init__(self, self.temp, mode)
1200 1197 def rename(self):
1201 1198 if not self.closed:
1202 1199 posixfile.close(self)
1203 1200 rename(self.temp, localpath(self.__name))
1204 1201 def __del__(self):
1205 1202 if not self.closed:
1206 1203 try:
1207 1204 os.unlink(self.temp)
1208 1205 except: pass
1209 1206 posixfile.close(self)
1210 1207
1211 1208 def o(path, mode="r", text=False, atomictemp=False):
1212 if audit_p:
1209 if audit:
1213 1210 audit_path(path)
1214 f = os.path.join(p, path)
1211 f = os.path.join(base, path)
1215 1212
1216 1213 if not text:
1217 1214 mode += "b" # for that other OS
1218 1215
1219 1216 if mode[0] != "r":
1220 1217 try:
1221 1218 nlink = nlinks(f)
1222 1219 except OSError:
1223 1220 nlink = 0
1224 1221 d = os.path.dirname(f)
1225 1222 if not os.path.isdir(d):
1226 1223 os.makedirs(d)
1227 1224 if atomictemp:
1228 1225 return atomictempfile(f, mode)
1229 1226 if nlink > 1:
1230 1227 rename(mktempcopy(f), f)
1231 1228 return posixfile(f, mode)
1232 1229
1233 1230 return o
1234 1231
1235 1232 class chunkbuffer(object):
1236 1233 """Allow arbitrary sized chunks of data to be efficiently read from an
1237 1234 iterator over chunks of arbitrary size."""
1238 1235
1239 1236 def __init__(self, in_iter, targetsize = 2**16):
1240 1237 """in_iter is the iterator that's iterating over the input chunks.
1241 1238 targetsize is how big a buffer to try to maintain."""
1242 1239 self.in_iter = iter(in_iter)
1243 1240 self.buf = ''
1244 1241 self.targetsize = int(targetsize)
1245 1242 if self.targetsize <= 0:
1246 1243 raise ValueError(_("targetsize must be greater than 0, was %d") %
1247 1244 targetsize)
1248 1245 self.iterempty = False
1249 1246
1250 1247 def fillbuf(self):
1251 1248 """Ignore target size; read every chunk from iterator until empty."""
1252 1249 if not self.iterempty:
1253 1250 collector = cStringIO.StringIO()
1254 1251 collector.write(self.buf)
1255 1252 for ch in self.in_iter:
1256 1253 collector.write(ch)
1257 1254 self.buf = collector.getvalue()
1258 1255 self.iterempty = True
1259 1256
1260 1257 def read(self, l):
1261 1258 """Read L bytes of data from the iterator of chunks of data.
1262 1259 Returns less than L bytes if the iterator runs dry."""
1263 1260 if l > len(self.buf) and not self.iterempty:
1264 1261 # Clamp to a multiple of self.targetsize
1265 1262 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1266 1263 collector = cStringIO.StringIO()
1267 1264 collector.write(self.buf)
1268 1265 collected = len(self.buf)
1269 1266 for chunk in self.in_iter:
1270 1267 collector.write(chunk)
1271 1268 collected += len(chunk)
1272 1269 if collected >= targetsize:
1273 1270 break
1274 1271 if collected < targetsize:
1275 1272 self.iterempty = True
1276 1273 self.buf = collector.getvalue()
1277 1274 s, self.buf = self.buf[:l], buffer(self.buf, l)
1278 1275 return s
1279 1276
1280 1277 def filechunkiter(f, size=65536, limit=None):
1281 1278 """Create a generator that produces the data in the file size
1282 1279 (default 65536) bytes at a time, up to optional limit (default is
1283 1280 to read all data). Chunks may be less than size bytes if the
1284 1281 chunk is the last chunk in the file, or the file is a socket or
1285 1282 some other type of file that sometimes reads less data than is
1286 1283 requested."""
1287 1284 assert size >= 0
1288 1285 assert limit is None or limit >= 0
1289 1286 while True:
1290 1287 if limit is None: nbytes = size
1291 1288 else: nbytes = min(limit, size)
1292 1289 s = nbytes and f.read(nbytes)
1293 1290 if not s: break
1294 1291 if limit: limit -= len(s)
1295 1292 yield s
1296 1293
1297 1294 def makedate():
1298 1295 lt = time.localtime()
1299 1296 if lt[8] == 1 and time.daylight:
1300 1297 tz = time.altzone
1301 1298 else:
1302 1299 tz = time.timezone
1303 1300 return time.mktime(lt), tz
1304 1301
1305 1302 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1306 1303 """represent a (unixtime, offset) tuple as a localized time.
1307 1304 unixtime is seconds since the epoch, and offset is the time zone's
1308 1305 number of seconds away from UTC. if timezone is false, do not
1309 1306 append time zone to string."""
1310 1307 t, tz = date or makedate()
1311 1308 s = time.strftime(format, time.gmtime(float(t) - tz))
1312 1309 if timezone:
1313 1310 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1314 1311 return s
1315 1312
1316 1313 def strdate(string, format, defaults):
1317 1314 """parse a localized time string and return a (unixtime, offset) tuple.
1318 1315 if the string cannot be parsed, ValueError is raised."""
1319 1316 def timezone(string):
1320 1317 tz = string.split()[-1]
1321 1318 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1322 1319 tz = int(tz)
1323 1320 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1324 1321 return offset
1325 1322 if tz == "GMT" or tz == "UTC":
1326 1323 return 0
1327 1324 return None
1328 1325
1329 1326 # NOTE: unixtime = localunixtime + offset
1330 1327 offset, date = timezone(string), string
1331 1328 if offset != None:
1332 1329 date = " ".join(string.split()[:-1])
1333 1330
1334 1331 # add missing elements from defaults
1335 1332 for part in defaults:
1336 1333 found = [True for p in part if ("%"+p) in format]
1337 1334 if not found:
1338 1335 date += "@" + defaults[part]
1339 1336 format += "@%" + part[0]
1340 1337
1341 1338 timetuple = time.strptime(date, format)
1342 1339 localunixtime = int(calendar.timegm(timetuple))
1343 1340 if offset is None:
1344 1341 # local timezone
1345 1342 unixtime = int(time.mktime(timetuple))
1346 1343 offset = unixtime - localunixtime
1347 1344 else:
1348 1345 unixtime = localunixtime + offset
1349 1346 return unixtime, offset
1350 1347
1351 1348 def parsedate(string, formats=None, defaults=None):
1352 1349 """parse a localized time string and return a (unixtime, offset) tuple.
1353 1350 The date may be a "unixtime offset" string or in one of the specified
1354 1351 formats."""
1355 1352 if not string:
1356 1353 return 0, 0
1357 1354 if not formats:
1358 1355 formats = defaultdateformats
1359 1356 string = string.strip()
1360 1357 try:
1361 1358 when, offset = map(int, string.split(' '))
1362 1359 except ValueError:
1363 1360 # fill out defaults
1364 1361 if not defaults:
1365 1362 defaults = {}
1366 1363 now = makedate()
1367 1364 for part in "d mb yY HI M S".split():
1368 1365 if part not in defaults:
1369 1366 if part[0] in "HMS":
1370 1367 defaults[part] = "00"
1371 1368 elif part[0] in "dm":
1372 1369 defaults[part] = "1"
1373 1370 else:
1374 1371 defaults[part] = datestr(now, "%" + part[0], False)
1375 1372
1376 1373 for format in formats:
1377 1374 try:
1378 1375 when, offset = strdate(string, format, defaults)
1379 1376 except ValueError:
1380 1377 pass
1381 1378 else:
1382 1379 break
1383 1380 else:
1384 1381 raise Abort(_('invalid date: %r ') % string)
1385 1382 # validate explicit (probably user-specified) date and
1386 1383 # time zone offset. values must fit in signed 32 bits for
1387 1384 # current 32-bit linux runtimes. timezones go from UTC-12
1388 1385 # to UTC+14
1389 1386 if abs(when) > 0x7fffffff:
1390 1387 raise Abort(_('date exceeds 32 bits: %d') % when)
1391 1388 if offset < -50400 or offset > 43200:
1392 1389 raise Abort(_('impossible time zone offset: %d') % offset)
1393 1390 return when, offset
1394 1391
1395 1392 def matchdate(date):
1396 1393 """Return a function that matches a given date match specifier
1397 1394
1398 1395 Formats include:
1399 1396
1400 1397 '{date}' match a given date to the accuracy provided
1401 1398
1402 1399 '<{date}' on or before a given date
1403 1400
1404 1401 '>{date}' on or after a given date
1405 1402
1406 1403 """
1407 1404
1408 1405 def lower(date):
1409 1406 return parsedate(date, extendeddateformats)[0]
1410 1407
1411 1408 def upper(date):
1412 1409 d = dict(mb="12", HI="23", M="59", S="59")
1413 1410 for days in "31 30 29".split():
1414 1411 try:
1415 1412 d["d"] = days
1416 1413 return parsedate(date, extendeddateformats, d)[0]
1417 1414 except:
1418 1415 pass
1419 1416 d["d"] = "28"
1420 1417 return parsedate(date, extendeddateformats, d)[0]
1421 1418
1422 1419 if date[0] == "<":
1423 1420 when = upper(date[1:])
1424 1421 return lambda x: x <= when
1425 1422 elif date[0] == ">":
1426 1423 when = lower(date[1:])
1427 1424 return lambda x: x >= when
1428 1425 elif date[0] == "-":
1429 1426 try:
1430 1427 days = int(date[1:])
1431 1428 except ValueError:
1432 1429 raise Abort(_("invalid day spec: %s") % date[1:])
1433 1430 when = makedate()[0] - days * 3600 * 24
1434 1431 return lambda x: x >= when
1435 1432 elif " to " in date:
1436 1433 a, b = date.split(" to ")
1437 1434 start, stop = lower(a), upper(b)
1438 1435 return lambda x: x >= start and x <= stop
1439 1436 else:
1440 1437 start, stop = lower(date), upper(date)
1441 1438 return lambda x: x >= start and x <= stop
1442 1439
1443 1440 def shortuser(user):
1444 1441 """Return a short representation of a user name or email address."""
1445 1442 f = user.find('@')
1446 1443 if f >= 0:
1447 1444 user = user[:f]
1448 1445 f = user.find('<')
1449 1446 if f >= 0:
1450 1447 user = user[f+1:]
1451 1448 f = user.find(' ')
1452 1449 if f >= 0:
1453 1450 user = user[:f]
1454 1451 f = user.find('.')
1455 1452 if f >= 0:
1456 1453 user = user[:f]
1457 1454 return user
1458 1455
1459 1456 def ellipsis(text, maxlength=400):
1460 1457 """Trim string to at most maxlength (default: 400) characters."""
1461 1458 if len(text) <= maxlength:
1462 1459 return text
1463 1460 else:
1464 1461 return "%s..." % (text[:maxlength-3])
1465 1462
1466 1463 def walkrepos(path):
1467 1464 '''yield every hg repository under path, recursively.'''
1468 1465 def errhandler(err):
1469 1466 if err.filename == path:
1470 1467 raise err
1471 1468
1472 1469 for root, dirs, files in os.walk(path, onerror=errhandler):
1473 1470 for d in dirs:
1474 1471 if d == '.hg':
1475 1472 yield root
1476 1473 dirs[:] = []
1477 1474 break
1478 1475
1479 1476 _rcpath = None
1480 1477
1481 1478 def os_rcpath():
1482 1479 '''return default os-specific hgrc search path'''
1483 1480 path = system_rcpath()
1484 1481 path.extend(user_rcpath())
1485 1482 path = [os.path.normpath(f) for f in path]
1486 1483 return path
1487 1484
1488 1485 def rcpath():
1489 1486 '''return hgrc search path. if env var HGRCPATH is set, use it.
1490 1487 for each item in path, if directory, use files ending in .rc,
1491 1488 else use item.
1492 1489 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1493 1490 if no HGRCPATH, use default os-specific path.'''
1494 1491 global _rcpath
1495 1492 if _rcpath is None:
1496 1493 if 'HGRCPATH' in os.environ:
1497 1494 _rcpath = []
1498 1495 for p in os.environ['HGRCPATH'].split(os.pathsep):
1499 1496 if not p: continue
1500 1497 if os.path.isdir(p):
1501 1498 for f in os.listdir(p):
1502 1499 if f.endswith('.rc'):
1503 1500 _rcpath.append(os.path.join(p, f))
1504 1501 else:
1505 1502 _rcpath.append(p)
1506 1503 else:
1507 1504 _rcpath = os_rcpath()
1508 1505 return _rcpath
1509 1506
1510 1507 def bytecount(nbytes):
1511 1508 '''return byte count formatted as readable string, with units'''
1512 1509
1513 1510 units = (
1514 1511 (100, 1<<30, _('%.0f GB')),
1515 1512 (10, 1<<30, _('%.1f GB')),
1516 1513 (1, 1<<30, _('%.2f GB')),
1517 1514 (100, 1<<20, _('%.0f MB')),
1518 1515 (10, 1<<20, _('%.1f MB')),
1519 1516 (1, 1<<20, _('%.2f MB')),
1520 1517 (100, 1<<10, _('%.0f KB')),
1521 1518 (10, 1<<10, _('%.1f KB')),
1522 1519 (1, 1<<10, _('%.2f KB')),
1523 1520 (1, 1, _('%.0f bytes')),
1524 1521 )
1525 1522
1526 1523 for multiplier, divisor, format in units:
1527 1524 if nbytes >= divisor * multiplier:
1528 1525 return format % (nbytes / float(divisor))
1529 1526 return units[-1][2] % nbytes
1530 1527
1531 1528 def drop_scheme(scheme, path):
1532 1529 sc = scheme + ':'
1533 1530 if path.startswith(sc):
1534 1531 path = path[len(sc):]
1535 1532 if path.startswith('//'):
1536 1533 path = path[2:]
1537 1534 return path
General Comments 0
You need to be logged in to leave comments. Login now