mercurial/windows.py
changeset 38483 3efadf2317c7
parent 37460 a6c6b7beb025
child 38601 af8d8513d7de
equal deleted inserted replaced
38482:5faaa31a6082 38483:3efadf2317c7
    10 import errno
    10 import errno
    11 import msvcrt
    11 import msvcrt
    12 import os
    12 import os
    13 import re
    13 import re
    14 import stat
    14 import stat
       
    15 import string
    15 import sys
    16 import sys
    16 
    17 
    17 from .i18n import _
    18 from .i18n import _
    18 from . import (
    19 from . import (
    19     encoding,
    20     encoding,
   250 normcasespec = encoding.normcasespecs.upper
   251 normcasespec = encoding.normcasespecs.upper
   251 normcasefallback = encoding.upperfallback
   252 normcasefallback = encoding.upperfallback
   252 
   253 
   253 def samestat(s1, s2):
   254 def samestat(s1, s2):
   254     return False
   255     return False
       
   256 
       
   257 def shelltocmdexe(path, env):
       
   258     r"""Convert shell variables in the form $var and ${var} inside ``path``
       
   259     to %var% form.  Existing Windows style variables are left unchanged.
       
   260 
       
   261     The variables are limited to the given environment.  Unknown variables are
       
   262     left unchanged.
       
   263 
       
   264     >>> e = {b'var1': b'v1', b'var2': b'v2', b'var3': b'v3'}
       
   265     >>> # Only valid values are expanded
       
   266     >>> shelltocmdexe(b'cmd $var1 ${var2} %var3% $missing ${missing} %missing%',
       
   267     ...               e)
       
   268     'cmd %var1% %var2% %var3% $missing ${missing} %missing%'
       
   269     >>> # Single quote prevents expansion, as does \$ escaping
       
   270     >>> shelltocmdexe(b"cmd '$var1 ${var2} %var3%' \$var1 \${var2} \\", e)
       
   271     "cmd '$var1 ${var2} %var3%' $var1 ${var2} \\"
       
   272     >>> # $$ -> $, %% is not special, but can be the end and start of variables
       
   273     >>> shelltocmdexe(b"cmd $$ %% %var1%%var2%", e)
       
   274     'cmd $ %% %var1%%var2%'
       
   275     >>> # No double substitution
       
   276     >>> shelltocmdexe(b"$var1 %var1%", {b'var1': b'%var2%', b'var2': b'boom'})
       
   277     '%var1% %var1%'
       
   278     """
       
   279     if b'$' not in path:
       
   280         return path
       
   281 
       
   282     varchars = pycompat.sysbytes(string.ascii_letters + string.digits) + b'_-'
       
   283 
       
   284     res = b''
       
   285     index = 0
       
   286     pathlen = len(path)
       
   287     while index < pathlen:
       
   288         c = path[index]
       
   289         if c == b'\'':   # no expansion within single quotes
       
   290             path = path[index + 1:]
       
   291             pathlen = len(path)
       
   292             try:
       
   293                 index = path.index(b'\'')
       
   294                 res += b'\'' + path[:index + 1]
       
   295             except ValueError:
       
   296                 res += c + path
       
   297                 index = pathlen - 1
       
   298         elif c == b'%':  # variable
       
   299             path = path[index + 1:]
       
   300             pathlen = len(path)
       
   301             try:
       
   302                 index = path.index(b'%')
       
   303             except ValueError:
       
   304                 res += b'%' + path
       
   305                 index = pathlen - 1
       
   306             else:
       
   307                 var = path[:index]
       
   308                 res += b'%' + var + b'%'
       
   309         elif c == b'$':  # variable or '$$'
       
   310             if path[index + 1:index + 2] == b'$':
       
   311                 res += c
       
   312                 index += 1
       
   313             elif path[index + 1:index + 2] == b'{':
       
   314                 path = path[index + 2:]
       
   315                 pathlen = len(path)
       
   316                 try:
       
   317                     index = path.index(b'}')
       
   318                     var = path[:index]
       
   319 
       
   320                     # See below for why empty variables are handled specially
       
   321                     if env.get(var, '') != '':
       
   322                         res += b'%' + var + b'%'
       
   323                     else:
       
   324                         res += b'${' + var + b'}'
       
   325                 except ValueError:
       
   326                     res += b'${' + path
       
   327                     index = pathlen - 1
       
   328             else:
       
   329                 var = b''
       
   330                 index += 1
       
   331                 c = path[index:index + 1]
       
   332                 while c != b'' and c in varchars:
       
   333                     var += c
       
   334                     index += 1
       
   335                     c = path[index:index + 1]
       
   336                 # Some variables (like HG_OLDNODE) may be defined, but have an
       
   337                 # empty value.  Those need to be skipped because when spawning
       
   338                 # cmd.exe to run the hook, it doesn't replace %VAR% for an empty
       
   339                 # VAR, and that really confuses things like revset expressions.
       
   340                 # OTOH, if it's left in Unix format and the hook runs sh.exe, it
       
   341                 # will substitute to an empty string, and everything is happy.
       
   342                 if env.get(var, '') != '':
       
   343                     res += b'%' + var + b'%'
       
   344                 else:
       
   345                     res += b'$' + var
       
   346 
       
   347                 if c != '':
       
   348                     index -= 1
       
   349         elif c == b'\\' and index + 1 < pathlen and path[index + 1] == b'$':
       
   350             # Skip '\', but only if it is escaping $
       
   351             res += b'$'
       
   352             index += 1
       
   353         else:
       
   354             res += c
       
   355 
       
   356         index += 1
       
   357     return res
   255 
   358 
   256 # A sequence of backslashes is special iff it precedes a double quote:
   359 # A sequence of backslashes is special iff it precedes a double quote:
   257 # - if there's an even number of backslashes, the double quote is not
   360 # - if there's an even number of backslashes, the double quote is not
   258 #   quoted (i.e. it ends the quoted region)
   361 #   quoted (i.e. it ends the quoted region)
   259 # - if there's an odd number of backslashes, the double quote is quoted
   362 # - if there's an odd number of backslashes, the double quote is quoted