Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
def test_expansion_limit(self):
cmd = 'a $(b $(c))'
m = matcher.matcher(cmd, s)
m.match()
class depthchecker(bashlex.ast.nodevisitor):
def __init__(self):
self.depth = 0
self.maxdepth = 0
def visitnode(self, node):
if 'substitution' in node.kind:
self.depth += 1
self.maxdepth = max(self.maxdepth, self.depth)
def visitendnode(self, node):
if 'substitution' in node.kind:
self.depth -= 1
v = depthchecker()
v.visit(m.ast)
self.assertEquals(v.maxdepth, 1)
def assertASTEquals(self, s, expected, **parserargs):
results = parse(s, **parserargs)
self.assertTrue(len(results) == 1, 'expected one ast from parse(), '
'got %d' % len(results))
result = results[0]
# make sure our words are not empty
class nullopvisitor(ast.nodevisitor):
def visitword(_, node, word):
self.assertTrue(word, 'node %r has no word' % node)
nullopvisitor().visit(result)
msg = 'ASTs not equal for %r\n\nresult:\n\n%s\n\n!=\n\nexpected:\n\n%s' % (s, result.dump(), expected.dump())
self.assertEquals(result, expected, msg)
def _adjustpositions(node_, base, endlimit):
class v(ast.nodevisitor):
def visitnode(self, node):
assert node.pos[1] + base <= endlimit
node.pos = (node.pos[0] + base, node.pos[1] + base)
visitor = v()
visitor.visit(node_)
- list - a series of one or more pipelines
- compound - contains constructs for { list; }, (list), if, for..
leafs are word nodes (which in turn can also contain any of the
aforementioned nodes due to command substitutions).
when strictmode is set to False, we will:
- skip reading a heredoc if we're at the end of the input
expansionlimit is used to limit the amount of recursive parsing done due to
command substitutions found during word expansion.
'''
p = _parser(s, strictmode=strictmode, expansionlimit=expansionlimit)
parts = [p.parse()]
class endfinder(ast.nodevisitor):
def __init__(self):
self.end = -1
def visitheredoc(self, node, value):
self.end = node.pos[1]
# find the 'real' end incase we have a heredoc in there
ef = _endfinder()
ef.visit(parts[-1])
index = max(parts[-1].pos[1], ef.end) + 1
while index < len(s):
part = _parser(s[index:], strictmode=strictmode).parse()
if not isinstance(part, ast.node):
break
ast.posshifter(index).visit(part)
parserstate=self.parserstate,
strictmode=strictmode,
**tokenizerargs)
self.redirstack = self.tok.redirstack
def parse(self):
# yacc.yacc returns a parser object that is not reentrant, it has
# some mutable state. we make a shallow copy of it so no
# state spills over to the next call to parse on it
theparser = copy.copy(yaccparser)
tree = theparser.parse(lexer=self.tok, context=self)
return tree
class _endfinder(ast.nodevisitor):
'''helper class to find the "real" end pos of a node that contains
a heredoc. this is a hack because heredoc aren't really part of any node
since they don't always follow the end of a node and might appear on
a different line'''
def __init__(self):
self.end = -1
def visitheredoc(self, node, value):
self.end = node.pos[1]
import sys
import argparse
from argparse import RawTextHelpFormatter
from bashlex import parser, ast
class nodevisitor(ast.nodevisitor):
def __init__(self, positions):
self.positions = positions
def visitcommandsubstitution(self, n, command):
# log the start and end positions of this command substitution
self.positions.append(n.pos)
# do not recurse into child nodes
return False
desc = '''replace all occurrences of $() and `` with the string given in -s
$ commandsubstitution-remover.py -s nope -c 'foo $(bar)'
foo nope
within words:
def findfirstkind(parts, kind):
for i, node in enumerate(parts):
if node.kind == kind:
return i
return -1
class posconverter(nodevisitor):
def __init__(self, string):
self.string = string
def visitnode(self, node):
assert hasattr(node, 'pos'), 'node %r is missing pos attr' % node
start, end = node.__dict__.pop('pos')
node.s = self.string[start:end]
class posshifter(nodevisitor):
def __init__(self, count):
self.count = count
def visitnode(self, node):
#assert node.pos[1] + base <= endlimit
node.pos = (node.pos[0] + self.count, node.pos[1] + self.count)
self.results = []
def __repr__(self):
return '' % (self.name, len(self.results))
class matchresult(collections.namedtuple('matchresult', 'start end text match')):
@property
def unknown(self):
return self.text is None
matchwordexpansion = collections.namedtuple('matchwordexpansion',
'start end kind')
logger = logging.getLogger(__name__)
class matcher(bashlex.ast.nodevisitor):
'''parse a command line and return a list of matchresults describing
each token.
'''
def __init__(self, s, store):
self.s = s.encode('latin1', 'replace')
self.store = store
self._prevoption = self._currentoption = None
self.groups = [matchgroup('shell')]
# a list of matchwordexpansions where expansions happened during word
# expansion
self.expansions = []
# a stack to manage nested command groups: whenever a new simple
# command is started, we push a tuple with:
# - the node that started this group. this is used to find it when
result.compdb.append({
'directory': working_dir,
'command': command_str,
'file': filepath,
})
else:
result.compdb.append({
'directory': working_dir,
'arguments': arguments,
'file': filepath,
})
return result
class SubstCommandVisitor(bashlex.ast.nodevisitor):
"""Uses bashlex to parse and process sh/bash substitution commands.
May result in a parsing exception for invalid commands."""
def __init__(self):
self.substs = []
def visitcommandsubstitution(self, n, cmd):
self.substs.append(n)
return False
class CommandProcessor(bashlex.ast.nodevisitor):
"""Uses bashlex to parse and traverse the resulting bash AST
looking for and extracting compilation commands."""
@staticmethod
def process(line, wd):
trees = bashlex.parser.parse(line)
else:
lines[-1] += ']'
return '\n'.join(lines)
return repr(n)
if not isinstance(tree, node):
raise TypeError('expected node, got %r' % tree.__class__.__name__)
return _format(tree)
def findfirstkind(parts, kind):
for i, node in enumerate(parts):
if node.kind == kind:
return i
return -1
class posconverter(nodevisitor):
def __init__(self, string):
self.string = string
def visitnode(self, node):
assert hasattr(node, 'pos'), 'node %r is missing pos attr' % node
start, end = node.__dict__.pop('pos')
node.s = self.string[start:end]
class posshifter(nodevisitor):
def __init__(self, count):
self.count = count
def visitnode(self, node):
#assert node.pos[1] + base <= endlimit
node.pos = (node.pos[0] + self.count, node.pos[1] + self.count)