diff options
Diffstat (limited to 'python/clidef.py')
| -rw-r--r-- | python/clidef.py | 293 |
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] + ) |
