Show More
@@ -12,6 +12,7 b' import msvcrt' | |||
|
12 | 12 | import os |
|
13 | 13 | import re |
|
14 | 14 | import stat |
|
15 | import string | |
|
15 | 16 | import sys |
|
16 | 17 | |
|
17 | 18 | from .i18n import _ |
@@ -253,6 +254,108 b' normcasefallback = encoding.upperfallbac' | |||
|
253 | 254 | def samestat(s1, s2): |
|
254 | 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 | 359 | # A sequence of backslashes is special iff it precedes a double quote: |
|
257 | 360 | # - if there's an even number of backslashes, the double quote is not |
|
258 | 361 | # quoted (i.e. it ends the quoted region) |
General Comments 0
You need to be logged in to leave comments.
Login now