comparison mercurial/revset.py @ 16778:2ac08d8b21aa

merge with stable
author Matt Mackall <mpm@selenic.com>
date Tue, 22 May 2012 14:37:20 -0500
parents d0b9ebba41e9 30e46d7138de
children 107a3270a24a
comparison
equal deleted inserted replaced
16777:058e14da7044 16778:2ac08d8b21aa
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, [])