##// END OF EJS Templates
py3: convert os.devnull to bytes using pycompat.bytestr...
Pulkit Goyal -
r36475:0e8b7664 default
parent child Browse files
Show More
@@ -1,495 +1,495 b''
1 1 # common.py - common code for the convert extension
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 from __future__ import absolute_import
8 8
9 9 import base64
10 10 import datetime
11 11 import errno
12 12 import os
13 13 import re
14 14 import subprocess
15 15
16 16 from mercurial.i18n import _
17 17 from mercurial import (
18 18 encoding,
19 19 error,
20 20 phases,
21 21 pycompat,
22 22 util,
23 23 )
24 24
25 25 pickle = util.pickle
26 26 propertycache = util.propertycache
27 27
28 28 def encodeargs(args):
29 29 def encodearg(s):
30 30 lines = base64.encodestring(s)
31 31 lines = [l.splitlines()[0] for l in lines]
32 32 return ''.join(lines)
33 33
34 34 s = pickle.dumps(args)
35 35 return encodearg(s)
36 36
37 37 def decodeargs(s):
38 38 s = base64.decodestring(s)
39 39 return pickle.loads(s)
40 40
41 41 class MissingTool(Exception):
42 42 pass
43 43
44 44 def checktool(exe, name=None, abort=True):
45 45 name = name or exe
46 46 if not util.findexe(exe):
47 47 if abort:
48 48 exc = error.Abort
49 49 else:
50 50 exc = MissingTool
51 51 raise exc(_('cannot find required "%s" tool') % name)
52 52
53 53 class NoRepo(Exception):
54 54 pass
55 55
56 56 SKIPREV = 'SKIP'
57 57
58 58 class commit(object):
59 59 def __init__(self, author, date, desc, parents, branch=None, rev=None,
60 60 extra=None, sortkey=None, saverev=True, phase=phases.draft,
61 61 optparents=None):
62 62 self.author = author or 'unknown'
63 63 self.date = date or '0 0'
64 64 self.desc = desc
65 65 self.parents = parents # will be converted and used as parents
66 66 self.optparents = optparents or [] # will be used if already converted
67 67 self.branch = branch
68 68 self.rev = rev
69 69 self.extra = extra or {}
70 70 self.sortkey = sortkey
71 71 self.saverev = saverev
72 72 self.phase = phase
73 73
74 74 class converter_source(object):
75 75 """Conversion source interface"""
76 76
77 77 def __init__(self, ui, repotype, path=None, revs=None):
78 78 """Initialize conversion source (or raise NoRepo("message")
79 79 exception if path is not a valid repository)"""
80 80 self.ui = ui
81 81 self.path = path
82 82 self.revs = revs
83 83 self.repotype = repotype
84 84
85 85 self.encoding = 'utf-8'
86 86
87 87 def checkhexformat(self, revstr, mapname='splicemap'):
88 88 """ fails if revstr is not a 40 byte hex. mercurial and git both uses
89 89 such format for their revision numbering
90 90 """
91 91 if not re.match(r'[0-9a-fA-F]{40,40}$', revstr):
92 92 raise error.Abort(_('%s entry %s is not a valid revision'
93 93 ' identifier') % (mapname, revstr))
94 94
95 95 def before(self):
96 96 pass
97 97
98 98 def after(self):
99 99 pass
100 100
101 101 def targetfilebelongstosource(self, targetfilename):
102 102 """Returns true if the given targetfile belongs to the source repo. This
103 103 is useful when only a subdirectory of the target belongs to the source
104 104 repo."""
105 105 # For normal full repo converts, this is always True.
106 106 return True
107 107
108 108 def setrevmap(self, revmap):
109 109 """set the map of already-converted revisions"""
110 110
111 111 def getheads(self):
112 112 """Return a list of this repository's heads"""
113 113 raise NotImplementedError
114 114
115 115 def getfile(self, name, rev):
116 116 """Return a pair (data, mode) where data is the file content
117 117 as a string and mode one of '', 'x' or 'l'. rev is the
118 118 identifier returned by a previous call to getchanges().
119 119 Data is None if file is missing/deleted in rev.
120 120 """
121 121 raise NotImplementedError
122 122
123 123 def getchanges(self, version, full):
124 124 """Returns a tuple of (files, copies, cleanp2).
125 125
126 126 files is a sorted list of (filename, id) tuples for all files
127 127 changed between version and its first parent returned by
128 128 getcommit(). If full, all files in that revision is returned.
129 129 id is the source revision id of the file.
130 130
131 131 copies is a dictionary of dest: source
132 132
133 133 cleanp2 is the set of files filenames that are clean against p2.
134 134 (Files that are clean against p1 are already not in files (unless
135 135 full). This makes it possible to handle p2 clean files similarly.)
136 136 """
137 137 raise NotImplementedError
138 138
139 139 def getcommit(self, version):
140 140 """Return the commit object for version"""
141 141 raise NotImplementedError
142 142
143 143 def numcommits(self):
144 144 """Return the number of commits in this source.
145 145
146 146 If unknown, return None.
147 147 """
148 148 return None
149 149
150 150 def gettags(self):
151 151 """Return the tags as a dictionary of name: revision
152 152
153 153 Tag names must be UTF-8 strings.
154 154 """
155 155 raise NotImplementedError
156 156
157 157 def recode(self, s, encoding=None):
158 158 if not encoding:
159 159 encoding = self.encoding or 'utf-8'
160 160
161 161 if isinstance(s, unicode):
162 162 return s.encode("utf-8")
163 163 try:
164 164 return s.decode(encoding).encode("utf-8")
165 165 except UnicodeError:
166 166 try:
167 167 return s.decode("latin-1").encode("utf-8")
168 168 except UnicodeError:
169 169 return s.decode(encoding, "replace").encode("utf-8")
170 170
171 171 def getchangedfiles(self, rev, i):
172 172 """Return the files changed by rev compared to parent[i].
173 173
174 174 i is an index selecting one of the parents of rev. The return
175 175 value should be the list of files that are different in rev and
176 176 this parent.
177 177
178 178 If rev has no parents, i is None.
179 179
180 180 This function is only needed to support --filemap
181 181 """
182 182 raise NotImplementedError
183 183
184 184 def converted(self, rev, sinkrev):
185 185 '''Notify the source that a revision has been converted.'''
186 186
187 187 def hasnativeorder(self):
188 188 """Return true if this source has a meaningful, native revision
189 189 order. For instance, Mercurial revisions are store sequentially
190 190 while there is no such global ordering with Darcs.
191 191 """
192 192 return False
193 193
194 194 def hasnativeclose(self):
195 195 """Return true if this source has ability to close branch.
196 196 """
197 197 return False
198 198
199 199 def lookuprev(self, rev):
200 200 """If rev is a meaningful revision reference in source, return
201 201 the referenced identifier in the same format used by getcommit().
202 202 return None otherwise.
203 203 """
204 204 return None
205 205
206 206 def getbookmarks(self):
207 207 """Return the bookmarks as a dictionary of name: revision
208 208
209 209 Bookmark names are to be UTF-8 strings.
210 210 """
211 211 return {}
212 212
213 213 def checkrevformat(self, revstr, mapname='splicemap'):
214 214 """revstr is a string that describes a revision in the given
215 215 source control system. Return true if revstr has correct
216 216 format.
217 217 """
218 218 return True
219 219
220 220 class converter_sink(object):
221 221 """Conversion sink (target) interface"""
222 222
223 223 def __init__(self, ui, repotype, path):
224 224 """Initialize conversion sink (or raise NoRepo("message")
225 225 exception if path is not a valid repository)
226 226
227 227 created is a list of paths to remove if a fatal error occurs
228 228 later"""
229 229 self.ui = ui
230 230 self.path = path
231 231 self.created = []
232 232 self.repotype = repotype
233 233
234 234 def revmapfile(self):
235 235 """Path to a file that will contain lines
236 236 source_rev_id sink_rev_id
237 237 mapping equivalent revision identifiers for each system."""
238 238 raise NotImplementedError
239 239
240 240 def authorfile(self):
241 241 """Path to a file that will contain lines
242 242 srcauthor=dstauthor
243 243 mapping equivalent authors identifiers for each system."""
244 244 return None
245 245
246 246 def putcommit(self, files, copies, parents, commit, source, revmap, full,
247 247 cleanp2):
248 248 """Create a revision with all changed files listed in 'files'
249 249 and having listed parents. 'commit' is a commit object
250 250 containing at a minimum the author, date, and message for this
251 251 changeset. 'files' is a list of (path, version) tuples,
252 252 'copies' is a dictionary mapping destinations to sources,
253 253 'source' is the source repository, and 'revmap' is a mapfile
254 254 of source revisions to converted revisions. Only getfile() and
255 255 lookuprev() should be called on 'source'. 'full' means that 'files'
256 256 is complete and all other files should be removed.
257 257 'cleanp2' is a set of the filenames that are unchanged from p2
258 258 (only in the common merge case where there two parents).
259 259
260 260 Note that the sink repository is not told to update itself to
261 261 a particular revision (or even what that revision would be)
262 262 before it receives the file data.
263 263 """
264 264 raise NotImplementedError
265 265
266 266 def puttags(self, tags):
267 267 """Put tags into sink.
268 268
269 269 tags: {tagname: sink_rev_id, ...} where tagname is an UTF-8 string.
270 270 Return a pair (tag_revision, tag_parent_revision), or (None, None)
271 271 if nothing was changed.
272 272 """
273 273 raise NotImplementedError
274 274
275 275 def setbranch(self, branch, pbranches):
276 276 """Set the current branch name. Called before the first putcommit
277 277 on the branch.
278 278 branch: branch name for subsequent commits
279 279 pbranches: (converted parent revision, parent branch) tuples"""
280 280
281 281 def setfilemapmode(self, active):
282 282 """Tell the destination that we're using a filemap
283 283
284 284 Some converter_sources (svn in particular) can claim that a file
285 285 was changed in a revision, even if there was no change. This method
286 286 tells the destination that we're using a filemap and that it should
287 287 filter empty revisions.
288 288 """
289 289
290 290 def before(self):
291 291 pass
292 292
293 293 def after(self):
294 294 pass
295 295
296 296 def putbookmarks(self, bookmarks):
297 297 """Put bookmarks into sink.
298 298
299 299 bookmarks: {bookmarkname: sink_rev_id, ...}
300 300 where bookmarkname is an UTF-8 string.
301 301 """
302 302
303 303 def hascommitfrommap(self, rev):
304 304 """Return False if a rev mentioned in a filemap is known to not be
305 305 present."""
306 306 raise NotImplementedError
307 307
308 308 def hascommitforsplicemap(self, rev):
309 309 """This method is for the special needs for splicemap handling and not
310 310 for general use. Returns True if the sink contains rev, aborts on some
311 311 special cases."""
312 312 raise NotImplementedError
313 313
314 314 class commandline(object):
315 315 def __init__(self, ui, command):
316 316 self.ui = ui
317 317 self.command = command
318 318
319 319 def prerun(self):
320 320 pass
321 321
322 322 def postrun(self):
323 323 pass
324 324
325 325 def _cmdline(self, cmd, *args, **kwargs):
326 326 kwargs = pycompat.byteskwargs(kwargs)
327 327 cmdline = [self.command, cmd] + list(args)
328 328 for k, v in kwargs.iteritems():
329 329 if len(k) == 1:
330 330 cmdline.append('-' + k)
331 331 else:
332 332 cmdline.append('--' + k.replace('_', '-'))
333 333 try:
334 334 if len(k) == 1:
335 335 cmdline.append('' + v)
336 336 else:
337 337 cmdline[-1] += '=' + v
338 338 except TypeError:
339 339 pass
340 340 cmdline = [util.shellquote(arg) for arg in cmdline]
341 341 if not self.ui.debugflag:
342 cmdline += ['2>', os.devnull]
342 cmdline += ['2>', pycompat.bytestr(os.devnull)]
343 343 cmdline = ' '.join(cmdline)
344 344 return cmdline
345 345
346 346 def _run(self, cmd, *args, **kwargs):
347 347 def popen(cmdline):
348 348 p = subprocess.Popen(cmdline, shell=True, bufsize=-1,
349 349 close_fds=util.closefds,
350 350 stdout=subprocess.PIPE)
351 351 return p
352 352 return self._dorun(popen, cmd, *args, **kwargs)
353 353
354 354 def _run2(self, cmd, *args, **kwargs):
355 355 return self._dorun(util.popen2, cmd, *args, **kwargs)
356 356
357 357 def _run3(self, cmd, *args, **kwargs):
358 358 return self._dorun(util.popen3, cmd, *args, **kwargs)
359 359
360 360 def _dorun(self, openfunc, cmd, *args, **kwargs):
361 361 cmdline = self._cmdline(cmd, *args, **kwargs)
362 362 self.ui.debug('running: %s\n' % (cmdline,))
363 363 self.prerun()
364 364 try:
365 365 return openfunc(cmdline)
366 366 finally:
367 367 self.postrun()
368 368
369 369 def run(self, cmd, *args, **kwargs):
370 370 p = self._run(cmd, *args, **kwargs)
371 371 output = p.communicate()[0]
372 372 self.ui.debug(output)
373 373 return output, p.returncode
374 374
375 375 def runlines(self, cmd, *args, **kwargs):
376 376 p = self._run(cmd, *args, **kwargs)
377 377 output = p.stdout.readlines()
378 378 p.wait()
379 379 self.ui.debug(''.join(output))
380 380 return output, p.returncode
381 381
382 382 def checkexit(self, status, output=''):
383 383 if status:
384 384 if output:
385 385 self.ui.warn(_('%s error:\n') % self.command)
386 386 self.ui.warn(output)
387 387 msg = util.explainexit(status)[0]
388 388 raise error.Abort('%s %s' % (self.command, msg))
389 389
390 390 def run0(self, cmd, *args, **kwargs):
391 391 output, status = self.run(cmd, *args, **kwargs)
392 392 self.checkexit(status, output)
393 393 return output
394 394
395 395 def runlines0(self, cmd, *args, **kwargs):
396 396 output, status = self.runlines(cmd, *args, **kwargs)
397 397 self.checkexit(status, ''.join(output))
398 398 return output
399 399
400 400 @propertycache
401 401 def argmax(self):
402 402 # POSIX requires at least 4096 bytes for ARG_MAX
403 403 argmax = 4096
404 404 try:
405 405 argmax = os.sysconf("SC_ARG_MAX")
406 406 except (AttributeError, ValueError):
407 407 pass
408 408
409 409 # Windows shells impose their own limits on command line length,
410 410 # down to 2047 bytes for cmd.exe under Windows NT/2k and 2500 bytes
411 411 # for older 4nt.exe. See http://support.microsoft.com/kb/830473 for
412 412 # details about cmd.exe limitations.
413 413
414 414 # Since ARG_MAX is for command line _and_ environment, lower our limit
415 415 # (and make happy Windows shells while doing this).
416 416 return argmax // 2 - 1
417 417
418 418 def _limit_arglist(self, arglist, cmd, *args, **kwargs):
419 419 cmdlen = len(self._cmdline(cmd, *args, **kwargs))
420 420 limit = self.argmax - cmdlen
421 421 numbytes = 0
422 422 fl = []
423 423 for fn in arglist:
424 424 b = len(fn) + 3
425 425 if numbytes + b < limit or len(fl) == 0:
426 426 fl.append(fn)
427 427 numbytes += b
428 428 else:
429 429 yield fl
430 430 fl = [fn]
431 431 numbytes = b
432 432 if fl:
433 433 yield fl
434 434
435 435 def xargs(self, arglist, cmd, *args, **kwargs):
436 436 for l in self._limit_arglist(arglist, cmd, *args, **kwargs):
437 437 self.run0(cmd, *(list(args) + l), **kwargs)
438 438
439 439 class mapfile(dict):
440 440 def __init__(self, ui, path):
441 441 super(mapfile, self).__init__()
442 442 self.ui = ui
443 443 self.path = path
444 444 self.fp = None
445 445 self.order = []
446 446 self._read()
447 447
448 448 def _read(self):
449 449 if not self.path:
450 450 return
451 451 try:
452 452 fp = open(self.path, 'rb')
453 453 except IOError as err:
454 454 if err.errno != errno.ENOENT:
455 455 raise
456 456 return
457 457 for i, line in enumerate(util.iterfile(fp)):
458 458 line = line.splitlines()[0].rstrip()
459 459 if not line:
460 460 # Ignore blank lines
461 461 continue
462 462 try:
463 463 key, value = line.rsplit(' ', 1)
464 464 except ValueError:
465 465 raise error.Abort(
466 466 _('syntax error in %s(%d): key/value pair expected')
467 467 % (self.path, i + 1))
468 468 if key not in self:
469 469 self.order.append(key)
470 470 super(mapfile, self).__setitem__(key, value)
471 471 fp.close()
472 472
473 473 def __setitem__(self, key, value):
474 474 if self.fp is None:
475 475 try:
476 476 self.fp = open(self.path, 'ab')
477 477 except IOError as err:
478 478 raise error.Abort(
479 479 _('could not open map file %r: %s') %
480 480 (self.path, encoding.strtolocal(err.strerror)))
481 481 self.fp.write(util.tonativeeol('%s %s\n' % (key, value)))
482 482 self.fp.flush()
483 483 super(mapfile, self).__setitem__(key, value)
484 484
485 485 def close(self):
486 486 if self.fp:
487 487 self.fp.close()
488 488 self.fp = None
489 489
490 490 def makedatetimestamp(t):
491 491 """Like util.makedate() but for time t instead of current time"""
492 492 delta = (datetime.datetime.utcfromtimestamp(t) -
493 493 datetime.datetime.fromtimestamp(t))
494 494 tz = delta.days * 86400 + delta.seconds
495 495 return t, tz
@@ -1,496 +1,496 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 20 error,
21 21 policy,
22 22 pycompat,
23 23 win32,
24 24 )
25 25
26 26 try:
27 27 import _winreg as winreg
28 28 winreg.CloseKey
29 29 except ImportError:
30 30 import winreg
31 31
32 32 osutil = policy.importmod(r'osutil')
33 33
34 34 executablepath = win32.executablepath
35 35 getfsmountpoint = win32.getvolumename
36 36 getfstype = win32.getfstype
37 37 getuser = win32.getuser
38 38 hidewindow = win32.hidewindow
39 39 makedir = win32.makedir
40 40 nlinks = win32.nlinks
41 41 oslink = win32.oslink
42 42 samedevice = win32.samedevice
43 43 samefile = win32.samefile
44 44 setsignalhandler = win32.setsignalhandler
45 45 spawndetached = win32.spawndetached
46 46 split = os.path.split
47 47 testpid = win32.testpid
48 48 unlink = win32.unlink
49 49
50 50 umask = 0o022
51 51
52 52 class mixedfilemodewrapper(object):
53 53 """Wraps a file handle when it is opened in read/write mode.
54 54
55 55 fopen() and fdopen() on Windows have a specific-to-Windows requirement
56 56 that files opened with mode r+, w+, or a+ make a call to a file positioning
57 57 function when switching between reads and writes. Without this extra call,
58 58 Python will raise a not very intuitive "IOError: [Errno 0] Error."
59 59
60 60 This class wraps posixfile instances when the file is opened in read/write
61 61 mode and automatically adds checks or inserts appropriate file positioning
62 62 calls when necessary.
63 63 """
64 64 OPNONE = 0
65 65 OPREAD = 1
66 66 OPWRITE = 2
67 67
68 68 def __init__(self, fp):
69 69 object.__setattr__(self, r'_fp', fp)
70 70 object.__setattr__(self, r'_lastop', 0)
71 71
72 72 def __enter__(self):
73 73 return self._fp.__enter__()
74 74
75 75 def __exit__(self, exc_type, exc_val, exc_tb):
76 76 self._fp.__exit__(exc_type, exc_val, exc_tb)
77 77
78 78 def __getattr__(self, name):
79 79 return getattr(self._fp, name)
80 80
81 81 def __setattr__(self, name, value):
82 82 return self._fp.__setattr__(name, value)
83 83
84 84 def _noopseek(self):
85 85 self._fp.seek(0, os.SEEK_CUR)
86 86
87 87 def seek(self, *args, **kwargs):
88 88 object.__setattr__(self, r'_lastop', self.OPNONE)
89 89 return self._fp.seek(*args, **kwargs)
90 90
91 91 def write(self, d):
92 92 if self._lastop == self.OPREAD:
93 93 self._noopseek()
94 94
95 95 object.__setattr__(self, r'_lastop', self.OPWRITE)
96 96 return self._fp.write(d)
97 97
98 98 def writelines(self, *args, **kwargs):
99 99 if self._lastop == self.OPREAD:
100 100 self._noopeseek()
101 101
102 102 object.__setattr__(self, r'_lastop', self.OPWRITE)
103 103 return self._fp.writelines(*args, **kwargs)
104 104
105 105 def read(self, *args, **kwargs):
106 106 if self._lastop == self.OPWRITE:
107 107 self._noopseek()
108 108
109 109 object.__setattr__(self, r'_lastop', self.OPREAD)
110 110 return self._fp.read(*args, **kwargs)
111 111
112 112 def readline(self, *args, **kwargs):
113 113 if self._lastop == self.OPWRITE:
114 114 self._noopseek()
115 115
116 116 object.__setattr__(self, r'_lastop', self.OPREAD)
117 117 return self._fp.readline(*args, **kwargs)
118 118
119 119 def readlines(self, *args, **kwargs):
120 120 if self._lastop == self.OPWRITE:
121 121 self._noopseek()
122 122
123 123 object.__setattr__(self, r'_lastop', self.OPREAD)
124 124 return self._fp.readlines(*args, **kwargs)
125 125
126 126 def posixfile(name, mode='r', buffering=-1):
127 127 '''Open a file with even more POSIX-like semantics'''
128 128 try:
129 129 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
130 130
131 131 # The position when opening in append mode is implementation defined, so
132 132 # make it consistent with other platforms, which position at EOF.
133 133 if 'a' in mode:
134 134 fp.seek(0, os.SEEK_END)
135 135
136 136 if '+' in mode:
137 137 return mixedfilemodewrapper(fp)
138 138
139 139 return fp
140 140 except WindowsError as err:
141 141 # convert to a friendlier exception
142 142 raise IOError(err.errno, '%s: %s' % (
143 143 name, encoding.strtolocal(err.strerror)))
144 144
145 145 # may be wrapped by win32mbcs extension
146 146 listdir = osutil.listdir
147 147
148 148 class winstdout(object):
149 149 '''stdout on windows misbehaves if sent through a pipe'''
150 150
151 151 def __init__(self, fp):
152 152 self.fp = fp
153 153
154 154 def __getattr__(self, key):
155 155 return getattr(self.fp, key)
156 156
157 157 def close(self):
158 158 try:
159 159 self.fp.close()
160 160 except IOError:
161 161 pass
162 162
163 163 def write(self, s):
164 164 try:
165 165 # This is workaround for "Not enough space" error on
166 166 # writing large size of data to console.
167 167 limit = 16000
168 168 l = len(s)
169 169 start = 0
170 170 self.softspace = 0
171 171 while start < l:
172 172 end = start + limit
173 173 self.fp.write(s[start:end])
174 174 start = end
175 175 except IOError as inst:
176 176 if inst.errno != 0:
177 177 raise
178 178 self.close()
179 179 raise IOError(errno.EPIPE, 'Broken pipe')
180 180
181 181 def flush(self):
182 182 try:
183 183 return self.fp.flush()
184 184 except IOError as inst:
185 185 if inst.errno != errno.EINVAL:
186 186 raise
187 187 raise IOError(errno.EPIPE, 'Broken pipe')
188 188
189 189 def _is_win_9x():
190 190 '''return true if run on windows 95, 98 or me.'''
191 191 try:
192 192 return sys.getwindowsversion()[3] == 1
193 193 except AttributeError:
194 194 return 'command' in encoding.environ.get('comspec', '')
195 195
196 196 def openhardlinks():
197 197 return not _is_win_9x()
198 198
199 199 def parsepatchoutput(output_line):
200 200 """parses the output produced by patch and returns the filename"""
201 201 pf = output_line[14:]
202 202 if pf[0] == '`':
203 203 pf = pf[1:-1] # Remove the quotes
204 204 return pf
205 205
206 206 def sshargs(sshcmd, host, user, port):
207 207 '''Build argument list for ssh or Plink'''
208 208 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
209 209 args = user and ("%s@%s" % (user, host)) or host
210 210 if args.startswith('-') or args.startswith('/'):
211 211 raise error.Abort(
212 212 _('illegal ssh hostname or username starting with - or /: %s') %
213 213 args)
214 214 args = shellquote(args)
215 215 if port:
216 216 args = '%s %s %s' % (pflag, shellquote(port), args)
217 217 return args
218 218
219 219 def setflags(f, l, x):
220 220 pass
221 221
222 222 def copymode(src, dst, mode=None):
223 223 pass
224 224
225 225 def checkexec(path):
226 226 return False
227 227
228 228 def checklink(path):
229 229 return False
230 230
231 231 def setbinary(fd):
232 232 # When run without console, pipes may expose invalid
233 233 # fileno(), usually set to -1.
234 234 fno = getattr(fd, 'fileno', None)
235 235 if fno is not None and fno() >= 0:
236 236 msvcrt.setmode(fno(), os.O_BINARY)
237 237
238 238 def pconvert(path):
239 239 return path.replace(pycompat.ossep, '/')
240 240
241 241 def localpath(path):
242 242 return path.replace('/', '\\')
243 243
244 244 def normpath(path):
245 245 return pconvert(os.path.normpath(path))
246 246
247 247 def normcase(path):
248 248 return encoding.upper(path) # NTFS compares via upper()
249 249
250 250 # see posix.py for definitions
251 251 normcasespec = encoding.normcasespecs.upper
252 252 normcasefallback = encoding.upperfallback
253 253
254 254 def samestat(s1, s2):
255 255 return False
256 256
257 257 # A sequence of backslashes is special iff it precedes a double quote:
258 258 # - if there's an even number of backslashes, the double quote is not
259 259 # quoted (i.e. it ends the quoted region)
260 260 # - if there's an odd number of backslashes, the double quote is quoted
261 261 # - in both cases, every pair of backslashes is unquoted into a single
262 262 # backslash
263 263 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
264 264 # So, to quote a string, we must surround it in double quotes, double
265 265 # the number of backslashes that precede double quotes and add another
266 266 # backslash before every double quote (being careful with the double
267 267 # quote we've appended to the end)
268 268 _quotere = None
269 269 _needsshellquote = None
270 270 def shellquote(s):
271 271 r"""
272 272 >>> shellquote(br'C:\Users\xyz')
273 273 '"C:\\Users\\xyz"'
274 274 >>> shellquote(br'C:\Users\xyz/mixed')
275 275 '"C:\\Users\\xyz/mixed"'
276 276 >>> # Would be safe not to quote too, since it is all double backslashes
277 277 >>> shellquote(br'C:\\Users\\xyz')
278 278 '"C:\\\\Users\\\\xyz"'
279 279 >>> # But this must be quoted
280 280 >>> shellquote(br'C:\\Users\\xyz/abc')
281 281 '"C:\\\\Users\\\\xyz/abc"'
282 282 """
283 283 global _quotere
284 284 if _quotere is None:
285 285 _quotere = re.compile(r'(\\*)("|\\$)')
286 286 global _needsshellquote
287 287 if _needsshellquote is None:
288 288 # ":" is also treated as "safe character", because it is used as a part
289 289 # of path name on Windows. "\" is also part of a path name, but isn't
290 290 # safe because shlex.split() (kind of) treats it as an escape char and
291 291 # drops it. It will leave the next character, even if it is another
292 292 # "\".
293 293 _needsshellquote = re.compile(r'[^a-zA-Z0-9._:/-]').search
294 294 if s and not _needsshellquote(s) and not _quotere.search(s):
295 295 # "s" shouldn't have to be quoted
296 296 return s
297 297 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
298 298
299 299 def _unquote(s):
300 300 if s.startswith(b'"') and s.endswith(b'"'):
301 301 return s[1:-1]
302 302 return s
303 303
304 304 def shellsplit(s):
305 305 """Parse a command string in cmd.exe way (best-effort)"""
306 306 return pycompat.maplist(_unquote, pycompat.shlexsplit(s, posix=False))
307 307
308 308 def quotecommand(cmd):
309 309 """Build a command string suitable for os.popen* calls."""
310 310 if sys.version_info < (2, 7, 1):
311 311 # Python versions since 2.7.1 do this extra quoting themselves
312 312 return '"' + cmd + '"'
313 313 return cmd
314 314
315 315 def popen(command, mode='r'):
316 316 # Work around "popen spawned process may not write to stdout
317 317 # under windows"
318 318 # http://bugs.python.org/issue1366
319 command += " 2> %s" % os.devnull
319 command += " 2> %s" % pycompat.bytestr(os.devnull)
320 320 return os.popen(quotecommand(command), mode)
321 321
322 322 def explainexit(code):
323 323 return _("exited with status %d") % code, code
324 324
325 325 # if you change this stub into a real check, please try to implement the
326 326 # username and groupname functions above, too.
327 327 def isowner(st):
328 328 return True
329 329
330 330 def findexe(command):
331 331 '''Find executable for command searching like cmd.exe does.
332 332 If command is a basename then PATH is searched for command.
333 333 PATH isn't searched if command is an absolute or relative path.
334 334 An extension from PATHEXT is found and added if not present.
335 335 If command isn't found None is returned.'''
336 336 pathext = encoding.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
337 337 pathexts = [ext for ext in pathext.lower().split(pycompat.ospathsep)]
338 338 if os.path.splitext(command)[1].lower() in pathexts:
339 339 pathexts = ['']
340 340
341 341 def findexisting(pathcommand):
342 342 'Will append extension (if needed) and return existing file'
343 343 for ext in pathexts:
344 344 executable = pathcommand + ext
345 345 if os.path.exists(executable):
346 346 return executable
347 347 return None
348 348
349 349 if pycompat.ossep in command:
350 350 return findexisting(command)
351 351
352 352 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
353 353 executable = findexisting(os.path.join(path, command))
354 354 if executable is not None:
355 355 return executable
356 356 return findexisting(os.path.expanduser(os.path.expandvars(command)))
357 357
358 358 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
359 359
360 360 def statfiles(files):
361 361 '''Stat each file in files. Yield each stat, or None if a file
362 362 does not exist or has a type we don't care about.
363 363
364 364 Cluster and cache stat per directory to minimize number of OS stat calls.'''
365 365 dircache = {} # dirname -> filename -> status | None if file does not exist
366 366 getkind = stat.S_IFMT
367 367 for nf in files:
368 368 nf = normcase(nf)
369 369 dir, base = os.path.split(nf)
370 370 if not dir:
371 371 dir = '.'
372 372 cache = dircache.get(dir, None)
373 373 if cache is None:
374 374 try:
375 375 dmap = dict([(normcase(n), s)
376 376 for n, k, s in listdir(dir, True)
377 377 if getkind(s.st_mode) in _wantedkinds])
378 378 except OSError as err:
379 379 # Python >= 2.5 returns ENOENT and adds winerror field
380 380 # EINVAL is raised if dir is not a directory.
381 381 if err.errno not in (errno.ENOENT, errno.EINVAL,
382 382 errno.ENOTDIR):
383 383 raise
384 384 dmap = {}
385 385 cache = dircache.setdefault(dir, dmap)
386 386 yield cache.get(base, None)
387 387
388 388 def username(uid=None):
389 389 """Return the name of the user with the given uid.
390 390
391 391 If uid is None, return the name of the current user."""
392 392 return None
393 393
394 394 def groupname(gid=None):
395 395 """Return the name of the group with the given gid.
396 396
397 397 If gid is None, return the name of the current group."""
398 398 return None
399 399
400 400 def removedirs(name):
401 401 """special version of os.removedirs that does not remove symlinked
402 402 directories or junction points if they actually contain files"""
403 403 if listdir(name):
404 404 return
405 405 os.rmdir(name)
406 406 head, tail = os.path.split(name)
407 407 if not tail:
408 408 head, tail = os.path.split(head)
409 409 while head and tail:
410 410 try:
411 411 if listdir(head):
412 412 return
413 413 os.rmdir(head)
414 414 except (ValueError, OSError):
415 415 break
416 416 head, tail = os.path.split(head)
417 417
418 418 def rename(src, dst):
419 419 '''atomically rename file src to dst, replacing dst if it exists'''
420 420 try:
421 421 os.rename(src, dst)
422 422 except OSError as e:
423 423 if e.errno != errno.EEXIST:
424 424 raise
425 425 unlink(dst)
426 426 os.rename(src, dst)
427 427
428 428 def gethgcmd():
429 429 return [sys.executable] + sys.argv[:1]
430 430
431 431 def groupmembers(name):
432 432 # Don't support groups on Windows for now
433 433 raise KeyError
434 434
435 435 def isexec(f):
436 436 return False
437 437
438 438 class cachestat(object):
439 439 def __init__(self, path):
440 440 pass
441 441
442 442 def cacheable(self):
443 443 return False
444 444
445 445 def lookupreg(key, valname=None, scope=None):
446 446 ''' Look up a key/value name in the Windows registry.
447 447
448 448 valname: value name. If unspecified, the default value for the key
449 449 is used.
450 450 scope: optionally specify scope for registry lookup, this can be
451 451 a sequence of scopes to look up in order. Default (CURRENT_USER,
452 452 LOCAL_MACHINE).
453 453 '''
454 454 if scope is None:
455 455 scope = (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE)
456 456 elif not isinstance(scope, (list, tuple)):
457 457 scope = (scope,)
458 458 for s in scope:
459 459 try:
460 460 val = winreg.QueryValueEx(winreg.OpenKey(s, key), valname)[0]
461 461 # never let a Unicode string escape into the wild
462 462 return encoding.unitolocal(val)
463 463 except EnvironmentError:
464 464 pass
465 465
466 466 expandglobs = True
467 467
468 468 def statislink(st):
469 469 '''check whether a stat result is a symlink'''
470 470 return False
471 471
472 472 def statisexec(st):
473 473 '''check whether a stat result is an executable file'''
474 474 return False
475 475
476 476 def poll(fds):
477 477 # see posix.py for description
478 478 raise NotImplementedError()
479 479
480 480 def readpipe(pipe):
481 481 """Read all available data from a pipe."""
482 482 chunks = []
483 483 while True:
484 484 size = win32.peekpipe(pipe)
485 485 if not size:
486 486 break
487 487
488 488 s = pipe.read(size)
489 489 if not s:
490 490 break
491 491 chunks.append(s)
492 492
493 493 return ''.join(chunks)
494 494
495 495 def bindunixsocket(sock, path):
496 496 raise NotImplementedError('unsupported platform')
General Comments 0
You need to be logged in to leave comments. Login now