frr/yang/embedmodel.py

113 lines
2.6 KiB
Python

#!/usr/bin/python3
# SPDX-License-Identifier: NONE
#
# YANG module to C wrapper
# written 2018 by David Lamparter, placed in Public Domain.
import sys
import os
import string
import re
inname = sys.argv[1]
outname = sys.argv[2]
outdir = os.path.dirname(os.path.abspath(outname))
try:
os.makedirs(outdir)
except FileExistsError:
pass
# these are regexes to avoid a compile-time/host dependency on yang-tools
# or python-yang. Cross-compiling FRR is already somewhat involved, no need
# to make it even harder.
re_name = re.compile(r"\bmodule\s+([^\s]+)\s+\{")
re_subname = re.compile(r"\bsubmodule\s+([^\s]+)\s+\{")
re_mainname = re.compile(r"\bbelongs-to\s+([^\s]+)\s+\{")
re_rev = re.compile(r"\brevision\s+([\d-]+)\s+\{")
template = """/* autogenerated by embedmodel.py. DO NOT EDIT */
#include <zebra.h>
#include "yang.h"
static const char model[] =
\t"%s";
static struct yang_module_embed embed = {
\t.mod_name = "%s",
\t.mod_rev = "%s",
\t.sub_mod_name = "%s",
\t.sub_mod_rev = "%s",
\t.data = model,
\t.format = %s,
};
static void embed_register(void) __attribute__((_CONSTRUCTOR(2000)));
static void embed_register(void)
{
\tyang_module_embed(&embed);
}
"""
passchars = set(string.printable) - set("\\'\"%\r\n\t\x0b\x0c")
def escapech(char):
if char in passchars:
return char
if char == "\n":
return "\\n"
if char == "\t":
return "\\t"
if char in "\"\\'":
return "\\" + char
return "\\x%02x" % (ord(char))
def escape(line):
return "".join([escapech(i) for i in line])
with open(inname, "r") as fd:
data = fd.read()
sub_name = ""
rev = ""
sub_rev = ""
# XML support isn't actively used currently, but it's here in case the need
# arises. It does avoid the regex'ing.
if "<?xml" in data:
from xml.etree import ElementTree
xml = ElementTree.fromstring(data)
name = xml.get("name")
rev = xml.find("{urn:ietf:params:xml:ns:yang:yin:1}revision").get("date")
fmt = "LYS_YIN"
else:
search_name = re_name.search(data)
if search_name:
name = search_name.group(1)
rev = re_rev.search(data).group(1)
else:
search_name = re_subname.search(data)
sub_name = search_name.group(1)
name = re_mainname.search(data).group(1)
sub_rev = re_rev.search(data).group(1)
fmt = "LYS_IN_YANG"
if name is None or rev is None:
raise ValueError("cannot determine YANG module name and revision")
lines = [escape(row) for row in data.split("\n")]
text = '\\n"\n\t"'.join(lines)
with open(outname, "w") as fd:
fd.write(
template
% (text, escape(name), escape(rev), escape(sub_name), escape(sub_rev), fmt)
)