]>
Commit | Line | Data |
---|---|---|
3a11599c DL |
1 | #!/usr/bin/python3 |
2 | # | |
3 | # YANG module to C wrapper | |
4 | # written 2018 by David Lamparter, placed in Public Domain. | |
5 | ||
261e7653 DL |
6 | import sys |
7 | import os | |
8 | import string | |
9 | import re | |
3a11599c DL |
10 | |
11 | inname = sys.argv[1] | |
12 | outname = sys.argv[2] | |
13 | ||
261e7653 DL |
14 | outdir = os.path.dirname(os.path.abspath(outname)) |
15 | if not os.path.isdir(outdir): | |
16 | os.makedirs(outdir) | |
17 | ||
3a11599c DL |
18 | # these are regexes to avoid a compile-time/host dependency on yang-tools |
19 | # or python-yang. Cross-compiling FRR is already somewhat involved, no need | |
20 | # to make it even harder. | |
21 | ||
701a0192 | 22 | re_name = re.compile(r"\bmodule\s+([^\s]+)\s+\{") |
23 | re_subname = re.compile(r"\bsubmodule\s+([^\s]+)\s+\{") | |
24 | re_mainname = re.compile(r"\bbelongs-to\s+([^\s]+)\s+\{") | |
25 | re_rev = re.compile(r"\brevision\s+([\d-]+)\s+\{") | |
3a11599c DL |
26 | |
27 | ||
701a0192 | 28 | template = """/* autogenerated by embedmodel.py. DO NOT EDIT */ |
3a11599c DL |
29 | |
30 | #include <zebra.h> | |
31 | #include "yang.h" | |
32 | ||
33 | static const char model[] = | |
34 | \t"%s"; | |
35 | ||
36 | static struct yang_module_embed embed = { | |
37 | \t.mod_name = "%s", | |
38 | \t.mod_rev = "%s", | |
65de8bc8 | 39 | \t.sub_mod_name = "%s", |
40 | \t.sub_mod_rev = "%s", | |
3a11599c DL |
41 | \t.data = model, |
42 | \t.format = %s, | |
43 | }; | |
44 | ||
45 | static void embed_register(void) __attribute__((_CONSTRUCTOR(2000))); | |
46 | static void embed_register(void) | |
47 | { | |
48 | \tyang_module_embed(&embed); | |
49 | } | |
701a0192 | 50 | """ |
51 | ||
52 | passchars = set(string.printable) - set("\\'\"%\r\n\t\x0b\x0c") | |
53 | ||
3a11599c | 54 | |
3a11599c DL |
55 | def escapech(char): |
56 | if char in passchars: | |
57 | return char | |
701a0192 | 58 | if char == "\n": |
59 | return "\\n" | |
60 | if char == "\t": | |
61 | return "\\t" | |
62 | if char in "\"\\'": | |
63 | return "\\" + char | |
64 | return "\\x%02x" % (ord(char)) | |
65 | ||
66 | ||
3a11599c | 67 | def escape(line): |
701a0192 | 68 | return "".join([escapech(i) for i in line]) |
69 | ||
3a11599c | 70 | |
701a0192 | 71 | with open(inname, "r") as fd: |
3a11599c DL |
72 | data = fd.read() |
73 | ||
65de8bc8 | 74 | sub_name = "" |
75 | rev = "" | |
76 | sub_rev = "" | |
77 | ||
3a11599c DL |
78 | # XML support isn't actively used currently, but it's here in case the need |
79 | # arises. It does avoid the regex'ing. | |
701a0192 | 80 | if "<?xml" in data: |
3a11599c | 81 | from xml.etree import ElementTree |
701a0192 | 82 | |
3a11599c | 83 | xml = ElementTree.fromstring(data) |
701a0192 | 84 | name = xml.get("name") |
85 | rev = xml.find("{urn:ietf:params:xml:ns:yang:yin:1}revision").get("date") | |
86 | fmt = "LYS_YIN" | |
3a11599c | 87 | else: |
65de8bc8 | 88 | search_name = re_name.search(data) |
701a0192 | 89 | if search_name: |
90 | name = search_name.group(1) | |
91 | rev = re_rev.search(data).group(1) | |
92 | else: | |
93 | search_name = re_subname.search(data) | |
94 | sub_name = search_name.group(1) | |
95 | name = re_mainname.search(data).group(1) | |
96 | sub_rev = re_rev.search(data).group(1) | |
97 | fmt = "LYS_YANG" | |
3a11599c DL |
98 | |
99 | if name is None or rev is None: | |
701a0192 | 100 | raise ValueError("cannot determine YANG module name and revision") |
3a11599c | 101 | |
701a0192 | 102 | lines = [escape(row) for row in data.split("\n")] |
3a11599c DL |
103 | text = '\\n"\n\t"'.join(lines) |
104 | ||
701a0192 | 105 | with open(outname, "w") as fd: |
106 | fd.write( | |
107 | template | |
108 | % (text, escape(name), escape(rev), escape(sub_name), escape(sub_rev), fmt) | |
109 | ) |