##// END OF EJS Templates
ssh: ban any username@host or host that starts with - (SEC)...
Augie Fackler -
r33707:e1074531 stable
parent child Browse files
Show More
@@ -1,649 +1,653 b''
1 1 # posix.py - Posix utility function implementations for Mercurial
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import errno
11 11 import fcntl
12 12 import getpass
13 13 import grp
14 14 import os
15 15 import pwd
16 16 import re
17 17 import select
18 18 import stat
19 19 import sys
20 20 import tempfile
21 21 import unicodedata
22 22
23 23 from .i18n import _
24 24 from . import (
25 25 encoding,
26 error,
26 27 pycompat,
27 28 )
28 29
29 30 posixfile = open
30 31 normpath = os.path.normpath
31 32 samestat = os.path.samestat
32 33 try:
33 34 oslink = os.link
34 35 except AttributeError:
35 36 # Some platforms build Python without os.link on systems that are
36 37 # vaguely unix-like but don't have hardlink support. For those
37 38 # poor souls, just say we tried and that it failed so we fall back
38 39 # to copies.
39 40 def oslink(src, dst):
40 41 raise OSError(errno.EINVAL,
41 42 'hardlinks not supported: %s to %s' % (src, dst))
42 43 unlink = os.unlink
43 44 rename = os.rename
44 45 removedirs = os.removedirs
45 46 expandglobs = False
46 47
47 48 umask = os.umask(0)
48 49 os.umask(umask)
49 50
50 51 def split(p):
51 52 '''Same as posixpath.split, but faster
52 53
53 54 >>> import posixpath
54 55 >>> for f in ['/absolute/path/to/file',
55 56 ... 'relative/path/to/file',
56 57 ... 'file_alone',
57 58 ... 'path/to/directory/',
58 59 ... '/multiple/path//separators',
59 60 ... '/file_at_root',
60 61 ... '///multiple_leading_separators_at_root',
61 62 ... '']:
62 63 ... assert split(f) == posixpath.split(f), f
63 64 '''
64 65 ht = p.rsplit('/', 1)
65 66 if len(ht) == 1:
66 67 return '', p
67 68 nh = ht[0].rstrip('/')
68 69 if nh:
69 70 return nh, ht[1]
70 71 return ht[0] + '/', ht[1]
71 72
72 73 def openhardlinks():
73 74 '''return true if it is safe to hold open file handles to hardlinks'''
74 75 return True
75 76
76 77 def nlinks(name):
77 78 '''return number of hardlinks for the given file'''
78 79 return os.lstat(name).st_nlink
79 80
80 81 def parsepatchoutput(output_line):
81 82 """parses the output produced by patch and returns the filename"""
82 83 pf = output_line[14:]
83 84 if pycompat.sysplatform == 'OpenVMS':
84 85 if pf[0] == '`':
85 86 pf = pf[1:-1] # Remove the quotes
86 87 else:
87 88 if pf.startswith("'") and pf.endswith("'") and " " in pf:
88 89 pf = pf[1:-1] # Remove the quotes
89 90 return pf
90 91
91 92 def sshargs(sshcmd, host, user, port):
92 93 '''Build argument list for ssh'''
93 94 args = user and ("%s@%s" % (user, host)) or host
95 if '-' in args[:2]:
96 raise error.Abort(
97 _('illegal ssh hostname or username starting with -: %s') % args)
94 98 return port and ("%s -p %s" % (args, port)) or args
95 99
96 100 def isexec(f):
97 101 """check whether a file is executable"""
98 102 return (os.lstat(f).st_mode & 0o100 != 0)
99 103
100 104 def setflags(f, l, x):
101 105 s = os.lstat(f).st_mode
102 106 if l:
103 107 if not stat.S_ISLNK(s):
104 108 # switch file to link
105 109 fp = open(f)
106 110 data = fp.read()
107 111 fp.close()
108 112 unlink(f)
109 113 try:
110 114 os.symlink(data, f)
111 115 except OSError:
112 116 # failed to make a link, rewrite file
113 117 fp = open(f, "w")
114 118 fp.write(data)
115 119 fp.close()
116 120 # no chmod needed at this point
117 121 return
118 122 if stat.S_ISLNK(s):
119 123 # switch link to file
120 124 data = os.readlink(f)
121 125 unlink(f)
122 126 fp = open(f, "w")
123 127 fp.write(data)
124 128 fp.close()
125 129 s = 0o666 & ~umask # avoid restatting for chmod
126 130
127 131 sx = s & 0o100
128 132 if x and not sx:
129 133 # Turn on +x for every +r bit when making a file executable
130 134 # and obey umask.
131 135 os.chmod(f, s | (s & 0o444) >> 2 & ~umask)
132 136 elif not x and sx:
133 137 # Turn off all +x bits
134 138 os.chmod(f, s & 0o666)
135 139
136 140 def copymode(src, dst, mode=None):
137 141 '''Copy the file mode from the file at path src to dst.
138 142 If src doesn't exist, we're using mode instead. If mode is None, we're
139 143 using umask.'''
140 144 try:
141 145 st_mode = os.lstat(src).st_mode & 0o777
142 146 except OSError as inst:
143 147 if inst.errno != errno.ENOENT:
144 148 raise
145 149 st_mode = mode
146 150 if st_mode is None:
147 151 st_mode = ~umask
148 152 st_mode &= 0o666
149 153 os.chmod(dst, st_mode)
150 154
151 155 def checkexec(path):
152 156 """
153 157 Check whether the given path is on a filesystem with UNIX-like exec flags
154 158
155 159 Requires a directory (like /foo/.hg)
156 160 """
157 161
158 162 # VFAT on some Linux versions can flip mode but it doesn't persist
159 163 # a FS remount. Frequently we can detect it if files are created
160 164 # with exec bit on.
161 165
162 166 try:
163 167 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
164 168 cachedir = os.path.join(path, '.hg', 'cache')
165 169 if os.path.isdir(cachedir):
166 170 checkisexec = os.path.join(cachedir, 'checkisexec')
167 171 checknoexec = os.path.join(cachedir, 'checknoexec')
168 172
169 173 try:
170 174 m = os.stat(checkisexec).st_mode
171 175 except OSError as e:
172 176 if e.errno != errno.ENOENT:
173 177 raise
174 178 # checkisexec does not exist - fall through ...
175 179 else:
176 180 # checkisexec exists, check if it actually is exec
177 181 if m & EXECFLAGS != 0:
178 182 # ensure checkisexec exists, check it isn't exec
179 183 try:
180 184 m = os.stat(checknoexec).st_mode
181 185 except OSError as e:
182 186 if e.errno != errno.ENOENT:
183 187 raise
184 188 open(checknoexec, 'w').close() # might fail
185 189 m = os.stat(checknoexec).st_mode
186 190 if m & EXECFLAGS == 0:
187 191 # check-exec is exec and check-no-exec is not exec
188 192 return True
189 193 # checknoexec exists but is exec - delete it
190 194 unlink(checknoexec)
191 195 # checkisexec exists but is not exec - delete it
192 196 unlink(checkisexec)
193 197
194 198 # check using one file, leave it as checkisexec
195 199 checkdir = cachedir
196 200 else:
197 201 # check directly in path and don't leave checkisexec behind
198 202 checkdir = path
199 203 checkisexec = None
200 204 fh, fn = tempfile.mkstemp(dir=checkdir, prefix='hg-checkexec-')
201 205 try:
202 206 os.close(fh)
203 207 m = os.stat(fn).st_mode
204 208 if m & EXECFLAGS == 0:
205 209 os.chmod(fn, m & 0o777 | EXECFLAGS)
206 210 if os.stat(fn).st_mode & EXECFLAGS != 0:
207 211 if checkisexec is not None:
208 212 os.rename(fn, checkisexec)
209 213 fn = None
210 214 return True
211 215 finally:
212 216 if fn is not None:
213 217 unlink(fn)
214 218 except (IOError, OSError):
215 219 # we don't care, the user probably won't be able to commit anyway
216 220 return False
217 221
218 222 def checklink(path):
219 223 """check whether the given path is on a symlink-capable filesystem"""
220 224 # mktemp is not racy because symlink creation will fail if the
221 225 # file already exists
222 226 while True:
223 227 cachedir = os.path.join(path, '.hg', 'cache')
224 228 checklink = os.path.join(cachedir, 'checklink')
225 229 # try fast path, read only
226 230 if os.path.islink(checklink):
227 231 return True
228 232 if os.path.isdir(cachedir):
229 233 checkdir = cachedir
230 234 else:
231 235 checkdir = path
232 236 cachedir = None
233 237 fscheckdir = pycompat.fsdecode(checkdir)
234 238 name = tempfile.mktemp(dir=fscheckdir,
235 239 prefix=r'checklink-')
236 240 name = pycompat.fsencode(name)
237 241 try:
238 242 fd = None
239 243 if cachedir is None:
240 244 fd = tempfile.NamedTemporaryFile(dir=fscheckdir,
241 245 prefix=r'hg-checklink-')
242 246 target = pycompat.fsencode(os.path.basename(fd.name))
243 247 else:
244 248 # create a fixed file to link to; doesn't matter if it
245 249 # already exists.
246 250 target = 'checklink-target'
247 251 open(os.path.join(cachedir, target), 'w').close()
248 252 try:
249 253 os.symlink(target, name)
250 254 if cachedir is None:
251 255 unlink(name)
252 256 else:
253 257 try:
254 258 os.rename(name, checklink)
255 259 except OSError:
256 260 unlink(name)
257 261 return True
258 262 except OSError as inst:
259 263 # link creation might race, try again
260 264 if inst[0] == errno.EEXIST:
261 265 continue
262 266 raise
263 267 finally:
264 268 if fd is not None:
265 269 fd.close()
266 270 except AttributeError:
267 271 return False
268 272 except OSError as inst:
269 273 # sshfs might report failure while successfully creating the link
270 274 if inst[0] == errno.EIO and os.path.exists(name):
271 275 unlink(name)
272 276 return False
273 277
274 278 def checkosfilename(path):
275 279 '''Check that the base-relative path is a valid filename on this platform.
276 280 Returns None if the path is ok, or a UI string describing the problem.'''
277 281 pass # on posix platforms, every path is ok
278 282
279 283 def setbinary(fd):
280 284 pass
281 285
282 286 def pconvert(path):
283 287 return path
284 288
285 289 def localpath(path):
286 290 return path
287 291
288 292 def samefile(fpath1, fpath2):
289 293 """Returns whether path1 and path2 refer to the same file. This is only
290 294 guaranteed to work for files, not directories."""
291 295 return os.path.samefile(fpath1, fpath2)
292 296
293 297 def samedevice(fpath1, fpath2):
294 298 """Returns whether fpath1 and fpath2 are on the same device. This is only
295 299 guaranteed to work for files, not directories."""
296 300 st1 = os.lstat(fpath1)
297 301 st2 = os.lstat(fpath2)
298 302 return st1.st_dev == st2.st_dev
299 303
300 304 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
301 305 def normcase(path):
302 306 return path.lower()
303 307
304 308 # what normcase does to ASCII strings
305 309 normcasespec = encoding.normcasespecs.lower
306 310 # fallback normcase function for non-ASCII strings
307 311 normcasefallback = normcase
308 312
309 313 if pycompat.sysplatform == 'darwin':
310 314
311 315 def normcase(path):
312 316 '''
313 317 Normalize a filename for OS X-compatible comparison:
314 318 - escape-encode invalid characters
315 319 - decompose to NFD
316 320 - lowercase
317 321 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff]
318 322
319 323 >>> normcase('UPPER')
320 324 'upper'
321 325 >>> normcase('Caf\xc3\xa9')
322 326 'cafe\\xcc\\x81'
323 327 >>> normcase('\xc3\x89')
324 328 'e\\xcc\\x81'
325 329 >>> normcase('\xb8\xca\xc3\xca\xbe\xc8.JPG') # issue3918
326 330 '%b8%ca%c3\\xca\\xbe%c8.jpg'
327 331 '''
328 332
329 333 try:
330 334 return encoding.asciilower(path) # exception for non-ASCII
331 335 except UnicodeDecodeError:
332 336 return normcasefallback(path)
333 337
334 338 normcasespec = encoding.normcasespecs.lower
335 339
336 340 def normcasefallback(path):
337 341 try:
338 342 u = path.decode('utf-8')
339 343 except UnicodeDecodeError:
340 344 # OS X percent-encodes any bytes that aren't valid utf-8
341 345 s = ''
342 346 pos = 0
343 347 l = len(path)
344 348 while pos < l:
345 349 try:
346 350 c = encoding.getutf8char(path, pos)
347 351 pos += len(c)
348 352 except ValueError:
349 353 c = '%%%02X' % ord(path[pos])
350 354 pos += 1
351 355 s += c
352 356
353 357 u = s.decode('utf-8')
354 358
355 359 # Decompose then lowercase (HFS+ technote specifies lower)
356 360 enc = unicodedata.normalize('NFD', u).lower().encode('utf-8')
357 361 # drop HFS+ ignored characters
358 362 return encoding.hfsignoreclean(enc)
359 363
360 364 if pycompat.sysplatform == 'cygwin':
361 365 # workaround for cygwin, in which mount point part of path is
362 366 # treated as case sensitive, even though underlying NTFS is case
363 367 # insensitive.
364 368
365 369 # default mount points
366 370 cygwinmountpoints = sorted([
367 371 "/usr/bin",
368 372 "/usr/lib",
369 373 "/cygdrive",
370 374 ], reverse=True)
371 375
372 376 # use upper-ing as normcase as same as NTFS workaround
373 377 def normcase(path):
374 378 pathlen = len(path)
375 379 if (pathlen == 0) or (path[0] != pycompat.ossep):
376 380 # treat as relative
377 381 return encoding.upper(path)
378 382
379 383 # to preserve case of mountpoint part
380 384 for mp in cygwinmountpoints:
381 385 if not path.startswith(mp):
382 386 continue
383 387
384 388 mplen = len(mp)
385 389 if mplen == pathlen: # mount point itself
386 390 return mp
387 391 if path[mplen] == pycompat.ossep:
388 392 return mp + encoding.upper(path[mplen:])
389 393
390 394 return encoding.upper(path)
391 395
392 396 normcasespec = encoding.normcasespecs.other
393 397 normcasefallback = normcase
394 398
395 399 # Cygwin translates native ACLs to POSIX permissions,
396 400 # but these translations are not supported by native
397 401 # tools, so the exec bit tends to be set erroneously.
398 402 # Therefore, disable executable bit access on Cygwin.
399 403 def checkexec(path):
400 404 return False
401 405
402 406 # Similarly, Cygwin's symlink emulation is likely to create
403 407 # problems when Mercurial is used from both Cygwin and native
404 408 # Windows, with other native tools, or on shared volumes
405 409 def checklink(path):
406 410 return False
407 411
408 412 _needsshellquote = None
409 413 def shellquote(s):
410 414 if pycompat.sysplatform == 'OpenVMS':
411 415 return '"%s"' % s
412 416 global _needsshellquote
413 417 if _needsshellquote is None:
414 418 _needsshellquote = re.compile(br'[^a-zA-Z0-9._/+-]').search
415 419 if s and not _needsshellquote(s):
416 420 # "s" shouldn't have to be quoted
417 421 return s
418 422 else:
419 423 return "'%s'" % s.replace("'", "'\\''")
420 424
421 425 def quotecommand(cmd):
422 426 return cmd
423 427
424 428 def popen(command, mode='r'):
425 429 return os.popen(command, mode)
426 430
427 431 def testpid(pid):
428 432 '''return False if pid dead, True if running or not sure'''
429 433 if pycompat.sysplatform == 'OpenVMS':
430 434 return True
431 435 try:
432 436 os.kill(pid, 0)
433 437 return True
434 438 except OSError as inst:
435 439 return inst.errno != errno.ESRCH
436 440
437 441 def explainexit(code):
438 442 """return a 2-tuple (desc, code) describing a subprocess status
439 443 (codes from kill are negative - not os.system/wait encoding)"""
440 444 if code >= 0:
441 445 return _("exited with status %d") % code, code
442 446 return _("killed by signal %d") % -code, -code
443 447
444 448 def isowner(st):
445 449 """Return True if the stat object st is from the current user."""
446 450 return st.st_uid == os.getuid()
447 451
448 452 def findexe(command):
449 453 '''Find executable for command searching like which does.
450 454 If command is a basename then PATH is searched for command.
451 455 PATH isn't searched if command is an absolute or relative path.
452 456 If command isn't found None is returned.'''
453 457 if pycompat.sysplatform == 'OpenVMS':
454 458 return command
455 459
456 460 def findexisting(executable):
457 461 'Will return executable if existing file'
458 462 if os.path.isfile(executable) and os.access(executable, os.X_OK):
459 463 return executable
460 464 return None
461 465
462 466 if pycompat.ossep in command:
463 467 return findexisting(command)
464 468
465 469 if pycompat.sysplatform == 'plan9':
466 470 return findexisting(os.path.join('/bin', command))
467 471
468 472 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
469 473 executable = findexisting(os.path.join(path, command))
470 474 if executable is not None:
471 475 return executable
472 476 return None
473 477
474 478 def setsignalhandler():
475 479 pass
476 480
477 481 _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
478 482
479 483 def statfiles(files):
480 484 '''Stat each file in files. Yield each stat, or None if a file does not
481 485 exist or has a type we don't care about.'''
482 486 lstat = os.lstat
483 487 getkind = stat.S_IFMT
484 488 for nf in files:
485 489 try:
486 490 st = lstat(nf)
487 491 if getkind(st.st_mode) not in _wantedkinds:
488 492 st = None
489 493 except OSError as err:
490 494 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
491 495 raise
492 496 st = None
493 497 yield st
494 498
495 499 def getuser():
496 500 '''return name of current user'''
497 501 return getpass.getuser()
498 502
499 503 def username(uid=None):
500 504 """Return the name of the user with the given uid.
501 505
502 506 If uid is None, return the name of the current user."""
503 507
504 508 if uid is None:
505 509 uid = os.getuid()
506 510 try:
507 511 return pwd.getpwuid(uid)[0]
508 512 except KeyError:
509 513 return str(uid)
510 514
511 515 def groupname(gid=None):
512 516 """Return the name of the group with the given gid.
513 517
514 518 If gid is None, return the name of the current group."""
515 519
516 520 if gid is None:
517 521 gid = os.getgid()
518 522 try:
519 523 return grp.getgrgid(gid)[0]
520 524 except KeyError:
521 525 return str(gid)
522 526
523 527 def groupmembers(name):
524 528 """Return the list of members of the group with the given
525 529 name, KeyError if the group does not exist.
526 530 """
527 531 return list(grp.getgrnam(name).gr_mem)
528 532
529 533 def spawndetached(args):
530 534 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
531 535 args[0], args)
532 536
533 537 def gethgcmd():
534 538 return sys.argv[:1]
535 539
536 540 def makedir(path, notindexed):
537 541 os.mkdir(path)
538 542
539 543 def lookupreg(key, name=None, scope=None):
540 544 return None
541 545
542 546 def hidewindow():
543 547 """Hide current shell window.
544 548
545 549 Used to hide the window opened when starting asynchronous
546 550 child process under Windows, unneeded on other systems.
547 551 """
548 552 pass
549 553
550 554 class cachestat(object):
551 555 def __init__(self, path):
552 556 self.stat = os.stat(path)
553 557
554 558 def cacheable(self):
555 559 return bool(self.stat.st_ino)
556 560
557 561 __hash__ = object.__hash__
558 562
559 563 def __eq__(self, other):
560 564 try:
561 565 # Only dev, ino, size, mtime and atime are likely to change. Out
562 566 # of these, we shouldn't compare atime but should compare the
563 567 # rest. However, one of the other fields changing indicates
564 568 # something fishy going on, so return False if anything but atime
565 569 # changes.
566 570 return (self.stat.st_mode == other.stat.st_mode and
567 571 self.stat.st_ino == other.stat.st_ino and
568 572 self.stat.st_dev == other.stat.st_dev and
569 573 self.stat.st_nlink == other.stat.st_nlink and
570 574 self.stat.st_uid == other.stat.st_uid and
571 575 self.stat.st_gid == other.stat.st_gid and
572 576 self.stat.st_size == other.stat.st_size and
573 577 self.stat.st_mtime == other.stat.st_mtime and
574 578 self.stat.st_ctime == other.stat.st_ctime)
575 579 except AttributeError:
576 580 return False
577 581
578 582 def __ne__(self, other):
579 583 return not self == other
580 584
581 585 def executablepath():
582 586 return None # available on Windows only
583 587
584 588 def statislink(st):
585 589 '''check whether a stat result is a symlink'''
586 590 return st and stat.S_ISLNK(st.st_mode)
587 591
588 592 def statisexec(st):
589 593 '''check whether a stat result is an executable file'''
590 594 return st and (st.st_mode & 0o100 != 0)
591 595
592 596 def poll(fds):
593 597 """block until something happens on any file descriptor
594 598
595 599 This is a generic helper that will check for any activity
596 600 (read, write. exception) and return the list of touched files.
597 601
598 602 In unsupported cases, it will raise a NotImplementedError"""
599 603 try:
600 604 while True:
601 605 try:
602 606 res = select.select(fds, fds, fds)
603 607 break
604 608 except select.error as inst:
605 609 if inst.args[0] == errno.EINTR:
606 610 continue
607 611 raise
608 612 except ValueError: # out of range file descriptor
609 613 raise NotImplementedError()
610 614 return sorted(list(set(sum(res, []))))
611 615
612 616 def readpipe(pipe):
613 617 """Read all available data from a pipe."""
614 618 # We can't fstat() a pipe because Linux will always report 0.
615 619 # So, we set the pipe to non-blocking mode and read everything
616 620 # that's available.
617 621 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
618 622 flags |= os.O_NONBLOCK
619 623 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
620 624
621 625 try:
622 626 chunks = []
623 627 while True:
624 628 try:
625 629 s = pipe.read()
626 630 if not s:
627 631 break
628 632 chunks.append(s)
629 633 except IOError:
630 634 break
631 635
632 636 return ''.join(chunks)
633 637 finally:
634 638 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
635 639
636 640 def bindunixsocket(sock, path):
637 641 """Bind the UNIX domain socket to the specified path"""
638 642 # use relative path instead of full path at bind() if possible, since
639 643 # AF_UNIX path has very small length limit (107 chars) on common
640 644 # platforms (see sys/un.h)
641 645 dirname, basename = os.path.split(path)
642 646 bakwdfd = None
643 647 if dirname:
644 648 bakwdfd = os.open('.', os.O_DIRECTORY)
645 649 os.chdir(dirname)
646 650 sock.bind(basename)
647 651 if bakwdfd:
648 652 os.fchdir(bakwdfd)
649 653 os.close(bakwdfd)
@@ -1,472 +1,477 b''
1 1 # windows.py - Windows utility function implementations for Mercurial
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import errno
11 11 import msvcrt
12 12 import os
13 13 import re
14 14 import stat
15 15 import sys
16 16
17 17 from .i18n import _
18 18 from . import (
19 19 encoding,
20 error,
20 21 osutil,
21 22 pycompat,
22 23 win32,
23 24 )
24 25
25 26 try:
26 27 import _winreg as winreg
27 28 winreg.CloseKey
28 29 except ImportError:
29 30 import winreg
30 31
31 32 executablepath = win32.executablepath
32 33 getuser = win32.getuser
33 34 hidewindow = win32.hidewindow
34 35 makedir = win32.makedir
35 36 nlinks = win32.nlinks
36 37 oslink = win32.oslink
37 38 samedevice = win32.samedevice
38 39 samefile = win32.samefile
39 40 setsignalhandler = win32.setsignalhandler
40 41 spawndetached = win32.spawndetached
41 42 split = os.path.split
42 43 testpid = win32.testpid
43 44 unlink = win32.unlink
44 45
45 46 umask = 0o022
46 47
47 48 class mixedfilemodewrapper(object):
48 49 """Wraps a file handle when it is opened in read/write mode.
49 50
50 51 fopen() and fdopen() on Windows have a specific-to-Windows requirement
51 52 that files opened with mode r+, w+, or a+ make a call to a file positioning
52 53 function when switching between reads and writes. Without this extra call,
53 54 Python will raise a not very intuitive "IOError: [Errno 0] Error."
54 55
55 56 This class wraps posixfile instances when the file is opened in read/write
56 57 mode and automatically adds checks or inserts appropriate file positioning
57 58 calls when necessary.
58 59 """
59 60 OPNONE = 0
60 61 OPREAD = 1
61 62 OPWRITE = 2
62 63
63 64 def __init__(self, fp):
64 65 object.__setattr__(self, r'_fp', fp)
65 66 object.__setattr__(self, r'_lastop', 0)
66 67
67 68 def __enter__(self):
68 69 return self._fp.__enter__()
69 70
70 71 def __exit__(self, exc_type, exc_val, exc_tb):
71 72 self._fp.__exit__(exc_type, exc_val, exc_tb)
72 73
73 74 def __getattr__(self, name):
74 75 return getattr(self._fp, name)
75 76
76 77 def __setattr__(self, name, value):
77 78 return self._fp.__setattr__(name, value)
78 79
79 80 def _noopseek(self):
80 81 self._fp.seek(0, os.SEEK_CUR)
81 82
82 83 def seek(self, *args, **kwargs):
83 84 object.__setattr__(self, r'_lastop', self.OPNONE)
84 85 return self._fp.seek(*args, **kwargs)
85 86
86 87 def write(self, d):
87 88 if self._lastop == self.OPREAD:
88 89 self._noopseek()
89 90
90 91 object.__setattr__(self, r'_lastop', self.OPWRITE)
91 92 return self._fp.write(d)
92 93
93 94 def writelines(self, *args, **kwargs):
94 95 if self._lastop == self.OPREAD:
95 96 self._noopeseek()
96 97
97 98 object.__setattr__(self, r'_lastop', self.OPWRITE)
98 99 return self._fp.writelines(*args, **kwargs)
99 100
100 101 def read(self, *args, **kwargs):
101 102 if self._lastop == self.OPWRITE:
102 103 self._noopseek()
103 104
104 105 object.__setattr__(self, r'_lastop', self.OPREAD)
105 106 return self._fp.read(*args, **kwargs)
106 107
107 108 def readline(self, *args, **kwargs):
108 109 if self._lastop == self.OPWRITE:
109 110 self._noopseek()
110 111
111 112 object.__setattr__(self, r'_lastop', self.OPREAD)
112 113 return self._fp.readline(*args, **kwargs)
113 114
114 115 def readlines(self, *args, **kwargs):
115 116 if self._lastop == self.OPWRITE:
116 117 self._noopseek()
117 118
118 119 object.__setattr__(self, r'_lastop', self.OPREAD)
119 120 return self._fp.readlines(*args, **kwargs)
120 121
121 122 def posixfile(name, mode='r', buffering=-1):
122 123 '''Open a file with even more POSIX-like semantics'''
123 124 try:
124 125 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
125 126
126 127 # The position when opening in append mode is implementation defined, so
127 128 # make it consistent with other platforms, which position at EOF.
128 129 if 'a' in mode:
129 130 fp.seek(0, os.SEEK_END)
130 131
131 132 if '+' in mode:
132 133 return mixedfilemodewrapper(fp)
133 134
134 135 return fp
135 136 except WindowsError as err:
136 137 # convert to a friendlier exception
137 138 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
138 139
139 140 class winstdout(object):
140 141 '''stdout on windows misbehaves if sent through a pipe'''
141 142
142 143 def __init__(self, fp):
143 144 self.fp = fp
144 145
145 146 def __getattr__(self, key):
146 147 return getattr(self.fp, key)
147 148
148 149 def close(self):
149 150 try:
150 151 self.fp.close()
151 152 except IOError:
152 153 pass
153 154
154 155 def write(self, s):
155 156 try:
156 157 # This is workaround for "Not enough space" error on
157 158 # writing large size of data to console.
158 159 limit = 16000
159 160 l = len(s)
160 161 start = 0
161 162 self.softspace = 0
162 163 while start < l:
163 164 end = start + limit
164 165 self.fp.write(s[start:end])
165 166 start = end
166 167 except IOError as inst:
167 168 if inst.errno != 0:
168 169 raise
169 170 self.close()
170 171 raise IOError(errno.EPIPE, 'Broken pipe')
171 172
172 173 def flush(self):
173 174 try:
174 175 return self.fp.flush()
175 176 except IOError as inst:
176 177 if inst.errno != errno.EINVAL:
177 178 raise
178 179 self.close()
179 180 raise IOError(errno.EPIPE, 'Broken pipe')
180 181
181 182 def _is_win_9x():
182 183 '''return true if run on windows 95, 98 or me.'''
183 184 try:
184 185 return sys.getwindowsversion()[3] == 1
185 186 except AttributeError:
186 187 return 'command' in encoding.environ.get('comspec', '')
187 188
188 189 def openhardlinks():
189 190 return not _is_win_9x()
190 191
191 192 def parsepatchoutput(output_line):
192 193 """parses the output produced by patch and returns the filename"""
193 194 pf = output_line[14:]
194 195 if pf[0] == '`':
195 196 pf = pf[1:-1] # Remove the quotes
196 197 return pf
197 198
198 199 def sshargs(sshcmd, host, user, port):
199 200 '''Build argument list for ssh or Plink'''
200 201 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
201 202 args = user and ("%s@%s" % (user, host)) or host
203 if args.startswith('-') or args.startswith('/'):
204 raise error.Abort(
205 _('illegal ssh hostname or username starting with - or /: %s') %
206 args)
202 207 return port and ("%s %s %s" % (args, pflag, port)) or args
203 208
204 209 def setflags(f, l, x):
205 210 pass
206 211
207 212 def copymode(src, dst, mode=None):
208 213 pass
209 214
210 215 def checkexec(path):
211 216 return False
212 217
213 218 def checklink(path):
214 219 return False
215 220
216 221 def setbinary(fd):
217 222 # When run without console, pipes may expose invalid
218 223 # fileno(), usually set to -1.
219 224 fno = getattr(fd, 'fileno', None)
220 225 if fno is not None and fno() >= 0:
221 226 msvcrt.setmode(fno(), os.O_BINARY)
222 227
223 228 def pconvert(path):
224 229 return path.replace(pycompat.ossep, '/')
225 230
226 231 def localpath(path):
227 232 return path.replace('/', '\\')
228 233
229 234 def normpath(path):
230 235 return pconvert(os.path.normpath(path))
231 236
232 237 def normcase(path):
233 238 return encoding.upper(path) # NTFS compares via upper()
234 239
235 240 # see posix.py for definitions
236 241 normcasespec = encoding.normcasespecs.upper
237 242 normcasefallback = encoding.upperfallback
238 243
239 244 def samestat(s1, s2):
240 245 return False
241 246
242 247 # A sequence of backslashes is special iff it precedes a double quote:
243 248 # - if there's an even number of backslashes, the double quote is not
244 249 # quoted (i.e. it ends the quoted region)
245 250 # - if there's an odd number of backslashes, the double quote is quoted
246 251 # - in both cases, every pair of backslashes is unquoted into a single
247 252 # backslash
248 253 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
249 254 # So, to quote a string, we must surround it in double quotes, double
250 255 # the number of backslashes that precede double quotes and add another
251 256 # backslash before every double quote (being careful with the double
252 257 # quote we've appended to the end)
253 258 _quotere = None
254 259 _needsshellquote = None
255 260 def shellquote(s):
256 261 r"""
257 262 >>> shellquote(r'C:\Users\xyz')
258 263 '"C:\\Users\\xyz"'
259 264 >>> shellquote(r'C:\Users\xyz/mixed')
260 265 '"C:\\Users\\xyz/mixed"'
261 266 >>> # Would be safe not to quote too, since it is all double backslashes
262 267 >>> shellquote(r'C:\\Users\\xyz')
263 268 '"C:\\\\Users\\\\xyz"'
264 269 >>> # But this must be quoted
265 270 >>> shellquote(r'C:\\Users\\xyz/abc')
266 271 '"C:\\\\Users\\\\xyz/abc"'
267 272 """
268 273 global _quotere
269 274 if _quotere is None:
270 275 _quotere = re.compile(r'(\\*)("|\\$)')
271 276 global _needsshellquote
272 277 if _needsshellquote is None:
273 278 # ":" is also treated as "safe character", because it is used as a part
274 279 # of path name on Windows. "\" is also part of a path name, but isn't
275 280 # safe because shlex.split() (kind of) treats it as an escape char and
276 281 # drops it. It will leave the next character, even if it is another
277 282 # "\".
278 283 _needsshellquote = re.compile(r'[^a-zA-Z0-9._:/-]').search
279 284 if s and not _needsshellquote(s) and not _quotere.search(s):
280 285 # "s" shouldn't have to be quoted
281 286 return s
282 287 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
283 288
284 289 def quotecommand(cmd):
285 290 """Build a command string suitable for os.popen* calls."""
286 291 if sys.version_info < (2, 7, 1):
287 292 # Python versions since 2.7.1 do this extra quoting themselves
288 293 return '"' + cmd + '"'
289 294 return cmd
290 295
291 296 def popen(command, mode='r'):
292 297 # Work around "popen spawned process may not write to stdout
293 298 # under windows"
294 299 # http://bugs.python.org/issue1366
295 300 command += " 2> %s" % os.devnull
296 301 return os.popen(quotecommand(command), mode)
297 302
298 303 def explainexit(code):
299 304 return _("exited with status %d") % code, code
300 305
301 306 # if you change this stub into a real check, please try to implement the
302 307 # username and groupname functions above, too.
303 308 def isowner(st):
304 309 return True
305 310
306 311 def findexe(command):
307 312 '''Find executable for command searching like cmd.exe does.
308 313 If command is a basename then PATH is searched for command.
309 314 PATH isn't searched if command is an absolute or relative path.
310 315 An extension from PATHEXT is found and added if not present.
311 316 If command isn't found None is returned.'''
312 317 pathext = encoding.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
313 318 pathexts = [ext for ext in pathext.lower().split(pycompat.ospathsep)]
314 319 if os.path.splitext(command)[1].lower() in pathexts:
315 320 pathexts = ['']
316 321
317 322 def findexisting(pathcommand):
318 323 'Will append extension (if needed) and return existing file'
319 324 for ext in pathexts:
320 325 executable = pathcommand + ext
321 326 if os.path.exists(executable):
322 327 return executable
323 328 return None
324 329
325 330 if pycompat.ossep in command:
326 331 return findexisting(command)
327 332
328 333 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
329 334 executable = findexisting(os.path.join(path, command))
330 335 if executable is not None:
331 336 return executable
332 337 return findexisting(os.path.expanduser(os.path.expandvars(command)))
333 338
334 339 _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
335 340
336 341 def statfiles(files):
337 342 '''Stat each file in files. Yield each stat, or None if a file
338 343 does not exist or has a type we don't care about.
339 344
340 345 Cluster and cache stat per directory to minimize number of OS stat calls.'''
341 346 dircache = {} # dirname -> filename -> status | None if file does not exist
342 347 getkind = stat.S_IFMT
343 348 for nf in files:
344 349 nf = normcase(nf)
345 350 dir, base = os.path.split(nf)
346 351 if not dir:
347 352 dir = '.'
348 353 cache = dircache.get(dir, None)
349 354 if cache is None:
350 355 try:
351 356 dmap = dict([(normcase(n), s)
352 357 for n, k, s in osutil.listdir(dir, True)
353 358 if getkind(s.st_mode) in _wantedkinds])
354 359 except OSError as err:
355 360 # Python >= 2.5 returns ENOENT and adds winerror field
356 361 # EINVAL is raised if dir is not a directory.
357 362 if err.errno not in (errno.ENOENT, errno.EINVAL,
358 363 errno.ENOTDIR):
359 364 raise
360 365 dmap = {}
361 366 cache = dircache.setdefault(dir, dmap)
362 367 yield cache.get(base, None)
363 368
364 369 def username(uid=None):
365 370 """Return the name of the user with the given uid.
366 371
367 372 If uid is None, return the name of the current user."""
368 373 return None
369 374
370 375 def groupname(gid=None):
371 376 """Return the name of the group with the given gid.
372 377
373 378 If gid is None, return the name of the current group."""
374 379 return None
375 380
376 381 def removedirs(name):
377 382 """special version of os.removedirs that does not remove symlinked
378 383 directories or junction points if they actually contain files"""
379 384 if osutil.listdir(name):
380 385 return
381 386 os.rmdir(name)
382 387 head, tail = os.path.split(name)
383 388 if not tail:
384 389 head, tail = os.path.split(head)
385 390 while head and tail:
386 391 try:
387 392 if osutil.listdir(head):
388 393 return
389 394 os.rmdir(head)
390 395 except (ValueError, OSError):
391 396 break
392 397 head, tail = os.path.split(head)
393 398
394 399 def rename(src, dst):
395 400 '''atomically rename file src to dst, replacing dst if it exists'''
396 401 try:
397 402 os.rename(src, dst)
398 403 except OSError as e:
399 404 if e.errno != errno.EEXIST:
400 405 raise
401 406 unlink(dst)
402 407 os.rename(src, dst)
403 408
404 409 def gethgcmd():
405 410 return [sys.executable] + sys.argv[:1]
406 411
407 412 def groupmembers(name):
408 413 # Don't support groups on Windows for now
409 414 raise KeyError
410 415
411 416 def isexec(f):
412 417 return False
413 418
414 419 class cachestat(object):
415 420 def __init__(self, path):
416 421 pass
417 422
418 423 def cacheable(self):
419 424 return False
420 425
421 426 def lookupreg(key, valname=None, scope=None):
422 427 ''' Look up a key/value name in the Windows registry.
423 428
424 429 valname: value name. If unspecified, the default value for the key
425 430 is used.
426 431 scope: optionally specify scope for registry lookup, this can be
427 432 a sequence of scopes to look up in order. Default (CURRENT_USER,
428 433 LOCAL_MACHINE).
429 434 '''
430 435 if scope is None:
431 436 scope = (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE)
432 437 elif not isinstance(scope, (list, tuple)):
433 438 scope = (scope,)
434 439 for s in scope:
435 440 try:
436 441 val = winreg.QueryValueEx(winreg.OpenKey(s, key), valname)[0]
437 442 # never let a Unicode string escape into the wild
438 443 return encoding.unitolocal(val)
439 444 except EnvironmentError:
440 445 pass
441 446
442 447 expandglobs = True
443 448
444 449 def statislink(st):
445 450 '''check whether a stat result is a symlink'''
446 451 return False
447 452
448 453 def statisexec(st):
449 454 '''check whether a stat result is an executable file'''
450 455 return False
451 456
452 457 def poll(fds):
453 458 # see posix.py for description
454 459 raise NotImplementedError()
455 460
456 461 def readpipe(pipe):
457 462 """Read all available data from a pipe."""
458 463 chunks = []
459 464 while True:
460 465 size = win32.peekpipe(pipe)
461 466 if not size:
462 467 break
463 468
464 469 s = pipe.read(size)
465 470 if not s:
466 471 break
467 472 chunks.append(s)
468 473
469 474 return ''.join(chunks)
470 475
471 476 def bindunixsocket(sock, path):
472 477 raise NotImplementedError('unsupported platform')
General Comments 0
You need to be logged in to leave comments. Login now