1306 else: |
1306 else: |
1307 w = 1 |
1307 w = 1 |
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 |
1314 |
1335 |
1315 def __init__(self, name, value): |
1336 def __init__(self, name, value): |
1322 if m: |
1343 if m: |
1323 self.name = m.group(1) |
1344 self.name = m.group(1) |
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) |
1331 |
1354 |
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 |
1338 otherwise. |
1363 otherwise. |
1339 """ |
1364 """ |
1350 if alias and alias.args is not None and alias.tree == tree[:2]: |
1375 if alias and alias.args is not None and alias.tree == tree[:2]: |
1351 return alias |
1376 return alias |
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 substitution value of the same name in args, recursively. |
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): |
1365 """Expand aliases in tree, recursively. |
1391 """Expand aliases in tree, recursively. |
1366 |
1392 |
1374 if alias is not None: |
1400 if alias is not None: |
1375 if alias in expanding: |
1401 if alias in expanding: |
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) |
1398 aliases[alias.name] = alias |
1424 aliases[alias.name] = alias |
1399 return _expandaliases(aliases, tree, []) |
1425 return _expandaliases(aliases, tree, []) |