Show More
@@ -1308,6 +1308,27 b' def optimize(x, small):' | |||||
1308 | return w + wa, (op, x[1], ta) |
|
1308 | return w + wa, (op, x[1], ta) | |
1309 | return 1, x |
|
1309 | return 1, x | |
1310 |
|
1310 | |||
|
1311 | _aliasarg = ('func', ('symbol', '_aliasarg')) | |||
|
1312 | def _getaliasarg(tree): | |||
|
1313 | """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X)) | |||
|
1314 | return X, None otherwise. | |||
|
1315 | """ | |||
|
1316 | if (len(tree) == 3 and tree[:2] == _aliasarg | |||
|
1317 | and tree[2][0] == 'string'): | |||
|
1318 | return tree[2][1] | |||
|
1319 | return None | |||
|
1320 | ||||
|
1321 | def _checkaliasarg(tree, known=None): | |||
|
1322 | """Check tree contains no _aliasarg construct or only ones which | |||
|
1323 | value is in known. Used to avoid alias placeholders injection. | |||
|
1324 | """ | |||
|
1325 | if isinstance(tree, tuple): | |||
|
1326 | arg = _getaliasarg(tree) | |||
|
1327 | if arg is not None and (not known or arg not in known): | |||
|
1328 | raise error.ParseError(_("not a function: %s") % '_aliasarg') | |||
|
1329 | for t in tree: | |||
|
1330 | _checkaliasarg(t, known) | |||
|
1331 | ||||
1311 | class revsetalias(object): |
|
1332 | class revsetalias(object): | |
1312 | funcre = re.compile('^([^(]+)\(([^)]+)\)$') |
|
1333 | funcre = re.compile('^([^(]+)\(([^)]+)\)$') | |
1313 | args = None |
|
1334 | args = None | |
@@ -1324,7 +1345,9 b' class revsetalias(object):' | |||||
1324 | self.tree = ('func', ('symbol', m.group(1))) |
|
1345 | self.tree = ('func', ('symbol', m.group(1))) | |
1325 | self.args = [x.strip() for x in m.group(2).split(',')] |
|
1346 | self.args = [x.strip() for x in m.group(2).split(',')] | |
1326 | for arg in self.args: |
|
1347 | for arg in self.args: | |
1327 | value = value.replace(arg, repr(arg)) |
|
1348 | # _aliasarg() is an unknown symbol only used separate | |
|
1349 | # alias argument placeholders from regular strings. | |||
|
1350 | value = value.replace(arg, '_aliasarg(%r)' % (arg,)) | |||
1328 | else: |
|
1351 | else: | |
1329 | self.name = name |
|
1352 | self.name = name | |
1330 | self.tree = ('symbol', name) |
|
1353 | self.tree = ('symbol', name) | |
@@ -1332,6 +1355,8 b' class revsetalias(object):' | |||||
1332 | self.replacement, pos = parse(value) |
|
1355 | self.replacement, pos = parse(value) | |
1333 | if pos != len(value): |
|
1356 | if pos != len(value): | |
1334 | raise error.ParseError(_('invalid token'), pos) |
|
1357 | raise error.ParseError(_('invalid token'), pos) | |
|
1358 | # Check for placeholder injection | |||
|
1359 | _checkaliasarg(self.replacement, self.args) | |||
1335 |
|
1360 | |||
1336 | def _getalias(aliases, tree): |
|
1361 | def _getalias(aliases, tree): | |
1337 | """If tree looks like an unexpanded alias, return it. Return None |
|
1362 | """If tree looks like an unexpanded alias, return it. Return None | |
@@ -1352,13 +1377,14 b' def _getalias(aliases, tree):' | |||||
1352 | return None |
|
1377 | return None | |
1353 |
|
1378 | |||
1354 | def _expandargs(tree, args): |
|
1379 | def _expandargs(tree, args): | |
1355 | """Replace all occurences of ('string', name) with the |
|
1380 | """Replace _aliasarg instances with the substitution value of the | |
1356 |
s |
|
1381 | same name in args, recursively. | |
1357 | """ |
|
1382 | """ | |
1358 | if not isinstance(tree, tuple): |
|
1383 | if not tree or not isinstance(tree, tuple): | |
1359 | return tree |
|
1384 | return tree | |
1360 | if len(tree) == 2 and tree[0] == 'string': |
|
1385 | arg = _getaliasarg(tree) | |
1361 | return args.get(tree[1], tree) |
|
1386 | if arg is not None: | |
|
1387 | return args[arg] | |||
1362 | return tuple(_expandargs(t, args) for t in tree) |
|
1388 | return tuple(_expandargs(t, args) for t in tree) | |
1363 |
|
1389 | |||
1364 | def _expandaliases(aliases, tree, expanding): |
|
1390 | def _expandaliases(aliases, tree, expanding): | |
@@ -1376,22 +1402,22 b' def _expandaliases(aliases, tree, expand' | |||||
1376 | raise error.ParseError(_('infinite expansion of revset alias "%s" ' |
|
1402 | raise error.ParseError(_('infinite expansion of revset alias "%s" ' | |
1377 | 'detected') % alias.name) |
|
1403 | 'detected') % alias.name) | |
1378 | expanding.append(alias) |
|
1404 | expanding.append(alias) | |
1379 | result = alias.replacement |
|
1405 | result = _expandaliases(aliases, alias.replacement, expanding) | |
|
1406 | expanding.pop() | |||
1380 | if alias.args is not None: |
|
1407 | if alias.args is not None: | |
1381 | l = getlist(tree[2]) |
|
1408 | l = getlist(tree[2]) | |
1382 | if len(l) != len(alias.args): |
|
1409 | if len(l) != len(alias.args): | |
1383 | raise error.ParseError( |
|
1410 | raise error.ParseError( | |
1384 | _('invalid number of arguments: %s') % len(l)) |
|
1411 | _('invalid number of arguments: %s') % len(l)) | |
|
1412 | l = [_expandaliases(aliases, a, []) for a in l] | |||
1385 | result = _expandargs(result, dict(zip(alias.args, l))) |
|
1413 | result = _expandargs(result, dict(zip(alias.args, l))) | |
1386 | # Recurse in place, the base expression may have been rewritten |
|
|||
1387 | result = _expandaliases(aliases, result, expanding) |
|
|||
1388 | expanding.pop() |
|
|||
1389 | else: |
|
1414 | else: | |
1390 | result = tuple(_expandaliases(aliases, t, expanding) |
|
1415 | result = tuple(_expandaliases(aliases, t, expanding) | |
1391 | for t in tree) |
|
1416 | for t in tree) | |
1392 | return result |
|
1417 | return result | |
1393 |
|
1418 | |||
1394 | def findaliases(ui, tree): |
|
1419 | def findaliases(ui, tree): | |
|
1420 | _checkaliasarg(tree) | |||
1395 | aliases = {} |
|
1421 | aliases = {} | |
1396 | for k, v in ui.configitems('revsetalias'): |
|
1422 | for k, v in ui.configitems('revsetalias'): | |
1397 | alias = revsetalias(k, v) |
|
1423 | alias = revsetalias(k, v) |
@@ -527,6 +527,27 b' test infinite recursion' | |||||
527 | hg: parse error: infinite expansion of revset alias "recurse1" detected |
|
527 | hg: parse error: infinite expansion of revset alias "recurse1" detected | |
528 | [255] |
|
528 | [255] | |
529 |
|
529 | |||
|
530 | $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc | |||
|
531 | $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc | |||
|
532 | $ try "level2(level1(1, 2), 3)" | |||
|
533 | (func | |||
|
534 | ('symbol', 'level2') | |||
|
535 | (list | |||
|
536 | (func | |||
|
537 | ('symbol', 'level1') | |||
|
538 | (list | |||
|
539 | ('symbol', '1') | |||
|
540 | ('symbol', '2'))) | |||
|
541 | ('symbol', '3'))) | |||
|
542 | (or | |||
|
543 | ('symbol', '3') | |||
|
544 | (or | |||
|
545 | ('symbol', '1') | |||
|
546 | ('symbol', '2'))) | |||
|
547 | 3 | |||
|
548 | 1 | |||
|
549 | 2 | |||
|
550 | ||||
530 | test nesting and variable passing |
|
551 | test nesting and variable passing | |
531 |
|
552 | |||
532 | $ echo 'nested($1) = nested2($1)' >> .hg/hgrc |
|
553 | $ echo 'nested($1) = nested2($1)' >> .hg/hgrc | |
@@ -565,6 +586,19 b' far away.' | |||||
565 | abort: unknown revision '$1'! |
|
586 | abort: unknown revision '$1'! | |
566 | [255] |
|
587 | [255] | |
567 |
|
588 | |||
|
589 | $ echo 'injectparamasstring2 = max(_aliasarg("$1"))' >> .hg/hgrc | |||
|
590 | $ echo 'callinjection2($1) = descendants(injectparamasstring2)' >> .hg/hgrc | |||
|
591 | $ try 'callinjection2(2:5)' | |||
|
592 | (func | |||
|
593 | ('symbol', 'callinjection2') | |||
|
594 | (range | |||
|
595 | ('symbol', '2') | |||
|
596 | ('symbol', '5'))) | |||
|
597 | hg: parse error: not a function: _aliasarg | |||
|
598 | [255] | |||
|
599 | >>> data = file('.hg/hgrc', 'rb').read() | |||
|
600 | >>> file('.hg/hgrc', 'wb').write(data.replace('_aliasarg', '')) | |||
|
601 | ||||
568 | $ try 'd(2:5)' |
|
602 | $ try 'd(2:5)' | |
569 | (func |
|
603 | (func | |
570 | ('symbol', 'd') |
|
604 | ('symbol', 'd') |
General Comments 0
You need to be logged in to leave comments.
Login now