Show More
@@ -12,6 +12,7 b' 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 _ | |
@@ -253,6 +254,108 b' normcasefallback = encoding.upperfallbac' | |||||
253 | def samestat(s1, s2): |
|
254 | def samestat(s1, s2): | |
254 | return False |
|
255 | return False | |
255 |
|
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 | |||
|
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) |
General Comments 0
You need to be logged in to leave comments.
Login now