#!/usr/bin/python3
#
# 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))
if not os.path.isdir(outdir):
    os.makedirs(outdir)

# 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_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))
