mercurial/revset.py
changeset 16778 2ac08d8b21aa
parent 16755 d0b9ebba41e9
parent 16772 30e46d7138de
child 16803 107a3270a24a
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, [])