summaryrefslogtreecommitdiff
path: root/python/clidef.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/clidef.py')
-rw-r--r--python/clidef.py293
1 files changed, 188 insertions, 105 deletions
diff --git a/python/clidef.py b/python/clidef.py
index baa6ed52b2..a47cee2d6b 100644
--- a/python/clidef.py
+++ b/python/clidef.py
@@ -26,39 +26,49 @@ from io import StringIO
# the various handlers generate output C code for a particular type of
# CLI token, choosing the most useful output C type.
+
class RenderHandler(object):
def __init__(self, token):
pass
+
def combine(self, other):
if type(self) == type(other):
return other
return StringHandler(None)
- deref = ''
+ deref = ""
drop_str = False
canfail = True
canassert = False
+
class StringHandler(RenderHandler):
- argtype = 'const char *'
- decl = Template('const char *$varname = NULL;')
- code = Template('$varname = (argv[_i]->type == WORD_TKN) ? argv[_i]->text : argv[_i]->arg;')
+ argtype = "const char *"
+ decl = Template("const char *$varname = NULL;")
+ code = Template(
+ "$varname = (argv[_i]->type == WORD_TKN) ? argv[_i]->text : argv[_i]->arg;"
+ )
drop_str = True
canfail = False
canassert = True
+
class LongHandler(RenderHandler):
- argtype = 'long'
- decl = Template('long $varname = 0;')
- code = Template('''\
+ argtype = "long"
+ decl = Template("long $varname = 0;")
+ code = Template(
+ """\
char *_end;
$varname = strtol(argv[_i]->arg, &_end, 10);
-_fail = (_end == argv[_i]->arg) || (*_end != '\\0');''')
+_fail = (_end == argv[_i]->arg) || (*_end != '\\0');"""
+ )
+
# A.B.C.D/M (prefix_ipv4) and
# X:X::X:X/M (prefix_ipv6) are "compatible" and can merge into a
# struct prefix:
+
class PrefixBase(RenderHandler):
def combine(self, other):
if type(self) == type(other):
@@ -66,23 +76,33 @@ class PrefixBase(RenderHandler):
if isinstance(other, PrefixBase):
return PrefixGenHandler(None)
return StringHandler(None)
- deref = '&'
+
+ deref = "&"
+
+
class Prefix4Handler(PrefixBase):
- argtype = 'const struct prefix_ipv4 *'
- decl = Template('struct prefix_ipv4 $varname = { };')
- code = Template('_fail = !str2prefix_ipv4(argv[_i]->arg, &$varname);')
+ argtype = "const struct prefix_ipv4 *"
+ decl = Template("struct prefix_ipv4 $varname = { };")
+ code = Template("_fail = !str2prefix_ipv4(argv[_i]->arg, &$varname);")
+
+
class Prefix6Handler(PrefixBase):
- argtype = 'const struct prefix_ipv6 *'
- decl = Template('struct prefix_ipv6 $varname = { };')
- code = Template('_fail = !str2prefix_ipv6(argv[_i]->arg, &$varname);')
+ argtype = "const struct prefix_ipv6 *"
+ decl = Template("struct prefix_ipv6 $varname = { };")
+ code = Template("_fail = !str2prefix_ipv6(argv[_i]->arg, &$varname);")
+
+
class PrefixEthHandler(PrefixBase):
- argtype = 'struct prefix_eth *'
- decl = Template('struct prefix_eth $varname = { };')
- code = Template('_fail = !str2prefix_eth(argv[_i]->arg, &$varname);')
+ argtype = "struct prefix_eth *"
+ decl = Template("struct prefix_eth $varname = { };")
+ code = Template("_fail = !str2prefix_eth(argv[_i]->arg, &$varname);")
+
+
class PrefixGenHandler(PrefixBase):
- argtype = 'const struct prefix *'
- decl = Template('struct prefix $varname = { };')
- code = Template('_fail = !str2prefix(argv[_i]->arg, &$varname);')
+ argtype = "const struct prefix *"
+ decl = Template("struct prefix $varname = { };")
+ code = Template("_fail = !str2prefix(argv[_i]->arg, &$varname);")
+
# same for IP addresses. result is union sockunion.
class IPBase(RenderHandler):
@@ -92,18 +112,27 @@ class IPBase(RenderHandler):
if type(other) in [IP4Handler, IP6Handler, IPGenHandler]:
return IPGenHandler(None)
return StringHandler(None)
+
+
class IP4Handler(IPBase):
- argtype = 'struct in_addr'
- decl = Template('struct in_addr $varname = { INADDR_ANY };')
- code = Template('_fail = !inet_aton(argv[_i]->arg, &$varname);')
+ argtype = "struct in_addr"
+ decl = Template("struct in_addr $varname = { INADDR_ANY };")
+ code = Template("_fail = !inet_aton(argv[_i]->arg, &$varname);")
+
+
class IP6Handler(IPBase):
- argtype = 'struct in6_addr'
- decl = Template('struct in6_addr $varname = {};')
- code = Template('_fail = !inet_pton(AF_INET6, argv[_i]->arg, &$varname);')
+ argtype = "struct in6_addr"
+ decl = Template("struct in6_addr $varname = {};")
+ code = Template("_fail = !inet_pton(AF_INET6, argv[_i]->arg, &$varname);")
+
+
class IPGenHandler(IPBase):
- argtype = 'const union sockunion *'
- decl = Template('''union sockunion s__$varname = { .sa.sa_family = AF_UNSPEC }, *$varname = NULL;''')
- code = Template('''\
+ argtype = "const union sockunion *"
+ decl = Template(
+ """union sockunion s__$varname = { .sa.sa_family = AF_UNSPEC }, *$varname = NULL;"""
+ )
+ code = Template(
+ """\
if (argv[_i]->text[0] == 'X') {
s__$varname.sa.sa_family = AF_INET6;
_fail = !inet_pton(AF_INET6, argv[_i]->arg, &s__$varname.sin6.sin6_addr);
@@ -112,26 +141,30 @@ if (argv[_i]->text[0] == 'X') {
s__$varname.sa.sa_family = AF_INET;
_fail = !inet_aton(argv[_i]->arg, &s__$varname.sin.sin_addr);
$varname = &s__$varname;
-}''')
+}"""
+ )
canassert = True
+
def mix_handlers(handlers):
def combine(a, b):
if a is None:
return b
return a.combine(b)
+
return reduce(combine, handlers, None)
+
handlers = {
- 'WORD_TKN': StringHandler,
- 'VARIABLE_TKN': StringHandler,
- 'RANGE_TKN': LongHandler,
- 'IPV4_TKN': IP4Handler,
- 'IPV4_PREFIX_TKN': Prefix4Handler,
- 'IPV6_TKN': IP6Handler,
- 'IPV6_PREFIX_TKN': Prefix6Handler,
- 'MAC_TKN': PrefixEthHandler,
- 'MAC_PREFIX_TKN': PrefixEthHandler,
+ "WORD_TKN": StringHandler,
+ "VARIABLE_TKN": StringHandler,
+ "RANGE_TKN": LongHandler,
+ "IPV4_TKN": IP4Handler,
+ "IPV4_PREFIX_TKN": Prefix4Handler,
+ "IPV6_TKN": IP6Handler,
+ "IPV6_PREFIX_TKN": Prefix6Handler,
+ "MAC_TKN": PrefixEthHandler,
+ "MAC_PREFIX_TKN": PrefixEthHandler,
}
# core template invoked for each occurence of DEFPY.
@@ -139,7 +172,8 @@ handlers = {
# the "#if $..." bits are there to keep this template unified into one
# common form, without requiring a more advanced template engine (e.g.
# jinja2)
-templ = Template('''/* $fnname => "$cmddef" */
+templ = Template(
+ """/* $fnname => "$cmddef" */
DEFUN_CMD_FUNC_DECL($fnname)
#define funcdecl_$fnname static int ${fnname}_magic(\\
const struct cmd_element *self __attribute__ ((unused)),\\
@@ -178,18 +212,22 @@ $argassert
return ${fnname}_magic(self, vty, argc, argv$arglist);
}
-''')
+"""
+)
# invoked for each named parameter
-argblock = Template('''
+argblock = Template(
+ """
if (!strcmp(argv[_i]->varname, \"$varname\")) {$strblock
$code
- }''')
+ }"""
+)
-def get_always_args(token, always_args, args = [], stack = []):
+
+def get_always_args(token, always_args, args=[], stack=[]):
if token in stack:
return
- if token.type == 'END_TKN':
+ if token.type == "END_TKN":
for arg in list(always_args):
if arg not in args:
always_args.remove(arg)
@@ -201,38 +239,45 @@ def get_always_args(token, always_args, args = [], stack = []):
for nexttkn in token.next():
get_always_args(nexttkn, always_args, args, stack)
+
class Macros(dict):
def load(self, filename):
filedata = clippy.parse(filename)
- for entry in filedata['data']:
- if entry['type'] != 'PREPROC':
+ for entry in filedata["data"]:
+ if entry["type"] != "PREPROC":
continue
- ppdir = entry['line'].lstrip().split(None, 1)
- if ppdir[0] != 'define' or len(ppdir) != 2:
+ ppdir = entry["line"].lstrip().split(None, 1)
+ if ppdir[0] != "define" or len(ppdir) != 2:
continue
ppdef = ppdir[1].split(None, 1)
name = ppdef[0]
- if '(' in name:
+ if "(" in name:
continue
- val = ppdef[1] if len(ppdef) == 2 else ''
+ val = ppdef[1] if len(ppdef) == 2 else ""
- val = val.strip(' \t\n\\')
+ val = val.strip(" \t\n\\")
if name in self:
- sys.stderr.write('warning: macro %s redefined!\n' % (name))
+ sys.stderr.write("warning: macro %s redefined!\n" % (name))
self[name] = val
+
def process_file(fn, ofd, dumpfd, all_defun, macros):
errors = 0
filedata = clippy.parse(fn)
- for entry in filedata['data']:
- if entry['type'].startswith('DEFPY') or (all_defun and entry['type'].startswith('DEFUN')):
- if len(entry['args'][0]) != 1:
- sys.stderr.write('%s:%d: DEFPY function name not parseable (%r)\n' % (fn, entry['lineno'], entry['args'][0]))
+ for entry in filedata["data"]:
+ if entry["type"].startswith("DEFPY") or (
+ all_defun and entry["type"].startswith("DEFUN")
+ ):
+ if len(entry["args"][0]) != 1:
+ sys.stderr.write(
+ "%s:%d: DEFPY function name not parseable (%r)\n"
+ % (fn, entry["lineno"], entry["args"][0])
+ )
errors += 1
continue
- cmddef = entry['args'][2]
+ cmddef = entry["args"][2]
cmddefx = []
for i in cmddef:
while i in macros:
@@ -241,13 +286,16 @@ def process_file(fn, ofd, dumpfd, all_defun, macros):
cmddefx.append(i[1:-1])
continue
- sys.stderr.write('%s:%d: DEFPY command string not parseable (%r)\n' % (fn, entry['lineno'], cmddef))
+ sys.stderr.write(
+ "%s:%d: DEFPY command string not parseable (%r)\n"
+ % (fn, entry["lineno"], cmddef)
+ )
errors += 1
cmddefx = None
break
if cmddefx is None:
continue
- cmddef = ''.join([i for i in cmddefx])
+ cmddef = "".join([i for i in cmddefx])
graph = clippy.Graph(cmddef)
args = OrderedDict()
@@ -263,12 +311,12 @@ def process_file(fn, ofd, dumpfd, all_defun, macros):
get_always_args(graph.first(), always_args)
- #print('-' * 76)
- #pprint(entry)
- #clippy.dump(graph)
- #pprint(args)
+ # print('-' * 76)
+ # pprint(entry)
+ # clippy.dump(graph)
+ # pprint(args)
- params = { 'cmddef': cmddef, 'fnname': entry['args'][0][0] }
+ params = {"cmddef": cmddef, "fnname": entry["args"][0][0]}
argdefs = []
argdecls = []
arglist = []
@@ -277,63 +325,96 @@ def process_file(fn, ofd, dumpfd, all_defun, macros):
doc = []
canfail = 0
- def do_add(handler, basename, varname, attr = ''):
- argdefs.append(',\\\n\t%s %s%s' % (handler.argtype, varname, attr))
- argdecls.append('\t%s\n' % (handler.decl.substitute({'varname': varname}).replace('\n', '\n\t')))
- arglist.append(', %s%s' % (handler.deref, varname))
+ def do_add(handler, basename, varname, attr=""):
+ argdefs.append(",\\\n\t%s %s%s" % (handler.argtype, varname, attr))
+ argdecls.append(
+ "\t%s\n"
+ % (
+ handler.decl.substitute({"varname": varname}).replace(
+ "\n", "\n\t"
+ )
+ )
+ )
+ arglist.append(", %s%s" % (handler.deref, varname))
if basename in always_args and handler.canassert:
- argassert.append('''\tif (!%s) {
+ argassert.append(
+ """\tif (!%s) {
\t\tvty_out(vty, "Internal CLI error [%%s]\\n", "%s");
\t\treturn CMD_WARNING;
-\t}\n''' % (varname, varname))
- if attr == '':
+\t}\n"""
+ % (varname, varname)
+ )
+ if attr == "":
at = handler.argtype
- if not at.startswith('const '):
- at = '. . . ' + at
- doc.append('\t%-26s %s %s' % (at, 'alw' if basename in always_args else 'opt', varname))
+ if not at.startswith("const "):
+ at = ". . . " + at
+ doc.append(
+ "\t%-26s %s %s"
+ % (at, "alw" if basename in always_args else "opt", varname)
+ )
for varname in args.keys():
handler = mix_handlers(args[varname])
- #print(varname, handler)
- if handler is None: continue
+ # print(varname, handler)
+ if handler is None:
+ continue
do_add(handler, varname, varname)
- code = handler.code.substitute({'varname': varname}).replace('\n', '\n\t\t\t')
+ code = handler.code.substitute({"varname": varname}).replace(
+ "\n", "\n\t\t\t"
+ )
if handler.canfail:
canfail = 1
- strblock = ''
+ strblock = ""
if not handler.drop_str:
- do_add(StringHandler(None), varname, '%s_str' % (varname), ' __attribute__ ((unused))')
- strblock = '\n\t\t\t%s_str = argv[_i]->arg;' % (varname)
- argblocks.append(argblock.substitute({'varname': varname, 'strblock': strblock, 'code': code}))
+ do_add(
+ StringHandler(None),
+ varname,
+ "%s_str" % (varname),
+ " __attribute__ ((unused))",
+ )
+ strblock = "\n\t\t\t%s_str = argv[_i]->arg;" % (varname)
+ argblocks.append(
+ argblock.substitute(
+ {"varname": varname, "strblock": strblock, "code": code}
+ )
+ )
if dumpfd is not None:
if len(arglist) > 0:
- dumpfd.write('"%s":\n%s\n\n' % (cmddef, '\n'.join(doc)))
+ dumpfd.write('"%s":\n%s\n\n' % (cmddef, "\n".join(doc)))
else:
dumpfd.write('"%s":\n\t---- no magic arguments ----\n\n' % (cmddef))
- params['argdefs'] = ''.join(argdefs)
- params['argdecls'] = ''.join(argdecls)
- params['arglist'] = ''.join(arglist)
- params['argblocks'] = ''.join(argblocks)
- params['canfail'] = canfail
- params['nonempty'] = len(argblocks)
- params['argassert'] = ''.join(argassert)
+ params["argdefs"] = "".join(argdefs)
+ params["argdecls"] = "".join(argdecls)
+ params["arglist"] = "".join(arglist)
+ params["argblocks"] = "".join(argblocks)
+ params["canfail"] = canfail
+ params["nonempty"] = len(argblocks)
+ params["argassert"] = "".join(argassert)
ofd.write(templ.substitute(params))
return errors
-if __name__ == '__main__':
+
+if __name__ == "__main__":
import argparse
- argp = argparse.ArgumentParser(description = 'FRR CLI preprocessor in Python')
- argp.add_argument('--all-defun', action = 'store_const', const = True,
- help = 'process DEFUN() statements in addition to DEFPY()')
- argp.add_argument('--show', action = 'store_const', const = True,
- help = 'print out list of arguments and types for each definition')
- argp.add_argument('-o', type = str, metavar = 'OUTFILE',
- help = 'output C file name')
- argp.add_argument('cfile', type = str)
+ argp = argparse.ArgumentParser(description="FRR CLI preprocessor in Python")
+ argp.add_argument(
+ "--all-defun",
+ action="store_const",
+ const=True,
+ help="process DEFUN() statements in addition to DEFPY()",
+ )
+ argp.add_argument(
+ "--show",
+ action="store_const",
+ const=True,
+ help="print out list of arguments and types for each definition",
+ )
+ argp.add_argument("-o", type=str, metavar="OUTFILE", help="output C file name")
+ argp.add_argument("cfile", type=str)
args = argp.parse_args()
dumpfd = None
@@ -349,15 +430,17 @@ if __name__ == '__main__':
basepath = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
macros = Macros()
- macros.load('lib/route_types.h')
- macros.load(os.path.join(basepath, 'lib/command.h'))
- macros.load(os.path.join(basepath, 'bgpd/bgp_vty.h'))
+ macros.load("lib/route_types.h")
+ macros.load(os.path.join(basepath, "lib/command.h"))
+ macros.load(os.path.join(basepath, "bgpd/bgp_vty.h"))
# sigh :(
- macros['PROTO_REDIST_STR'] = 'FRR_REDIST_STR_ISISD'
+ macros["PROTO_REDIST_STR"] = "FRR_REDIST_STR_ISISD"
errors = process_file(args.cfile, ofd, dumpfd, args.all_defun, macros)
if errors != 0:
sys.exit(1)
if args.o is not None:
- clippy.wrdiff(args.o, ofd, [args.cfile, os.path.realpath(__file__), sys.executable])
+ clippy.wrdiff(
+ args.o, ofd, [args.cfile, os.path.realpath(__file__), sys.executable]
+ )