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