[RFCv3 00/25] RFC: Generate parsexml/formatbuf functions based on directives

V2 here: [https://listman.redhat.com/archives/libvir-list/2020-September/msg00204.html] Differ from V2: * Add tests for xmlgen to illustrate all the different features we can use and make sure its proper functions in the future. * Add docs/xmlgen.rst to explain the usage of all directives and the tool itself. * Now xmlgen can check whether the first item of enum ends with _NONE, _DEFAULT or _ABSENT and generate proper code. So we no longer need to add extra 'default' item for enum. * Now xmlgen can provide extra [tips] when we execute its command-line to show generated code for preview. * Enable/disable hooks by macros rather than by special directives. * Add virStrToBoolYesNo/virStrToBoolTrueFalse/virStrToBoolOnOff and explicitly check both the true and false values. * Stronger check for python3-clang and libclang.so to make sure it can work. * Add python3-clang to the libvirt.spec.in and the mingw-libvirt.spec.in. Thanks! Shi Lei (25): scripts: Add a tool to generate xml parse/format functions maint: Check python3-clang and libclang maint: Call xmlgen automatically when c-head-files change docs: Add xmlgen.rst to explain how to use it build-aux: Only check *.[ch] for sc_prohibit_useless_translation tests: Add tests for xmlgen util: Add some xml-helper-functions to cooperate with xmlgen util: Add helper aliases and functions for 'bool' and 'time_t' to cooperate with xmlgen util: Add parsexml/formatbuf helper functions for virSocketAddr util: Add virUUID type and parse/format functions conf: Extract error-checking code from virNetworkDNSTxtDefParseXML conf: Replace virNetworkDNSTxtDefParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSTxtDefFormatBuf conf: Extract error-checking code from virNetworkDNSSrvDefParseXML conf: Replace virNetworkDNSSrvDefParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSSrvDefFormatBuf conf: Extract error-checking code from virNetworkDNSHostDefParseXML conf: Replace virNetworkDNSHostDefParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSHostDefFormatBuf conf: Extract virNetworkDNSForwarderParseXML from virNetworkDNSParseXML conf: Replace virNetworkDNSForwarderParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSForwarderFormatBuf conf: Extract error-checking code from virNetworkDNSDefParseXML conf: Replace virNetworkDNSDefParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSDefFormatBuf build-aux/syntax-check.mk | 2 +- docs/meson.build | 1 + docs/xmlgen.rst | 684 +++++++++++++ libvirt.spec.in | 1 + meson.build | 10 + mingw-libvirt.spec.in | 1 + po/POTFILES.in | 2 + scripts/meson.build | 8 + scripts/xmlgen/directive.py | 1192 ++++++++++++++++++++++ scripts/xmlgen/go | 29 + scripts/xmlgen/main.py | 534 ++++++++++ scripts/xmlgen/utils.py | 121 +++ src/conf/meson.build | 37 + src/conf/network_conf.c | 463 ++------- src/conf/network_conf.h | 59 +- src/internal.h | 8 + src/libvirt_private.syms | 13 + src/meson.build | 6 + src/util/meson.build | 36 + src/util/virbuffer.c | 44 + src/util/virbuffer.h | 8 +- src/util/virsocketaddr.c | 42 + src/util/virsocketaddr.h | 23 +- src/util/virstring.c | 102 ++ src/util/virstring.h | 15 + src/util/viruuid.c | 31 + src/util/viruuid.h | 18 + src/util/virxml.c | 120 +++ src/util/virxml.h | 6 + tests/meson.build | 3 + tests/xmlgenin/conf/array.h | 17 + tests/xmlgenin/conf/empty.h | 7 + tests/xmlgenin/conf/enum-first-item.h | 12 + tests/xmlgenin/conf/external.h | 9 + tests/xmlgenin/conf/genformat-separate.h | 11 + tests/xmlgenin/conf/genformat.h | 11 + tests/xmlgenin/conf/genparse.h | 11 + tests/xmlgenin/conf/namespace.h | 12 + tests/xmlgenin/conf/required.h | 9 + tests/xmlgenin/conf/skipparse.h | 10 + tests/xmlgenin/conf/specify.h | 13 + tests/xmlgenin/conf/xmlattr.h | 10 + tests/xmlgenin/conf/xmlelem.h | 10 + tests/xmlgenin/conf/xmlgroup.h | 8 + tests/xmlgenin/conf/xmlswitch.h | 17 + tests/xmlgenin/util/enums.h | 58 ++ tests/xmlgenin/util/structs.h | 67 ++ tests/xmlgenout/array.txt | 364 +++++++ tests/xmlgenout/empty.txt | 181 ++++ tests/xmlgenout/enum-first-item.txt | 297 ++++++ tests/xmlgenout/external.txt | 205 ++++ tests/xmlgenout/genformat-separate.txt | 190 ++++ tests/xmlgenout/genformat.txt | 142 +++ tests/xmlgenout/genparse.txt | 154 +++ tests/xmlgenout/namespace.txt | 222 ++++ tests/xmlgenout/required.txt | 236 +++++ tests/xmlgenout/skipparse.txt | 223 ++++ tests/xmlgenout/specify.txt | 291 ++++++ tests/xmlgenout/xmlattr.txt | 252 +++++ tests/xmlgenout/xmlelem.txt | 243 +++++ tests/xmlgenout/xmlgroup.txt | 204 ++++ tests/xmlgenout/xmlswitch.txt | 470 +++++++++ tests/xmlgentest.c | 107 ++ tools/meson.build | 3 + 64 files changed, 7289 insertions(+), 406 deletions(-) create mode 100644 docs/xmlgen.rst create mode 100644 scripts/xmlgen/directive.py create mode 100755 scripts/xmlgen/go create mode 100755 scripts/xmlgen/main.py create mode 100644 scripts/xmlgen/utils.py create mode 100644 tests/xmlgenin/conf/array.h create mode 100644 tests/xmlgenin/conf/empty.h create mode 100644 tests/xmlgenin/conf/enum-first-item.h create mode 100644 tests/xmlgenin/conf/external.h create mode 100644 tests/xmlgenin/conf/genformat-separate.h create mode 100644 tests/xmlgenin/conf/genformat.h create mode 100644 tests/xmlgenin/conf/genparse.h create mode 100644 tests/xmlgenin/conf/namespace.h create mode 100644 tests/xmlgenin/conf/required.h create mode 100644 tests/xmlgenin/conf/skipparse.h create mode 100644 tests/xmlgenin/conf/specify.h create mode 100644 tests/xmlgenin/conf/xmlattr.h create mode 100644 tests/xmlgenin/conf/xmlelem.h create mode 100644 tests/xmlgenin/conf/xmlgroup.h create mode 100644 tests/xmlgenin/conf/xmlswitch.h create mode 100644 tests/xmlgenin/util/enums.h create mode 100644 tests/xmlgenin/util/structs.h create mode 100644 tests/xmlgenout/array.txt create mode 100644 tests/xmlgenout/empty.txt create mode 100644 tests/xmlgenout/enum-first-item.txt create mode 100644 tests/xmlgenout/external.txt create mode 100644 tests/xmlgenout/genformat-separate.txt create mode 100644 tests/xmlgenout/genformat.txt create mode 100644 tests/xmlgenout/genparse.txt create mode 100644 tests/xmlgenout/namespace.txt create mode 100644 tests/xmlgenout/required.txt create mode 100644 tests/xmlgenout/skipparse.txt create mode 100644 tests/xmlgenout/specify.txt create mode 100644 tests/xmlgenout/xmlattr.txt create mode 100644 tests/xmlgenout/xmlelem.txt create mode 100644 tests/xmlgenout/xmlgroup.txt create mode 100644 tests/xmlgenout/xmlswitch.txt create mode 100644 tests/xmlgentest.c -- 2.25.1

This tool is used to generate parsexml/formatbuf/clear functions. It is based on libclang and its python-binding. Some directives (such as genparse, xmlattr, etc.) need to be added on the declarations of structs to direct the tool. Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- po/POTFILES.in | 1 + scripts/xmlgen/directive.py | 1192 +++++++++++++++++++++++++++++++++++ scripts/xmlgen/go | 29 + scripts/xmlgen/main.py | 534 ++++++++++++++++ scripts/xmlgen/utils.py | 121 ++++ 5 files changed, 1877 insertions(+) create mode 100644 scripts/xmlgen/directive.py create mode 100755 scripts/xmlgen/go create mode 100755 scripts/xmlgen/main.py create mode 100644 scripts/xmlgen/utils.py diff --git a/po/POTFILES.in b/po/POTFILES.in index 413783ee..9740bb2b 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -5,6 +5,7 @@ @BUILDDIR@src/admin/admin_server_dispatch_stubs.h @BUILDDIR@src/remote/remote_client_bodies.h @BUILDDIR@src/remote/remote_daemon_dispatch_stubs.h +@SRCDIR@scripts/xmlgen/directive.py @SRCDIR@src/access/viraccessdriverpolkit.c @SRCDIR@src/access/viraccessmanager.c @SRCDIR@src/admin/admin_server.c diff --git a/scripts/xmlgen/directive.py b/scripts/xmlgen/directive.py new file mode 100644 index 00000000..cc8fb5aa --- /dev/null +++ b/scripts/xmlgen/directive.py @@ -0,0 +1,1192 @@ +# +# Copyright (C) 2021 Shandong Massclouds Co.,Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see +# <http://www.gnu.org/licenses/>. +# + +import json +from collections import OrderedDict +from utils import singleton, dedup, Block, Terms, render + +BUILTIN_TYPES = { + 'String': {}, + 'Bool': { + 'conv': 'virStrToBoolYesNo(${mdvar}, &def->${name})' + }, + 'BoolYesNo': { + 'conv': 'virStrToBoolYesNo(${mdvar}, &def->${name})' + }, + 'BoolOnOff': { + 'conv': 'virStrToBoolOnOff(${mdvar}, &def->${name})' + }, + 'BoolTrueFalse': { + 'conv': 'virStrToBoolTrueFalse(${mdvar}, &def->${name})' + }, + 'Chars': { + 'conv': 'virStrcpyStatic(def->${name}, ${name}Str)' + }, + 'UChars': { + 'conv': 'virStrcpyStatic((char *)def->${name}, ${mdvar})' + }, + 'Int': { + 'fmt': '%d', + 'conv': 'virStrToLong_i(${mdvar}, NULL, 0, &def->${name})' + }, + 'UInt': { + 'fmt': '%u', + 'conv': 'virStrToLong_uip(${mdvar}, NULL, 0, &def->${name})' + }, + 'ULong': { + 'fmt': '%lu', + 'conv': 'virStrToLong_ulp(${mdvar}, NULL, 0, &def->${name})' + }, + 'ULongLong': { + 'fmt': '%llu', + 'conv': 'virStrToLong_ullp(${mdvar}, NULL, 0, &def->${name})' + }, + 'U8': { + 'fmt': '%u', + 'conv': 'virStrToLong_u8p(${mdvar}, NULL, 0, &def->${name})' + }, + 'U32': { + 'fmt': '%u', + 'conv': 'virStrToLong_uip(${mdvar}, NULL, 0, &def->${name})' + }, + 'Time': { + 'conv': 'virStrToTime(${mdvar}, &def->${name})' + }, +} + + +@singleton +class TypeTable(OrderedDict): + def __init__(self): + OrderedDict.__init__(self) + for name, kvs in BUILTIN_TYPES.items(): + kvs['name'] = name + kvs['meta'] = 'Builtin' + self[name] = kvs + + def register(self, kvs): + name = kvs['name'] + if name not in self: + self[name] = kvs + return name + + def get(self, name): + if name in self: + return self[name] + return {'meta': 'Struct', 'name': name, 'external': True} + + def check(self, name): + return name in self + + +T_NAMESPACE_PARSE = [ + 'if (xmlopt)', + ' def->ns = xmlopt->ns;', + 'if (def->ns.parse) {', + ' if (virXMLNamespaceRegister(ctxt, &def->ns) < 0)', + ' goto error;', + ' if ((def->ns.parse)(ctxt, &def->namespaceData) < 0)', + ' goto error;', + '}' +] + +T_NAMESPACE_FORMAT_BEGIN = [ + 'if (def->namespaceData && def->ns.format)', + ' virXMLNamespaceFormatNS(buf, &def->ns);' +] + +T_NAMESPACE_FORMAT_END = [ + 'if (def->namespaceData && def->ns.format) {', + ' if ((def->ns.format)(buf, def->namespaceData) < 0)', + ' return -1;', + '}' +] + + +def funcSignature(rtype, name, args, semicolon=''): + alignment = ' ' * (len(name) + 1) + connector = ',\n' + alignment + + ret = [] + ret.append(rtype) + ret.append('%s(%s)%s' % (name, connector.join(args), semicolon)) + return Block(ret) + + +def counterName(member): + if isinstance(member['array'], bool): + return 'n' + member['name'] + return member['array'] + + +def loop(count, block, condition=None): + assert isinstance(block, list) and len(block) > 0 + lp = ' {' if len(block) > 1 else '' + condition = condition if condition else count + + ret = Block() + ret.format('if (${condition}) {', condition=condition) + ret.format(' size_t i;') + ret.format(' for (i = 0; i < ${count}; i++)${lp}', count=count, lp=lp) + ret.mapfmt(' ${_each_line_}', block) + ret.format(' }' if lp else None) + ret.format('}') + return ret + + +def ispointer(member): + mtype = TypeTable().get(member['type']) + return member['pointer'] and not mtype.get('external') + + +def clearMember(member, pre_name=None): + if not member.get('xmlattr') and not member.get('xmlelem') \ + and not member.get('xmlgroup') and not member.get('xmlswitch'): + return None + + # Callback for make_switch + def _switch_clear_cb(child, switch, _): + return clearMember(child, switch['name']) + + if member.get('xmlswitch'): + return makeSwitch(member, _switch_clear_cb, None) + + mtype = TypeTable().get(member['type']) + + if pre_name: + ref = 'def->%s.%s' % (pre_name, member['name']) + else: + ref = 'def->%s' % member['name'] + + if member.get('array'): + ref += '[i]' + + ret = Block() + if mtype['meta'] == 'Struct': + if ispointer(member): + ret.format('${tname}Clear(${ref});', tname=mtype['name'], ref=ref) + ret.format('g_free(${ref});', ref=ref) + ret.format('${ref} = NULL;', ref=ref) + else: + ret.format('${tname}Clear(&${ref});', tname=mtype['name'], ref=ref) + elif mtype['name'] == 'String': + ret.format('g_free(${ref});', ref=ref) + ret.format('${ref} = NULL;', ref=ref) + elif mtype['name'] in ['Chars', 'UChars']: + ret.format('memset(${ref}, 0, sizeof(${ref}));', ref=ref) + elif not member.get('array'): + ret.format('${ref} = 0;', ref=ref) + + if member.get('specified'): + assert not member.get('array'), "'specified' can't come with 'array'." + sname, managed = member['specified'] + if managed: + ret.format('def->${sname} = false;', sname=sname) + + if member.get('array') and len(ret) > 0: + count = 'def->' + counterName(member) + ret = loop(count, ret) + ret.format('g_free(def->${name});', name=member['name']) + ret.format('def->${name} = NULL;', name=member['name']) + ret.format('${count} = 0;', count=count) + + return ret + + +def makeClearFunc(writer, atype): + if 'genparse' not in atype: + return + + args = ['%s *def' % atype['name']] + signature = funcSignature('void', atype['name'] + 'Clear', args) + writer.write(atype, 'clearfunc', '.h', signature.output() + ';') + + ret = Block() + ret.extend(signature) + ret.format('{') + ret.format(' if (!def)') + ret.format(' return;') + ret.newline() + + delay_members = [] + for member in atype['members']: + if member.get('delay_clear'): + delay_members.append(member) + continue + + code = clearMember(member) + if code: + ret.mapfmt(' ${_each_line_}', code) + ret.newline() + + for member in delay_members: + code = clearMember(member) + if code: + ret.mapfmt(' ${_each_line_}', code) + ret.newline() + + if 'namespace' in atype: + ret.format(' if (def->namespaceData && def->ns.free)') + ret.format(' (def->ns.free)(def->namespaceData);') + else: + ret.pop() # Remove last newline + + ret.format('}') + writer.write(atype, 'clearfunc', '.c', ret.output()) + + +def reportInvalid(tname, mdvar): + ret = Block() + ret.append('virReportError(VIR_ERR_XML_ERROR,') + ret.append(" _(\"Invalid '%s' setting '%s' in '%s'\"),") + ret.format(' "${tname}", ${mdvar}, instname);', + tname=tname, mdvar=mdvar) + return ret + + +def reportMissing(tname): + ret = Block() + ret.append('virReportError(VIR_ERR_XML_ERROR,') + ret.append(" _(\"Missing '%s' setting in '%s'\"),") + ret.format(' "${tname}", instname);', tname=tname) + return ret + + +def checkError(kind, cond, tname, mdvar): + if kind == 'Invalid': + report = reportInvalid(tname, mdvar) + elif kind == 'Missing': + report = reportMissing(tname) + else: + assert False, '%s is unsupported.' % kind + + ret = Block() + ret.format('if (${cond}) {', cond=cond) + ret.mapfmt(' ${_each_line_}', report) + ret.append(' goto error;') + ret.append('}') + return ret + + +def parseMember(member, parent, tmpvars, pre_name=None): + mtype = TypeTable().get(member['type']) + + # + # Helper functions + # + def _middle_var(member): + ret = member['name'].replace('.', '_') + if member.get('xmlgroup'): + ret = 'node' + elif mtype['name'] == 'String': + ret = 'def->' + ret + elif member.get('xmlattr') or \ + member.get('xmltext') or mtype['meta'] != 'Struct': + ret += 'Str' + else: + ret += 'Node' + return ret + + def _read_xml_func(mdvar, tname): + tagname = tname + if member.get('xmlattr'): + if '/' in tname: + funcname = 'virXMLChildPropString' + else: + funcname = 'virXMLPropString' + elif member.get('xmlelem'): + if not member.get('xmltext') and mtype['meta'] == 'Struct': + funcname = 'virXMLChildNode' + else: + funcname = 'virXMLChildNodeContent' + else: + return None + + return render('${mdvar} = ${funcname}(node, "${tname}");', + mdvar=mdvar, funcname=funcname, tname=tagname) + + def _assign_struct(name, member, mdvar): + ret = Block() + if ispointer(member): + ret.format('def->${name} = g_new0(typeof(*def->${name}), 1);', + name=name) + + func = mtype['name'] + 'ParseXML' + amp = '' if ispointer(member) else '&' + tmpl = '${func}(${mdvar}, ${amp}def->${name}, instname, ' \ + 'arg_parent, arg_opaque)' + fn = render(tmpl, func=func, mdvar=mdvar, amp=amp, name=name) + ret.extend(if_cond(fn + ' < 0', ['goto error;'])) + return ret + + def _assign_non_struct(name, member, mdvar): + if mtype['meta'] == 'Enum': + typename = mtype['name'] + if not typename.endswith('Type'): + typename += 'Type' + expr = render('(def->${name} = ${typename}FromString(${mdvar}))', + name=name, typename=typename, mdvar=mdvar) + if mtype['with_default']: + expr += ' <= 0' + else: + expr += ' < 0' + else: + builtin = BUILTIN_TYPES.get(mtype['name']) + assert builtin, mtype['name'] + tmpl = builtin.get('conv', None) + if tmpl: + expr = render(tmpl, name=name, mdvar=mdvar, tname=tname) + expr += ' < 0' + else: + return None + + return checkError('Invalid', expr, tname, mdvar) + + def _parse_array(name, member, tname): + num = 'n%sNodes' % Terms.upperInitial(tname) + tmpvars.append(num) + tmpvars.append('nodes') + count = counterName(member) + + if mtype['meta'] == 'Struct' and not member.get('xmltext'): + item = _assign_struct(name + '[i]', member, 'tnode') + else: + item = ['def->%s[i] = virXMLNodeContentString(tnode);' % name] + + ret = Block() + ret.format('${num} = virXMLChildNodeSet(node, "${tname}", &nodes);', + num=num, tname=tname) + ret.format('if (${num} > 0) {', num=num) + ret.format(' size_t i;') + ret.newline() + ret.format(' def->${name} = g_new0(typeof(*def->${name}), ${num});', + name=name, num=num) + ret.format(' for (i = 0; i < ${num}; i++) {', num=num) + ret.format(' xmlNodePtr tnode = nodes[i];') + ret.mapfmt(' ${_each_line_}', item) + ret.format(' }') + ret.format(' def->${count} = ${num};', count=count, num=num) + ret.format(' g_free(nodes);') + ret.format(' nodes = NULL;') + ret.format('} else if (${num} < 0) {', num=num) + ret.format(' virReportError(VIR_ERR_XML_ERROR,') + ret.format(' _("Invalid %s element found."),') + ret.format(' "${tname}");', tname=tname) + ret.format(' goto error;') + + if member.get('required'): + ret.append('} else {') + ret.mapfmt(' ${_each_line_}', reportMissing(tname)) + ret.append(' goto error;') + + ret.append('}') + return ret + + def getTag(member): + if member.get('xmlattr'): + return member['xmlattr'] + elif member.get('xmlelem'): + return member['xmlelem'] + else: + return None + + # + # Main routine + # + + if member.get('skipparse'): + return None + + tname = getTag(member) + if not tname and not member.get('xmlgroup'): + return None + + if pre_name: + name = pre_name + '.' + member['name'] + else: + name = member['name'] + + # For array member + if member.get('array'): + return _parse_array(name, member, tname) + + # For common member + mdvar = _middle_var(member) + if mdvar.endswith('Str') or mdvar.endswith('Node'): + tmpvars.append(mdvar) + + block = Block() + if tname: + block.append(_read_xml_func(mdvar, tname)) + if member.get('required'): + cond = render('${mdvar} == NULL', mdvar=mdvar) + block.extend(checkError('Missing', cond, tname, mdvar)) + + if mtype['meta'] == 'Struct': + assignment = _assign_struct(name, member, mdvar) + else: + assignment = _assign_non_struct(name, member, mdvar) + if not assignment: + return block + + if member.get('specified'): + sname, managed = member['specified'] + if managed: + assignment.format('def->${sname} = true;', sname=sname) + + if tname: + block.extend(if_cond(mdvar, assignment)) + else: + block.extend(assignment) + + return block + + +def makeParseFunc(writer, atype): + + # + # Helper functions + # + def _switch_parse_cb(child, switch, tmpvars): + return parseMember(child, atype, tmpvars, switch['name']) + + def _members_block(tmpvars): + block = Block() + for member in atype['members']: + if member.get('xmlswitch'): + code = makeSwitch(member, _switch_parse_cb, tmpvars) + else: + code = parseMember(member, atype, tmpvars) + + if code: + block.extend(code) + block.newline() + return block + + def _post_hook(tmpvars): + args = ['node', 'def', 'instname', 'parent', 'opaque'] + if 'namespace' in atype: + args.append('ctxt') + args.append('xmlopt') + + args.extend(tmpvars) + if 'nodes' in args: + args.remove('nodes') + + funcname = atype['name'] + 'ParseHook' + cond = '%s(%s) < 0' % (funcname, ', '.join(args)) + return if_cond(cond, ['goto error;']) + + def _handle_tmpvars(tmpvars): + args, heads, tails = [], [], [] + for var in tmpvars: + if var == 'nodes': + heads.append('xmlNodePtr *nodes = NULL;') + tails.append('g_free(nodes);') + tails.append('nodes = NULL;') + elif var.endswith('Str'): + heads.append('g_autofree char *%s = NULL;' % var) + args.append('const char *%s' % var) + elif var.endswith('Node'): + heads.append('xmlNodePtr %s = NULL;' % var) + args.append('xmlNodePtr %s' % var) + else: + assert var.endswith('Nodes') and var.startswith('n') + heads.append('int %s = 0;' % var) + args.append('int %s' % var) + + heads.append('void *arg_parent G_GNUC_UNUSED = def;') + heads.append('void *arg_opaque G_GNUC_UNUSED = opaque;') + heads.append('') + + heads.append('if (!def)') + heads.append(' goto error;') + tails.append('%sClear(def);' % atype['name']) + return args, heads, tails + + # + # Composite + # + if 'genparse' not in atype: + return + + typename = atype['name'] + funcname = typename + 'ParseXML' + + # Declare virXXXParseXML + args = Block([ + 'xmlNodePtr node', + '%s *def' % typename, + 'const char *instname', + 'void *parent', + 'void *opaque' + ]) + + if 'namespace' in atype: + args.append('xmlXPathContextPtr ctxt') + args.append('virNetworkXMLOption *xmlopt') + + signature = funcSignature('int', funcname, args) + writer.write(atype, 'parsefunc', '.h', signature.output() + ';') + + # Prepare for implementation + tmpvars = [] + parseblock = _members_block(tmpvars) + tmpvars = dedup(tmpvars) + tmpargs, headlines, cleanup = _handle_tmpvars(tmpvars) + posthook = _post_hook(tmpvars) + + # Declare virXXXParseXMLHook + macro = 'ENABLE_' + Terms.allcaps(typename) + '_PARSE_HOOK' + signature = funcSignature('int', typename + 'ParseHook', + args + tmpargs, ';') + + hook_decl = Block() + hook_decl.format('#ifdef ${macro}', macro=macro) + hook_decl.newline() + hook_decl.extend(signature) + hook_decl.newline() + hook_decl.format('#endif') + writer.write(atype, 'parsefunc', '.h', hook_decl.output()) + + setargs_args = ['xmlNodePtr node', 'void *parent', + 'void **pparent', 'void **popaque'] + setargs_signature = funcSignature('void', funcname + 'SetArgs', + setargs_args, ';') + + setargs_decl = Block() + setargs_decl.format('#ifdef ${macro}_SET_ARGS', macro=macro) + setargs_decl.newline() + setargs_decl.extend(setargs_signature) + setargs_decl.newline() + setargs_decl.format('#endif') + writer.write(atype, 'parsefunc', '.h', setargs_decl.output()) + + # Implement virXXXParseXML + args[2] += ' G_GNUC_UNUSED' + args[3] += ' G_GNUC_UNUSED' + args[4] += ' G_GNUC_UNUSED' + + impl = funcSignature('int', funcname, args) + impl.format('{') + impl.mapfmt(' ${_each_line_}', headlines) + + impl.newline() + impl.format('#ifdef ${macro}_SET_ARGS', macro=macro) + impl.format(' ${funcname}(node, parent, &arg_parent, &arg_opaque);', + funcname=(funcname + 'SetArgs')) + impl.format('#endif') + impl.newline() + + impl.mapfmt(' ${_each_line_}', parseblock) + + impl.format('#ifdef ${macro}', macro=macro) + impl.mapfmt(' ${_each_line_}', posthook) + impl.format('#endif') + impl.newline() + + if 'namespace' in atype: + impl.mapfmt(' ${_each_line_}', T_NAMESPACE_PARSE) + + impl.format(' return 0;') + impl.newline() + impl.format(' error:') + impl.mapfmt(' ${_each_line_}', cleanup) + impl.format(' return -1;') + impl.format('}') + + writer.write(atype, 'parsefunc', '.c', impl.output()) + + +def if_cond(condition, block): + assert isinstance(block, list) and len(block) > 0 + lp = ' {' if len(block) > 1 else '' + + ret = Block() + ret.format('if (${condition})${lp}', condition=condition, lp=lp) + ret.mapfmt(' ${_each_line_}', block) + ret.format('}' if lp else None) + return ret + + +def formatMember(member, parent): + mtype = TypeTable().get(member['type']) + + # + # Helper functions. + # + def _checkOnCondition(var): + ret = None + if ispointer(member): + ret = var + elif member.get('specified'): + sname, _ = member['specified'] + ret = 'def->' + sname + if ret.startswith('&'): + ret = ret[1:] + elif mtype['meta'] == 'Struct': + ret = '%sCheck(&%s, def, opaque)' % (mtype['name'], var) + elif mtype['meta'] == 'Enum': + if mtype['with_default']: + ret = var + ' > 0' + else: + ret = var + ' >= 0' + elif mtype['meta'] == 'Builtin': + if mtype['name'] in ['Chars', 'UChars']: + ret = var + '[0]' + else: + ret = var + + return ret + + def _format(layout, var): + if member.get('array'): + buf = 'buf' + elif member.get('wrap'): + buf = '&%s_%sBuf' % (member['wrap'], member['xmlattr']) + else: + buf = '&' + member['name'].replace('.', '_') + 'Buf' + + if mtype['meta'] == 'Struct': + if not ispointer(member): + var = '&' + var + + fname = mtype['name'] + 'FormatBuf' + cond = '%s(%s, "%s", %s, def, opaque) < 0' % \ + (fname, buf, layout, var) + return if_cond(cond, ['return -1;']) + elif mtype['meta'] == 'Enum': + name = mtype['name'] + if not name.endswith('Type'): + name += 'Type' + + ret = Block() + ret.format('const char *str = ${name}ToString(${var});', + name=name, var=var) + ret.format('if (!str) {') + ret.format(' virReportError(VIR_ERR_INTERNAL_ERROR,') + ret.format(' _("Unknown %s type %d"),') + ret.format(' "${tname}", ${var});', + tname=member['xmlattr'], var=var) + ret.format(' return -1;') + ret.format('}') + ret.format('virBufferAsprintf(${buf}, "${layout}", str);', + buf=buf, layout=layout) + return ret + elif mtype['name'] in ['Bool', 'BoolYesNo']: + var = '%s ? "yes" : "no"' % var + return ['virBufferEscapeString(%s, "%s", %s);' % (buf, layout, var)] + elif mtype['name'] == 'BoolOnOff': + var = '%s ? "on" : "off"' % var + return ['virBufferEscapeString(%s, "%s", %s);' % (buf, layout, var)] + elif mtype['name'] == 'BoolTrueFalse': + var = '%s ? "true" : "false"' % var + return ['virBufferEscapeString(%s, "%s", %s);' % (buf, layout, var)] + elif mtype['name'] == 'String': + return ['virBufferEscapeString(%s, "%s", %s);' % (buf, layout, var)] + elif mtype['name'] == 'Time': + return ['virTimeFormatBuf(%s, "%s", %s);' % (buf, layout, var)] + else: + return ['virBufferAsprintf(%s, "%s", %s);' % (buf, layout, var)] + + def _handleAttr(tagname, var): + if 'xmlattr' not in member: + return None + + fmt = '%s' + if mtype['meta'] == 'Builtin': + fmt = BUILTIN_TYPES[mtype['name']].get('fmt', '%s') + + if tagname: + layout = " %s='%s'" % (tagname, fmt) + else: + layout = "%s" + return _format(layout, var) + + def _handleElem(tagname, var): + if 'xmlattr' in member: + return None + + if member.get('xmltext') or mtype['meta'] != 'Struct': + layout = '<%s>%%s</%s>\\n' % (tagname, tagname) + else: + layout = tagname + + code = _format(layout, var) + return code + + # + # Main routine + # + name = member['name'] + if member.get('array'): + name = name + '[i]' + var = 'def->' + name + + ret = None + if 'xmlattr' in member: + tagname = member['xmlattr'] + ret = _handleAttr(tagname, var) + else: + tagname = member['xmlelem'] + ret = _handleElem(tagname, var) + + if not ret: + return None, None + + if member.get('wrap'): + hookbuf = '%s_%sBuf' % (member['wrap'], member['xmlattr']) + else: + hookbuf = member['name'].replace('.', '_') + 'Buf' + + checks = '!virBufferTouched(&%s)' % (hookbuf) + + # For array + if member.get('array'): + counter = 'def->' + counterName(member) + cond = checks + ' && ' + counter + code = Block() + code.format('virBufferInheritIndent(&${hbuf}, buf);', hbuf=hookbuf) + code.extend(loop(counter, ret, cond)) + code.format('virBufferAddBuffer(buf, &${hbuf});', hbuf=hookbuf) + return code, counter + + # Not array + check = _checkOnCondition(var) + if check and not member.get('required'): + if '&&' in check or '||' in check: + check = '(%s)' % check + checks = checks + ' && ' + check + + code = Block() + code.format('virBufferInheritIndent(&${hbuf}, buf);', hbuf=hookbuf) + code.extend(if_cond(checks, ret)) + code.format('virBufferAddBuffer(buf, &${hbuf});', hbuf=hookbuf) + return code, check + + +def makeSwitch(switch, callback, opaque=None): + assert switch.get('xmlswitch', None) and switch.get('switch_type', None) + + captype = Terms.allcaps(switch['switch_type']) + block = Block() + block.format('switch (def->${etype}) {', etype=switch['xmlswitch']) + block.newline() + for child in switch['members']: + value = captype + '_' + Terms.allcaps(child['name']) + block.format('case ${value}:', value=value) + block.mapfmt(' ${_each_line_}', callback(child, switch, opaque)) + block.format(' break;') + block.newline() + + enum = TypeTable().get(switch['switch_type']) + if enum['with_default']: + block.format('case ${captype}_NONE:', captype=captype) + + block.format('case ${captype}_LAST:', captype=captype) + block.format(' break;') + block.format('}') + return block + + +def makeFormatFunc(writer, atype): + if 'genformat' not in atype: + return + + # + # Helper functions and classes. + # + def _handleHeads(tmpvars): + tmpvars = dedup(tmpvars) + heads = Block() + heads.format('virTristateBool empty G_GNUC_UNUSED = ' + 'VIR_TRISTATE_BOOL_ABSENT;') + heads.format('virTristateBool shortcut G_GNUC_UNUSED = ' + 'VIR_TRISTATE_BOOL_ABSENT;') + heads.mapfmt( + 'g_auto(virBuffer) ${_each_line_} = VIR_BUFFER_INITIALIZER;', + tmpvars + ) + heads.newline() + + heads.format('if (!def || !buf)') + heads.format(' return 0;') + return '\n'.join(heads) + + def _handleHook(tmpvars, typename, kind=''): + funcname = typename + 'Format' + kind + 'Hook' + macro = 'ENABLE_' + Terms.allcaps(funcname) + + tmpvars = dedup(tmpvars) + args = Block(['def', 'parent', 'opaque']) + if not kind: + args.append('&empty') + args.append('&shortcut') + args.mapfmt('&${_each_line_}', tmpvars) + + heads = Block() + heads.format('if (%s(%s) < 0)' % (funcname, ', '.join(args))) + heads.format(' return -1;') + + args = Block([ + 'const %s *def' % typename, + 'const void *parent', + 'const void *opaque' + ]) + if not kind: + args.append('virTristateBool *empty') + args.append('virTristateBool *shortcut') + args.mapfmt('virBuffer *${_each_line_}', tmpvars) + + fmt_signature = funcSignature('int', funcname, args, ';') + + hook_decl = Block() + hook_decl.format('#ifdef ${macro}', macro=macro) + hook_decl.newline() + hook_decl.extend(fmt_signature) + hook_decl.newline() + hook_decl.format('#endif /* ${macro} */', macro=macro) + writer.write(atype, 'formatfunc', '.h', hook_decl.output()) + return '\n'.join(heads) + + def _handle_check_func(checks, kind=''): + # Declare virXXXCheck[Attr|Elem] + typename = atype['name'] + funcname = typename + 'Check' + kind + args = [ + 'const %s *def' % typename, + 'const void *parent', + 'void *opaque' + ] + signature = funcSignature('bool', funcname, args) + writer.write(atype, 'formatfunc', '.h', signature.output() + ';') + + # Implement virXXXFormat[Attr|Elem] + check = ' || '.join(checks) if checks else 'false' + + args[1] += ' G_GNUC_UNUSED' + args[2] += ' G_GNUC_UNUSED' + + macro = 'RESET_' + Terms.allcaps(funcname) + + impl = Block() + impl.format('#ifndef ${macro}', macro=macro) + impl.newline() + impl.extend(funcSignature('bool', funcname, args)) + impl.format('{') + impl.format(' if (!def)') + impl.format(' return false;') + impl.newline() + impl.format(' return ${check};', check=check) + impl.format('}') + impl.newline() + impl.format('#endif /* ${macro} */', macro=macro) + writer.write(atype, 'formatfunc', '.c', impl.output()) + + def _format_group(child, switch, kind): + kind = kind if kind else '' + prefix = (switch['name'] + '.') if switch else '' + mtype = TypeTable().get(child['type']) + funcname = '%sFormat%s' % (mtype['name'], kind) + var = 'def->%s%s' % (prefix, child['name']) + if not ispointer(child): + var = '&' + var + r_check = '%sCheck%s(%s, def, opaque)' % (mtype['name'], kind, var) + cond = '%s(buf, %s, def, opaque) < 0' % (funcname, var) + return if_cond(cond, ['return -1;']), r_check + + def _switch_format_cb(child, switch, kind): + return _format_group(child, switch, kind)[0] + + def _handle_wrap_attr(member): + member['wrap'], member['xmlattr'] = member['xmlattr'].split('/') + if member['xmlattr'] == '.': + member['xmlattr'] = '' + ret, check = formatMember(member, atype) + return ret, check + + def _switch_check_cb(child, switch, kind): + return ['ret = %s;' % _format_group(child, switch, kind)[1]] + + def _prepare_member(member, atype): + attr, attr_chk, elem, elem_chk = None, None, None, None + if member.get('xmlswitch'): + attr = makeSwitch(member, _switch_format_cb, 'Attr') + elem = makeSwitch(member, _switch_format_cb, 'Elem') + basename = atype['name'] + Terms.upperInitial(member['name']) + attr_chk = '%sCheckAttr(def, opaque)' % basename + elem_chk = '%sCheckElem(def, opaque)' % basename + + # Declare virXXX<UnionName>Check[Attr|Elem] for switch. + for kind in ['Attr', 'Elem']: + args = ['const %s *def' % atype['name'], 'void *opaque'] + funcname = basename + 'Check' + kind + decl = funcSignature('bool', funcname, args) + writer.write(atype, 'formatfunc', '.h', decl.output() + ';') + + # Implement virXXX<UnionName>Check[Attr|Elem] for switch. + checks = makeSwitch(member, _switch_check_cb, kind) + + args[1] += ' G_GNUC_UNUSED' + macro = 'RESET_' + Terms.allcaps(funcname) + + impl = Block() + impl.format('#ifndef ${macro}', macro=macro) + impl.newline() + impl.extend(funcSignature('bool', funcname, args)) + impl.format('{') + impl.format(' bool ret = false;') + impl.format(' if (!def)') + impl.format(' return false;') + impl.newline() + impl.mapfmt(' ${_each_line_}', checks) + impl.newline() + impl.format(' return ret;') + impl.format('}') + impl.newline() + impl.format('#endif /* ${macro} */', macro=macro) + writer.write(atype, 'formatfunc', '.c', impl.output()) + + elif member.get('xmlattr'): + if '/' in member['xmlattr']: + attr, attr_chk = _handle_wrap_attr(member) + else: + attr, attr_chk = formatMember(member, atype) + elif member.get('xmlelem'): + elem, elem_chk = formatMember(member, atype) + elif member.get('xmlgroup'): + attr, attr_chk = _format_group(member, None, 'Attr') + elem, elem_chk = _format_group(member, None, 'Elem') + return attr, attr_chk, elem, elem_chk + + class _WrapItem: + def __init__(self): + self.attrs = Block() + self.checks = [] + self.hchecks = Block() + self.optional = True + self.pos = 0 + + def _prepare(): + attrs = Block() + elems = Block() + attr_checks = [] + elem_checks = [] + attr_hook_vars = [] + elem_hook_vars = [] + wraps = OrderedDict() + for member in atype['members']: + attr, attr_chk, elem, elem_chk = _prepare_member(member, atype) + if member.get('wrap'): + item = wraps.setdefault(member['wrap'], _WrapItem()) + item.pos = len(elems) + if member['xmlattr']: + item.attrs.extend(attr) + item.attrs.newline() + item.checks.append(attr_chk) + if member.get('required'): + item.optional = False + wrap_var = '%s_%sBuf' % (member['wrap'], member['xmlattr']) + elem_hook_vars.append(wrap_var) + item.hchecks.format('virBufferUse(&${buf})', buf=wrap_var) + continue + + attrs.extend(attr) + attrs.newline(attr) + elems.extend(elem) + elems.newline(elem) + if attr_chk: + attr_checks.append(attr_chk) + if elem_chk: + elem_checks.append(elem_chk) + if member.get('xmlattr'): + attr_hook_vars.append(member['name'].replace('.', '_') + 'Buf') + elif member.get('xmlelem'): + elem_hook_vars.append(member['name'].replace('.', '_') + 'Buf') + + while wraps: + wrap, item = wraps.popitem() + lines = Block() + lines.format('virBufferAddLit(buf, "<${name}");', name=wrap) + if item.attrs: + lines.newline() + lines.extend(item.attrs) + lines.format('virBufferAddLit(buf, "/>\\n");') + + checks = item.hchecks + if item.optional: + checks.extend(item.checks) + elem_checks.extend(item.checks) + + if checks: + lines = if_cond(' || '.join(checks), lines) + lines.newline() + + for line in reversed(lines): + elems.insert(item.pos, line) + + attr_checks = dedup(attr_checks) + elem_checks = dedup(elem_checks) + return (attrs, attr_checks, attr_hook_vars, + elems, elem_checks, elem_hook_vars) + + def _compose_full(attrs, attr_checks, attr_hook_vars, + elems, elem_checks, elem_hook_vars): + typename = atype['name'] + + # Declare virXXXFormatBuf + args = [ + 'virBuffer *buf', + 'const char *name', + 'const %s *def' % typename, + 'const void *parent', + 'void *opaque' + ] + signature = funcSignature('int', typename + 'FormatBuf', args) + writer.write(atype, 'formatfunc', '.h', signature.output() + ';') + + # Implement virXXXFormatBuf + headlines = _handleHeads(attr_hook_vars + elem_hook_vars) + hooklines = _handleHook(attr_hook_vars + elem_hook_vars, typename) + + args[3] += ' G_GNUC_UNUSED' + args[4] += ' G_GNUC_UNUSED' + + macro = 'ENABLE_' + Terms.allcaps(typename) + '_FORMAT_HOOK' + + impl = funcSignature('int', typename + 'FormatBuf', args) + impl.format('{') + impl.mapfmt(' ${_each_line_}', headlines.split('\n')) + impl.newline() + impl.format('#ifdef ${macro}', macro=macro) + impl.mapfmt(' ${_each_line_}', hooklines.split('\n')) + impl.format('#endif /* ${macro} */', macro=macro) + impl.newline() + + empty_checks = '' + if attr_checks or elem_checks: + empty_checks = ' || !(%s)' % ' || '.join(attr_checks + elem_checks) + + impl.format(' if (empty != VIR_TRISTATE_BOOL_NO)') + impl.format(' if (empty${checks})', checks=empty_checks) + impl.format(' return 0;') + impl.newline() + impl.format(' virBufferAsprintf(buf, "<%s", name);') + impl.newline() + + if 'namespace' in atype: + impl.mapfmt(' ${_each_line_}', T_NAMESPACE_FORMAT_BEGIN) + + impl.mapfmt(' ${_each_line_}', attrs) + + if elems: + if attrs: + + shortcut_checks = '' + if elem_checks: + shortcut_checks = ' || !(%s)' % ' || '.join(elem_checks) + + impl.format(' if (shortcut != VIR_TRISTATE_BOOL_NO) {') + impl.format(' if (shortcut${checks}) {', + checks=shortcut_checks) + impl.format(' virBufferAddLit(buf, "/>\\n");') + impl.format(' return 0;') + impl.format(' }') + impl.format(' }') + impl.newline() + + impl.format(' virBufferAddLit(buf, ">\\n");') + impl.newline() + impl.format(' virBufferAdjustIndent(buf, 2);') + impl.newline() + impl.mapfmt(' ${_each_line_}', elems) + + if 'namespace' in atype: + impl.mapfmt(' ${_each_line_}', T_NAMESPACE_FORMAT_END) + impl.newline() + + impl.format(' virBufferAdjustIndent(buf, -2);') + impl.format(' virBufferAsprintf(buf, "</%s>\\n", name);') + else: + impl.format(' virBufferAddLit(buf, "/>\\n");') + + impl.newline() + impl.format(' return 0;') + impl.format('}') + writer.write(atype, 'formatfunc', '.c', impl.output()) + + # Construct check function + _handle_check_func(attr_checks + elem_checks) + + def _compose_part(kind, block, checks, hook_vars): + typename = atype['name'] + funcname = typename + 'Format' + kind + headlines = _handleHeads(hook_vars) + hooklines = _handleHook(hook_vars, atype['name'], kind) + if not block: + block = ['/* no code */', ''] + + # Declare virXXXFormat[Attr|Elem] + args = [ + 'virBuffer *buf', + 'const %s *def' % typename, + 'const void *parent', + 'void *opaque' + ] + signature = funcSignature('int', funcname, args) + writer.write(atype, 'formatfunc', '.h', signature.output() + ';') + + # Implement virXXXFormat[Attr|Elem] + args[2] += ' G_GNUC_UNUSED' + args[3] += ' G_GNUC_UNUSED' + + macro = 'ENABLE_' + Terms.allcaps(typename + 'Format' + kind) + '_HOOK' + + impl = funcSignature('int', funcname, args) + impl.format('{') + impl.mapfmt(' ${_each_line_}', headlines.split('\n')) + impl.newline() + impl.format('#ifdef ${macro}', macro=macro) + impl.mapfmt(' ${_each_line_}', hooklines.split('\n')) + impl.format('#endif /* ${macro} */', macro=macro) + impl.newline() + impl.mapfmt(' ${_each_line_}', block) + impl.format(' return 0;') + impl.format('}') + writer.write(atype, 'formatfunc', '.c', impl.output()) + + # Construct check function + _handle_check_func(checks, kind) + + # + # Main routine of formating. + # + (attrs, attr_checks, attr_hook_vars, + elems, elem_checks, elem_hook_vars) = _prepare() + + if atype['genformat'] in ['separate']: + _compose_part('Attr', attrs, attr_checks, attr_hook_vars) + _compose_part('Elem', elems, elem_checks, elem_hook_vars) + else: + _compose_full(attrs, attr_checks, attr_hook_vars, + elems, elem_checks, elem_hook_vars,) + + +def showDirective(atype): + print('\n[Directive]\n') + print(json.dumps(atype, indent=4) + '\n') diff --git a/scripts/xmlgen/go b/scripts/xmlgen/go new file mode 100755 index 00000000..6c4abe7a --- /dev/null +++ b/scripts/xmlgen/go @@ -0,0 +1,29 @@ +# This is a command-line tool +# +# Copyright (C) 2021 Shandong Massclouds Co.,Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see +# <http://www.gnu.org/licenses/>. +# + +DIR="src" +if [ ${FOR_TEST} ]; then + DIR="tests/xmlgenin" +fi + +export PYTHONDONTWRITEBYTECODE=1 +WORK_DIR=$(cd $(dirname $0); pwd) +SRCDIR="${WORK_DIR}/../../${DIR}" +BUILDDIR="${WORK_DIR}/../../build/${DIR}" +${WORK_DIR}/main.py -s ${SRCDIR} -b ${BUILDDIR} $@ diff --git a/scripts/xmlgen/main.py b/scripts/xmlgen/main.py new file mode 100755 index 00000000..48c94984 --- /dev/null +++ b/scripts/xmlgen/main.py @@ -0,0 +1,534 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 Shandong Massclouds Co.,Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see +# <http://www.gnu.org/licenses/>. +# + +import os +import re +import sys +import argparse +from clang.cindex import Index, CursorKind +from clang.cindex import SourceLocation, SourceRange, TokenKind +from datetime import datetime +from directive import TypeTable, showDirective +from directive import makeClearFunc, makeParseFunc, makeFormatFunc +from utils import Terms, Block + +TOOL_DESC = ''' +Generate xml parse/format functions based on directives. + +Subcommand:\n + list: List types. By default, only list structs tagged by + 'genparse'/'genformat'. When the option '-a' is specified, + list all types discovered by this tool.\n + show: Show the target type's directives and its code for preview. + Specify target type by its name. + The option '-k' can be used to filter output.\n + generate: Generate code. To be called by Makefile. + The option '-k' can be used to filter output. + +Option:\n + -k: Specify kinds to filter code output. More than one kind can be + specified, 'p' for parsefunc and 'f' for formatfunc. + When it is omitted, all functions will be outputed.\n + The option '-k' is only valid for show and generate. +''' + +DIRECTIVES = [ + 'genparse', 'genformat', 'xmlattr', 'xmlelem', 'xmltext', 'xmlgroup', + 'required', 'array', 'specify', 'xmlswitch', 'skipparse', +] + +BUILTIN_MAP = { + 'bool': 'Bool', + 'virBoolYesNo': 'BoolYesNo', + 'virBoolOnOff': 'BoolOnOff', + 'virBoolTrueFalse': 'BoolTrueFalse', + 'char': 'Char', + 'unsigned char': 'UChar', + 'int': 'Int', + 'unsigned int': 'UInt', + 'long': 'Long', + 'unsigned long': 'ULong', + 'long long': 'LongLong', + 'unsigned long long': 'ULongLong', + 'uint8_t': 'U8', + 'uint32_t': 'U32', + 'time_t': 'Time', +} + + +# Three builtin types need to be handled specially: +# 'char *' => String +# 'char XXXX[...]' => Chars +# 'unsigned char XXXX[...]' => UChars +def getBuiltinType(ctype, ptr=False, size=None): + if ctype == 'char': + if ptr: + return 'String' + elif size: + return 'Chars' + + if ctype == 'unsigned char' and size: + return 'UChars' + + return BUILTIN_MAP.get(ctype, None) + + +def cursorLineExtent(cursor, tu): + loc = cursor.location + start = SourceLocation.from_position(tu, loc.file, loc.line, 1) + end = SourceLocation.from_position(tu, loc.file, loc.line, -1) + return SourceRange.from_locations(start, end) + + +def getTokens(cursor, tu): + return tu.get_tokens(extent=cursorLineExtent(cursor, tu)) + + +def createDirectives(text): + tlist = re.findall(r'/\*(.*)\*/', text) + if len(tlist) != 1: + return None + + tlist = tlist[0].split(',') + if len(tlist) == 0: + return None + + directives = {} + for item in tlist: + item = item.strip() + if ':' in item: + key, value = item.split(':') + else: + key, value = item, None + + if key in DIRECTIVES: + directives[key] = value + return directives + + +def getDirectives(tokens, cursor): + for token in tokens: + if token.location.column <= cursor.location.column: + continue + if token.kind == TokenKind.COMMENT: + directive = createDirectives(token.spelling) + if directive: + return directive + return None + + +def determinType(kvs, tokens, cursor): + prefix = [] + kind = None + for token in tokens: + if token.location.column >= cursor.location.column: + break + if not kind: + kind = token.kind + prefix.append(token.spelling) + + suffix = [] + for token in tokens: + if token.spelling == ';': + break + suffix.append(token.spelling) + + size = None + if len(suffix) == 3 and suffix[0] == '[' and suffix[2] == ']': + size = suffix[1] + + assert kind in [TokenKind.IDENTIFIER, TokenKind.KEYWORD], \ + 'Bad field "%s" (%s).' % (cursor.spelling, kind) + + assert prefix + typename = ' '.join(prefix) + + # For array, remove the most-outer pointer + if kvs.get('array'): + if typename.endswith('*'): + typename = typename[:-1].strip() + + ptr = False + if typename.endswith('*'): + typename = typename[:-1].strip() + ptr = True + + ret = getBuiltinType(typename, ptr, size) + if ret: + typename = ret + + kvs.update({'type': typename, 'pointer': ptr}) + if size: + kvs['size'] = size + return kvs + + +def analyseMember(cursor, tu, struct): + if cursor.spelling == 'namespaceData': + struct['namespace'] = True + + dvs = getDirectives(getTokens(cursor, tu), cursor) + if not dvs: + return None + + kvs = {'name': cursor.spelling} + kvs.update(dvs) + + # Formalize member + for key in ['array', 'required', 'skipparse', 'xmltext']: + if key in kvs and not kvs[key]: + kvs[key] = True + + if kvs.get('specify'): + assert isinstance(kvs['specify'], str) and len(kvs['specify']) > 0 + + for tag in ['xmlattr', 'xmlelem', 'xmlgroup']: + if tag in kvs: + if not kvs[tag]: + if kvs.get('array'): + kvs[tag] = Terms.singularize(kvs['name']) + else: + kvs[tag] = kvs['name'] + + return determinType(kvs, getTokens(cursor, tu), cursor) + + +def findMember(struct, name): + for member in struct['members']: + if member['name'] == name: + return member + + return None + + +def analyseStruct(struct, cursor, tu): + tokens = getTokens(cursor, tu) + kvs = getDirectives(tokens, cursor) + if not kvs: + return struct + + path, _ = os.path.splitext(cursor.location.file.name) + path, filename = os.path.split(path) + _, dirname = os.path.split(path) + kvs['output'] = dirname + '/' + filename + struct.update(kvs) + + last_kind = None + inner_members = [] + for child in cursor.get_children(): + if inner_members: + if last_kind == CursorKind.STRUCT_DECL: + # Flatten the members of embedded struct + for member in inner_members: + member['name'] = child.spelling + '.' + member['name'] + struct['members'].append(member) + else: + # Embedded union + union = getDirectives(getTokens(child, tu), child) + if union and 'xmlswitch' in union: + switch = findMember(struct, union['xmlswitch']) + switch['delay_clear'] = True + union['switch_type'] = switch['type'] + union['name'] = child.spelling + union['members'] = inner_members + struct['members'].append(union) + struct['xmlswitch'] = union['xmlswitch'] + + inner_members = [] + last_kind = None + continue + + if child.kind in [CursorKind.STRUCT_DECL, CursorKind.UNION_DECL]: + last_kind = child.kind + for ichild in child.get_children(): + member = analyseMember(ichild, tu, struct) + if member: + inner_members.append(member) + continue + + member = analyseMember(child, tu, struct) + if member: + struct['members'].append(member) + + return struct + + +# Check whether the first item of enum is default. +# (i.e. it ends with '_NONE' or '_DEFAULT') +def checkFirstEnumItem(cursor): + find_paren = False + for token in cursor.get_tokens(): + # skip all parts in front of '{' + if not find_paren: + if token.spelling != '{': + continue + find_paren = True + + if token.kind == TokenKind.IDENTIFIER: + return token.spelling.endswith(('_NONE', '_DEFAULT', '_ABSENT')) + + return False + + +def rescanStruct(struct): + for member in struct['members']: + if member.get('specify'): + tname = member['specify'] + target = findMember(struct, tname) + assert target, "Can't find '%s' in '%s'" % (tname, struct['name']) + managed = not (member.get('xmlattr') or member.get('xmlelem')) + target['specified'] = (member['name'], managed) + + +def discoverStructures(tu): + for cursor in tu.cursor.get_children(): + if cursor.kind == CursorKind.STRUCT_DECL and cursor.is_definition(): + # Detect structs + name = cursor.spelling + if not name: + continue + if name.startswith('_'): + name = name[1:] + if TypeTable().check(name): + continue + struct = {'name': name, 'meta': 'Struct', 'members': []} + analyseStruct(struct, cursor, tu) + rescanStruct(struct) + TypeTable().register(struct) + elif cursor.kind == CursorKind.TYPEDEF_DECL: + # Detect enums + # We can't seek out enums by CursorKind.ENUM_DECL, + # since almost all enums are anonymous. + token = cursor.get_tokens() + try: + next(token) # skip 'typedef' + if next(token).spelling == 'enum': + if not TypeTable().check(cursor.spelling): + enum = {'name': cursor.spelling, 'meta': 'Enum', + 'with_default': checkFirstEnumItem(cursor)} + TypeTable().register(enum) + except StopIteration: + pass + + +class CodeWriter(object): + def __init__(self, args): + self._buildtop = args.buildtop + self._curdir = args.curdir + self._cmd = args.cmd + self._files = {} + self._filters = {} + self._filters['clearfunc'] = args.kinds and 'p' in args.kinds + self._filters['parsefunc'] = args.kinds and 'p' in args.kinds + self._filters['formatfunc'] = args.kinds and 'f' in args.kinds + if args.cmd == 'show': + self._filters['target'] = args.target + + def _getFile(self, path, ext): + assert ext in ['.h', '.c'] + _, basename = os.path.split(path) + path = '%s.generated%s' % (path, ext) + f = self._files.get(path) + if f is None: + f = open(path, 'w') + f.write('/* Generated by scripts/xmlgen */\n\n') + if ext in ['.c']: + f.write('#include <config.h>\n') + f.write('#include "%s.h"\n' % basename) + f.write('#include "viralloc.h"\n') + f.write('#include "virerror.h"\n') + f.write('#include "virstring.h"\n\n') + f.write('#include "virenum.h"\n\n') + f.write('#define VIR_FROM_THIS VIR_FROM_NONE\n') + else: + f.write('#pragma once\n\n') + f.write('#include "internal.h"\n') + f.write('#include "virxml.h"\n') + self._files[path] = f + return f + + def write(self, atype, kind, extname, content): + if not self._filters[kind]: + return + + if self._cmd == 'show': + target = self._filters['target'] + if not target or target == atype['name']: + outputname = atype.get('output', '') + '.generated' + if extname == '.h': + print('\n[%s.h]' % outputname) + else: + print('\n[%s.c]' % outputname) + print('\n' + content + '\n') + return + + assert self._cmd == 'generate' + + if atype.get('output') and atype['output'].startswith(self._curdir): + lfs = '\n' if extname == '.h' else '\n\n' + path = self._buildtop + '/' + atype['output'] + f = self._getFile(path, extname) + f.write(lfs + content + '\n') + + def complete(self): + for name in self._files: + self._files[name].close() + self._files.clear() + + +def getHFiles(path): + retlist = [] + for fname in os.listdir(path): + if fname.endswith('.h'): + retlist.append(os.path.join(path, fname)) + return retlist + + +def showTips(target, kinds): + atype = TypeTable().get(target) + if not atype or atype.get('external'): + sys.exit(0) + + kind_parse = kinds and 'p' in kinds and 'genparse' in atype + kind_format = kinds and 'f' in kinds and 'genformat' in atype + if not kind_parse and not kind_format: + sys.exit(0) + + captype = Terms.allcaps(target) + pathname = atype.get('output', '') + subdir, basename = os.path.split(pathname) + + tips = Block() + tips.newline() + tips.append('[Tips]') + tips.newline() + tips.format('/* Put these lines at the bottom of "${path}.h" */', + path=pathname) + tips.format('/* Makesure "${base}.h" to be appended into ' + '${cur}_xmlgen_input in src/${cur}/meson.build */', + base=basename, cur=subdir) + + if kind_parse or kind_format: + tips.newline() + tips.append('/* Define macro to enable hook or redefine check ' + 'when necessary */') + + if kind_parse: + tips.format('/* #define ENABLE_${cap}_PARSE_HOOK */', cap=captype) + tips.format('/* #define ENABLE_${cap}_PARSE_HOOK_SET_ARGS */', + cap=captype) + + if kind_format: + if atype.get('genformat') in ['separate']: + tips.format('/* #define ENABLE_${cap}_FORMAT_ATTR_HOOK */', + cap=captype) + tips.format('/* #define ENABLE_${cap}_FORMAT_ELEM_HOOK */', + cap=captype) + tips.newline() + tips.format('/* #define RESET_${cap}_CHECK_ATTR */', + cap=captype) + tips.format('/* #define RESET_${cap}_CHECK_ELEM */', + cap=captype) + else: + tips.format('/* #define ENABLE_${cap}_FORMAT_HOOK */', + cap=captype) + tips.newline() + tips.format('/* #define RESET_${cap}_CHECK */', cap=captype) + + tips.newline() + tips.append('/* Makesure below is the bottom line! */') + tips.format('#include "${base}.generated.h"', base=basename) + print(tips.output()) + + +HELP_LIST = 'list structs tagged by "genparse"/"genformat"' +HELP_LIST_ALL = 'list all discovered types' + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description=TOOL_DESC) + parser.add_argument('-s', dest='srctop', help='top source directory') + parser.add_argument('-b', dest='buildtop', help='top build directory') + parser.add_argument('-d', dest='curdir', + help='directory to be dealt with. (util or conf)') + subparsers = parser.add_subparsers(dest='cmd') + parser_list = subparsers.add_parser('list', help=HELP_LIST) + parser_list.add_argument('-a', dest='list_all', action='store_true', + default=False, help=HELP_LIST_ALL) + parser_show = subparsers.add_parser('show', help='show target code') + parser_show.add_argument('target', help='target for being previewed') + parser_show.add_argument('-k', dest='kinds', default='pf', + help='kinds of code to be previewed') + parser_show.add_argument('-V', dest='verbose', action='store_true', + help='internal information for debug') + parser_generate = subparsers.add_parser('generate', help='generate code') + parser_generate.add_argument('-k', dest='kinds', default='pf', + help='kinds of code to be generated') + args = parser.parse_args() + + if not args.cmd or not args.srctop or not args.buildtop: + parser.print_help() + sys.exit(1) + + if args.cmd == 'generate': + print('###### xmlgen: work in src/%s ... ######' % args.curdir) + + timestamp = datetime.now() + + # Examine all *.h in "$(srctop)/[util|conf]" + index = Index.create() + srctop = args.srctop + hfiles = getHFiles(srctop + '/util') + getHFiles(srctop + '/conf') + for hfile in hfiles: + tu = index.parse(hfile) + discoverStructures(tu) # find all structs and enums + + if args.cmd == 'list': + print('%-64s %s' % ('TYPENAME', 'META')) + for name, kvs in TypeTable().items(): + if not args.list_all: + if not ('genparse' in kvs or 'genformat' in kvs): + continue + print('%-64s %s' % (name, kvs['meta'])) + sys.exit(0) + elif args.cmd == 'show': + assert args.target, args + atype = TypeTable().get(args.target) + if not atype: + sys.exit(0) + if args.verbose: + showDirective(atype) + + writer = CodeWriter(args) + + for atype in TypeTable().values(): + makeClearFunc(writer, atype) + makeParseFunc(writer, atype) + makeFormatFunc(writer, atype) + + writer.complete() + + if args.cmd == 'generate': + elapse = (datetime.now() - timestamp).microseconds + print('\n###### xmlgen: elapse %d(us) ######\n' % elapse) + elif args.cmd == 'show': + showTips(args.target, args.kinds) + + sys.exit(0) diff --git a/scripts/xmlgen/utils.py b/scripts/xmlgen/utils.py new file mode 100644 index 00000000..922cd6d9 --- /dev/null +++ b/scripts/xmlgen/utils.py @@ -0,0 +1,121 @@ +# +# Copyright (C) 2021 Shandong Massclouds Co.,Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see +# <http://www.gnu.org/licenses/>. +# + +from string import Template + + +def singleton(cls): + _instances = {} + + def inner(): + if cls not in _instances: + _instances[cls] = cls() + return _instances[cls] + return inner + + +class Terms(object): + abbrs = ['uuid', 'pci', 'zpci', 'ptr', 'mac', 'mtu', 'dns', 'ip', 'dhcp'] + plurals = {'addresses': 'address'} + caps = {'NET_DEV': 'NETDEV', 'MACTABLE': 'MAC_TABLE'} + + @classmethod + def _split(cls, word): + ret = [] + if not word: + return ret + head = 0 + for pos in range(1, len(word)): + if word[pos].isupper() and not word[pos - 1].isupper(): + ret.append(word[head:pos]) + head = pos + ret.append(word[head:]) + return ret + + @classmethod + def singularize(cls, name): + ret = cls.plurals.get(name, None) + if ret: + return ret + assert name.endswith('s') + return name[:-1] + + # Don't use str.capitalize() which force other letters to be lowercase. + @classmethod + def upperInitial(cls, word): + if not word: + return '' + if word in cls.abbrs: + return word.upper() + if len(word) > 0 and word[0].isupper(): + return word + return word[0].upper() + word[1:] + + @classmethod + def allcaps(cls, word): + if len(word) == 0: + return word + parts = cls._split(word) + ret = '_'.join([part.upper() for part in parts]) + for key, value in cls.caps.items(): + ret = ret.replace('_%s_' % key, '_%s_' % value) + return ret + + +def render(template, **kwargs): + return Template(template).substitute(kwargs) + + +class Block(list): + def format(self, template, **args): + if template: + self.append(Template(template).substitute(**args)) + + def extend(self, block): + if isinstance(block, list): + super(Block, self).extend(block) + + # ${_each_line_} is the only legal key for template + # and represents each line of the block. + def mapfmt(self, template, block): + if not block or not template: + return + + assert isinstance(block, list), block + for line in block: + if line: + self.append(Template(template).substitute(_each_line_=line)) + else: + self.append('') + + def newline(self, condition=True): + if condition: + super(Block, self).append('') + + def output(self, connector='\n'): + return connector.join(self) + + +def dedup(alist): + assert isinstance(alist, list) + ret = [] + for e in alist: + if e not in ret: + ret.append(e) + + return ret -- 2.25.1

Make sure 'python3-clang' and 'libclang' have been installed and can work. Also, add 'python3-clang' into libvirt.spec.in and mingw-libvirt.spec.in. Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- libvirt.spec.in | 1 + meson.build | 10 ++++++++++ mingw-libvirt.spec.in | 1 + 3 files changed, 12 insertions(+) diff --git a/libvirt.spec.in b/libvirt.spec.in index be74964b..4ebd67ce 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -277,6 +277,7 @@ BuildRequires: perl-interpreter BuildRequires: perl %endif BuildRequires: python3 +BuildRequires: python3-clang BuildRequires: systemd-units %if %{with_libxl} BuildRequires: xen-devel diff --git a/meson.build b/meson.build index 837955de..a99be250 100644 --- a/meson.build +++ b/meson.build @@ -2406,3 +2406,13 @@ if conf.has('WITH_QEMU') } summary(priv_summary, section: 'Privileges') endif + +py3_clang = run_command('python3', '-c', 'import clang.cindex;print("ok")') +if py3_clang.returncode() != 0 + error('python3-clang is required.') +endif + +py3_clang_working = run_command('python3', '-c', 'import clang.cindex;clang.cindex.Index.create()') +if py3_clang_working.returncode() != 0 + error('python3-clang is present, but not working. Perhaps libclang is missing?') +endif diff --git a/mingw-libvirt.spec.in b/mingw-libvirt.spec.in index 288f533d..00b54d4a 100644 --- a/mingw-libvirt.spec.in +++ b/mingw-libvirt.spec.in @@ -52,6 +52,7 @@ BuildRequires: pkgconfig BuildRequires: gettext BuildRequires: libxslt BuildRequires: python3 +BuildRequires: python3-clang BuildRequires: perl-interpreter BuildRequires: perl(Getopt::Long) BuildRequires: meson -- 2.25.1

Monitor changes of header-files in src/util and src/conf. Whenever that happens, the tool xmlgen will generate parse/format functions based on these files automatically. Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- scripts/meson.build | 8 ++++++++ src/conf/meson.build | 36 ++++++++++++++++++++++++++++++++++++ src/meson.build | 6 ++++++ src/util/meson.build | 36 ++++++++++++++++++++++++++++++++++++ tests/meson.build | 2 ++ tools/meson.build | 3 +++ 6 files changed, 91 insertions(+) diff --git a/scripts/meson.build b/scripts/meson.build index 421e3d2a..5399868c 100644 --- a/scripts/meson.build +++ b/scripts/meson.build @@ -35,3 +35,11 @@ foreach name : scripts sname = name.split('.')[0].underscorify() set_variable('@0@_prog'.format(sname), find_program(name)) endforeach + +xmlgen_self = files( + 'xmlgen/main.py', + 'xmlgen/directive.py', + 'xmlgen/utils.py' +) + +set_variable('virxmlgen_prog', find_program('xmlgen/main.py')) diff --git a/src/conf/meson.build b/src/conf/meson.build index bd35d87e..1439c31d 100644 --- a/src/conf/meson.build +++ b/src/conf/meson.build @@ -1,3 +1,38 @@ +conf_xmlgen_input = [ +] + +conf_xmlgen_output = [] +foreach name : conf_xmlgen_input + conf_xmlgen_output += '@0@.generated.c'.format(name.split('.')[0]) + conf_xmlgen_output += '@0@.generated.h'.format(name.split('.')[0]) +endforeach + +conf_xmlgen_headers = [] +if conf_xmlgen_output.length() > 0 + conf_xmlgen_objects = custom_target( + 'virxmlgen', + input: xmlgen_self + conf_xmlgen_input, + output: conf_xmlgen_output, + command: [ + meson_python_prog, python3_prog.path(), '-B', virxmlgen_prog.path(), + '-s', meson.source_root() / 'src', '-b', meson.build_root() / 'src', + '-d', 'conf', 'generate', + ], + ) + + index = 0 + foreach header : conf_xmlgen_objects.to_list() + if index % 2 == 1 + conf_xmlgen_headers += header + endif + index += 1 + endforeach +else + conf_xmlgen_objects = [] +endif + +conf_xmlgen_dep = declare_dependency(sources: conf_xmlgen_headers) + netdev_conf_sources = [ 'netdev_bandwidth_conf.c', 'netdev_vlan_conf.c', @@ -90,6 +125,7 @@ device_conf_sources = [ virt_conf_lib = static_library( 'virt_conf', [ + conf_xmlgen_objects, chrdev_conf_sources, cpu_conf_sources, device_conf_sources, diff --git a/src/meson.build b/src/meson.build index c7ff9e97..f8ae47b4 100644 --- a/src/meson.build +++ b/src/meson.build @@ -245,6 +245,12 @@ src_dep = declare_dependency( ) subdir('conf') + +src_dep = declare_dependency( + dependencies: [ src_dep, util_xmlgen_dep, conf_xmlgen_dep ], + include_directories: [ conf_inc_dir ], +) + subdir('rpc') subdir('access') subdir('cpu') diff --git a/src/util/meson.build b/src/util/meson.build index 05934f68..0d41de92 100644 --- a/src/util/meson.build +++ b/src/util/meson.build @@ -1,3 +1,38 @@ +util_xmlgen_input = [ +] + +util_xmlgen_output = [] +foreach name : util_xmlgen_input + util_xmlgen_output += '@0@.generated.c'.format(name.split('.')[0]) + util_xmlgen_output += '@0@.generated.h'.format(name.split('.')[0]) +endforeach + +util_xmlgen_headers = [] +if util_xmlgen_output.length() > 0 + util_xmlgen_objects = custom_target( + 'virxmlgen', + input: xmlgen_self + util_xmlgen_input, + output: util_xmlgen_output, + command: [ + meson_python_prog, python3_prog.path(), '-B', virxmlgen_prog.path(), + '-s', meson.source_root() / 'src', '-b', meson.build_root() / 'src', + '-d', 'util', 'generate', + ], + ) + + index = 0 + foreach header : util_xmlgen_objects.to_list() + if index % 2 == 1 + util_xmlgen_headers += header + endif + index += 1 + endforeach +else + util_xmlgen_objects = [] +endif + +util_xmlgen_dep = declare_dependency(sources: util_xmlgen_headers) + util_sources = [ 'glibcompat.c', 'viralloc.c', @@ -179,6 +214,7 @@ io_helper_sources = [ virt_util_lib = static_library( 'virt_util', [ + util_xmlgen_objects, util_sources, util_public_sources, keycode_gen_sources, diff --git a/tests/meson.build b/tests/meson.build index 05c3e901..14ace476 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -17,6 +17,8 @@ tests_dep = declare_dependency( selinux_dep, xdr_dep, yajl_dep, + util_xmlgen_dep, + conf_xmlgen_dep, ], include_directories: [ conf_inc_dir, diff --git a/tools/meson.build b/tools/meson.build index 2acf7b0a..162db0e8 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -5,11 +5,14 @@ tools_dep = declare_dependency( dependencies: [ libxml_dep, glib_dep, + util_xmlgen_dep, + conf_xmlgen_dep, ], include_directories: [ libvirt_inc, src_inc_dir, util_inc_dir, + conf_inc_dir, top_inc_dir, ], link_args: ( -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- docs/meson.build | 1 + docs/xmlgen.rst | 684 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 685 insertions(+) create mode 100644 docs/xmlgen.rst diff --git a/docs/meson.build b/docs/meson.build index f550629d..a8a58815 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -124,6 +124,7 @@ docs_rst_files = [ 'programming-languages', 'styleguide', 'submitting-patches', + 'xmlgen', ] # list of web targets to build for docs/web rule diff --git a/docs/xmlgen.rst b/docs/xmlgen.rst new file mode 100644 index 00000000..caea1f99 --- /dev/null +++ b/docs/xmlgen.rst @@ -0,0 +1,684 @@ +====== +xmlgen +====== + +In libvirt, developers usually have to implement and maintain parse/format functions. This tool xmlgen aims to generate most of these functions automatically and help relieve developers' burden. + +A Quick Start +============= + +Take virNetworkDNSDef for example, which is in 'src/conf/network_conf.h'. + +In the past, we have to manually implement virNetworkDNSDefParseXML and virNetworkDNSFormatBuf. +And now, we can just take several steps to have xmlgen generate these functions and apply them into libvirt project. + +Step1. Add directives on the struct's declaration. +-------------------------------------------------- + +Directives for xmlgen are used to help direct the generating process. As below: + + :: + + typedef struct _virNetworkDNSDef virNetworkDNSDef; + struct _virNetworkDNSDef { /* genparse, genformat */ + virTristateBool enable; /* xmlattr */ + virTristateBool forwardPlainNames; /* xmlattr */ + size_t nfwds; + virNetworkDNSForwarder *forwarders; /* xmlelem, array:nfwds */ + size_t ntxts; + virNetworkDNSTxtDef *txts; /* xmlelem, array */ + ... ... + }; + +On the line of struct's declaration, we set two directives **genparse** and **genformat**, which direct xmlgen to generate parse/format functions for this struct respectively. In this example, these functions include virNetworkDNSDefParseXML, virNetworkDNSDefFormatBuf and some auxilliary functions. Other directives are for members. They direct xmlgen to generate code blocks in the parse/format functions. Directive **xmlattr** indicates that the member matches an xml attribute, and **xmlelem** is for an xml element. Additional directive **array** indicates that the member matches an array of xml node. + +Step2. Preview generated functions. +----------------------------------- + +By the below command line: + + :: + + # ./scripts/xmlgen/go list + +Got a list of structs detected by xmlgen, including *virNetworkDNSDef*. +Then we execute the command line as below: + + :: + + # ./scripts/xmlgen/go show virNetworkDNSDef + +All the generated functions related to virNetworkDNSDef are displayed, then we ought to preview them before really use them into libvirt project. +There is a special part **[Tips]** except the generated functions and the declaration of hooks. + +[**Tips**] provides instructions about how to apply these generated functions into project and how to enable hooks. [**Tips**] will be used in step3. + +Also, we got the declaration of hooks. In step4, we will implement a hook according to it. + +Step3. Enable hooks and include generated functions. +---------------------------------------------------- + +According to [**Tips**] that we got in step2: + + :: + + [Tips] + + /* Put these lines at the bottom of "conf/network_conf.h" */ + /* Makesure "network_conf.h" to be appended into conf_xmlgen_input in src/conf/meson.build */ + + /* Define macro to enable hook or redefine check when necessary */ + /* #define ENABLE_VIR_NETWORK_DNSDEF_PARSE_HOOK */ + /* #define ENABLE_VIR_NETWORK_DNSDEF_PARSE_HOOK_SET_ARGS */ + /* #define ENABLE_VIR_NETWORK_DNSDEF_FORMAT_HOOK */ + + /* #define RESET_VIR_NETWORK_DNSDEF_CHECK */ + + /* Makesure below is the bottom line! */ + #include "network_conf.generated.h" + +Uncomment macros and enable hooks when necessary. In this example, we only need to define ENABLE_VIR_NETWORK_DNSDEF_PARSE_HOOK to enable the post-hook for parse-function. + +Include the header-file to apply the generated functions. +In this example, we just include "network_conf.generated.h" into "conf/network_conf.h" and modify "src/conf/meson.build" as instructed. + +Step4. Implement hooks when necessary. +-------------------------------------- + +In original implementation of virNetworkDNSDefParseXML, there's a piece of error-checking code as below: + + :: + + if (def->enable == VIR_TRISTATE_BOOL_NO && (nfwds || nhosts || nsrvs || ntxts)) { + virReportError(VIR_ERR_XML_ERROR, + _("Extra data in disabled network '%s'"), + networkName); + } + +Since this piece of code can't be generated, we need a hook to do the check. +In step2, we have gotten the declaration of virNetworkDNSDefParseHook, as below: + + :: + + #ifdef ENABLE_VIR_NETWORK_DNSDEF_PARSE_HOOK + + int + virNetworkDNSDefParseHook(xmlNodePtr node, + virNetworkDNSDef *def, + const char *instname, + void *parent, + void *opaque, + const char *enableStr, + const char *forwardPlainNamesStr, + int nForwarderNodes, + int nTxtNodes, + int nSrvNodes, + int nHostNodes); + + #endif + +The arguments provide all necessary information for us to do some extra things. It's easy for us to transfer the error-checking code into this hook. +Then we put the implementation into a file, which should include the declaration of the hook. In this example, we use 'conf/network_conf.c'. + +Directives +========== + +Directives help direct xmlgen to generate parse/format functions. They are in the form of comment, so they are only valid for xmlgen and ignored by **C** compilers. Directives work on either a struct or a member, so they must appear in the same line of struct's or member's declaration. + +On the other hand, some directives are basic, which can be used on their own; and the others are addional, which must be accompanied with baisc ones. + +genparse +-------- + +(only for struct; basic) + +Generate parse-function for the struct. At the meanwhile, clear-function is also generated automatically, which is used for cleaning all the members when parse-function fails. + +E.g. set **genparse** on virNetworkDNSTxtDef, as below: + + :: + + typedef struct _virNetworkDNSTxtDef virNetworkDNSTxtDef; + struct _virNetworkDNSTxtDef { /* genparse */ + char *name; /* xmlattr, required */ + char *value; /* xmlattr */ + }; + +Then we'll get the implementation of virNetworkDNSTxtDefParseXML (parse-function) and virNetworkDNSTxtDefClear (clear-function). + +We can enable a pre-hook and a post-hook in the parse function when necessary. The usage of hooks will be explained in detail in the following chapter **Hooks**. + +Note: Specify **xmlattr** or **xmlelem** for at least one member, or we'll get an empty parse-function. + +genformat[:separate] +-------------------- + +(only for struct; basic) + +Generate format-function for the struct. At the meanwhile, check-function is also generated automatically, which is used to determine whether an instance of this type is empty. + +E.g. set **genformat** on virNetworkDHCPRangeDef, as below: + + :: + + typedef struct _virNetworkDHCPRangeDef virNetworkDHCPRangeDef; + struct _virNetworkDHCPRangeDef { /* genformat */ + struct { + virSocketAddr start; /* xmlattr, required */ + virSocketAddr end; /* xmlattr, required */ + } addr; + + virNetworkDHCPLeaseTimeDef *lease; /* xmlelem */ + }; + +Then we'll get the implementation of virNetworkDHCPRangeDefFormatBuf (format-function) and virNetworkDHCPRangeDefCheck (check-function). + +By default, the format-function outputs xml for all attributes and all elements. + +But in some special cases, we need two separate format-functions: one for formatting attributes and another for formatting elements. For that, we should specify **genformat:separate**. + +E.g., for virDomainGraphicsAuthDef + + :: + + struct _virDomainGraphicsAuthDef { /* genformat:separate */ + char *passwd; /* xmlattr */ + /* Whether there is an expiry time set */ + bool expires; /* specify:validTo */ + /* seconds since epoch */ + time_t validTo; /* xmlattr:passwdValidTo */ + /* action if connected */ + virDomainGraphicsAuthConnectedType connected; /* xmlattr */ + }; + +Then we'll get virDomainGraphicsAuthDefFormatAttr and virDomainGraphicsAuthDefFormatElem, which are for formatting attributes and elements respectively. In the meanwhile, virDomainGraphicsAuthDefCheckAttr and virDomainGraphicsAuthDefCheckElem are generated to check whether all attributes/elements are all empty. + +We can enable a pre-hook for format-function when necessary. Also, the check-function can be redefined if it's not suitable. The usage of hook and check-function will be explained in detail in the following chapter **Hooks**. + +Note: Specify **xmlattr** or **xmlelem** for at least one member, or we'll get an empty format-function. + +xmlattr[:[parentname/]thename] +------------------------------ + +(only for member; basic) + +Indicate that the member matches an xml attribute.There're 3 cases: + +1) By default, use the member's name as attribute's name to generate parse/format code block. + +2) Specify the attribute's name by **thename** when necessary. + +3) Use the form of **xmlattr:parentname/thename** to indicate that this member matches an attribute of an child-element. + +E.g.: + + :: + + struct _virNetworkIPDef { /* genparse, genformat */ + ... ... + char *family; /* xmlattr */ + virTristateBool localPTR; /* xmlattr:localPtr */ + char *tftproot; /* xmlattr:tftp/root */ + ... ... + }; + +This example demonstrates all those three usages: + +The member **family** has the same name with its corresponding xml attribute. As in: <ip family='..' />. + +But for **localPTR**, the name of its corresponding attribute is "localPtr", as in: <ip localPtr='..' />. So we have to specify it explicitly. + +And for **tftproot**, in fact it matches the attribute *root* of the child-element *tftp*, as in: <ip><tftp root='..' /></ip> in xml. So we use the most complicated form 'xmlattr:tftp/root'. + +xmlelem[:thename] +----------------- + +(only for member; basic) + +Indicate that the member matches an xml element. + +By default, use the member's name as element's name to generate parse/format code block. + +Specify the element's name by **thename** when necessary. + +E.g.: + + :: + + struct _virNetworkForwardNatDef { /* genparse, genformat */ + virSocketAddrRange addr; /* xmlelem:address */ + virPortRange port; /* xmlelem */ + ... ... + }; + +For the member **addr**, it matches xml element <address ... /> in <nat>; +and **port** matches <port ... /> in <nat>. + +In special cases, the corresponding element is a *text-node*, like +<name>...</name>. An additional directive **xmltext** should be appended to +handle this situation. + +xmltext +----------------- + +(only for member; additional) + +Indicate that this member matches *text node*. It must be with **xmlelem**. + +E.g.: + + :: + + typedef struct _virNetworkDef virNetworkDef; + struct _virNetworkDef { /* genparse, genformat */ + virUUID uuid; /* xmlelem, xmltext */ + ... ... + }; + +The member **uuid** matches <uuid>...</uuid>. + + +array[:countername] +------------------- + +(only for member; additional) + +Indicate that this member matches an array of xml node. It must be with **xmlattr** or **xmlelem**. + +Each array member is accompanied with a counter member in the same struct, which name should be in the form of: 'n' + member-name. + +If the counter member's name doesn't follow the common rule, we should specify it. As in: + + :: + + struct _virNetworkDNSDef { /* genparse, genformat */ + ... ... + size_t nfwds; + virNetworkDNSForwarder *forwarders; /* xmlelem, array:nfwds */ + size_t ntxts; + virNetworkDNSTxtDef *txts; /* xmlelem, array */ + ... ... + }; + +For member **forwarders**, its counter member **nfwds** doesn't follow the common name rule, so we have to specify it explicitly. + +Another note: for array member, use the **singular** form of its name as its corresponding xml node's name. +As the member **txts**, it matches a group of element *<txt ... />*. + +required +-------- + +(only for member; additional) + +Indicate that the corresponding xml node of this member must exist and have valid value. It must be with **xmlattr** or **xmlelem**. E.g.: + + :: + + struct _virNetworkDNSTxtDef { /* genparse, genformat */ + char *name; /* xmlattr, required */ + ... ... + }; + +In parse-function, there will be a piece of code to check the existence of corresponding attribute, or it will report an error. Just like: + + :: + + def->name = virXMLPropString(node, "name"); + if (def->name == NULL) { + virReportError(VIR_ERR_XML_ERROR, + _("Missing '%s' setting in '%s'"), + "name", instname); + goto error; + } + +specify:thename +--------------- + +(only for member; basic) + +Indicate that this member specify the existence of another member named with **thename**. E.g.: + + :: + + struct _virNetworkDef { /* genparse, genformat */ + ... ... + virMacAddr mac; /* xmlattr:mac/address */ + bool mac_specified; /* specify:mac */ + }; + +The member **mac_specified** specify the existence of another member **mac**. + +In parse-function, **mac_specified** will be automatically set when **mac** exists. + +In format-function, whether to format **mac** depends on whether **mac_specified** is *TRUE*. + +skipparse +--------- + +(only for member; additional) + +Ignore this member in parse-function. E.g.: + + :: + + struct _virNetworkDef { /* genparse, genformat */ + int connections; /* xmlattr, skipparse */ + ... ... + }; + +The member **connections** won't appear in the generated parse-function, but it still appears in the format-function. + +xmlgroup +-------- + +(only for member which type is a struct; basic) + +This member matches an group of xml nodes rather than an xml element. E.g.: + + :: + + struct _virDomainGraphicsVNCDef { /* genparse */ + ... ... + virDomainGraphicsAuthDef auth; /* xmlgroup */ + }; + +For the member **auth**, we can't find an corresponding element in xml. +In fact, it matches an group of attributes, including *passwd*, *passwdValidTo*, and *connected*. +Then, parse/format code block will skip over this level and traverse its children directly. + +xmlswitch:thename +----------------- + +(only for member which type is a union; basic) + +Indicate that this member matches an xml choice and generates *switch* statement to parse/format xml. + +The union member should have a relative enum member which is specified by **thename**. E.g., + + :: + + /* Implementation of virDomainGraphicsType */ + VIR_ENUM_IMPL(virDomainGraphics, + VIR_DOMAIN_GRAPHICS_TYPE_LAST, + "sdl", + "vnc", + "rdp", + "desktop", + "spice", + "egl-headless", + ); + + struct _virDomainGraphicsDef { /* genparse, genformat */ + virDomainGraphicsType type; /* xmlattr */ + ... ... + union { + virDomainGraphicsSDLDef sdl; /* xmlgroup */ + virDomainGraphicsVNCDef vnc; /* xmlgroup */ + virDomainGraphicsRDPDef rdp; /* xmlgroup */ + virDomainGraphicsDesktopDef desktop; /* xmlgroup */ + virDomainGraphicsSpiceDef spice; /* xmlgroup */ + virDomainGraphicsEGLHeadlessDef egl_headless; /* xmlgroup */ + } data; /* xmlswitch:type */ + }; + +For the union member **data**, the enum member **type** is its relative counterpart. + +Note: Each child of **data** has the same name with each child of **type** in order. + +Automatic Features +================== + +Other than directives, xmlgen can automatically detect some features. + +Default item of enum +-------------------- + +For all enums in libvirt, there're two cases: + +- Most of them have an extra default item which value is *ZERO*. + It usually represents that the absence of the corresponding attribute in xml. E.g. + + :: + + typedef enum { + VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_DEFAULT = 0, + VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_KERNEL, + VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_LIBVIRT, + VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_LAST, + } virNetworkBridgeMACTableManagerType; + +In generated parse-function, it's illegal that virNetworkBridgeMACTableManagerTypeFromString returns ZERO. + +- The others have no this kind of default item. All items have their actual meanings. E.g., + + :: + + typedef enum { + VIR_NETWORK_DHCP_LEASETIME_UNIT_SECONDS = 0, + VIR_NETWORK_DHCP_LEASETIME_UNIT_MINUTES, + VIR_NETWORK_DHCP_LEASETIME_UNIT_HOURS, + VIR_NETWORK_DHCP_LEASETIME_UNIT_LAST, + } virNetworkDHCPLeaseTimeUnitType; + +In generated parse-function, the return-value of virNetworkDHCPLeaseTimeUnitTypeFromString can be ZERO, +which indicates the first item VIR_NETWORK_DHCP_LEASETIME_UNIT_SECONDS. + +The tool xmlgen can distinguish them and generate proper parsing/formatting code by checking whether the first item ends with '_DEFAULT', '_NONE' or '_ABSENT'. + +Namespace +--------- + +For some top structs, such as virNetworkDef, virDomainDef, etc., there're some code blocks about **namespace**. + +The tool xmlgen generates extra code block to deal with **namespace** in parse/format/clear function if it finds that a struct has a member named '**namespaceData**'. + +Pointer +------- + +Some members' type is a pointer. The tool xmlgen determines it by checking "*" or "Ptr" so that it can generate proper code. + +Hooks +===== + +Generated parse/format functions have some hooks which provide flexibility. +By default, hooks are disabled, so we should implement and enable them when necessary. + +Post-hook for parse-function +---------------------------- + +This hook is used to hold error-checking code and even to set/change any member's value in the post process. E.g.: + +:: + + int + virNetworkDNSDefParseXML(xmlNodePtr node, + virNetworkDNSDef *def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) + { + /* Parsing block for each member */ + ... ... + + #ifdef ENABLE_VIR_NETWORK_DNSDEF_PARSE_HOOK + if (virNetworkDNSDefParseHook(node, def, instname, parent, opaque, enableStr, forwardPlainNamesStr, nForwarderNodes, nTxtNodes, nSrvNodes, nHostNodes) < 0) + goto error; + #endif + + return 0; + + error: + ... ... + return -1; + } + +The hook virNetworkDNSDefParseHook is after all members' parsing blocks. +Now we can take 3 steps to enable this hook to hold some error-checking code. + +step1. Look through helpful information. +........................................ + +Execute below command line: + + :: + + # ./scripts/xmlgen/go show virNetworkDNSDef -kp + +Then we got the declaration of virNetworkDNSDefParseHook and tips. +The declaration will be used in step3. And the tips is as below: + + :: + + [Tips] + + /* Put these lines at the bottom of "conf/network_conf.h" */ + /* Makesure "network_conf.h" to be appended into conf_xmlgen_input in src/conf/meson.build */ + + /* Define macro to enable hook or redefine check when necessary */ + /* #define ENABLE_VIR_NETWORK_DNSDEF_PARSE_HOOK */ + /* #define ENABLE_VIR_NETWORK_DNSDEF_PARSE_HOOK_SET_ARGS */ + + /* Makesure below is the bottom line! */ + #include "network_conf.generated.h" + +step2. Enable hook and apply generated functions. +................................................. + +According to tips from step1, we define ENABLE_VIR_NETWORK_DNSDEF_PARSE_HOOK to enable the hook and include the header-file. + +step3. Implement hook. +...................... + +Implement virNetworkDNSDefParseHook according to its delcaration and put the error-checking code into it. + +This implementation should be written into a C source file that includes "network_conf.h". + +In this example, "network_conf.c" is a good choice. + +Pre-hook for parse-function +--------------------------- + +This hook is used to change the common rule of passing some special arguments, including **parent** and **opaque**. +The common rule is: + +For each parse-function, the argument **parent** holds the pointer of its parent struct instance, and the argument **def** will be passed into child parse-function as their **parent**. + +The argument **opaque** holds an opaque data, and it will be passed from parent parse-function to child parse-function recursively. + +But sometimes, we should change this rule for some generated parse-functions. As in: + +:: + + int + virNetworkDHCPDefParseXML(xmlNodePtr node, + virNetworkDHCPDef *def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) + { + ... ... + void *arg_parent G_GNUC_UNUSED = def; + void *arg_opaque G_GNUC_UNUSED = opaque; + + if (!def) + goto error; + + #ifdef ENABLE_VIR_NETWORK_DHCPDEF_PARSE_HOOK_SET_ARGS + virNetworkDHCPDefParseXMLSetArgs(node, parent, &arg_parent, &arg_opaque); + #endif + + if (nRangeNodes > 0) { + ... ... + if (virNetworkDHCPRangeDefParseXML(tnode, &def->ranges[i], instname, arg_parent, arg_opaque) < 0) + goto error; + } + ... ... + } + +The hook virNetworkDHCPDefParseXMLSetArgs has a chance to intercept **parent/opaque** in the chain. In this example, we need pass the pointer of virNetworkDef rather than that of virNetworkDHCPDef to virNetworkDHCPRangeDefParseXML as its **parent**, so we enable the hook virNetworkDHCPDefParseXMLSetArgs to implement it. + +The steps to enable the hook are just similar as mentioned in post-hook for parse-function. + +Pre-hook for format-function +---------------------------- + +The generated format-function has a pre-hook that can override the default xml output of any member and even the whole output of struct instance. + +E.g., for virNetworkForwardDefFormatBuf, we can implement the hook as below: + +:: + + int + virNetworkForwardDefFormatHook(const virNetworkForwardDef *def, + const void *parent, + const void *opaque G_GNUC_UNUSED, + virTristateBool *empty, + virTristateBool *shortcut, + virBuffer *devBuf, + virBuffer *typeBuf G_GNUC_UNUSED, + virBuffer *managedBuf, + virBuffer *driver_nameBuf G_GNUC_UNUSED, + virBuffer *natBuf G_GNUC_UNUSED, + virBuffer *pfsBuf G_GNUC_UNUSED, + virBuffer *ifsBuf G_GNUC_UNUSED) + { + if (def->type == VIR_NETWORK_FORWARD_NONE) { + *empty = VIR_TRISTATE_BOOL_YES; + return 0; + } + + if (!def->npfs) { + const char *dev = virNetworkDefForwardIf(parent, 0); + virBufferEscapeString(devBuf, " dev='%s'", dev); + } else { + virBufferIgnore(devBuf); + } + + if (def->type != VIR_NETWORK_FORWARD_HOSTDEV) + virBufferIgnore(managedBuf); + + if (!(def->nifs || def->npfs || def->nat.port.start || def->nat.port.end || + VIR_SOCKET_ADDR_VALID(&def->nat.addr.start) || + VIR_SOCKET_ADDR_VALID(&def->nat.addr.end) || + (def->driverName != VIR_NETWORK_FORWARD_DRIVER_NAME_DEFAULT) || + def->nat.natIPv6)) + *shortcut = VIR_TRISTATE_BOOL_YES; + + return 0; + } + +In the hook, we redefine four things: + +1) Redefine the xml output for the attribute **dev** by resetting *devBuf*. + +2) Indicate that the attribute **managed** should be ignored (by *virBufferIgnore*) under some conditions. + +3) Set **shortcut** to indicate that <forward>...</forward> should use the shortut form like <forward/> under some conditions. + +4) Set **empty** to indicate that <forward> shouldn't appear under some conditions. + +The arguments **shortcut** and **empty** are both the type of virTristateBool. + +By default, they are VIR_TRISTATE_BOOL_ABSENT which means they don't influence the default behavior of the generated format-function. + +Set them with VIR_TRISTATE_BOOL_YES or VIR_TRISTATE_BOOL_NO to turn on/off them respectively. + +Check-function +-------------- + +It is an auxiliary function for format-function. + +Before a format-function is called to output an xml node, its auxiliary check-function should be invoked to determine whether the xml node is empty. +For each struct with **genformat**, their check-functions are generated automatically. + +Sometimes the default implementation is not satisfied, we can redefine it. + +E.g., for virDomainGraphicsListenDef, we look through its check-function by calling command line as below: + + :: + + # ./scripts/xmlgen/go show virDomainGraphicsListenDef -kf + +We got the default implementation of virDomainGraphicsListenDefCheck and the tips. + +Since it doesn't satisfy our needs, we can define RESET_VIR_DOMAIN_GRAPHICS_LISTEN_DEF_CHECK and redefine it. -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- build-aux/syntax-check.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-aux/syntax-check.mk b/build-aux/syntax-check.mk index 552d6391..614f21bc 100644 --- a/build-aux/syntax-check.mk +++ b/build-aux/syntax-check.mk @@ -735,7 +735,7 @@ sc_prohibit_useless_translation: halt='found useless translation' \ $(_sc_search_regexp) @prohibit='\<N?_ *\(' \ - in_vc_files='(tests|examples)/' \ + in_vc_files='(tests|examples)/.*\.[ch]$$' \ halt='no translations in tests or examples' \ $(_sc_search_regexp) -- 2.25.1

Add some tests to make sure xmlgen's validity. Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- tests/meson.build | 1 + tests/xmlgenin/conf/array.h | 17 + tests/xmlgenin/conf/empty.h | 7 + tests/xmlgenin/conf/enum-first-item.h | 12 + tests/xmlgenin/conf/external.h | 9 + tests/xmlgenin/conf/genformat-separate.h | 11 + tests/xmlgenin/conf/genformat.h | 11 + tests/xmlgenin/conf/genparse.h | 11 + tests/xmlgenin/conf/namespace.h | 12 + tests/xmlgenin/conf/required.h | 9 + tests/xmlgenin/conf/skipparse.h | 10 + tests/xmlgenin/conf/specify.h | 13 + tests/xmlgenin/conf/xmlattr.h | 10 + tests/xmlgenin/conf/xmlelem.h | 10 + tests/xmlgenin/conf/xmlgroup.h | 8 + tests/xmlgenin/conf/xmlswitch.h | 17 + tests/xmlgenin/util/enums.h | 58 +++ tests/xmlgenin/util/structs.h | 67 ++++ tests/xmlgenout/array.txt | 364 ++++++++++++++++++ tests/xmlgenout/empty.txt | 181 +++++++++ tests/xmlgenout/enum-first-item.txt | 297 ++++++++++++++ tests/xmlgenout/external.txt | 205 ++++++++++ tests/xmlgenout/genformat-separate.txt | 190 +++++++++ tests/xmlgenout/genformat.txt | 142 +++++++ tests/xmlgenout/genparse.txt | 154 ++++++++ tests/xmlgenout/namespace.txt | 222 +++++++++++ tests/xmlgenout/required.txt | 236 ++++++++++++ tests/xmlgenout/skipparse.txt | 223 +++++++++++ tests/xmlgenout/specify.txt | 291 ++++++++++++++ tests/xmlgenout/xmlattr.txt | 252 ++++++++++++ tests/xmlgenout/xmlelem.txt | 243 ++++++++++++ tests/xmlgenout/xmlgroup.txt | 204 ++++++++++ tests/xmlgenout/xmlswitch.txt | 470 +++++++++++++++++++++++ tests/xmlgentest.c | 107 ++++++ 34 files changed, 4074 insertions(+) create mode 100644 tests/xmlgenin/conf/array.h create mode 100644 tests/xmlgenin/conf/empty.h create mode 100644 tests/xmlgenin/conf/enum-first-item.h create mode 100644 tests/xmlgenin/conf/external.h create mode 100644 tests/xmlgenin/conf/genformat-separate.h create mode 100644 tests/xmlgenin/conf/genformat.h create mode 100644 tests/xmlgenin/conf/genparse.h create mode 100644 tests/xmlgenin/conf/namespace.h create mode 100644 tests/xmlgenin/conf/required.h create mode 100644 tests/xmlgenin/conf/skipparse.h create mode 100644 tests/xmlgenin/conf/specify.h create mode 100644 tests/xmlgenin/conf/xmlattr.h create mode 100644 tests/xmlgenin/conf/xmlelem.h create mode 100644 tests/xmlgenin/conf/xmlgroup.h create mode 100644 tests/xmlgenin/conf/xmlswitch.h create mode 100644 tests/xmlgenin/util/enums.h create mode 100644 tests/xmlgenin/util/structs.h create mode 100644 tests/xmlgenout/array.txt create mode 100644 tests/xmlgenout/empty.txt create mode 100644 tests/xmlgenout/enum-first-item.txt create mode 100644 tests/xmlgenout/external.txt create mode 100644 tests/xmlgenout/genformat-separate.txt create mode 100644 tests/xmlgenout/genformat.txt create mode 100644 tests/xmlgenout/genparse.txt create mode 100644 tests/xmlgenout/namespace.txt create mode 100644 tests/xmlgenout/required.txt create mode 100644 tests/xmlgenout/skipparse.txt create mode 100644 tests/xmlgenout/specify.txt create mode 100644 tests/xmlgenout/xmlattr.txt create mode 100644 tests/xmlgenout/xmlelem.txt create mode 100644 tests/xmlgenout/xmlgroup.txt create mode 100644 tests/xmlgenout/xmlswitch.txt create mode 100644 tests/xmlgentest.c diff --git a/tests/meson.build b/tests/meson.build index 14ace476..166416cc 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -340,6 +340,7 @@ tests += [ { 'name': 'viruritest' }, { 'name': 'vshtabletest', 'link_with': [ libvirt_shell_lib ] }, { 'name': 'virmigtest' }, + { 'name': 'xmlgentest' }, ] if host_machine.system() == 'linux' diff --git a/tests/xmlgenin/conf/array.h b/tests/xmlgenin/conf/array.h new file mode 100644 index 00000000..8f003be4 --- /dev/null +++ b/tests/xmlgenin/conf/array.h @@ -0,0 +1,17 @@ +/* Test array features */ + +#pragma once + +typedef struct _virArrayDef virArrayDef; +struct _virArrayDef { /* genparse, genformat */ + size_t nnames; + char **names; /* xmlelem:hostname, array */ + size_t nfwds; + virNetworkDNSForwarder *forwarders; /* xmlelem, array:nfwds */ + size_t ntxts; + virNetworkDNSTxtDef *txts; /* xmlelem, array */ + + size_t nroutes; + /* ptr to array of static routes on this interface */ + virNetDevIPRoute **routes; /* xmlelem, array */ +}; diff --git a/tests/xmlgenin/conf/empty.h b/tests/xmlgenin/conf/empty.h new file mode 100644 index 00000000..c8292c8e --- /dev/null +++ b/tests/xmlgenin/conf/empty.h @@ -0,0 +1,7 @@ +/* Test empty struct */ + +#pragma once + +typedef struct _virEmptyDef virEmptyDef; +struct _virEmptyDef { /* genparse, genformat */ +}; diff --git a/tests/xmlgenin/conf/enum-first-item.h b/tests/xmlgenin/conf/enum-first-item.h new file mode 100644 index 00000000..c62dfe8a --- /dev/null +++ b/tests/xmlgenin/conf/enum-first-item.h @@ -0,0 +1,12 @@ +/* Test first item for enum features */ + +#pragma once + +typedef struct _virEnumFirstItemDef virEnumFirstItemDef; +struct _virEnumFirstItemDef { /* genparse, genformat */ + virNetworkBridgeMACTableManagerType has_def; /* xmlattr */ + virNetworkForwardType has_none; /* xmlattr */ + virTristateBool has_absent; /* xmlattr */ + + virDomainGraphicsType no_default; /* xmlattr */ +}; diff --git a/tests/xmlgenin/conf/external.h b/tests/xmlgenin/conf/external.h new file mode 100644 index 00000000..52b46acd --- /dev/null +++ b/tests/xmlgenin/conf/external.h @@ -0,0 +1,9 @@ +/* Test specify features */ + +#pragma once + +typedef struct _virExternalDef virExternalDef; +struct _virExternalDef { /* genparse, genformat */ + /* xmlNode is an external type which is not defined in libvirt */ + xmlNode *metadata; /* xmlelem */ +}; diff --git a/tests/xmlgenin/conf/genformat-separate.h b/tests/xmlgenin/conf/genformat-separate.h new file mode 100644 index 00000000..8e4643af --- /dev/null +++ b/tests/xmlgenin/conf/genformat-separate.h @@ -0,0 +1,11 @@ +/* Test genformat-separate features */ + +#pragma once + +typedef struct _virSeparateDef virSeparateDef; +struct _virSeparateDef { /* genformat:separate */ + virTristateBool enable; /* xmlattr */ + virSocketAddr ip; /* xmlelem */ + size_t ntxts; + virNetworkDNSTxtDef *txts; /* xmlelem, array */ +}; diff --git a/tests/xmlgenin/conf/genformat.h b/tests/xmlgenin/conf/genformat.h new file mode 100644 index 00000000..fb92351a --- /dev/null +++ b/tests/xmlgenin/conf/genformat.h @@ -0,0 +1,11 @@ +/* Test genformat features */ + +#pragma once + +typedef struct _virGenFormatDef virGenFormatDef; +struct _virGenFormatDef { /* genformat */ + virTristateBool enable; /* xmlattr */ + virSocketAddr ip; /* xmlelem */ + size_t ntxts; + virNetworkDNSTxtDef *txts; /* xmlelem, array */ +}; diff --git a/tests/xmlgenin/conf/genparse.h b/tests/xmlgenin/conf/genparse.h new file mode 100644 index 00000000..daf53817 --- /dev/null +++ b/tests/xmlgenin/conf/genparse.h @@ -0,0 +1,11 @@ +/* Test genparse features */ + +#pragma once + +typedef struct _virGenParseDef virGenParseDef; +struct _virGenParseDef { /* genparse */ + virTristateBool enable; /* xmlattr */ + virSocketAddr ip; /* xmlelem */ + size_t ntxts; + virNetworkDNSTxtDef *txts; /* xmlelem, array */ +}; diff --git a/tests/xmlgenin/conf/namespace.h b/tests/xmlgenin/conf/namespace.h new file mode 100644 index 00000000..0637e64a --- /dev/null +++ b/tests/xmlgenin/conf/namespace.h @@ -0,0 +1,12 @@ +/* Test namespace features */ + +#pragma once + +typedef struct _virNameSpaceDef virNameSpaceDef; +struct _virNameSpaceDef { /* genparse, genformat */ + char *name; /* xmlelem */ + + /* Network specific XML namespace data */ + void *namespaceData; + virXMLNamespace ns; +}; diff --git a/tests/xmlgenin/conf/required.h b/tests/xmlgenin/conf/required.h new file mode 100644 index 00000000..d45af727 --- /dev/null +++ b/tests/xmlgenin/conf/required.h @@ -0,0 +1,9 @@ +/* Test required features */ + +#pragma once + +typedef struct _virRequiredDef virRequiredDef; +struct _virRequiredDef { /* genparse, genformat */ + char *name; /* xmlattr, required */ + virNetworkBootpDef bootp; /* xmlelem, required */ +}; diff --git a/tests/xmlgenin/conf/skipparse.h b/tests/xmlgenin/conf/skipparse.h new file mode 100644 index 00000000..680a1107 --- /dev/null +++ b/tests/xmlgenin/conf/skipparse.h @@ -0,0 +1,10 @@ +/* Test skipparse features */ + +#pragma once + +typedef struct _virSkipParseDef virSkipParseDef; +struct _virSkipParseDef { /* genparse, genformat */ + int connections; /* xmlattr, skipparse */ + size_t nifs; + virNetworkForwardIfDef *ifs; /* xmlelem, array, skipparse */ +}; diff --git a/tests/xmlgenin/conf/specify.h b/tests/xmlgenin/conf/specify.h new file mode 100644 index 00000000..419bd24d --- /dev/null +++ b/tests/xmlgenin/conf/specify.h @@ -0,0 +1,13 @@ +/* Test specify features */ + +#pragma once + +typedef struct _virSpecifyDef virSpecifyDef; +struct _virSpecifyDef { /* genparse, genformat */ + virNetworkDHCPLeaseTimeUnitType unit; /* xmlattr */ + unsigned long expiry; /* xmlattr, specify:unit */ + virMacAddr mac; /* xmlattr:mac/address */ + bool mac_specified; /* specify:mac */ + virUUID uuid; /* xmlelem, xmltext */ + bool uuid_specified; /* specify:uuid */ +}; diff --git a/tests/xmlgenin/conf/xmlattr.h b/tests/xmlgenin/conf/xmlattr.h new file mode 100644 index 00000000..60dfae9a --- /dev/null +++ b/tests/xmlgenin/conf/xmlattr.h @@ -0,0 +1,10 @@ +/* Test xmlattr features */ + +#pragma once + +typedef struct _virXMLAttrDef virXMLAttrDef; +struct _virXMLAttrDef { /* genparse, genformat */ + char *family; /* xmlattr */ + virTristateBool localPTR; /* xmlattr:localPtr */ + char *tftproot; /* xmlattr:tftp/root */ +}; diff --git a/tests/xmlgenin/conf/xmlelem.h b/tests/xmlgenin/conf/xmlelem.h new file mode 100644 index 00000000..d111f2b5 --- /dev/null +++ b/tests/xmlgenin/conf/xmlelem.h @@ -0,0 +1,10 @@ +/* Test xmlelem features */ + +#pragma once + +typedef struct _virXMLElemDef virXMLElemDef; +struct _virXMLElemDef { /* genparse, genformat */ + virPortRange port; /* xmlelem */ + virSocketAddrRange addr; /* xmlelem:address */ + virUUID uuid; /* xmlelem, xmltext */ +}; diff --git a/tests/xmlgenin/conf/xmlgroup.h b/tests/xmlgenin/conf/xmlgroup.h new file mode 100644 index 00000000..ed12d919 --- /dev/null +++ b/tests/xmlgenin/conf/xmlgroup.h @@ -0,0 +1,8 @@ +/* Test xmlgroup features */ + +#pragma once + +typedef struct _virXMLGroupDef virXMLGroupDef; +struct _virXMLGroupDef { /* genparse, genformat */ + virUtilAuthDef auth; /* xmlgroup */ +}; diff --git a/tests/xmlgenin/conf/xmlswitch.h b/tests/xmlgenin/conf/xmlswitch.h new file mode 100644 index 00000000..8730f1b3 --- /dev/null +++ b/tests/xmlgenin/conf/xmlswitch.h @@ -0,0 +1,17 @@ +/* Test xmlswitch features */ + +#pragma once + +typedef struct _virXMLSwitchDef virXMLSwitchDef; +struct _virXMLSwitchDef { /* genparse, genformat */ + virDomainGraphicsType type; /* xmlattr */ + + union { + virDomainGraphicsSDLDef sdl; /* xmlgroup */ + virDomainGraphicsVNCDef vnc; /* xmlgroup */ + virDomainGraphicsRDPDef rdp; /* xmlgroup */ + virDomainGraphicsDesktopDef desktop; /* xmlgroup */ + virDomainGraphicsSpiceDef spice; /* xmlgroup */ + virDomainGraphicsEGLHeadlessDef egl_headless; /* xmlgroup */ + } data; /* xmlswitch:type */ +}; diff --git a/tests/xmlgenin/util/enums.h b/tests/xmlgenin/util/enums.h new file mode 100644 index 00000000..89d2eccd --- /dev/null +++ b/tests/xmlgenin/util/enums.h @@ -0,0 +1,58 @@ +/* Basic enum */ + +#pragma once + +/* + * The tool xmlgen scans all header-files in 'util' and 'conf' + * to find all enum types, and further, to determine whether + * they have default item. + * + */ + +typedef enum { + VIR_DOMAIN_GRAPHICS_TYPE_SDL, + VIR_DOMAIN_GRAPHICS_TYPE_VNC, + VIR_DOMAIN_GRAPHICS_TYPE_RDP, + VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP, + VIR_DOMAIN_GRAPHICS_TYPE_SPICE, + VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS, + + VIR_DOMAIN_GRAPHICS_TYPE_LAST +} virDomainGraphicsType; + +typedef enum { + VIR_NETWORK_FORWARD_NONE = 0, + VIR_NETWORK_FORWARD_NAT, + VIR_NETWORK_FORWARD_ROUTE, + VIR_NETWORK_FORWARD_OPEN, + VIR_NETWORK_FORWARD_BRIDGE, + VIR_NETWORK_FORWARD_PRIVATE, + VIR_NETWORK_FORWARD_VEPA, + VIR_NETWORK_FORWARD_PASSTHROUGH, + VIR_NETWORK_FORWARD_HOSTDEV, + + VIR_NETWORK_FORWARD_LAST, +} virNetworkForwardType; + +typedef enum { + VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_DEFAULT = 0, + VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_KERNEL, + VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_LIBVIRT, + VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_LAST, +} virNetworkBridgeMACTableManagerType; + +typedef enum { + VIR_TRISTATE_BOOL_ABSENT = 0, + VIR_TRISTATE_BOOL_YES, + VIR_TRISTATE_BOOL_NO, + + VIR_TRISTATE_BOOL_LAST +} virTristateBool; + +typedef enum { + VIR_NETWORK_DHCP_LEASETIME_UNIT_SECONDS = 0, + VIR_NETWORK_DHCP_LEASETIME_UNIT_MINUTES, + VIR_NETWORK_DHCP_LEASETIME_UNIT_HOURS, + + VIR_NETWORK_DHCP_LEASETIME_UNIT_LAST, +} virNetworkDHCPLeaseTimeUnitType; diff --git a/tests/xmlgenin/util/structs.h b/tests/xmlgenin/util/structs.h new file mode 100644 index 00000000..80c10096 --- /dev/null +++ b/tests/xmlgenin/util/structs.h @@ -0,0 +1,67 @@ +/* Basic structs */ + +#pragma once + +/* + * The tool xmlgen scans all header-files in 'util' and 'conf' + * to find all struct types. + * For tests, it's unnecessary to include any members for these structs. + */ + +typedef struct _virNetDevIPRoute virNetDevIPRoute; +struct _virNetDevIPRoute { +}; + +typedef struct _virNetworkDNSForwarder virNetworkDNSForwarder; +struct _virNetworkDNSForwarder { +}; + +typedef struct _virNetworkDNSTxtDef virNetworkDNSTxtDef; +struct _virNetworkDNSTxtDef { +}; + +typedef struct _virSocketAddr virSocketAddr; +struct _virSocketAddr { +}; + +typedef struct _virNetworkForwardIfDef virNetworkForwardIfDef; +struct _virNetworkForwardIfDef { +}; + +typedef struct _virPortRange virPortRange; +struct _virPortRange { +}; + +typedef struct _virSocketAddrRange virSocketAddrRange; +struct _virSocketAddrRange { +}; + +typedef struct _virUtilAuthDef virUtilAuthDef; +struct _virUtilAuthDef { +}; + +typedef struct _virNetworkBootpDef virNetworkBootpDef; +struct _virNetworkBootpDef { +}; + +typedef struct _virMacAddr virMacAddr; +struct _virMacAddr { +}; + +struct _virDomainGraphicsSDLDef { +}; + +struct _virDomainGraphicsVNCDef { +}; + +struct _virDomainGraphicsRDPDef { +}; + +struct _virDomainGraphicsDesktopDef { +}; + +struct _virDomainGraphicsSpiceDef { +}; + +struct _virDomainGraphicsEGLHeadlessDef { +}; diff --git a/tests/xmlgenout/array.txt b/tests/xmlgenout/array.txt new file mode 100644 index 00000000..ccb2d168 --- /dev/null +++ b/tests/xmlgenout/array.txt @@ -0,0 +1,364 @@ +[conf/array.generated.h] + +void +virArrayDefClear(virArrayDef *def); + + +[conf/array.generated.c] + +void +virArrayDefClear(virArrayDef *def) +{ + if (!def) + return; + + if (def->nnames) { + size_t i; + for (i = 0; i < def->nnames; i++) { + g_free(def->names[i]); + def->names[i] = NULL; + } + } + g_free(def->names); + def->names = NULL; + def->nnames = 0; + + if (def->nfwds) { + size_t i; + for (i = 0; i < def->nfwds; i++) + virNetworkDNSForwarderClear(&def->forwarders[i]); + } + g_free(def->forwarders); + def->forwarders = NULL; + def->nfwds = 0; + + if (def->ntxts) { + size_t i; + for (i = 0; i < def->ntxts; i++) + virNetworkDNSTxtDefClear(&def->txts[i]); + } + g_free(def->txts); + def->txts = NULL; + def->ntxts = 0; + + if (def->nroutes) { + size_t i; + for (i = 0; i < def->nroutes; i++) { + virNetDevIPRouteClear(def->routes[i]); + g_free(def->routes[i]); + def->routes[i] = NULL; + } + } + g_free(def->routes); + def->routes = NULL; + def->nroutes = 0; +} + + +[conf/array.generated.h] + +int +virArrayDefParseXML(xmlNodePtr node, + virArrayDef *def, + const char *instname, + void *parent, + void *opaque); + + +[conf/array.generated.h] + +#ifdef ENABLE_VIR_ARRAY_DEF_PARSE_HOOK + +int +virArrayDefParseHook(xmlNodePtr node, + virArrayDef *def, + const char *instname, + void *parent, + void *opaque, + int nHostnameNodes, + int nForwarderNodes, + int nTxtNodes, + int nRouteNodes); + +#endif + + +[conf/array.generated.h] + +#ifdef ENABLE_VIR_ARRAY_DEF_PARSE_HOOK_SET_ARGS + +void +virArrayDefParseXMLSetArgs(xmlNodePtr node, + void *parent, + void **pparent, + void **popaque); + +#endif + + +[conf/array.generated.c] + +int +virArrayDefParseXML(xmlNodePtr node, + virArrayDef *def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + int nHostnameNodes = 0; + xmlNodePtr *nodes = NULL; + int nForwarderNodes = 0; + int nTxtNodes = 0; + int nRouteNodes = 0; + void *arg_parent G_GNUC_UNUSED = def; + void *arg_opaque G_GNUC_UNUSED = opaque; + + if (!def) + goto error; + +#ifdef ENABLE_VIR_ARRAY_DEF_PARSE_HOOK_SET_ARGS + virArrayDefParseXMLSetArgs(node, parent, &arg_parent, &arg_opaque); +#endif + + nHostnameNodes = virXMLChildNodeSet(node, "hostname", &nodes); + if (nHostnameNodes > 0) { + size_t i; + + def->names = g_new0(typeof(*def->names), nHostnameNodes); + for (i = 0; i < nHostnameNodes; i++) { + xmlNodePtr tnode = nodes[i]; + def->names[i] = virXMLNodeContentString(tnode); + } + def->nnames = nHostnameNodes; + g_free(nodes); + nodes = NULL; + } else if (nHostnameNodes < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid %s element found."), + "hostname"); + goto error; + } + + nForwarderNodes = virXMLChildNodeSet(node, "forwarder", &nodes); + if (nForwarderNodes > 0) { + size_t i; + + def->forwarders = g_new0(typeof(*def->forwarders), nForwarderNodes); + for (i = 0; i < nForwarderNodes; i++) { + xmlNodePtr tnode = nodes[i]; + if (virNetworkDNSForwarderParseXML(tnode, &def->forwarders[i], instname, arg_parent, arg_opaque) < 0) + goto error; + } + def->nfwds = nForwarderNodes; + g_free(nodes); + nodes = NULL; + } else if (nForwarderNodes < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid %s element found."), + "forwarder"); + goto error; + } + + nTxtNodes = virXMLChildNodeSet(node, "txt", &nodes); + if (nTxtNodes > 0) { + size_t i; + + def->txts = g_new0(typeof(*def->txts), nTxtNodes); + for (i = 0; i < nTxtNodes; i++) { + xmlNodePtr tnode = nodes[i]; + if (virNetworkDNSTxtDefParseXML(tnode, &def->txts[i], instname, arg_parent, arg_opaque) < 0) + goto error; + } + def->ntxts = nTxtNodes; + g_free(nodes); + nodes = NULL; + } else if (nTxtNodes < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid %s element found."), + "txt"); + goto error; + } + + nRouteNodes = virXMLChildNodeSet(node, "route", &nodes); + if (nRouteNodes > 0) { + size_t i; + + def->routes = g_new0(typeof(*def->routes), nRouteNodes); + for (i = 0; i < nRouteNodes; i++) { + xmlNodePtr tnode = nodes[i]; + def->routes[i] = g_new0(typeof(*def->routes[i]), 1); + if (virNetDevIPRouteParseXML(tnode, def->routes[i], instname, arg_parent, arg_opaque) < 0) + goto error; + } + def->nroutes = nRouteNodes; + g_free(nodes); + nodes = NULL; + } else if (nRouteNodes < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid %s element found."), + "route"); + goto error; + } + +#ifdef ENABLE_VIR_ARRAY_DEF_PARSE_HOOK + if (virArrayDefParseHook(node, def, instname, parent, opaque, nHostnameNodes, nForwarderNodes, nTxtNodes, nRouteNodes) < 0) + goto error; +#endif + + return 0; + + error: + g_free(nodes); + nodes = NULL; + virArrayDefClear(def); + return -1; +} + + +[conf/array.generated.h] + +int +virArrayDefFormatBuf(virBuffer *buf, + const char *name, + const virArrayDef *def, + const void *parent, + void *opaque); + + +[conf/array.generated.h] + +#ifdef ENABLE_VIR_ARRAY_DEF_FORMAT_HOOK + +int +virArrayDefFormatHook(const virArrayDef *def, + const void *parent, + const void *opaque, + virTristateBool *empty, + virTristateBool *shortcut, + virBuffer *namesBuf, + virBuffer *forwardersBuf, + virBuffer *txtsBuf, + virBuffer *routesBuf); + +#endif /* ENABLE_VIR_ARRAY_DEF_FORMAT_HOOK */ + + +[conf/array.generated.c] + +int +virArrayDefFormatBuf(virBuffer *buf, + const char *name, + const virArrayDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + virTristateBool empty G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + virTristateBool shortcut G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + g_auto(virBuffer) namesBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) forwardersBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) txtsBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) routesBuf = VIR_BUFFER_INITIALIZER; + + if (!def || !buf) + return 0; + +#ifdef ENABLE_VIR_ARRAY_DEF_FORMAT_HOOK + if (virArrayDefFormatHook(def, parent, opaque, &empty, &shortcut, &namesBuf, &forwardersBuf, &txtsBuf, &routesBuf) < 0) + return -1; +#endif /* ENABLE_VIR_ARRAY_DEF_FORMAT_HOOK */ + + if (empty != VIR_TRISTATE_BOOL_NO) + if (empty || !(def->nnames || def->nfwds || def->ntxts || def->nroutes)) + return 0; + + virBufferAsprintf(buf, "<%s", name); + + virBufferAddLit(buf, ">\n"); + + virBufferAdjustIndent(buf, 2); + + virBufferInheritIndent(&namesBuf, buf); + if (!virBufferTouched(&namesBuf) && def->nnames) { + size_t i; + for (i = 0; i < def->nnames; i++) + virBufferEscapeString(buf, "<hostname>%s</hostname>\n", def->names[i]); + } + virBufferAddBuffer(buf, &namesBuf); + + virBufferInheritIndent(&forwardersBuf, buf); + if (!virBufferTouched(&forwardersBuf) && def->nfwds) { + size_t i; + for (i = 0; i < def->nfwds; i++) { + if (virNetworkDNSForwarderFormatBuf(buf, "forwarder", &def->forwarders[i], def, opaque) < 0) + return -1; + } + } + virBufferAddBuffer(buf, &forwardersBuf); + + virBufferInheritIndent(&txtsBuf, buf); + if (!virBufferTouched(&txtsBuf) && def->ntxts) { + size_t i; + for (i = 0; i < def->ntxts; i++) { + if (virNetworkDNSTxtDefFormatBuf(buf, "txt", &def->txts[i], def, opaque) < 0) + return -1; + } + } + virBufferAddBuffer(buf, &txtsBuf); + + virBufferInheritIndent(&routesBuf, buf); + if (!virBufferTouched(&routesBuf) && def->nroutes) { + size_t i; + for (i = 0; i < def->nroutes; i++) { + if (virNetDevIPRouteFormatBuf(buf, "route", def->routes[i], def, opaque) < 0) + return -1; + } + } + virBufferAddBuffer(buf, &routesBuf); + + virBufferAdjustIndent(buf, -2); + virBufferAsprintf(buf, "</%s>\n", name); + + return 0; +} + + +[conf/array.generated.h] + +bool +virArrayDefCheck(const virArrayDef *def, + const void *parent, + void *opaque); + + +[conf/array.generated.c] + +#ifndef RESET_VIR_ARRAY_DEF_CHECK + +bool +virArrayDefCheck(const virArrayDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + if (!def) + return false; + + return def->nnames || def->nfwds || def->ntxts || def->nroutes; +} + +#endif /* RESET_VIR_ARRAY_DEF_CHECK */ + + +[Tips] + +/* Put these lines at the bottom of "conf/array.h" */ +/* Makesure "array.h" to be appended into conf_xmlgen_input in src/conf/meson.build */ + +/* Define macro to enable hook or redefine check when necessary */ +/* #define ENABLE_VIR_ARRAY_DEF_PARSE_HOOK */ +/* #define ENABLE_VIR_ARRAY_DEF_PARSE_HOOK_SET_ARGS */ +/* #define ENABLE_VIR_ARRAY_DEF_FORMAT_HOOK */ + +/* #define RESET_VIR_ARRAY_DEF_CHECK */ + +/* Makesure below is the bottom line! */ +#include "array.generated.h" diff --git a/tests/xmlgenout/empty.txt b/tests/xmlgenout/empty.txt new file mode 100644 index 00000000..c83a2c12 --- /dev/null +++ b/tests/xmlgenout/empty.txt @@ -0,0 +1,181 @@ +[conf/empty.generated.h] + +void +virEmptyDefClear(virEmptyDef *def); + + +[conf/empty.generated.c] + +void +virEmptyDefClear(virEmptyDef *def) +{ + if (!def) + return; +} + + +[conf/empty.generated.h] + +int +virEmptyDefParseXML(xmlNodePtr node, + virEmptyDef *def, + const char *instname, + void *parent, + void *opaque); + + +[conf/empty.generated.h] + +#ifdef ENABLE_VIR_EMPTY_DEF_PARSE_HOOK + +int +virEmptyDefParseHook(xmlNodePtr node, + virEmptyDef *def, + const char *instname, + void *parent, + void *opaque); + +#endif + + +[conf/empty.generated.h] + +#ifdef ENABLE_VIR_EMPTY_DEF_PARSE_HOOK_SET_ARGS + +void +virEmptyDefParseXMLSetArgs(xmlNodePtr node, + void *parent, + void **pparent, + void **popaque); + +#endif + + +[conf/empty.generated.c] + +int +virEmptyDefParseXML(xmlNodePtr node, + virEmptyDef *def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + void *arg_parent G_GNUC_UNUSED = def; + void *arg_opaque G_GNUC_UNUSED = opaque; + + if (!def) + goto error; + +#ifdef ENABLE_VIR_EMPTY_DEF_PARSE_HOOK_SET_ARGS + virEmptyDefParseXMLSetArgs(node, parent, &arg_parent, &arg_opaque); +#endif + +#ifdef ENABLE_VIR_EMPTY_DEF_PARSE_HOOK + if (virEmptyDefParseHook(node, def, instname, parent, opaque) < 0) + goto error; +#endif + + return 0; + + error: + virEmptyDefClear(def); + return -1; +} + + +[conf/empty.generated.h] + +int +virEmptyDefFormatBuf(virBuffer *buf, + const char *name, + const virEmptyDef *def, + const void *parent, + void *opaque); + + +[conf/empty.generated.h] + +#ifdef ENABLE_VIR_EMPTY_DEF_FORMAT_HOOK + +int +virEmptyDefFormatHook(const virEmptyDef *def, + const void *parent, + const void *opaque, + virTristateBool *empty, + virTristateBool *shortcut); + +#endif /* ENABLE_VIR_EMPTY_DEF_FORMAT_HOOK */ + + +[conf/empty.generated.c] + +int +virEmptyDefFormatBuf(virBuffer *buf, + const char *name, + const virEmptyDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + virTristateBool empty G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + virTristateBool shortcut G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + + if (!def || !buf) + return 0; + +#ifdef ENABLE_VIR_EMPTY_DEF_FORMAT_HOOK + if (virEmptyDefFormatHook(def, parent, opaque, &empty, &shortcut) < 0) + return -1; +#endif /* ENABLE_VIR_EMPTY_DEF_FORMAT_HOOK */ + + if (empty != VIR_TRISTATE_BOOL_NO) + if (empty) + return 0; + + virBufferAsprintf(buf, "<%s", name); + + virBufferAddLit(buf, "/>\n"); + + return 0; +} + + +[conf/empty.generated.h] + +bool +virEmptyDefCheck(const virEmptyDef *def, + const void *parent, + void *opaque); + + +[conf/empty.generated.c] + +#ifndef RESET_VIR_EMPTY_DEF_CHECK + +bool +virEmptyDefCheck(const virEmptyDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + if (!def) + return false; + + return false; +} + +#endif /* RESET_VIR_EMPTY_DEF_CHECK */ + + +[Tips] + +/* Put these lines at the bottom of "conf/empty.h" */ +/* Makesure "empty.h" to be appended into conf_xmlgen_input in src/conf/meson.build */ + +/* Define macro to enable hook or redefine check when necessary */ +/* #define ENABLE_VIR_EMPTY_DEF_PARSE_HOOK */ +/* #define ENABLE_VIR_EMPTY_DEF_PARSE_HOOK_SET_ARGS */ +/* #define ENABLE_VIR_EMPTY_DEF_FORMAT_HOOK */ + +/* #define RESET_VIR_EMPTY_DEF_CHECK */ + +/* Makesure below is the bottom line! */ +#include "empty.generated.h" diff --git a/tests/xmlgenout/enum-first-item.txt b/tests/xmlgenout/enum-first-item.txt new file mode 100644 index 00000000..4186d46d --- /dev/null +++ b/tests/xmlgenout/enum-first-item.txt @@ -0,0 +1,297 @@ +[conf/enum-first-item.generated.h] + +void +virEnumFirstItemDefClear(virEnumFirstItemDef *def); + + +[conf/enum-first-item.generated.c] + +void +virEnumFirstItemDefClear(virEnumFirstItemDef *def) +{ + if (!def) + return; + + def->has_def = 0; + + def->has_none = 0; + + def->has_absent = 0; + + def->no_default = 0; +} + + +[conf/enum-first-item.generated.h] + +int +virEnumFirstItemDefParseXML(xmlNodePtr node, + virEnumFirstItemDef *def, + const char *instname, + void *parent, + void *opaque); + + +[conf/enum-first-item.generated.h] + +#ifdef ENABLE_VIR_ENUM_FIRST_ITEM_DEF_PARSE_HOOK + +int +virEnumFirstItemDefParseHook(xmlNodePtr node, + virEnumFirstItemDef *def, + const char *instname, + void *parent, + void *opaque, + const char *has_defStr, + const char *has_noneStr, + const char *has_absentStr, + const char *no_defaultStr); + +#endif + + +[conf/enum-first-item.generated.h] + +#ifdef ENABLE_VIR_ENUM_FIRST_ITEM_DEF_PARSE_HOOK_SET_ARGS + +void +virEnumFirstItemDefParseXMLSetArgs(xmlNodePtr node, + void *parent, + void **pparent, + void **popaque); + +#endif + + +[conf/enum-first-item.generated.c] + +int +virEnumFirstItemDefParseXML(xmlNodePtr node, + virEnumFirstItemDef *def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + g_autofree char *has_defStr = NULL; + g_autofree char *has_noneStr = NULL; + g_autofree char *has_absentStr = NULL; + g_autofree char *no_defaultStr = NULL; + void *arg_parent G_GNUC_UNUSED = def; + void *arg_opaque G_GNUC_UNUSED = opaque; + + if (!def) + goto error; + +#ifdef ENABLE_VIR_ENUM_FIRST_ITEM_DEF_PARSE_HOOK_SET_ARGS + virEnumFirstItemDefParseXMLSetArgs(node, parent, &arg_parent, &arg_opaque); +#endif + + has_defStr = virXMLPropString(node, "has_def"); + if (has_defStr) { + if ((def->has_def = virNetworkBridgeMACTableManagerTypeFromString(has_defStr)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid '%s' setting '%s' in '%s'"), + "has_def", has_defStr, instname); + goto error; + } + } + + has_noneStr = virXMLPropString(node, "has_none"); + if (has_noneStr) { + if ((def->has_none = virNetworkForwardTypeFromString(has_noneStr)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid '%s' setting '%s' in '%s'"), + "has_none", has_noneStr, instname); + goto error; + } + } + + has_absentStr = virXMLPropString(node, "has_absent"); + if (has_absentStr) { + if ((def->has_absent = virTristateBoolTypeFromString(has_absentStr)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid '%s' setting '%s' in '%s'"), + "has_absent", has_absentStr, instname); + goto error; + } + } + + no_defaultStr = virXMLPropString(node, "no_default"); + if (no_defaultStr) { + if ((def->no_default = virDomainGraphicsTypeFromString(no_defaultStr)) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid '%s' setting '%s' in '%s'"), + "no_default", no_defaultStr, instname); + goto error; + } + } + +#ifdef ENABLE_VIR_ENUM_FIRST_ITEM_DEF_PARSE_HOOK + if (virEnumFirstItemDefParseHook(node, def, instname, parent, opaque, has_defStr, has_noneStr, has_absentStr, no_defaultStr) < 0) + goto error; +#endif + + return 0; + + error: + virEnumFirstItemDefClear(def); + return -1; +} + + +[conf/enum-first-item.generated.h] + +int +virEnumFirstItemDefFormatBuf(virBuffer *buf, + const char *name, + const virEnumFirstItemDef *def, + const void *parent, + void *opaque); + + +[conf/enum-first-item.generated.h] + +#ifdef ENABLE_VIR_ENUM_FIRST_ITEM_DEF_FORMAT_HOOK + +int +virEnumFirstItemDefFormatHook(const virEnumFirstItemDef *def, + const void *parent, + const void *opaque, + virTristateBool *empty, + virTristateBool *shortcut, + virBuffer *has_defBuf, + virBuffer *has_noneBuf, + virBuffer *has_absentBuf, + virBuffer *no_defaultBuf); + +#endif /* ENABLE_VIR_ENUM_FIRST_ITEM_DEF_FORMAT_HOOK */ + + +[conf/enum-first-item.generated.c] + +int +virEnumFirstItemDefFormatBuf(virBuffer *buf, + const char *name, + const virEnumFirstItemDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + virTristateBool empty G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + virTristateBool shortcut G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + g_auto(virBuffer) has_defBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) has_noneBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) has_absentBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) no_defaultBuf = VIR_BUFFER_INITIALIZER; + + if (!def || !buf) + return 0; + +#ifdef ENABLE_VIR_ENUM_FIRST_ITEM_DEF_FORMAT_HOOK + if (virEnumFirstItemDefFormatHook(def, parent, opaque, &empty, &shortcut, &has_defBuf, &has_noneBuf, &has_absentBuf, &no_defaultBuf) < 0) + return -1; +#endif /* ENABLE_VIR_ENUM_FIRST_ITEM_DEF_FORMAT_HOOK */ + + if (empty != VIR_TRISTATE_BOOL_NO) + if (empty || !(def->has_def > 0 || def->has_none > 0 || def->has_absent > 0 || def->no_default >= 0)) + return 0; + + virBufferAsprintf(buf, "<%s", name); + + virBufferInheritIndent(&has_defBuf, buf); + if (!virBufferTouched(&has_defBuf) && def->has_def > 0) { + const char *str = virNetworkBridgeMACTableManagerTypeToString(def->has_def); + if (!str) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown %s type %d"), + "has_def", def->has_def); + return -1; + } + virBufferAsprintf(&has_defBuf, " has_def='%s'", str); + } + virBufferAddBuffer(buf, &has_defBuf); + + virBufferInheritIndent(&has_noneBuf, buf); + if (!virBufferTouched(&has_noneBuf) && def->has_none > 0) { + const char *str = virNetworkForwardTypeToString(def->has_none); + if (!str) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown %s type %d"), + "has_none", def->has_none); + return -1; + } + virBufferAsprintf(&has_noneBuf, " has_none='%s'", str); + } + virBufferAddBuffer(buf, &has_noneBuf); + + virBufferInheritIndent(&has_absentBuf, buf); + if (!virBufferTouched(&has_absentBuf) && def->has_absent > 0) { + const char *str = virTristateBoolTypeToString(def->has_absent); + if (!str) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown %s type %d"), + "has_absent", def->has_absent); + return -1; + } + virBufferAsprintf(&has_absentBuf, " has_absent='%s'", str); + } + virBufferAddBuffer(buf, &has_absentBuf); + + virBufferInheritIndent(&no_defaultBuf, buf); + if (!virBufferTouched(&no_defaultBuf) && def->no_default >= 0) { + const char *str = virDomainGraphicsTypeToString(def->no_default); + if (!str) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown %s type %d"), + "no_default", def->no_default); + return -1; + } + virBufferAsprintf(&no_defaultBuf, " no_default='%s'", str); + } + virBufferAddBuffer(buf, &no_defaultBuf); + + virBufferAddLit(buf, "/>\n"); + + return 0; +} + + +[conf/enum-first-item.generated.h] + +bool +virEnumFirstItemDefCheck(const virEnumFirstItemDef *def, + const void *parent, + void *opaque); + + +[conf/enum-first-item.generated.c] + +#ifndef RESET_VIR_ENUM_FIRST_ITEM_DEF_CHECK + +bool +virEnumFirstItemDefCheck(const virEnumFirstItemDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + if (!def) + return false; + + return def->has_def > 0 || def->has_none > 0 || def->has_absent > 0 || def->no_default >= 0; +} + +#endif /* RESET_VIR_ENUM_FIRST_ITEM_DEF_CHECK */ + + +[Tips] + +/* Put these lines at the bottom of "conf/enum-first-item.h" */ +/* Makesure "enum-first-item.h" to be appended into conf_xmlgen_input in src/conf/meson.build */ + +/* Define macro to enable hook or redefine check when necessary */ +/* #define ENABLE_VIR_ENUM_FIRST_ITEM_DEF_PARSE_HOOK */ +/* #define ENABLE_VIR_ENUM_FIRST_ITEM_DEF_PARSE_HOOK_SET_ARGS */ +/* #define ENABLE_VIR_ENUM_FIRST_ITEM_DEF_FORMAT_HOOK */ + +/* #define RESET_VIR_ENUM_FIRST_ITEM_DEF_CHECK */ + +/* Makesure below is the bottom line! */ +#include "enum-first-item.generated.h" diff --git a/tests/xmlgenout/external.txt b/tests/xmlgenout/external.txt new file mode 100644 index 00000000..4f0ea691 --- /dev/null +++ b/tests/xmlgenout/external.txt @@ -0,0 +1,205 @@ +[conf/external.generated.h] + +void +virExternalDefClear(virExternalDef *def); + + +[conf/external.generated.c] + +void +virExternalDefClear(virExternalDef *def) +{ + if (!def) + return; + + xmlNodeClear(&def->metadata); +} + + +[conf/external.generated.h] + +int +virExternalDefParseXML(xmlNodePtr node, + virExternalDef *def, + const char *instname, + void *parent, + void *opaque); + + +[conf/external.generated.h] + +#ifdef ENABLE_VIR_EXTERNAL_DEF_PARSE_HOOK + +int +virExternalDefParseHook(xmlNodePtr node, + virExternalDef *def, + const char *instname, + void *parent, + void *opaque, + xmlNodePtr metadataNode); + +#endif + + +[conf/external.generated.h] + +#ifdef ENABLE_VIR_EXTERNAL_DEF_PARSE_HOOK_SET_ARGS + +void +virExternalDefParseXMLSetArgs(xmlNodePtr node, + void *parent, + void **pparent, + void **popaque); + +#endif + + +[conf/external.generated.c] + +int +virExternalDefParseXML(xmlNodePtr node, + virExternalDef *def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + xmlNodePtr metadataNode = NULL; + void *arg_parent G_GNUC_UNUSED = def; + void *arg_opaque G_GNUC_UNUSED = opaque; + + if (!def) + goto error; + +#ifdef ENABLE_VIR_EXTERNAL_DEF_PARSE_HOOK_SET_ARGS + virExternalDefParseXMLSetArgs(node, parent, &arg_parent, &arg_opaque); +#endif + + metadataNode = virXMLChildNode(node, "metadata"); + if (metadataNode) { + if (xmlNodeParseXML(metadataNode, &def->metadata, instname, arg_parent, arg_opaque) < 0) + goto error; + } + +#ifdef ENABLE_VIR_EXTERNAL_DEF_PARSE_HOOK + if (virExternalDefParseHook(node, def, instname, parent, opaque, metadataNode) < 0) + goto error; +#endif + + return 0; + + error: + virExternalDefClear(def); + return -1; +} + + +[conf/external.generated.h] + +int +virExternalDefFormatBuf(virBuffer *buf, + const char *name, + const virExternalDef *def, + const void *parent, + void *opaque); + + +[conf/external.generated.h] + +#ifdef ENABLE_VIR_EXTERNAL_DEF_FORMAT_HOOK + +int +virExternalDefFormatHook(const virExternalDef *def, + const void *parent, + const void *opaque, + virTristateBool *empty, + virTristateBool *shortcut, + virBuffer *metadataBuf); + +#endif /* ENABLE_VIR_EXTERNAL_DEF_FORMAT_HOOK */ + + +[conf/external.generated.c] + +int +virExternalDefFormatBuf(virBuffer *buf, + const char *name, + const virExternalDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + virTristateBool empty G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + virTristateBool shortcut G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + g_auto(virBuffer) metadataBuf = VIR_BUFFER_INITIALIZER; + + if (!def || !buf) + return 0; + +#ifdef ENABLE_VIR_EXTERNAL_DEF_FORMAT_HOOK + if (virExternalDefFormatHook(def, parent, opaque, &empty, &shortcut, &metadataBuf) < 0) + return -1; +#endif /* ENABLE_VIR_EXTERNAL_DEF_FORMAT_HOOK */ + + if (empty != VIR_TRISTATE_BOOL_NO) + if (empty || !(xmlNodeCheck(&def->metadata, def, opaque))) + return 0; + + virBufferAsprintf(buf, "<%s", name); + + virBufferAddLit(buf, ">\n"); + + virBufferAdjustIndent(buf, 2); + + virBufferInheritIndent(&metadataBuf, buf); + if (!virBufferTouched(&metadataBuf) && xmlNodeCheck(&def->metadata, def, opaque)) { + if (xmlNodeFormatBuf(&metadataBuf, "metadata", &def->metadata, def, opaque) < 0) + return -1; + } + virBufferAddBuffer(buf, &metadataBuf); + + virBufferAdjustIndent(buf, -2); + virBufferAsprintf(buf, "</%s>\n", name); + + return 0; +} + + +[conf/external.generated.h] + +bool +virExternalDefCheck(const virExternalDef *def, + const void *parent, + void *opaque); + + +[conf/external.generated.c] + +#ifndef RESET_VIR_EXTERNAL_DEF_CHECK + +bool +virExternalDefCheck(const virExternalDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + if (!def) + return false; + + return xmlNodeCheck(&def->metadata, def, opaque); +} + +#endif /* RESET_VIR_EXTERNAL_DEF_CHECK */ + + +[Tips] + +/* Put these lines at the bottom of "conf/external.h" */ +/* Makesure "external.h" to be appended into conf_xmlgen_input in src/conf/meson.build */ + +/* Define macro to enable hook or redefine check when necessary */ +/* #define ENABLE_VIR_EXTERNAL_DEF_PARSE_HOOK */ +/* #define ENABLE_VIR_EXTERNAL_DEF_PARSE_HOOK_SET_ARGS */ +/* #define ENABLE_VIR_EXTERNAL_DEF_FORMAT_HOOK */ + +/* #define RESET_VIR_EXTERNAL_DEF_CHECK */ + +/* Makesure below is the bottom line! */ +#include "external.generated.h" diff --git a/tests/xmlgenout/genformat-separate.txt b/tests/xmlgenout/genformat-separate.txt new file mode 100644 index 00000000..90729d2a --- /dev/null +++ b/tests/xmlgenout/genformat-separate.txt @@ -0,0 +1,190 @@ +[conf/genformat-separate.generated.h] + +#ifdef ENABLE_VIR_SEPARATE_DEF_FORMAT_ATTR_HOOK + +int +virSeparateDefFormatAttrHook(const virSeparateDef *def, + const void *parent, + const void *opaque, + virBuffer *enableBuf); + +#endif /* ENABLE_VIR_SEPARATE_DEF_FORMAT_ATTR_HOOK */ + + +[conf/genformat-separate.generated.h] + +int +virSeparateDefFormatAttr(virBuffer *buf, + const virSeparateDef *def, + const void *parent, + void *opaque); + + +[conf/genformat-separate.generated.c] + +int +virSeparateDefFormatAttr(virBuffer *buf, + const virSeparateDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + virTristateBool empty G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + virTristateBool shortcut G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + g_auto(virBuffer) enableBuf = VIR_BUFFER_INITIALIZER; + + if (!def || !buf) + return 0; + +#ifdef ENABLE_VIR_SEPARATE_DEF_FORMAT_ATTR_HOOK + if (virSeparateDefFormatAttrHook(def, parent, opaque, &enableBuf) < 0) + return -1; +#endif /* ENABLE_VIR_SEPARATE_DEF_FORMAT_ATTR_HOOK */ + + virBufferInheritIndent(&enableBuf, buf); + if (!virBufferTouched(&enableBuf) && def->enable > 0) { + const char *str = virTristateBoolTypeToString(def->enable); + if (!str) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown %s type %d"), + "enable", def->enable); + return -1; + } + virBufferAsprintf(&enableBuf, " enable='%s'", str); + } + virBufferAddBuffer(buf, &enableBuf); + + return 0; +} + + +[conf/genformat-separate.generated.h] + +bool +virSeparateDefCheckAttr(const virSeparateDef *def, + const void *parent, + void *opaque); + + +[conf/genformat-separate.generated.c] + +#ifndef RESET_VIR_SEPARATE_DEF_CHECK_ATTR + +bool +virSeparateDefCheckAttr(const virSeparateDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + if (!def) + return false; + + return def->enable > 0; +} + +#endif /* RESET_VIR_SEPARATE_DEF_CHECK_ATTR */ + + +[conf/genformat-separate.generated.h] + +#ifdef ENABLE_VIR_SEPARATE_DEF_FORMAT_ELEM_HOOK + +int +virSeparateDefFormatElemHook(const virSeparateDef *def, + const void *parent, + const void *opaque, + virBuffer *ipBuf, + virBuffer *txtsBuf); + +#endif /* ENABLE_VIR_SEPARATE_DEF_FORMAT_ELEM_HOOK */ + + +[conf/genformat-separate.generated.h] + +int +virSeparateDefFormatElem(virBuffer *buf, + const virSeparateDef *def, + const void *parent, + void *opaque); + + +[conf/genformat-separate.generated.c] + +int +virSeparateDefFormatElem(virBuffer *buf, + const virSeparateDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + virTristateBool empty G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + virTristateBool shortcut G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + g_auto(virBuffer) ipBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) txtsBuf = VIR_BUFFER_INITIALIZER; + + if (!def || !buf) + return 0; + +#ifdef ENABLE_VIR_SEPARATE_DEF_FORMAT_ELEM_HOOK + if (virSeparateDefFormatElemHook(def, parent, opaque, &ipBuf, &txtsBuf) < 0) + return -1; +#endif /* ENABLE_VIR_SEPARATE_DEF_FORMAT_ELEM_HOOK */ + + virBufferInheritIndent(&ipBuf, buf); + if (!virBufferTouched(&ipBuf) && virSocketAddrCheck(&def->ip, def, opaque)) { + if (virSocketAddrFormatBuf(&ipBuf, "ip", &def->ip, def, opaque) < 0) + return -1; + } + virBufferAddBuffer(buf, &ipBuf); + + virBufferInheritIndent(&txtsBuf, buf); + if (!virBufferTouched(&txtsBuf) && def->ntxts) { + size_t i; + for (i = 0; i < def->ntxts; i++) { + if (virNetworkDNSTxtDefFormatBuf(buf, "txt", &def->txts[i], def, opaque) < 0) + return -1; + } + } + virBufferAddBuffer(buf, &txtsBuf); + + return 0; +} + + +[conf/genformat-separate.generated.h] + +bool +virSeparateDefCheckElem(const virSeparateDef *def, + const void *parent, + void *opaque); + + +[conf/genformat-separate.generated.c] + +#ifndef RESET_VIR_SEPARATE_DEF_CHECK_ELEM + +bool +virSeparateDefCheckElem(const virSeparateDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + if (!def) + return false; + + return virSocketAddrCheck(&def->ip, def, opaque) || def->ntxts; +} + +#endif /* RESET_VIR_SEPARATE_DEF_CHECK_ELEM */ + + +[Tips] + +/* Put these lines at the bottom of "conf/genformat-separate.h" */ +/* Makesure "genformat-separate.h" to be appended into conf_xmlgen_input in src/conf/meson.build */ + +/* Define macro to enable hook or redefine check when necessary */ +/* #define ENABLE_VIR_SEPARATE_DEF_FORMAT_ATTR_HOOK */ +/* #define ENABLE_VIR_SEPARATE_DEF_FORMAT_ELEM_HOOK */ + +/* #define RESET_VIR_SEPARATE_DEF_CHECK_ATTR */ +/* #define RESET_VIR_SEPARATE_DEF_CHECK_ELEM */ + +/* Makesure below is the bottom line! */ +#include "genformat-separate.generated.h" diff --git a/tests/xmlgenout/genformat.txt b/tests/xmlgenout/genformat.txt new file mode 100644 index 00000000..72e4e0c0 --- /dev/null +++ b/tests/xmlgenout/genformat.txt @@ -0,0 +1,142 @@ +[conf/genformat.generated.h] + +int +virGenFormatDefFormatBuf(virBuffer *buf, + const char *name, + const virGenFormatDef *def, + const void *parent, + void *opaque); + + +[conf/genformat.generated.h] + +#ifdef ENABLE_VIR_GEN_FORMAT_DEF_FORMAT_HOOK + +int +virGenFormatDefFormatHook(const virGenFormatDef *def, + const void *parent, + const void *opaque, + virTristateBool *empty, + virTristateBool *shortcut, + virBuffer *enableBuf, + virBuffer *ipBuf, + virBuffer *txtsBuf); + +#endif /* ENABLE_VIR_GEN_FORMAT_DEF_FORMAT_HOOK */ + + +[conf/genformat.generated.c] + +int +virGenFormatDefFormatBuf(virBuffer *buf, + const char *name, + const virGenFormatDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + virTristateBool empty G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + virTristateBool shortcut G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + g_auto(virBuffer) enableBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) ipBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) txtsBuf = VIR_BUFFER_INITIALIZER; + + if (!def || !buf) + return 0; + +#ifdef ENABLE_VIR_GEN_FORMAT_DEF_FORMAT_HOOK + if (virGenFormatDefFormatHook(def, parent, opaque, &empty, &shortcut, &enableBuf, &ipBuf, &txtsBuf) < 0) + return -1; +#endif /* ENABLE_VIR_GEN_FORMAT_DEF_FORMAT_HOOK */ + + if (empty != VIR_TRISTATE_BOOL_NO) + if (empty || !(def->enable > 0 || virSocketAddrCheck(&def->ip, def, opaque) || def->ntxts)) + return 0; + + virBufferAsprintf(buf, "<%s", name); + + virBufferInheritIndent(&enableBuf, buf); + if (!virBufferTouched(&enableBuf) && def->enable > 0) { + const char *str = virTristateBoolTypeToString(def->enable); + if (!str) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown %s type %d"), + "enable", def->enable); + return -1; + } + virBufferAsprintf(&enableBuf, " enable='%s'", str); + } + virBufferAddBuffer(buf, &enableBuf); + + if (shortcut != VIR_TRISTATE_BOOL_NO) { + if (shortcut || !(virSocketAddrCheck(&def->ip, def, opaque) || def->ntxts)) { + virBufferAddLit(buf, "/>\n"); + return 0; + } + } + + virBufferAddLit(buf, ">\n"); + + virBufferAdjustIndent(buf, 2); + + virBufferInheritIndent(&ipBuf, buf); + if (!virBufferTouched(&ipBuf) && virSocketAddrCheck(&def->ip, def, opaque)) { + if (virSocketAddrFormatBuf(&ipBuf, "ip", &def->ip, def, opaque) < 0) + return -1; + } + virBufferAddBuffer(buf, &ipBuf); + + virBufferInheritIndent(&txtsBuf, buf); + if (!virBufferTouched(&txtsBuf) && def->ntxts) { + size_t i; + for (i = 0; i < def->ntxts; i++) { + if (virNetworkDNSTxtDefFormatBuf(buf, "txt", &def->txts[i], def, opaque) < 0) + return -1; + } + } + virBufferAddBuffer(buf, &txtsBuf); + + virBufferAdjustIndent(buf, -2); + virBufferAsprintf(buf, "</%s>\n", name); + + return 0; +} + + +[conf/genformat.generated.h] + +bool +virGenFormatDefCheck(const virGenFormatDef *def, + const void *parent, + void *opaque); + + +[conf/genformat.generated.c] + +#ifndef RESET_VIR_GEN_FORMAT_DEF_CHECK + +bool +virGenFormatDefCheck(const virGenFormatDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + if (!def) + return false; + + return def->enable > 0 || virSocketAddrCheck(&def->ip, def, opaque) || def->ntxts; +} + +#endif /* RESET_VIR_GEN_FORMAT_DEF_CHECK */ + + +[Tips] + +/* Put these lines at the bottom of "conf/genformat.h" */ +/* Makesure "genformat.h" to be appended into conf_xmlgen_input in src/conf/meson.build */ + +/* Define macro to enable hook or redefine check when necessary */ +/* #define ENABLE_VIR_GEN_FORMAT_DEF_FORMAT_HOOK */ + +/* #define RESET_VIR_GEN_FORMAT_DEF_CHECK */ + +/* Makesure below is the bottom line! */ +#include "genformat.generated.h" diff --git a/tests/xmlgenout/genparse.txt b/tests/xmlgenout/genparse.txt new file mode 100644 index 00000000..e24762e1 --- /dev/null +++ b/tests/xmlgenout/genparse.txt @@ -0,0 +1,154 @@ +[conf/genparse.generated.h] + +void +virGenParseDefClear(virGenParseDef *def); + + +[conf/genparse.generated.c] + +void +virGenParseDefClear(virGenParseDef *def) +{ + if (!def) + return; + + def->enable = 0; + + virSocketAddrClear(&def->ip); + + if (def->ntxts) { + size_t i; + for (i = 0; i < def->ntxts; i++) + virNetworkDNSTxtDefClear(&def->txts[i]); + } + g_free(def->txts); + def->txts = NULL; + def->ntxts = 0; +} + + +[conf/genparse.generated.h] + +int +virGenParseDefParseXML(xmlNodePtr node, + virGenParseDef *def, + const char *instname, + void *parent, + void *opaque); + + +[conf/genparse.generated.h] + +#ifdef ENABLE_VIR_GEN_PARSE_DEF_PARSE_HOOK + +int +virGenParseDefParseHook(xmlNodePtr node, + virGenParseDef *def, + const char *instname, + void *parent, + void *opaque, + const char *enableStr, + xmlNodePtr ipNode, + int nTxtNodes); + +#endif + + +[conf/genparse.generated.h] + +#ifdef ENABLE_VIR_GEN_PARSE_DEF_PARSE_HOOK_SET_ARGS + +void +virGenParseDefParseXMLSetArgs(xmlNodePtr node, + void *parent, + void **pparent, + void **popaque); + +#endif + + +[conf/genparse.generated.c] + +int +virGenParseDefParseXML(xmlNodePtr node, + virGenParseDef *def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + g_autofree char *enableStr = NULL; + xmlNodePtr ipNode = NULL; + int nTxtNodes = 0; + xmlNodePtr *nodes = NULL; + void *arg_parent G_GNUC_UNUSED = def; + void *arg_opaque G_GNUC_UNUSED = opaque; + + if (!def) + goto error; + +#ifdef ENABLE_VIR_GEN_PARSE_DEF_PARSE_HOOK_SET_ARGS + virGenParseDefParseXMLSetArgs(node, parent, &arg_parent, &arg_opaque); +#endif + + enableStr = virXMLPropString(node, "enable"); + if (enableStr) { + if ((def->enable = virTristateBoolTypeFromString(enableStr)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid '%s' setting '%s' in '%s'"), + "enable", enableStr, instname); + goto error; + } + } + + ipNode = virXMLChildNode(node, "ip"); + if (ipNode) { + if (virSocketAddrParseXML(ipNode, &def->ip, instname, arg_parent, arg_opaque) < 0) + goto error; + } + + nTxtNodes = virXMLChildNodeSet(node, "txt", &nodes); + if (nTxtNodes > 0) { + size_t i; + + def->txts = g_new0(typeof(*def->txts), nTxtNodes); + for (i = 0; i < nTxtNodes; i++) { + xmlNodePtr tnode = nodes[i]; + if (virNetworkDNSTxtDefParseXML(tnode, &def->txts[i], instname, arg_parent, arg_opaque) < 0) + goto error; + } + def->ntxts = nTxtNodes; + g_free(nodes); + nodes = NULL; + } else if (nTxtNodes < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid %s element found."), + "txt"); + goto error; + } + +#ifdef ENABLE_VIR_GEN_PARSE_DEF_PARSE_HOOK + if (virGenParseDefParseHook(node, def, instname, parent, opaque, enableStr, ipNode, nTxtNodes) < 0) + goto error; +#endif + + return 0; + + error: + g_free(nodes); + nodes = NULL; + virGenParseDefClear(def); + return -1; +} + + +[Tips] + +/* Put these lines at the bottom of "conf/genparse.h" */ +/* Makesure "genparse.h" to be appended into conf_xmlgen_input in src/conf/meson.build */ + +/* Define macro to enable hook or redefine check when necessary */ +/* #define ENABLE_VIR_GEN_PARSE_DEF_PARSE_HOOK */ +/* #define ENABLE_VIR_GEN_PARSE_DEF_PARSE_HOOK_SET_ARGS */ + +/* Makesure below is the bottom line! */ +#include "genparse.generated.h" diff --git a/tests/xmlgenout/namespace.txt b/tests/xmlgenout/namespace.txt new file mode 100644 index 00000000..df1660dd --- /dev/null +++ b/tests/xmlgenout/namespace.txt @@ -0,0 +1,222 @@ +[conf/namespace.generated.h] + +void +virNameSpaceDefClear(virNameSpaceDef *def); + + +[conf/namespace.generated.c] + +void +virNameSpaceDefClear(virNameSpaceDef *def) +{ + if (!def) + return; + + g_free(def->name); + def->name = NULL; + + if (def->namespaceData && def->ns.free) + (def->ns.free)(def->namespaceData); +} + + +[conf/namespace.generated.h] + +int +virNameSpaceDefParseXML(xmlNodePtr node, + virNameSpaceDef *def, + const char *instname, + void *parent, + void *opaque, + xmlXPathContextPtr ctxt, + virNetworkXMLOption *xmlopt); + + +[conf/namespace.generated.h] + +#ifdef ENABLE_VIR_NAME_SPACE_DEF_PARSE_HOOK + +int +virNameSpaceDefParseHook(xmlNodePtr node, + virNameSpaceDef *def, + const char *instname, + void *parent, + void *opaque, + xmlXPathContextPtr ctxt, + virNetworkXMLOption *xmlopt); + +#endif + + +[conf/namespace.generated.h] + +#ifdef ENABLE_VIR_NAME_SPACE_DEF_PARSE_HOOK_SET_ARGS + +void +virNameSpaceDefParseXMLSetArgs(xmlNodePtr node, + void *parent, + void **pparent, + void **popaque); + +#endif + + +[conf/namespace.generated.c] + +int +virNameSpaceDefParseXML(xmlNodePtr node, + virNameSpaceDef *def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED, + xmlXPathContextPtr ctxt, + virNetworkXMLOption *xmlopt) +{ + void *arg_parent G_GNUC_UNUSED = def; + void *arg_opaque G_GNUC_UNUSED = opaque; + + if (!def) + goto error; + +#ifdef ENABLE_VIR_NAME_SPACE_DEF_PARSE_HOOK_SET_ARGS + virNameSpaceDefParseXMLSetArgs(node, parent, &arg_parent, &arg_opaque); +#endif + + def->name = virXMLChildNodeContent(node, "name"); + +#ifdef ENABLE_VIR_NAME_SPACE_DEF_PARSE_HOOK + if (virNameSpaceDefParseHook(node, def, instname, parent, opaque, ctxt, xmlopt) < 0) + goto error; +#endif + + if (xmlopt) + def->ns = xmlopt->ns; + if (def->ns.parse) { + if (virXMLNamespaceRegister(ctxt, &def->ns) < 0) + goto error; + if ((def->ns.parse)(ctxt, &def->namespaceData) < 0) + goto error; + } + return 0; + + error: + virNameSpaceDefClear(def); + return -1; +} + + +[conf/namespace.generated.h] + +int +virNameSpaceDefFormatBuf(virBuffer *buf, + const char *name, + const virNameSpaceDef *def, + const void *parent, + void *opaque); + + +[conf/namespace.generated.h] + +#ifdef ENABLE_VIR_NAME_SPACE_DEF_FORMAT_HOOK + +int +virNameSpaceDefFormatHook(const virNameSpaceDef *def, + const void *parent, + const void *opaque, + virTristateBool *empty, + virTristateBool *shortcut, + virBuffer *nameBuf); + +#endif /* ENABLE_VIR_NAME_SPACE_DEF_FORMAT_HOOK */ + + +[conf/namespace.generated.c] + +int +virNameSpaceDefFormatBuf(virBuffer *buf, + const char *name, + const virNameSpaceDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + virTristateBool empty G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + virTristateBool shortcut G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + g_auto(virBuffer) nameBuf = VIR_BUFFER_INITIALIZER; + + if (!def || !buf) + return 0; + +#ifdef ENABLE_VIR_NAME_SPACE_DEF_FORMAT_HOOK + if (virNameSpaceDefFormatHook(def, parent, opaque, &empty, &shortcut, &nameBuf) < 0) + return -1; +#endif /* ENABLE_VIR_NAME_SPACE_DEF_FORMAT_HOOK */ + + if (empty != VIR_TRISTATE_BOOL_NO) + if (empty || !(def->name)) + return 0; + + virBufferAsprintf(buf, "<%s", name); + + if (def->namespaceData && def->ns.format) + virXMLNamespaceFormatNS(buf, &def->ns); + virBufferAddLit(buf, ">\n"); + + virBufferAdjustIndent(buf, 2); + + virBufferInheritIndent(&nameBuf, buf); + if (!virBufferTouched(&nameBuf) && def->name) + virBufferEscapeString(&nameBuf, "<name>%s</name>\n", def->name); + virBufferAddBuffer(buf, &nameBuf); + + if (def->namespaceData && def->ns.format) { + if ((def->ns.format)(buf, def->namespaceData) < 0) + return -1; + } + + virBufferAdjustIndent(buf, -2); + virBufferAsprintf(buf, "</%s>\n", name); + + return 0; +} + + +[conf/namespace.generated.h] + +bool +virNameSpaceDefCheck(const virNameSpaceDef *def, + const void *parent, + void *opaque); + + +[conf/namespace.generated.c] + +#ifndef RESET_VIR_NAME_SPACE_DEF_CHECK + +bool +virNameSpaceDefCheck(const virNameSpaceDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + if (!def) + return false; + + return def->name; +} + +#endif /* RESET_VIR_NAME_SPACE_DEF_CHECK */ + + +[Tips] + +/* Put these lines at the bottom of "conf/namespace.h" */ +/* Makesure "namespace.h" to be appended into conf_xmlgen_input in src/conf/meson.build */ + +/* Define macro to enable hook or redefine check when necessary */ +/* #define ENABLE_VIR_NAME_SPACE_DEF_PARSE_HOOK */ +/* #define ENABLE_VIR_NAME_SPACE_DEF_PARSE_HOOK_SET_ARGS */ +/* #define ENABLE_VIR_NAME_SPACE_DEF_FORMAT_HOOK */ + +/* #define RESET_VIR_NAME_SPACE_DEF_CHECK */ + +/* Makesure below is the bottom line! */ +#include "namespace.generated.h" diff --git a/tests/xmlgenout/required.txt b/tests/xmlgenout/required.txt new file mode 100644 index 00000000..df509284 --- /dev/null +++ b/tests/xmlgenout/required.txt @@ -0,0 +1,236 @@ +[conf/required.generated.h] + +void +virRequiredDefClear(virRequiredDef *def); + + +[conf/required.generated.c] + +void +virRequiredDefClear(virRequiredDef *def) +{ + if (!def) + return; + + g_free(def->name); + def->name = NULL; + + virNetworkBootpDefClear(&def->bootp); +} + + +[conf/required.generated.h] + +int +virRequiredDefParseXML(xmlNodePtr node, + virRequiredDef *def, + const char *instname, + void *parent, + void *opaque); + + +[conf/required.generated.h] + +#ifdef ENABLE_VIR_REQUIRED_DEF_PARSE_HOOK + +int +virRequiredDefParseHook(xmlNodePtr node, + virRequiredDef *def, + const char *instname, + void *parent, + void *opaque, + xmlNodePtr bootpNode); + +#endif + + +[conf/required.generated.h] + +#ifdef ENABLE_VIR_REQUIRED_DEF_PARSE_HOOK_SET_ARGS + +void +virRequiredDefParseXMLSetArgs(xmlNodePtr node, + void *parent, + void **pparent, + void **popaque); + +#endif + + +[conf/required.generated.c] + +int +virRequiredDefParseXML(xmlNodePtr node, + virRequiredDef *def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + xmlNodePtr bootpNode = NULL; + void *arg_parent G_GNUC_UNUSED = def; + void *arg_opaque G_GNUC_UNUSED = opaque; + + if (!def) + goto error; + +#ifdef ENABLE_VIR_REQUIRED_DEF_PARSE_HOOK_SET_ARGS + virRequiredDefParseXMLSetArgs(node, parent, &arg_parent, &arg_opaque); +#endif + + def->name = virXMLPropString(node, "name"); + if (def->name == NULL) { + virReportError(VIR_ERR_XML_ERROR, + _("Missing '%s' setting in '%s'"), + "name", instname); + goto error; + } + + bootpNode = virXMLChildNode(node, "bootp"); + if (bootpNode == NULL) { + virReportError(VIR_ERR_XML_ERROR, + _("Missing '%s' setting in '%s'"), + "bootp", instname); + goto error; + } + if (bootpNode) { + if (virNetworkBootpDefParseXML(bootpNode, &def->bootp, instname, arg_parent, arg_opaque) < 0) + goto error; + } + +#ifdef ENABLE_VIR_REQUIRED_DEF_PARSE_HOOK + if (virRequiredDefParseHook(node, def, instname, parent, opaque, bootpNode) < 0) + goto error; +#endif + + return 0; + + error: + virRequiredDefClear(def); + return -1; +} + + +[conf/required.generated.h] + +int +virRequiredDefFormatBuf(virBuffer *buf, + const char *name, + const virRequiredDef *def, + const void *parent, + void *opaque); + + +[conf/required.generated.h] + +#ifdef ENABLE_VIR_REQUIRED_DEF_FORMAT_HOOK + +int +virRequiredDefFormatHook(const virRequiredDef *def, + const void *parent, + const void *opaque, + virTristateBool *empty, + virTristateBool *shortcut, + virBuffer *nameBuf, + virBuffer *bootpBuf); + +#endif /* ENABLE_VIR_REQUIRED_DEF_FORMAT_HOOK */ + + +[conf/required.generated.c] + +int +virRequiredDefFormatBuf(virBuffer *buf, + const char *name, + const virRequiredDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + virTristateBool empty G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + virTristateBool shortcut G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + g_auto(virBuffer) nameBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) bootpBuf = VIR_BUFFER_INITIALIZER; + + if (!def || !buf) + return 0; + +#ifdef ENABLE_VIR_REQUIRED_DEF_FORMAT_HOOK + if (virRequiredDefFormatHook(def, parent, opaque, &empty, &shortcut, &nameBuf, &bootpBuf) < 0) + return -1; +#endif /* ENABLE_VIR_REQUIRED_DEF_FORMAT_HOOK */ + + if (empty != VIR_TRISTATE_BOOL_NO) + if (empty || !(def->name || virNetworkBootpDefCheck(&def->bootp, def, opaque))) + return 0; + + virBufferAsprintf(buf, "<%s", name); + + virBufferInheritIndent(&nameBuf, buf); + if (!virBufferTouched(&nameBuf)) + virBufferEscapeString(&nameBuf, " name='%s'", def->name); + virBufferAddBuffer(buf, &nameBuf); + + if (shortcut != VIR_TRISTATE_BOOL_NO) { + if (shortcut || !(virNetworkBootpDefCheck(&def->bootp, def, opaque))) { + virBufferAddLit(buf, "/>\n"); + return 0; + } + } + + virBufferAddLit(buf, ">\n"); + + virBufferAdjustIndent(buf, 2); + + virBufferInheritIndent(&bootpBuf, buf); + if (!virBufferTouched(&bootpBuf)) { + if (virNetworkBootpDefFormatBuf(&bootpBuf, "bootp", &def->bootp, def, opaque) < 0) + return -1; + } + virBufferAddBuffer(buf, &bootpBuf); + + virBufferAdjustIndent(buf, -2); + virBufferAsprintf(buf, "</%s>\n", name); + + return 0; +} + + +[conf/required.generated.h] + +bool +virRequiredDefCheck(const virRequiredDef *def, + const void *parent, + void *opaque); + + +[conf/required.generated.c] + +#ifndef RESET_VIR_REQUIRED_DEF_CHECK + +bool +virRequiredDefCheck(const virRequiredDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + if (!def) + return false; + + return def->name || virNetworkBootpDefCheck(&def->bootp, def, opaque); +} + +#endif /* RESET_VIR_REQUIRED_DEF_CHECK */ + + +[Tips] + +/* Put these lines at the bottom of "conf/required.h" */ +/* Makesure "required.h" to be appended into conf_xmlgen_input in src/conf/meson.build */ + +/* Define macro to enable hook or redefine check when necessary */ +/* #define ENABLE_VIR_REQUIRED_DEF_PARSE_HOOK */ +/* #define ENABLE_VIR_REQUIRED_DEF_PARSE_HOOK_SET_ARGS */ +/* #define ENABLE_VIR_REQUIRED_DEF_FORMAT_HOOK */ + +/* #define RESET_VIR_REQUIRED_DEF_CHECK */ + +/* Makesure below is the bottom line! */ +#include "required.generated.h" diff --git a/tests/xmlgenout/skipparse.txt b/tests/xmlgenout/skipparse.txt new file mode 100644 index 00000000..b6f6546a --- /dev/null +++ b/tests/xmlgenout/skipparse.txt @@ -0,0 +1,223 @@ +[conf/skipparse.generated.h] + +void +virSkipParseDefClear(virSkipParseDef *def); + + +[conf/skipparse.generated.c] + +void +virSkipParseDefClear(virSkipParseDef *def) +{ + if (!def) + return; + + def->connections = 0; + + if (def->nifs) { + size_t i; + for (i = 0; i < def->nifs; i++) + virNetworkForwardIfDefClear(&def->ifs[i]); + } + g_free(def->ifs); + def->ifs = NULL; + def->nifs = 0; +} + + +[conf/skipparse.generated.h] + +int +virSkipParseDefParseXML(xmlNodePtr node, + virSkipParseDef *def, + const char *instname, + void *parent, + void *opaque); + + +[conf/skipparse.generated.h] + +#ifdef ENABLE_VIR_SKIP_PARSE_DEF_PARSE_HOOK + +int +virSkipParseDefParseHook(xmlNodePtr node, + virSkipParseDef *def, + const char *instname, + void *parent, + void *opaque); + +#endif + + +[conf/skipparse.generated.h] + +#ifdef ENABLE_VIR_SKIP_PARSE_DEF_PARSE_HOOK_SET_ARGS + +void +virSkipParseDefParseXMLSetArgs(xmlNodePtr node, + void *parent, + void **pparent, + void **popaque); + +#endif + + +[conf/skipparse.generated.c] + +int +virSkipParseDefParseXML(xmlNodePtr node, + virSkipParseDef *def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + void *arg_parent G_GNUC_UNUSED = def; + void *arg_opaque G_GNUC_UNUSED = opaque; + + if (!def) + goto error; + +#ifdef ENABLE_VIR_SKIP_PARSE_DEF_PARSE_HOOK_SET_ARGS + virSkipParseDefParseXMLSetArgs(node, parent, &arg_parent, &arg_opaque); +#endif + +#ifdef ENABLE_VIR_SKIP_PARSE_DEF_PARSE_HOOK + if (virSkipParseDefParseHook(node, def, instname, parent, opaque) < 0) + goto error; +#endif + + return 0; + + error: + virSkipParseDefClear(def); + return -1; +} + + +[conf/skipparse.generated.h] + +int +virSkipParseDefFormatBuf(virBuffer *buf, + const char *name, + const virSkipParseDef *def, + const void *parent, + void *opaque); + + +[conf/skipparse.generated.h] + +#ifdef ENABLE_VIR_SKIP_PARSE_DEF_FORMAT_HOOK + +int +virSkipParseDefFormatHook(const virSkipParseDef *def, + const void *parent, + const void *opaque, + virTristateBool *empty, + virTristateBool *shortcut, + virBuffer *connectionsBuf, + virBuffer *ifsBuf); + +#endif /* ENABLE_VIR_SKIP_PARSE_DEF_FORMAT_HOOK */ + + +[conf/skipparse.generated.c] + +int +virSkipParseDefFormatBuf(virBuffer *buf, + const char *name, + const virSkipParseDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + virTristateBool empty G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + virTristateBool shortcut G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + g_auto(virBuffer) connectionsBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) ifsBuf = VIR_BUFFER_INITIALIZER; + + if (!def || !buf) + return 0; + +#ifdef ENABLE_VIR_SKIP_PARSE_DEF_FORMAT_HOOK + if (virSkipParseDefFormatHook(def, parent, opaque, &empty, &shortcut, &connectionsBuf, &ifsBuf) < 0) + return -1; +#endif /* ENABLE_VIR_SKIP_PARSE_DEF_FORMAT_HOOK */ + + if (empty != VIR_TRISTATE_BOOL_NO) + if (empty || !(def->connections || def->nifs)) + return 0; + + virBufferAsprintf(buf, "<%s", name); + + virBufferInheritIndent(&connectionsBuf, buf); + if (!virBufferTouched(&connectionsBuf) && def->connections) + virBufferAsprintf(&connectionsBuf, " connections='%d'", def->connections); + virBufferAddBuffer(buf, &connectionsBuf); + + if (shortcut != VIR_TRISTATE_BOOL_NO) { + if (shortcut || !(def->nifs)) { + virBufferAddLit(buf, "/>\n"); + return 0; + } + } + + virBufferAddLit(buf, ">\n"); + + virBufferAdjustIndent(buf, 2); + + virBufferInheritIndent(&ifsBuf, buf); + if (!virBufferTouched(&ifsBuf) && def->nifs) { + size_t i; + for (i = 0; i < def->nifs; i++) { + if (virNetworkForwardIfDefFormatBuf(buf, "if", &def->ifs[i], def, opaque) < 0) + return -1; + } + } + virBufferAddBuffer(buf, &ifsBuf); + + virBufferAdjustIndent(buf, -2); + virBufferAsprintf(buf, "</%s>\n", name); + + return 0; +} + + +[conf/skipparse.generated.h] + +bool +virSkipParseDefCheck(const virSkipParseDef *def, + const void *parent, + void *opaque); + + +[conf/skipparse.generated.c] + +#ifndef RESET_VIR_SKIP_PARSE_DEF_CHECK + +bool +virSkipParseDefCheck(const virSkipParseDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + if (!def) + return false; + + return def->connections || def->nifs; +} + +#endif /* RESET_VIR_SKIP_PARSE_DEF_CHECK */ + + +[Tips] + +/* Put these lines at the bottom of "conf/skipparse.h" */ +/* Makesure "skipparse.h" to be appended into conf_xmlgen_input in src/conf/meson.build */ + +/* Define macro to enable hook or redefine check when necessary */ +/* #define ENABLE_VIR_SKIP_PARSE_DEF_PARSE_HOOK */ +/* #define ENABLE_VIR_SKIP_PARSE_DEF_PARSE_HOOK_SET_ARGS */ +/* #define ENABLE_VIR_SKIP_PARSE_DEF_FORMAT_HOOK */ + +/* #define RESET_VIR_SKIP_PARSE_DEF_CHECK */ + +/* Makesure below is the bottom line! */ +#include "skipparse.generated.h" diff --git a/tests/xmlgenout/specify.txt b/tests/xmlgenout/specify.txt new file mode 100644 index 00000000..dd6dac9c --- /dev/null +++ b/tests/xmlgenout/specify.txt @@ -0,0 +1,291 @@ +[conf/specify.generated.h] + +void +virSpecifyDefClear(virSpecifyDef *def); + + +[conf/specify.generated.c] + +void +virSpecifyDefClear(virSpecifyDef *def) +{ + if (!def) + return; + + def->unit = 0; + + def->expiry = 0; + + virMacAddrClear(&def->mac); + def->mac_specified = false; + + virUUIDClear(&def->uuid); + def->uuid_specified = false; +} + + +[conf/specify.generated.h] + +int +virSpecifyDefParseXML(xmlNodePtr node, + virSpecifyDef *def, + const char *instname, + void *parent, + void *opaque); + + +[conf/specify.generated.h] + +#ifdef ENABLE_VIR_SPECIFY_DEF_PARSE_HOOK + +int +virSpecifyDefParseHook(xmlNodePtr node, + virSpecifyDef *def, + const char *instname, + void *parent, + void *opaque, + const char *unitStr, + const char *expiryStr, + const char *macStr, + const char *uuidStr); + +#endif + + +[conf/specify.generated.h] + +#ifdef ENABLE_VIR_SPECIFY_DEF_PARSE_HOOK_SET_ARGS + +void +virSpecifyDefParseXMLSetArgs(xmlNodePtr node, + void *parent, + void **pparent, + void **popaque); + +#endif + + +[conf/specify.generated.c] + +int +virSpecifyDefParseXML(xmlNodePtr node, + virSpecifyDef *def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + g_autofree char *unitStr = NULL; + g_autofree char *expiryStr = NULL; + g_autofree char *macStr = NULL; + g_autofree char *uuidStr = NULL; + void *arg_parent G_GNUC_UNUSED = def; + void *arg_opaque G_GNUC_UNUSED = opaque; + + if (!def) + goto error; + +#ifdef ENABLE_VIR_SPECIFY_DEF_PARSE_HOOK_SET_ARGS + virSpecifyDefParseXMLSetArgs(node, parent, &arg_parent, &arg_opaque); +#endif + + unitStr = virXMLPropString(node, "unit"); + if (unitStr) { + if ((def->unit = virNetworkDHCPLeaseTimeUnitTypeFromString(unitStr)) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid '%s' setting '%s' in '%s'"), + "unit", unitStr, instname); + goto error; + } + } + + expiryStr = virXMLPropString(node, "expiry"); + if (expiryStr) { + if (virStrToLong_ulp(expiryStr, NULL, 0, &def->expiry) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid '%s' setting '%s' in '%s'"), + "expiry", expiryStr, instname); + goto error; + } + } + + macStr = virXMLChildPropString(node, "mac/address"); + if (macStr) { + if (virMacAddrParseXML(macStr, &def->mac, instname, arg_parent, arg_opaque) < 0) + goto error; + def->mac_specified = true; + } + + uuidStr = virXMLChildNodeContent(node, "uuid"); + if (uuidStr) { + if (virUUIDParseXML(uuidStr, &def->uuid, instname, arg_parent, arg_opaque) < 0) + goto error; + def->uuid_specified = true; + } + +#ifdef ENABLE_VIR_SPECIFY_DEF_PARSE_HOOK + if (virSpecifyDefParseHook(node, def, instname, parent, opaque, unitStr, expiryStr, macStr, uuidStr) < 0) + goto error; +#endif + + return 0; + + error: + virSpecifyDefClear(def); + return -1; +} + + +[conf/specify.generated.h] + +int +virSpecifyDefFormatBuf(virBuffer *buf, + const char *name, + const virSpecifyDef *def, + const void *parent, + void *opaque); + + +[conf/specify.generated.h] + +#ifdef ENABLE_VIR_SPECIFY_DEF_FORMAT_HOOK + +int +virSpecifyDefFormatHook(const virSpecifyDef *def, + const void *parent, + const void *opaque, + virTristateBool *empty, + virTristateBool *shortcut, + virBuffer *unitBuf, + virBuffer *expiryBuf, + virBuffer *mac_addressBuf, + virBuffer *uuidBuf); + +#endif /* ENABLE_VIR_SPECIFY_DEF_FORMAT_HOOK */ + + +[conf/specify.generated.c] + +int +virSpecifyDefFormatBuf(virBuffer *buf, + const char *name, + const virSpecifyDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + virTristateBool empty G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + virTristateBool shortcut G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + g_auto(virBuffer) unitBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) expiryBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) mac_addressBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) uuidBuf = VIR_BUFFER_INITIALIZER; + + if (!def || !buf) + return 0; + +#ifdef ENABLE_VIR_SPECIFY_DEF_FORMAT_HOOK + if (virSpecifyDefFormatHook(def, parent, opaque, &empty, &shortcut, &unitBuf, &expiryBuf, &mac_addressBuf, &uuidBuf) < 0) + return -1; +#endif /* ENABLE_VIR_SPECIFY_DEF_FORMAT_HOOK */ + + if (empty != VIR_TRISTATE_BOOL_NO) + if (empty || !(def->expiry || def->uuid_specified || def->mac_specified)) + return 0; + + virBufferAsprintf(buf, "<%s", name); + + virBufferInheritIndent(&unitBuf, buf); + if (!virBufferTouched(&unitBuf) && def->expiry) { + const char *str = virNetworkDHCPLeaseTimeUnitTypeToString(def->unit); + if (!str) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown %s type %d"), + "unit", def->unit); + return -1; + } + virBufferAsprintf(&unitBuf, " unit='%s'", str); + } + virBufferAddBuffer(buf, &unitBuf); + + virBufferInheritIndent(&expiryBuf, buf); + if (!virBufferTouched(&expiryBuf) && def->expiry) + virBufferAsprintf(&expiryBuf, " expiry='%lu'", def->expiry); + virBufferAddBuffer(buf, &expiryBuf); + + if (shortcut != VIR_TRISTATE_BOOL_NO) { + if (shortcut || !(def->uuid_specified || def->mac_specified)) { + virBufferAddLit(buf, "/>\n"); + return 0; + } + } + + virBufferAddLit(buf, ">\n"); + + virBufferAdjustIndent(buf, 2); + + if (virBufferUse(&mac_addressBuf) || def->mac_specified) { + virBufferAddLit(buf, "<mac"); + + virBufferInheritIndent(&mac_addressBuf, buf); + if (!virBufferTouched(&mac_addressBuf) && def->mac_specified) { + if (virMacAddrFormatBuf(&mac_addressBuf, " address='%s'", &def->mac, def, opaque) < 0) + return -1; + } + virBufferAddBuffer(buf, &mac_addressBuf); + + virBufferAddLit(buf, "/>\n"); + } + + virBufferInheritIndent(&uuidBuf, buf); + if (!virBufferTouched(&uuidBuf) && def->uuid_specified) { + if (virUUIDFormatBuf(&uuidBuf, "<uuid>%s</uuid>\n", &def->uuid, def, opaque) < 0) + return -1; + } + virBufferAddBuffer(buf, &uuidBuf); + + virBufferAdjustIndent(buf, -2); + virBufferAsprintf(buf, "</%s>\n", name); + + return 0; +} + + +[conf/specify.generated.h] + +bool +virSpecifyDefCheck(const virSpecifyDef *def, + const void *parent, + void *opaque); + + +[conf/specify.generated.c] + +#ifndef RESET_VIR_SPECIFY_DEF_CHECK + +bool +virSpecifyDefCheck(const virSpecifyDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + if (!def) + return false; + + return def->expiry || def->uuid_specified || def->mac_specified; +} + +#endif /* RESET_VIR_SPECIFY_DEF_CHECK */ + + +[Tips] + +/* Put these lines at the bottom of "conf/specify.h" */ +/* Makesure "specify.h" to be appended into conf_xmlgen_input in src/conf/meson.build */ + +/* Define macro to enable hook or redefine check when necessary */ +/* #define ENABLE_VIR_SPECIFY_DEF_PARSE_HOOK */ +/* #define ENABLE_VIR_SPECIFY_DEF_PARSE_HOOK_SET_ARGS */ +/* #define ENABLE_VIR_SPECIFY_DEF_FORMAT_HOOK */ + +/* #define RESET_VIR_SPECIFY_DEF_CHECK */ + +/* Makesure below is the bottom line! */ +#include "specify.generated.h" diff --git a/tests/xmlgenout/xmlattr.txt b/tests/xmlgenout/xmlattr.txt new file mode 100644 index 00000000..228eb3cc --- /dev/null +++ b/tests/xmlgenout/xmlattr.txt @@ -0,0 +1,252 @@ +[conf/xmlattr.generated.h] + +void +virXMLAttrDefClear(virXMLAttrDef *def); + + +[conf/xmlattr.generated.c] + +void +virXMLAttrDefClear(virXMLAttrDef *def) +{ + if (!def) + return; + + g_free(def->family); + def->family = NULL; + + def->localPTR = 0; + + g_free(def->tftproot); + def->tftproot = NULL; +} + + +[conf/xmlattr.generated.h] + +int +virXMLAttrDefParseXML(xmlNodePtr node, + virXMLAttrDef *def, + const char *instname, + void *parent, + void *opaque); + + +[conf/xmlattr.generated.h] + +#ifdef ENABLE_VIR_XMLATTR_DEF_PARSE_HOOK + +int +virXMLAttrDefParseHook(xmlNodePtr node, + virXMLAttrDef *def, + const char *instname, + void *parent, + void *opaque, + const char *localPTRStr); + +#endif + + +[conf/xmlattr.generated.h] + +#ifdef ENABLE_VIR_XMLATTR_DEF_PARSE_HOOK_SET_ARGS + +void +virXMLAttrDefParseXMLSetArgs(xmlNodePtr node, + void *parent, + void **pparent, + void **popaque); + +#endif + + +[conf/xmlattr.generated.c] + +int +virXMLAttrDefParseXML(xmlNodePtr node, + virXMLAttrDef *def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + g_autofree char *localPTRStr = NULL; + void *arg_parent G_GNUC_UNUSED = def; + void *arg_opaque G_GNUC_UNUSED = opaque; + + if (!def) + goto error; + +#ifdef ENABLE_VIR_XMLATTR_DEF_PARSE_HOOK_SET_ARGS + virXMLAttrDefParseXMLSetArgs(node, parent, &arg_parent, &arg_opaque); +#endif + + def->family = virXMLPropString(node, "family"); + + localPTRStr = virXMLPropString(node, "localPtr"); + if (localPTRStr) { + if ((def->localPTR = virTristateBoolTypeFromString(localPTRStr)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid '%s' setting '%s' in '%s'"), + "localPtr", localPTRStr, instname); + goto error; + } + } + + def->tftproot = virXMLChildPropString(node, "tftp/root"); + +#ifdef ENABLE_VIR_XMLATTR_DEF_PARSE_HOOK + if (virXMLAttrDefParseHook(node, def, instname, parent, opaque, localPTRStr) < 0) + goto error; +#endif + + return 0; + + error: + virXMLAttrDefClear(def); + return -1; +} + + +[conf/xmlattr.generated.h] + +int +virXMLAttrDefFormatBuf(virBuffer *buf, + const char *name, + const virXMLAttrDef *def, + const void *parent, + void *opaque); + + +[conf/xmlattr.generated.h] + +#ifdef ENABLE_VIR_XMLATTR_DEF_FORMAT_HOOK + +int +virXMLAttrDefFormatHook(const virXMLAttrDef *def, + const void *parent, + const void *opaque, + virTristateBool *empty, + virTristateBool *shortcut, + virBuffer *familyBuf, + virBuffer *localPTRBuf, + virBuffer *tftp_rootBuf); + +#endif /* ENABLE_VIR_XMLATTR_DEF_FORMAT_HOOK */ + + +[conf/xmlattr.generated.c] + +int +virXMLAttrDefFormatBuf(virBuffer *buf, + const char *name, + const virXMLAttrDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + virTristateBool empty G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + virTristateBool shortcut G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + g_auto(virBuffer) familyBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) localPTRBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) tftp_rootBuf = VIR_BUFFER_INITIALIZER; + + if (!def || !buf) + return 0; + +#ifdef ENABLE_VIR_XMLATTR_DEF_FORMAT_HOOK + if (virXMLAttrDefFormatHook(def, parent, opaque, &empty, &shortcut, &familyBuf, &localPTRBuf, &tftp_rootBuf) < 0) + return -1; +#endif /* ENABLE_VIR_XMLATTR_DEF_FORMAT_HOOK */ + + if (empty != VIR_TRISTATE_BOOL_NO) + if (empty || !(def->family || def->localPTR > 0 || def->tftproot)) + return 0; + + virBufferAsprintf(buf, "<%s", name); + + virBufferInheritIndent(&familyBuf, buf); + if (!virBufferTouched(&familyBuf) && def->family) + virBufferEscapeString(&familyBuf, " family='%s'", def->family); + virBufferAddBuffer(buf, &familyBuf); + + virBufferInheritIndent(&localPTRBuf, buf); + if (!virBufferTouched(&localPTRBuf) && def->localPTR > 0) { + const char *str = virTristateBoolTypeToString(def->localPTR); + if (!str) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown %s type %d"), + "localPtr", def->localPTR); + return -1; + } + virBufferAsprintf(&localPTRBuf, " localPtr='%s'", str); + } + virBufferAddBuffer(buf, &localPTRBuf); + + if (shortcut != VIR_TRISTATE_BOOL_NO) { + if (shortcut || !(def->tftproot)) { + virBufferAddLit(buf, "/>\n"); + return 0; + } + } + + virBufferAddLit(buf, ">\n"); + + virBufferAdjustIndent(buf, 2); + + if (virBufferUse(&tftp_rootBuf) || def->tftproot) { + virBufferAddLit(buf, "<tftp"); + + virBufferInheritIndent(&tftp_rootBuf, buf); + if (!virBufferTouched(&tftp_rootBuf) && def->tftproot) + virBufferEscapeString(&tftp_rootBuf, " root='%s'", def->tftproot); + virBufferAddBuffer(buf, &tftp_rootBuf); + + virBufferAddLit(buf, "/>\n"); + } + + virBufferAdjustIndent(buf, -2); + virBufferAsprintf(buf, "</%s>\n", name); + + return 0; +} + + +[conf/xmlattr.generated.h] + +bool +virXMLAttrDefCheck(const virXMLAttrDef *def, + const void *parent, + void *opaque); + + +[conf/xmlattr.generated.c] + +#ifndef RESET_VIR_XMLATTR_DEF_CHECK + +bool +virXMLAttrDefCheck(const virXMLAttrDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + if (!def) + return false; + + return def->family || def->localPTR > 0 || def->tftproot; +} + +#endif /* RESET_VIR_XMLATTR_DEF_CHECK */ + + +[Tips] + +/* Put these lines at the bottom of "conf/xmlattr.h" */ +/* Makesure "xmlattr.h" to be appended into conf_xmlgen_input in src/conf/meson.build */ + +/* Define macro to enable hook or redefine check when necessary */ +/* #define ENABLE_VIR_XMLATTR_DEF_PARSE_HOOK */ +/* #define ENABLE_VIR_XMLATTR_DEF_PARSE_HOOK_SET_ARGS */ +/* #define ENABLE_VIR_XMLATTR_DEF_FORMAT_HOOK */ + +/* #define RESET_VIR_XMLATTR_DEF_CHECK */ + +/* Makesure below is the bottom line! */ +#include "xmlattr.generated.h" diff --git a/tests/xmlgenout/xmlelem.txt b/tests/xmlgenout/xmlelem.txt new file mode 100644 index 00000000..d910eda2 --- /dev/null +++ b/tests/xmlgenout/xmlelem.txt @@ -0,0 +1,243 @@ +[conf/xmlelem.generated.h] + +void +virXMLElemDefClear(virXMLElemDef *def); + + +[conf/xmlelem.generated.c] + +void +virXMLElemDefClear(virXMLElemDef *def) +{ + if (!def) + return; + + virPortRangeClear(&def->port); + + virSocketAddrRangeClear(&def->addr); + + virUUIDClear(&def->uuid); +} + + +[conf/xmlelem.generated.h] + +int +virXMLElemDefParseXML(xmlNodePtr node, + virXMLElemDef *def, + const char *instname, + void *parent, + void *opaque); + + +[conf/xmlelem.generated.h] + +#ifdef ENABLE_VIR_XMLELEM_DEF_PARSE_HOOK + +int +virXMLElemDefParseHook(xmlNodePtr node, + virXMLElemDef *def, + const char *instname, + void *parent, + void *opaque, + xmlNodePtr portNode, + xmlNodePtr addrNode, + const char *uuidStr); + +#endif + + +[conf/xmlelem.generated.h] + +#ifdef ENABLE_VIR_XMLELEM_DEF_PARSE_HOOK_SET_ARGS + +void +virXMLElemDefParseXMLSetArgs(xmlNodePtr node, + void *parent, + void **pparent, + void **popaque); + +#endif + + +[conf/xmlelem.generated.c] + +int +virXMLElemDefParseXML(xmlNodePtr node, + virXMLElemDef *def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + xmlNodePtr portNode = NULL; + xmlNodePtr addrNode = NULL; + g_autofree char *uuidStr = NULL; + void *arg_parent G_GNUC_UNUSED = def; + void *arg_opaque G_GNUC_UNUSED = opaque; + + if (!def) + goto error; + +#ifdef ENABLE_VIR_XMLELEM_DEF_PARSE_HOOK_SET_ARGS + virXMLElemDefParseXMLSetArgs(node, parent, &arg_parent, &arg_opaque); +#endif + + portNode = virXMLChildNode(node, "port"); + if (portNode) { + if (virPortRangeParseXML(portNode, &def->port, instname, arg_parent, arg_opaque) < 0) + goto error; + } + + addrNode = virXMLChildNode(node, "address"); + if (addrNode) { + if (virSocketAddrRangeParseXML(addrNode, &def->addr, instname, arg_parent, arg_opaque) < 0) + goto error; + } + + uuidStr = virXMLChildNodeContent(node, "uuid"); + if (uuidStr) { + if (virUUIDParseXML(uuidStr, &def->uuid, instname, arg_parent, arg_opaque) < 0) + goto error; + } + +#ifdef ENABLE_VIR_XMLELEM_DEF_PARSE_HOOK + if (virXMLElemDefParseHook(node, def, instname, parent, opaque, portNode, addrNode, uuidStr) < 0) + goto error; +#endif + + return 0; + + error: + virXMLElemDefClear(def); + return -1; +} + + +[conf/xmlelem.generated.h] + +int +virXMLElemDefFormatBuf(virBuffer *buf, + const char *name, + const virXMLElemDef *def, + const void *parent, + void *opaque); + + +[conf/xmlelem.generated.h] + +#ifdef ENABLE_VIR_XMLELEM_DEF_FORMAT_HOOK + +int +virXMLElemDefFormatHook(const virXMLElemDef *def, + const void *parent, + const void *opaque, + virTristateBool *empty, + virTristateBool *shortcut, + virBuffer *portBuf, + virBuffer *addrBuf, + virBuffer *uuidBuf); + +#endif /* ENABLE_VIR_XMLELEM_DEF_FORMAT_HOOK */ + + +[conf/xmlelem.generated.c] + +int +virXMLElemDefFormatBuf(virBuffer *buf, + const char *name, + const virXMLElemDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + virTristateBool empty G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + virTristateBool shortcut G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + g_auto(virBuffer) portBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) addrBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) uuidBuf = VIR_BUFFER_INITIALIZER; + + if (!def || !buf) + return 0; + +#ifdef ENABLE_VIR_XMLELEM_DEF_FORMAT_HOOK + if (virXMLElemDefFormatHook(def, parent, opaque, &empty, &shortcut, &portBuf, &addrBuf, &uuidBuf) < 0) + return -1; +#endif /* ENABLE_VIR_XMLELEM_DEF_FORMAT_HOOK */ + + if (empty != VIR_TRISTATE_BOOL_NO) + if (empty || !(virPortRangeCheck(&def->port, def, opaque) || virSocketAddrRangeCheck(&def->addr, def, opaque) || virUUIDCheck(&def->uuid, def, opaque))) + return 0; + + virBufferAsprintf(buf, "<%s", name); + + virBufferAddLit(buf, ">\n"); + + virBufferAdjustIndent(buf, 2); + + virBufferInheritIndent(&portBuf, buf); + if (!virBufferTouched(&portBuf) && virPortRangeCheck(&def->port, def, opaque)) { + if (virPortRangeFormatBuf(&portBuf, "port", &def->port, def, opaque) < 0) + return -1; + } + virBufferAddBuffer(buf, &portBuf); + + virBufferInheritIndent(&addrBuf, buf); + if (!virBufferTouched(&addrBuf) && virSocketAddrRangeCheck(&def->addr, def, opaque)) { + if (virSocketAddrRangeFormatBuf(&addrBuf, "address", &def->addr, def, opaque) < 0) + return -1; + } + virBufferAddBuffer(buf, &addrBuf); + + virBufferInheritIndent(&uuidBuf, buf); + if (!virBufferTouched(&uuidBuf) && virUUIDCheck(&def->uuid, def, opaque)) { + if (virUUIDFormatBuf(&uuidBuf, "<uuid>%s</uuid>\n", &def->uuid, def, opaque) < 0) + return -1; + } + virBufferAddBuffer(buf, &uuidBuf); + + virBufferAdjustIndent(buf, -2); + virBufferAsprintf(buf, "</%s>\n", name); + + return 0; +} + + +[conf/xmlelem.generated.h] + +bool +virXMLElemDefCheck(const virXMLElemDef *def, + const void *parent, + void *opaque); + + +[conf/xmlelem.generated.c] + +#ifndef RESET_VIR_XMLELEM_DEF_CHECK + +bool +virXMLElemDefCheck(const virXMLElemDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + if (!def) + return false; + + return virPortRangeCheck(&def->port, def, opaque) || virSocketAddrRangeCheck(&def->addr, def, opaque) || virUUIDCheck(&def->uuid, def, opaque); +} + +#endif /* RESET_VIR_XMLELEM_DEF_CHECK */ + + +[Tips] + +/* Put these lines at the bottom of "conf/xmlelem.h" */ +/* Makesure "xmlelem.h" to be appended into conf_xmlgen_input in src/conf/meson.build */ + +/* Define macro to enable hook or redefine check when necessary */ +/* #define ENABLE_VIR_XMLELEM_DEF_PARSE_HOOK */ +/* #define ENABLE_VIR_XMLELEM_DEF_PARSE_HOOK_SET_ARGS */ +/* #define ENABLE_VIR_XMLELEM_DEF_FORMAT_HOOK */ + +/* #define RESET_VIR_XMLELEM_DEF_CHECK */ + +/* Makesure below is the bottom line! */ +#include "xmlelem.generated.h" diff --git a/tests/xmlgenout/xmlgroup.txt b/tests/xmlgenout/xmlgroup.txt new file mode 100644 index 00000000..ff9d7e09 --- /dev/null +++ b/tests/xmlgenout/xmlgroup.txt @@ -0,0 +1,204 @@ +[conf/xmlgroup.generated.h] + +void +virXMLGroupDefClear(virXMLGroupDef *def); + + +[conf/xmlgroup.generated.c] + +void +virXMLGroupDefClear(virXMLGroupDef *def) +{ + if (!def) + return; + + virUtilAuthDefClear(&def->auth); +} + + +[conf/xmlgroup.generated.h] + +int +virXMLGroupDefParseXML(xmlNodePtr node, + virXMLGroupDef *def, + const char *instname, + void *parent, + void *opaque); + + +[conf/xmlgroup.generated.h] + +#ifdef ENABLE_VIR_XMLGROUP_DEF_PARSE_HOOK + +int +virXMLGroupDefParseHook(xmlNodePtr node, + virXMLGroupDef *def, + const char *instname, + void *parent, + void *opaque); + +#endif + + +[conf/xmlgroup.generated.h] + +#ifdef ENABLE_VIR_XMLGROUP_DEF_PARSE_HOOK_SET_ARGS + +void +virXMLGroupDefParseXMLSetArgs(xmlNodePtr node, + void *parent, + void **pparent, + void **popaque); + +#endif + + +[conf/xmlgroup.generated.c] + +int +virXMLGroupDefParseXML(xmlNodePtr node, + virXMLGroupDef *def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + void *arg_parent G_GNUC_UNUSED = def; + void *arg_opaque G_GNUC_UNUSED = opaque; + + if (!def) + goto error; + +#ifdef ENABLE_VIR_XMLGROUP_DEF_PARSE_HOOK_SET_ARGS + virXMLGroupDefParseXMLSetArgs(node, parent, &arg_parent, &arg_opaque); +#endif + + if (virUtilAuthDefParseXML(node, &def->auth, instname, arg_parent, arg_opaque) < 0) + goto error; + +#ifdef ENABLE_VIR_XMLGROUP_DEF_PARSE_HOOK + if (virXMLGroupDefParseHook(node, def, instname, parent, opaque) < 0) + goto error; +#endif + + return 0; + + error: + virXMLGroupDefClear(def); + return -1; +} + + +[conf/xmlgroup.generated.h] + +int +virXMLGroupDefFormatBuf(virBuffer *buf, + const char *name, + const virXMLGroupDef *def, + const void *parent, + void *opaque); + + +[conf/xmlgroup.generated.h] + +#ifdef ENABLE_VIR_XMLGROUP_DEF_FORMAT_HOOK + +int +virXMLGroupDefFormatHook(const virXMLGroupDef *def, + const void *parent, + const void *opaque, + virTristateBool *empty, + virTristateBool *shortcut); + +#endif /* ENABLE_VIR_XMLGROUP_DEF_FORMAT_HOOK */ + + +[conf/xmlgroup.generated.c] + +int +virXMLGroupDefFormatBuf(virBuffer *buf, + const char *name, + const virXMLGroupDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + virTristateBool empty G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + virTristateBool shortcut G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + + if (!def || !buf) + return 0; + +#ifdef ENABLE_VIR_XMLGROUP_DEF_FORMAT_HOOK + if (virXMLGroupDefFormatHook(def, parent, opaque, &empty, &shortcut) < 0) + return -1; +#endif /* ENABLE_VIR_XMLGROUP_DEF_FORMAT_HOOK */ + + if (empty != VIR_TRISTATE_BOOL_NO) + if (empty || !(virUtilAuthDefCheckAttr(&def->auth, def, opaque) || virUtilAuthDefCheckElem(&def->auth, def, opaque))) + return 0; + + virBufferAsprintf(buf, "<%s", name); + + if (virUtilAuthDefFormatAttr(buf, &def->auth, def, opaque) < 0) + return -1; + + if (shortcut != VIR_TRISTATE_BOOL_NO) { + if (shortcut || !(virUtilAuthDefCheckElem(&def->auth, def, opaque))) { + virBufferAddLit(buf, "/>\n"); + return 0; + } + } + + virBufferAddLit(buf, ">\n"); + + virBufferAdjustIndent(buf, 2); + + if (virUtilAuthDefFormatElem(buf, &def->auth, def, opaque) < 0) + return -1; + + virBufferAdjustIndent(buf, -2); + virBufferAsprintf(buf, "</%s>\n", name); + + return 0; +} + + +[conf/xmlgroup.generated.h] + +bool +virXMLGroupDefCheck(const virXMLGroupDef *def, + const void *parent, + void *opaque); + + +[conf/xmlgroup.generated.c] + +#ifndef RESET_VIR_XMLGROUP_DEF_CHECK + +bool +virXMLGroupDefCheck(const virXMLGroupDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + if (!def) + return false; + + return virUtilAuthDefCheckAttr(&def->auth, def, opaque) || virUtilAuthDefCheckElem(&def->auth, def, opaque); +} + +#endif /* RESET_VIR_XMLGROUP_DEF_CHECK */ + + +[Tips] + +/* Put these lines at the bottom of "conf/xmlgroup.h" */ +/* Makesure "xmlgroup.h" to be appended into conf_xmlgen_input in src/conf/meson.build */ + +/* Define macro to enable hook or redefine check when necessary */ +/* #define ENABLE_VIR_XMLGROUP_DEF_PARSE_HOOK */ +/* #define ENABLE_VIR_XMLGROUP_DEF_PARSE_HOOK_SET_ARGS */ +/* #define ENABLE_VIR_XMLGROUP_DEF_FORMAT_HOOK */ + +/* #define RESET_VIR_XMLGROUP_DEF_CHECK */ + +/* Makesure below is the bottom line! */ +#include "xmlgroup.generated.h" diff --git a/tests/xmlgenout/xmlswitch.txt b/tests/xmlgenout/xmlswitch.txt new file mode 100644 index 00000000..772f1353 --- /dev/null +++ b/tests/xmlgenout/xmlswitch.txt @@ -0,0 +1,470 @@ +[conf/xmlswitch.generated.h] + +void +virXMLSwitchDefClear(virXMLSwitchDef *def); + + +[conf/xmlswitch.generated.c] + +void +virXMLSwitchDefClear(virXMLSwitchDef *def) +{ + if (!def) + return; + + switch (def->type) { + + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: + virDomainGraphicsSDLDefClear(&def->data.sdl); + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_VNC: + virDomainGraphicsVNCDefClear(&def->data.vnc); + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + virDomainGraphicsRDPDefClear(&def->data.rdp); + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: + virDomainGraphicsDesktopDefClear(&def->data.desktop); + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + virDomainGraphicsSpiceDefClear(&def->data.spice); + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: + virDomainGraphicsEGLHeadlessDefClear(&def->data.egl_headless); + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_LAST: + break; + } + + def->type = 0; +} + + +[conf/xmlswitch.generated.h] + +int +virXMLSwitchDefParseXML(xmlNodePtr node, + virXMLSwitchDef *def, + const char *instname, + void *parent, + void *opaque); + + +[conf/xmlswitch.generated.h] + +#ifdef ENABLE_VIR_XMLSWITCH_DEF_PARSE_HOOK + +int +virXMLSwitchDefParseHook(xmlNodePtr node, + virXMLSwitchDef *def, + const char *instname, + void *parent, + void *opaque, + const char *typeStr); + +#endif + + +[conf/xmlswitch.generated.h] + +#ifdef ENABLE_VIR_XMLSWITCH_DEF_PARSE_HOOK_SET_ARGS + +void +virXMLSwitchDefParseXMLSetArgs(xmlNodePtr node, + void *parent, + void **pparent, + void **popaque); + +#endif + + +[conf/xmlswitch.generated.c] + +int +virXMLSwitchDefParseXML(xmlNodePtr node, + virXMLSwitchDef *def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + g_autofree char *typeStr = NULL; + void *arg_parent G_GNUC_UNUSED = def; + void *arg_opaque G_GNUC_UNUSED = opaque; + + if (!def) + goto error; + +#ifdef ENABLE_VIR_XMLSWITCH_DEF_PARSE_HOOK_SET_ARGS + virXMLSwitchDefParseXMLSetArgs(node, parent, &arg_parent, &arg_opaque); +#endif + + typeStr = virXMLPropString(node, "type"); + if (typeStr) { + if ((def->type = virDomainGraphicsTypeFromString(typeStr)) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid '%s' setting '%s' in '%s'"), + "type", typeStr, instname); + goto error; + } + } + + switch (def->type) { + + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: + if (virDomainGraphicsSDLDefParseXML(node, &def->data.sdl, instname, arg_parent, arg_opaque) < 0) + goto error; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_VNC: + if (virDomainGraphicsVNCDefParseXML(node, &def->data.vnc, instname, arg_parent, arg_opaque) < 0) + goto error; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + if (virDomainGraphicsRDPDefParseXML(node, &def->data.rdp, instname, arg_parent, arg_opaque) < 0) + goto error; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: + if (virDomainGraphicsDesktopDefParseXML(node, &def->data.desktop, instname, arg_parent, arg_opaque) < 0) + goto error; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + if (virDomainGraphicsSpiceDefParseXML(node, &def->data.spice, instname, arg_parent, arg_opaque) < 0) + goto error; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: + if (virDomainGraphicsEGLHeadlessDefParseXML(node, &def->data.egl_headless, instname, arg_parent, arg_opaque) < 0) + goto error; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_LAST: + break; + } + +#ifdef ENABLE_VIR_XMLSWITCH_DEF_PARSE_HOOK + if (virXMLSwitchDefParseHook(node, def, instname, parent, opaque, typeStr) < 0) + goto error; +#endif + + return 0; + + error: + virXMLSwitchDefClear(def); + return -1; +} + + +[conf/xmlswitch.generated.h] + +bool +virXMLSwitchDefDataCheckAttr(const virXMLSwitchDef *def, + void *opaque); + + +[conf/xmlswitch.generated.c] + +#ifndef RESET_VIR_XMLSWITCH_DEF_DATA_CHECK_ATTR + +bool +virXMLSwitchDefDataCheckAttr(const virXMLSwitchDef *def, + void *opaque G_GNUC_UNUSED) +{ + bool ret = false; + if (!def) + return false; + + switch (def->type) { + + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: + ret = virDomainGraphicsSDLDefCheckAttr(&def->data.sdl, def, opaque); + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_VNC: + ret = virDomainGraphicsVNCDefCheckAttr(&def->data.vnc, def, opaque); + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + ret = virDomainGraphicsRDPDefCheckAttr(&def->data.rdp, def, opaque); + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: + ret = virDomainGraphicsDesktopDefCheckAttr(&def->data.desktop, def, opaque); + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + ret = virDomainGraphicsSpiceDefCheckAttr(&def->data.spice, def, opaque); + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: + ret = virDomainGraphicsEGLHeadlessDefCheckAttr(&def->data.egl_headless, def, opaque); + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_LAST: + break; + } + + return ret; +} + +#endif /* RESET_VIR_XMLSWITCH_DEF_DATA_CHECK_ATTR */ + + +[conf/xmlswitch.generated.h] + +bool +virXMLSwitchDefDataCheckElem(const virXMLSwitchDef *def, + void *opaque); + + +[conf/xmlswitch.generated.c] + +#ifndef RESET_VIR_XMLSWITCH_DEF_DATA_CHECK_ELEM + +bool +virXMLSwitchDefDataCheckElem(const virXMLSwitchDef *def, + void *opaque G_GNUC_UNUSED) +{ + bool ret = false; + if (!def) + return false; + + switch (def->type) { + + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: + ret = virDomainGraphicsSDLDefCheckElem(&def->data.sdl, def, opaque); + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_VNC: + ret = virDomainGraphicsVNCDefCheckElem(&def->data.vnc, def, opaque); + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + ret = virDomainGraphicsRDPDefCheckElem(&def->data.rdp, def, opaque); + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: + ret = virDomainGraphicsDesktopDefCheckElem(&def->data.desktop, def, opaque); + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + ret = virDomainGraphicsSpiceDefCheckElem(&def->data.spice, def, opaque); + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: + ret = virDomainGraphicsEGLHeadlessDefCheckElem(&def->data.egl_headless, def, opaque); + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_LAST: + break; + } + + return ret; +} + +#endif /* RESET_VIR_XMLSWITCH_DEF_DATA_CHECK_ELEM */ + + +[conf/xmlswitch.generated.h] + +int +virXMLSwitchDefFormatBuf(virBuffer *buf, + const char *name, + const virXMLSwitchDef *def, + const void *parent, + void *opaque); + + +[conf/xmlswitch.generated.h] + +#ifdef ENABLE_VIR_XMLSWITCH_DEF_FORMAT_HOOK + +int +virXMLSwitchDefFormatHook(const virXMLSwitchDef *def, + const void *parent, + const void *opaque, + virTristateBool *empty, + virTristateBool *shortcut, + virBuffer *typeBuf); + +#endif /* ENABLE_VIR_XMLSWITCH_DEF_FORMAT_HOOK */ + + +[conf/xmlswitch.generated.c] + +int +virXMLSwitchDefFormatBuf(virBuffer *buf, + const char *name, + const virXMLSwitchDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + virTristateBool empty G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + virTristateBool shortcut G_GNUC_UNUSED = VIR_TRISTATE_BOOL_ABSENT; + g_auto(virBuffer) typeBuf = VIR_BUFFER_INITIALIZER; + + if (!def || !buf) + return 0; + +#ifdef ENABLE_VIR_XMLSWITCH_DEF_FORMAT_HOOK + if (virXMLSwitchDefFormatHook(def, parent, opaque, &empty, &shortcut, &typeBuf) < 0) + return -1; +#endif /* ENABLE_VIR_XMLSWITCH_DEF_FORMAT_HOOK */ + + if (empty != VIR_TRISTATE_BOOL_NO) + if (empty || !(def->type >= 0 || virXMLSwitchDefDataCheckAttr(def, opaque) || virXMLSwitchDefDataCheckElem(def, opaque))) + return 0; + + virBufferAsprintf(buf, "<%s", name); + + virBufferInheritIndent(&typeBuf, buf); + if (!virBufferTouched(&typeBuf) && def->type >= 0) { + const char *str = virDomainGraphicsTypeToString(def->type); + if (!str) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown %s type %d"), + "type", def->type); + return -1; + } + virBufferAsprintf(&typeBuf, " type='%s'", str); + } + virBufferAddBuffer(buf, &typeBuf); + + switch (def->type) { + + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: + if (virDomainGraphicsSDLDefFormatAttr(buf, &def->data.sdl, def, opaque) < 0) + return -1; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_VNC: + if (virDomainGraphicsVNCDefFormatAttr(buf, &def->data.vnc, def, opaque) < 0) + return -1; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + if (virDomainGraphicsRDPDefFormatAttr(buf, &def->data.rdp, def, opaque) < 0) + return -1; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: + if (virDomainGraphicsDesktopDefFormatAttr(buf, &def->data.desktop, def, opaque) < 0) + return -1; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + if (virDomainGraphicsSpiceDefFormatAttr(buf, &def->data.spice, def, opaque) < 0) + return -1; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: + if (virDomainGraphicsEGLHeadlessDefFormatAttr(buf, &def->data.egl_headless, def, opaque) < 0) + return -1; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_LAST: + break; + } + + if (shortcut != VIR_TRISTATE_BOOL_NO) { + if (shortcut || !(virXMLSwitchDefDataCheckElem(def, opaque))) { + virBufferAddLit(buf, "/>\n"); + return 0; + } + } + + virBufferAddLit(buf, ">\n"); + + virBufferAdjustIndent(buf, 2); + + switch (def->type) { + + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: + if (virDomainGraphicsSDLDefFormatElem(buf, &def->data.sdl, def, opaque) < 0) + return -1; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_VNC: + if (virDomainGraphicsVNCDefFormatElem(buf, &def->data.vnc, def, opaque) < 0) + return -1; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + if (virDomainGraphicsRDPDefFormatElem(buf, &def->data.rdp, def, opaque) < 0) + return -1; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: + if (virDomainGraphicsDesktopDefFormatElem(buf, &def->data.desktop, def, opaque) < 0) + return -1; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + if (virDomainGraphicsSpiceDefFormatElem(buf, &def->data.spice, def, opaque) < 0) + return -1; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: + if (virDomainGraphicsEGLHeadlessDefFormatElem(buf, &def->data.egl_headless, def, opaque) < 0) + return -1; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_LAST: + break; + } + + virBufferAdjustIndent(buf, -2); + virBufferAsprintf(buf, "</%s>\n", name); + + return 0; +} + + +[conf/xmlswitch.generated.h] + +bool +virXMLSwitchDefCheck(const virXMLSwitchDef *def, + const void *parent, + void *opaque); + + +[conf/xmlswitch.generated.c] + +#ifndef RESET_VIR_XMLSWITCH_DEF_CHECK + +bool +virXMLSwitchDefCheck(const virXMLSwitchDef *def, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + if (!def) + return false; + + return def->type >= 0 || virXMLSwitchDefDataCheckAttr(def, opaque) || virXMLSwitchDefDataCheckElem(def, opaque); +} + +#endif /* RESET_VIR_XMLSWITCH_DEF_CHECK */ + + +[Tips] + +/* Put these lines at the bottom of "conf/xmlswitch.h" */ +/* Makesure "xmlswitch.h" to be appended into conf_xmlgen_input in src/conf/meson.build */ + +/* Define macro to enable hook or redefine check when necessary */ +/* #define ENABLE_VIR_XMLSWITCH_DEF_PARSE_HOOK */ +/* #define ENABLE_VIR_XMLSWITCH_DEF_PARSE_HOOK_SET_ARGS */ +/* #define ENABLE_VIR_XMLSWITCH_DEF_FORMAT_HOOK */ + +/* #define RESET_VIR_XMLSWITCH_DEF_CHECK */ + +/* Makesure below is the bottom line! */ +#include "xmlswitch.generated.h" diff --git a/tests/xmlgentest.c b/tests/xmlgentest.c new file mode 100644 index 00000000..a20773dc --- /dev/null +++ b/tests/xmlgentest.c @@ -0,0 +1,107 @@ +#include <config.h> + +#include "internal.h" +#include "testutils.h" +#include "vircommand.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +typedef enum { + TEST_COMPARE_RESULT_SUCCESS, + TEST_COMPARE_RESULT_FAIL_GENERATE, + TEST_COMPARE_RESULT_FAIL_COMPARE, +} testCompareResult; + +struct testInfo { + const char *name; + const char *target; + testCompareResult expectResult; +}; + +static int +testCompareGenFiles(const void *data) +{ + int ret = 0; + testCompareResult result = TEST_COMPARE_RESULT_SUCCESS; + const struct testInfo *info = data; + g_autofree char *outbuf = NULL; + g_autoptr(virCommand) cmd = NULL; + g_autofree char *outfile = NULL; + char *actual = NULL; + + cmd = virCommandNewArgList(PYTHON3, "-B", + abs_top_srcdir "/scripts/xmlgen/main.py", + "-s", abs_srcdir "/xmlgenin", + "-b", abs_builddir "/xmlgenin", + "-d", "", + "show", info->target, NULL); + + virCommandSetOutputBuffer(cmd, &outbuf); + + if (virCommandRun(cmd, NULL) < 0) { + result = TEST_COMPARE_RESULT_FAIL_GENERATE; + goto cleanup; + } + + /* Skip first empty line */ + if (outbuf && outbuf[0] == '\n') + actual = outbuf + 1; + else + actual = outbuf; + + outfile = g_strdup_printf("%s/xmlgenout/%s.txt", abs_srcdir, info->name); + if (virTestCompareToFile(actual, outfile) < 0) { + result = TEST_COMPARE_RESULT_FAIL_COMPARE; + goto cleanup; + } + + cleanup: + if (result == info->expectResult) { + ret = 0; + if (info->expectResult != TEST_COMPARE_RESULT_SUCCESS) { + VIR_TEST_DEBUG("Got expected failure code=%d msg=%s", + result, virGetLastErrorMessage()); + } + } else { + ret = -1; + VIR_TEST_DEBUG("Expected result code=%d but received code=%d", + info->expectResult, result); + } + virResetLastError(); + + return ret; +} + +static int +mymain(void) +{ + int ret = 0; + +#define DO_TEST(name, target) \ + do { \ + const struct testInfo info = {name, target, TEST_COMPARE_RESULT_SUCCESS}; \ + if (virTestRun("xmlgen: generate for " target " in test " name, \ + testCompareGenFiles, &info) < 0) \ + ret = -1; \ + } while (0) + + DO_TEST("empty", "virEmptyDef"); + DO_TEST("genparse", "virGenParseDef"); + DO_TEST("genformat", "virGenFormatDef"); + DO_TEST("genformat-separate", "virSeparateDef"); + DO_TEST("xmlattr", "virXMLAttrDef"); + DO_TEST("xmlelem", "virXMLElemDef"); + DO_TEST("array", "virArrayDef"); + DO_TEST("required", "virRequiredDef"); + DO_TEST("specify", "virSpecifyDef"); + DO_TEST("skipparse", "virSkipParseDef"); + DO_TEST("xmlgroup", "virXMLGroupDef"); + DO_TEST("xmlswitch", "virXMLSwitchDef"); + DO_TEST("enum-first-item", "virEnumFirstItemDef"); + DO_TEST("namespace", "virNameSpaceDef"); + DO_TEST("external", "virExternalDef"); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIR_TEST_MAIN(mymain) -- 2.25.1

1) virXMLChildNode and virXMLChildNodeSet Parse xml based on 'node' rather than 'ctxt'. 2) virXMLChildPropString Support to parse child node's property value by path, which is as "child_node_name/prop_name". 3) virXMLChildNodeContent Support to parse child node's content. 4) virBufferIgnore Mark a buffer with the flag 'ignored'. 5) virBufferTouched Check whether the buffer has been touched, which means this buffer has been used or ignored. 6) virBufferInheritIndent Inherit the indentation from another buffer. Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/libvirt_private.syms | 6 ++ src/util/virbuffer.c | 44 ++++++++++++++ src/util/virbuffer.h | 8 ++- src/util/virxml.c | 120 +++++++++++++++++++++++++++++++++++++++ src/util/virxml.h | 6 ++ 5 files changed, 182 insertions(+), 2 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index e9bb2391..fad0ff71 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1864,9 +1864,12 @@ virBufferEscapeString; virBufferFreeAndReset; virBufferGetEffectiveIndent; virBufferGetIndent; +virBufferIgnore; +virBufferInheritIndent; virBufferSetIndent; virBufferStrcat; virBufferStrcatVArgs; +virBufferTouched; virBufferTrim; virBufferTrimChars; virBufferTrimLen; @@ -3539,6 +3542,9 @@ virVsockSetGuestCid; virParseScaledValue; virXMLBufferCreate; virXMLCheckIllegalChars; +virXMLChildNode; +virXMLChildNodeSet; +virXMLChildPropString; virXMLExtractNamespaceXML; virXMLFormatElement; virXMLNewNode; diff --git a/src/util/virbuffer.c b/src/util/virbuffer.c index 8f9cd57e..b6f936c8 100644 --- a/src/util/virbuffer.c +++ b/src/util/virbuffer.c @@ -80,6 +80,24 @@ virBufferSetIndent(virBuffer *buf, int indent) } +/** + * virBufferInheritIndent: + * @buf: the buffer + * @frombuf: new indentation size from it. + * + * Just like virBufferSetIndent, but the indentation comes from + * another buffer. + */ +void +virBufferInheritIndent(virBuffer *buf, virBuffer *frombuf) +{ + if (!buf || !frombuf) + return; + + buf->indent = virBufferGetEffectiveIndent(frombuf); +} + + /** * virBufferGetIndent: * @buf: the buffer @@ -291,6 +309,32 @@ virBufferUse(const virBuffer *buf) return buf->str->len; } +/** + * virBufferTouched: + * @buf: the buffer to be checked + * + * Return true when the buffer has content or + * it has been ignored(by calling virBufferIgnore); + * otherwise, return false. + */ +bool +virBufferTouched(virBuffer *buf) +{ + return buf->ignored || virBufferUse(buf); +} + +/** + * virBufferIgnore: + * @buf: the buffer to be ignored + * + * Ignore the buffer explicitly. + */ +void +virBufferIgnore(virBuffer *buf) +{ + buf->ignored = true; +} + /** * virBufferAsprintf: * @buf: the buffer to append to diff --git a/src/util/virbuffer.h b/src/util/virbuffer.h index 0e72d078..ddb29cb5 100644 --- a/src/util/virbuffer.h +++ b/src/util/virbuffer.h @@ -32,7 +32,7 @@ */ typedef struct _virBuffer virBuffer; -#define VIR_BUFFER_INITIALIZER { NULL, 0 } +#define VIR_BUFFER_INITIALIZER { NULL, 0, false } /** * VIR_BUFFER_INIT_CHILD: @@ -41,11 +41,12 @@ typedef struct _virBuffer virBuffer; * Initialize a virBuffer structure and set up the indentation level for * formatting XML subelements of @parentbuf. */ -#define VIR_BUFFER_INIT_CHILD(parentbuf) { NULL, (parentbuf)->indent + 2 } +#define VIR_BUFFER_INIT_CHILD(parentbuf) { NULL, (parentbuf)->indent + 2, false } struct _virBuffer { GString *str; int indent; + bool ignored; }; const char *virBufferCurrentContent(virBuffer *buf); @@ -86,6 +87,7 @@ void virBufferURIEncodeString(virBuffer *buf, const char *str); void virBufferAdjustIndent(virBuffer *buf, int indent); void virBufferSetIndent(virBuffer *, int indent); +void virBufferInheritIndent(virBuffer *buf, virBuffer *frombuf); size_t virBufferGetIndent(const virBuffer *buf); size_t virBufferGetEffectiveIndent(const virBuffer *buf); @@ -94,3 +96,5 @@ void virBufferTrim(virBuffer *buf, const char *trim); void virBufferTrimChars(virBuffer *buf, const char *trim); void virBufferTrimLen(virBuffer *buf, int len); void virBufferAddStr(virBuffer *buf, const char *str); +bool virBufferTouched(virBuffer *buf); +void virBufferIgnore(virBuffer *buf); diff --git a/src/util/virxml.c b/src/util/virxml.c index 5ceef738..a6e50459 100644 --- a/src/util/virxml.c +++ b/src/util/virxml.c @@ -1746,3 +1746,123 @@ virXMLNewNode(xmlNsPtr ns, return ret; } + + +/** + * virXMLChildNode: + * @node: Parent XML dom node pointer + * @name: Name of the child element + * + * Convenience function to return the child element of a XML node. + * + * Returns the pointer of child element node or NULL in case of failure. + * If there are many nodes match condition, it only returns the first node. + */ +xmlNodePtr +virXMLChildNode(xmlNodePtr node, const char *name) +{ + xmlNodePtr cur = node->children; + while (cur) { + if (cur->type == XML_ELEMENT_NODE && virXMLNodeNameEqual(cur, name)) + return cur; + cur = cur->next; + } + return NULL; +} + + +/** + * virXMLChildPropString: + * @node: Parent XML dom node pointer + * @path: Path of the property (attribute) to get + * + * Convenience function to return copy of an attribute value by a path. + * Path consists of names of child elements and an attribute. + * The delimiter is '/'. + * + * E.g.: For <self><son><grandson name="..."></grandson></son></self>, + * the path is 'son/grandSon/name' to get attribute 'name'. + * + * Returns the property (attribute) value as string or NULL in case of failure. + * The caller is responsible for freeing the returned buffer. + */ +char * +virXMLChildPropString(xmlNodePtr node, const char *path) +{ + char *next; + char *sep; + xmlNodePtr tnode = node; + g_autofree char *tmp = NULL; + tmp = g_strdup(path); + + next = tmp; + while ((sep = strchr(next, '/'))) { + *sep = '\0'; + + if ((tnode = virXMLChildNode(tnode, next)) == NULL) + return NULL; + + next = sep + 1; + } + + return virXMLPropString(tnode, next); +} + + +/** + * virXMLChildNodeSet: + * @node: Parent XML dom node pointer + * @name: Name of the children element + * @list: the returned list of nodes (or NULL if only count matters) + * + * Convenience function to evaluate a set of children elements + * + * Returns the number of nodes found in which case @list is set (and + * must be freed) or -1 if the evaluation failed. + */ +int +virXMLChildNodeSet(xmlNodePtr node, const char *name, xmlNodePtr **list) +{ + size_t count = 0; + xmlNodePtr cur = node->children; + + if (list != NULL) + *list = NULL; + + while (cur) { + if (cur->type == XML_ELEMENT_NODE && virXMLNodeNameEqual(cur, name)) { + if (list != NULL) { + if (VIR_APPEND_ELEMENT_COPY(*list, count, cur) < 0) + return -1; + } else { + count++; + } + } + cur = cur->next; + } + return count; +} + + +/** + * virXMLChildNodeContent: + * @node: XML parent node pointer + * @name: child node name + * + * Convenience function to return copy of content of an child node. + * E.g., for <parent><child>content</child></parent>, call + * virXMLChildNodeContent(parent_node_ptr, "child") to get the content + * of the child node. + * + * Returns the content value as string or NULL in case of failure. + * The caller is responsible for freeing the returned buffer. + */ +char * +virXMLChildNodeContent(xmlNodePtr node, const char *name) +{ + xmlNodePtr child = virXMLChildNode(node, name); + if (!child) + return NULL; + + return virXMLNodeContentString(child); +} diff --git a/src/util/virxml.h b/src/util/virxml.h index c83d16a1..7dac1d3e 100644 --- a/src/util/virxml.h +++ b/src/util/virxml.h @@ -134,6 +134,10 @@ virXMLPropUInt(xmlNodePtr node, unsigned int *result) ATTRIBUTE_NONNULL(0) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(4); +xmlNodePtr virXMLChildNode(xmlNodePtr node, const char *name); +int virXMLChildNodeSet(xmlNodePtr node, const char *name, xmlNodePtr **list); +char * virXMLChildPropString(xmlNodePtr node, const char *path); + int virXMLPropEnum(xmlNodePtr node, const char* name, @@ -375,3 +379,5 @@ virXMLBufferCreate(void); xmlNodePtr virXMLNewNode(xmlNsPtr ns, const char *name); + +char * virXMLChildNodeContent(xmlNodePtr node, const char *name); -- 2.25.1

Add three aliases virBoolYesNo, virBoolOnOff and virBoolTrueFalse for type 'bool'. The tool xmlgen depends on them to determine their ture values in XML. Also, add corresponding functions to parse them. Add virStrToTime and virTimeFormatBuf to convert 'time_t' and 'string'. Add virStrToLong_u8p to convert 'string' to 'uint_8'. Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/internal.h | 8 +++ src/libvirt_private.syms | 5 ++ src/util/virstring.c | 102 +++++++++++++++++++++++++++++++++++++++ src/util/virstring.h | 15 ++++++ 4 files changed, 130 insertions(+) diff --git a/src/internal.h b/src/internal.h index 0a03dfc4..3342934f 100644 --- a/src/internal.h +++ b/src/internal.h @@ -529,3 +529,11 @@ enum { # define fprintf(fh, ...) g_fprintf(fh, __VA_ARGS__) #endif /* VIR_NO_GLIB_STDIO */ + +/* These are aliases for bool. + * The xmlgen depends on them to determine their true values + * in XML. + */ +typedef bool virBoolYesNo; /* True values: 'yes', 'no' */ +typedef bool virBoolOnOff; /* True values: 'on', 'off' */ +typedef bool virBoolTrueFalse; /* True values: 'true', 'false' */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index fad0ff71..e78491dc 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3275,6 +3275,9 @@ virStringStripIPv6Brackets; virStringStripSuffix; virStringToUpper; virStringTrimOptionalNewline; +virStrToBoolOnOff; +virStrToBoolTrueFalse; +virStrToBoolYesNo; virStrToDouble; virStrToLong_i; virStrToLong_l; @@ -3285,6 +3288,8 @@ virStrToLong_ul; virStrToLong_ull; virStrToLong_ullp; virStrToLong_ulp; +virStrToTime; +virTimeFormatBuf; virTrimSpaces; diff --git a/src/util/virstring.c b/src/util/virstring.c index a2c07e5c..ac6c0aae 100644 --- a/src/util/virstring.c +++ b/src/util/virstring.c @@ -233,6 +233,21 @@ virStrToLong_ulp(char const *s, char **end_ptr, int base, return 0; } +/* Just like virStrToLong_uip, above, but produce an "uint8_t" value. + * This version rejects any negative signs. */ +int +virStrToLong_u8p(char const *s, char **end_ptr, int base, uint8_t *result) +{ + unsigned int val; + int ret; + ret = virStrToLong_uip(s, end_ptr, base, &val); + if (ret < 0 || val > 0xff) + return -1; + + *result = (uint8_t) val; + return 0; +} + /* Just like virStrToLong_i, above, but produce a "long long" value. */ int virStrToLong_ll(char const *s, char **end_ptr, int base, long long *result) @@ -1077,3 +1092,90 @@ int virStringParseYesNo(const char *str, bool *result) return 0; } + + +int +virStrToBoolYesNo(const char *str, bool *result) +{ + if (STREQ(str, "yes")) + *result = true; + else if (STREQ(str, "no")) + *result = false; + else + return -1; + + return 0; +} + + +int +virStrToBoolOnOff(const char *str, bool *result) +{ + if (STREQ(str, "on")) + *result = true; + else if (STREQ(str, "off")) + *result = false; + else + return -1; + + return 0; +} + + +int +virStrToBoolTrueFalse(const char *str, bool *result) +{ + if (STREQ(str, "true")) + *result = true; + else if (STREQ(str, "false")) + *result = false; + else + return -1; + + return 0; +} + + +int virStrToTime(const char *str, time_t *result) +{ + g_autoptr(GDateTime) then = NULL; + g_autoptr(GTimeZone) tz = g_time_zone_new_utc(); + char *tmp; + int year, mon, mday, hour, min, sec; + + /* Expect: YYYY-MM-DDTHH:MM:SS (%d-%d-%dT%d:%d:%d) eg 2010-11-28T14:29:01 */ + if (/* year */ + virStrToLong_i(str, &tmp, 10, &year) < 0 || *tmp != '-' || + /* month */ + virStrToLong_i(tmp+1, &tmp, 10, &mon) < 0 || *tmp != '-' || + /* day */ + virStrToLong_i(tmp+1, &tmp, 10, &mday) < 0 || *tmp != 'T' || + /* hour */ + virStrToLong_i(tmp+1, &tmp, 10, &hour) < 0 || *tmp != ':' || + /* minute */ + virStrToLong_i(tmp+1, &tmp, 10, &min) < 0 || *tmp != ':' || + /* second */ + virStrToLong_i(tmp+1, &tmp, 10, &sec) < 0 || *tmp != '\0') { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid time '%s', expect YYYY-MM-DDTHH:MM:SS"), + str); + return -1; + } + + then = g_date_time_new(tz, year, mon, mday, hour, min, sec); + *result = (time_t)g_date_time_to_unix(then); + return 0; +} + + +int +virTimeFormatBuf(virBuffer *buf, const char *layout, const time_t time) +{ + g_autoptr(GDateTime) then = NULL; + g_autofree char *thenstr = NULL; + + then = g_date_time_new_from_unix_utc(time); + thenstr = g_date_time_format(then, "%Y-%m-%dT%H:%M:%S"); + virBufferAsprintf(buf, layout, thenstr); + return 0; +} diff --git a/src/util/virstring.h b/src/util/virstring.h index 7cc1d8c5..e16da824 100644 --- a/src/util/virstring.h +++ b/src/util/virstring.h @@ -19,6 +19,7 @@ #pragma once #include "internal.h" +#include "virbuffer.h" #define VIR_INT64_STR_BUFLEN 21 @@ -59,6 +60,11 @@ int virStrToLong_ulp(char const *s, int base, unsigned long *result) G_GNUC_WARN_UNUSED_RESULT; +int virStrToLong_u8p(char const *s, + char **end_ptr, + int base, + uint8_t *result) + G_GNUC_WARN_UNUSED_RESULT; int virStrToLong_ll(char const *s, char **end_ptr, int base, @@ -141,3 +147,12 @@ int virStringParsePort(const char *str, int virStringParseYesNo(const char *str, bool *result) G_GNUC_WARN_UNUSED_RESULT; + +int virStrToBoolYesNo(const char *str, bool *result); +int virStrToBoolOnOff(const char *str, bool *result); +int virStrToBoolTrueFalse(const char *str, bool *result); + +int virStrToTime(const char *str, time_t *result) + G_GNUC_WARN_UNUSED_RESULT; + +int virTimeFormatBuf(virBuffer *buf, const char *layout, const time_t time); -- 2.25.1

Implement the parsexml/formatbuf functions for virSocketAddr. Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/libvirt_private.syms | 2 ++ src/util/virsocketaddr.c | 42 ++++++++++++++++++++++++++++++++++++++++ src/util/virsocketaddr.h | 23 ++++++++++++++++++++-- 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index e78491dc..055396d0 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3213,6 +3213,7 @@ virSocketAddrBroadcastByPrefix; virSocketAddrCheckNetmask; virSocketAddrEqual; virSocketAddrFormat; +virSocketAddrFormatBuf; virSocketAddrFormatFull; virSocketAddrGetIPPrefix; virSocketAddrGetNumNetmaskBits; @@ -3230,6 +3231,7 @@ virSocketAddrParse; virSocketAddrParseAny; virSocketAddrParseIPv4; virSocketAddrParseIPv6; +virSocketAddrParseXML; virSocketAddrPrefixToNetmask; virSocketAddrPTRDomain; virSocketAddrResolveService; diff --git a/src/util/virsocketaddr.c b/src/util/virsocketaddr.c index 94cbfc62..fcad7f8a 100644 --- a/src/util/virsocketaddr.c +++ b/src/util/virsocketaddr.c @@ -154,6 +154,15 @@ int virSocketAddrParse(virSocketAddr *addr, const char *val, int family) return len; } +int virSocketAddrParseXML(const char *val, + virSocketAddr *addr, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + return virSocketAddrParse(addr, val, AF_UNSPEC); +} + /** * virSocketAddrParseAny: * @addr: where to store the return value, optional. @@ -1306,3 +1315,36 @@ virSocketAddrFree(virSocketAddr *addr) { g_free(addr); } + +void +virSocketAddrClear(virSocketAddr *addr) +{ + memset(addr, 0, sizeof(virSocketAddr)); +} + +int +virSocketAddrFormatBuf(virBuffer *buf, + const char *fmt, + const virSocketAddr *addr, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + g_autofree char *str = NULL; + if (!VIR_SOCKET_ADDR_VALID(addr)) + return 0; + + str = virSocketAddrFormatFull(addr, false, NULL); + if (!str) + return -1; + + virBufferAsprintf(buf, fmt, str); + return 0; +} + +bool +virSocketAddrCheck(const virSocketAddr *addr, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + return VIR_SOCKET_ADDR_VALID(addr); +} diff --git a/src/util/virsocketaddr.h b/src/util/virsocketaddr.h index f76e2297..cba87390 100644 --- a/src/util/virsocketaddr.h +++ b/src/util/virsocketaddr.h @@ -18,11 +18,13 @@ #pragma once +#include "virbuffer.h" #include "virsocket.h" #define VIR_LOOPBACK_IPV4_ADDR "127.0.0.1" -typedef struct { +typedef struct _virSocketAddr virSocketAddr; +struct _virSocketAddr { union { struct sockaddr sa; struct sockaddr_storage stor; @@ -33,7 +35,7 @@ typedef struct { #endif } data; socklen_t len; -} virSocketAddr; +}; #define VIR_SOCKET_ADDR_VALID(s) \ ((s)->data.sa.sa_family != AF_UNSPEC) @@ -66,6 +68,12 @@ int virSocketAddrParse(virSocketAddr *addr, const char *val, int family); +int virSocketAddrParseXML(const char *val, + virSocketAddr *addr, + const char *instname, + void *parent, + void *opaque); + int virSocketAddrParseAny(virSocketAddr *addr, const char *val, int family, @@ -89,6 +97,12 @@ char *virSocketAddrFormatFull(const virSocketAddr *addr, bool withService, const char *separator); +int virSocketAddrFormatBuf(virBuffer *buf, + const char *fmt, + const virSocketAddr *addr, + const void *parent, + void *opaque); + char *virSocketAddrGetPath(virSocketAddr *addr); int virSocketAddrSetPort(virSocketAddr *addr, int port); @@ -141,5 +155,10 @@ int virSocketAddrPTRDomain(const virSocketAddr *addr, ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); void virSocketAddrFree(virSocketAddr *addr); +void virSocketAddrClear(virSocketAddr *addr); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virSocketAddr, virSocketAddrFree); + +bool virSocketAddrCheck(const virSocketAddr *addr, + const void *parent, + void *opaque); -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/util/viruuid.c | 31 +++++++++++++++++++++++++++++++ src/util/viruuid.h | 18 ++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/util/viruuid.c b/src/util/viruuid.c index 558fbb9c..c6e6272f 100644 --- a/src/util/viruuid.c +++ b/src/util/viruuid.c @@ -134,6 +134,18 @@ virUUIDParse(const char *uuidstr, unsigned char *uuid) return 0; } + +int +virUUIDParseXML(const char *uuidstr, + virUUID *puuid, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + return virUUIDParse(uuidstr, *puuid); +} + + /** * virUUIDFormat: * @uuid: array of VIR_UUID_BUFLEN bytes to store the raw UUID @@ -159,6 +171,20 @@ virUUIDFormat(const unsigned char *uuid, char *uuidstr) } +int +virUUIDFormatBuf(virBuffer *buf, + const char *fmt, + const virUUID *puuid, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(*puuid, uuidstr); + virBufferAsprintf(buf, fmt, uuidstr); + + return 0; +} + /** * virUUIDIsValid @@ -263,3 +289,8 @@ int virGetHostUUID(unsigned char *uuid) return ret; } + +void virUUIDClear(virUUID *puuid G_GNUC_UNUSED) +{ + memset(*puuid, 0, VIR_UUID_BUFLEN); +} diff --git a/src/util/viruuid.h b/src/util/viruuid.h index b403b190..36766356 100644 --- a/src/util/viruuid.h +++ b/src/util/viruuid.h @@ -21,6 +21,7 @@ #pragma once #include "internal.h" +#include "virbuffer.h" /** @@ -39,6 +40,7 @@ } \ } while (0) +typedef unsigned char virUUID[VIR_UUID_BUFLEN]; int virSetHostUUIDStr(const char *host_uuid); int virGetHostUUID(unsigned char *host_uuid) ATTRIBUTE_NONNULL(1) G_GNUC_NO_INLINE; @@ -51,5 +53,21 @@ int virUUIDParse(const char *uuidstr, unsigned char *uuid) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT; +int +virUUIDParseXML(const char *uuidstr, + virUUID *puuid, + const char *instname, + void *parent, + void *opaque); + const char *virUUIDFormat(const unsigned char *uuid, char *uuidstr) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + +int +virUUIDFormatBuf(virBuffer *buf, + const char *fmt, + const virUUID *puuid, + const void *parent, + void *opaque); + +void virUUIDClear(virUUID *puuid); -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 49 ++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index d6eafa3f..87157591 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -887,26 +887,26 @@ virNetworkDNSSrvDefParseXML(const char *networkName, static int -virNetworkDNSTxtDefParseXML(const char *networkName, - xmlNodePtr node, - virNetworkDNSTxtDef *def, - bool partialOkay) +virNetworkDNSTxtDefParseHook(xmlNodePtr node G_GNUC_UNUSED, + virNetworkDNSTxtDef *def, + const char *networkName, + void *parent G_GNUC_UNUSED, + void *opaque) { const char *bad = " ,"; + bool partialOkay = false; + + if (opaque) + partialOkay = *((bool *) opaque); - if (!(def->name = virXMLPropString(node, "name"))) { - virReportError(VIR_ERR_XML_DETAIL, - _("missing required name attribute in DNS TXT record " - "of network %s"), networkName); - goto error; - } if (strcspn(def->name, bad) != strlen(def->name)) { virReportError(VIR_ERR_XML_DETAIL, _("prohibited character in DNS TXT record " "name '%s' of network %s"), def->name, networkName); goto error; } - if (!(def->value = virXMLPropString(node, "value")) && !partialOkay) { + + if (!def->value && !partialOkay) { virReportError(VIR_ERR_XML_DETAIL, _("missing required value attribute in DNS TXT record " "named '%s' of network %s"), def->name, networkName); @@ -919,6 +919,33 @@ virNetworkDNSTxtDefParseXML(const char *networkName, "in DNS TXT record of network %s"), networkName); goto error; } + + return 0; + + error: + return -1; +} + + +static int +virNetworkDNSTxtDefParseXML(const char *networkName, + xmlNodePtr node, + virNetworkDNSTxtDef *def, + bool partialOkay) +{ + if (!(def->name = virXMLPropString(node, "name"))) { + virReportError(VIR_ERR_XML_DETAIL, + _("missing required name attribute in DNS TXT record " + "of network %s"), networkName); + goto error; + } + + def->value = virXMLPropString(node, "value"); + + if (virNetworkDNSTxtDefParseHook(node, def, networkName, + NULL, &partialOkay) < 0) + goto error; + return 0; error: -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- po/POTFILES.in | 1 + src/conf/meson.build | 1 + src/conf/network_conf.c | 45 ++++++----------------------------------- src/conf/network_conf.h | 9 ++++++--- 4 files changed, 14 insertions(+), 42 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index 9740bb2b..fe20b9d7 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -3,6 +3,7 @@ @BUILDDIR@src/access/viraccessapicheckqemu.c @BUILDDIR@src/admin/admin_client.h @BUILDDIR@src/admin/admin_server_dispatch_stubs.h +@BUILDDIR@src/conf/network_conf.generated.c @BUILDDIR@src/remote/remote_client_bodies.h @BUILDDIR@src/remote/remote_daemon_dispatch_stubs.h @SRCDIR@scripts/xmlgen/directive.py diff --git a/src/conf/meson.build b/src/conf/meson.build index 1439c31d..f1cfe35c 100644 --- a/src/conf/meson.build +++ b/src/conf/meson.build @@ -1,4 +1,5 @@ conf_xmlgen_input = [ + 'network_conf.h', ] conf_xmlgen_output = [] diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 87157591..a6c2f11a 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -174,14 +174,6 @@ virNetworkIPDefClear(virNetworkIPDef *def) } -static void -virNetworkDNSTxtDefClear(virNetworkDNSTxtDef *def) -{ - VIR_FREE(def->name); - VIR_FREE(def->value); -} - - static void virNetworkDNSHostDefClear(virNetworkDNSHostDef *def) { @@ -886,7 +878,7 @@ virNetworkDNSSrvDefParseXML(const char *networkName, } -static int +int virNetworkDNSTxtDefParseHook(xmlNodePtr node G_GNUC_UNUSED, virNetworkDNSTxtDef *def, const char *networkName, @@ -927,33 +919,6 @@ virNetworkDNSTxtDefParseHook(xmlNodePtr node G_GNUC_UNUSED, } -static int -virNetworkDNSTxtDefParseXML(const char *networkName, - xmlNodePtr node, - virNetworkDNSTxtDef *def, - bool partialOkay) -{ - if (!(def->name = virXMLPropString(node, "name"))) { - virReportError(VIR_ERR_XML_DETAIL, - _("missing required name attribute in DNS TXT record " - "of network %s"), networkName); - goto error; - } - - def->value = virXMLPropString(node, "value"); - - if (virNetworkDNSTxtDefParseHook(node, def, networkName, - NULL, &partialOkay) < 0) - goto error; - - return 0; - - error: - virNetworkDNSTxtDefClear(def); - return -1; -} - - static int virNetworkDNSDefParseXML(const char *networkName, xmlNodePtr node, @@ -1077,8 +1042,8 @@ virNetworkDNSDefParseXML(const char *networkName, def->txts = g_new0(virNetworkDNSTxtDef, ntxts); for (i = 0; i < ntxts; i++) { - if (virNetworkDNSTxtDefParseXML(networkName, txtNodes[i], - &def->txts[def->ntxts], false) < 0) { + if (virNetworkDNSTxtDefParseXML(txtNodes[i], &def->txts[def->ntxts], + networkName, def, NULL) < 0) { return -1; } def->ntxts++; @@ -3614,6 +3579,7 @@ virNetworkDefUpdateDNSTxt(virNetworkDef *def, virNetworkDNSTxtDef txt; bool isAdd = (command == VIR_NETWORK_UPDATE_COMMAND_ADD_FIRST || command == VIR_NETWORK_UPDATE_COMMAND_ADD_LAST); + bool notAdd; memset(&txt, 0, sizeof(txt)); @@ -3627,7 +3593,8 @@ virNetworkDefUpdateDNSTxt(virNetworkDef *def, if (virNetworkDefUpdateCheckElementName(def, ctxt->node, "txt") < 0) goto cleanup; - if (virNetworkDNSTxtDefParseXML(def->name, ctxt->node, &txt, !isAdd) < 0) + notAdd = !isAdd; + if (virNetworkDNSTxtDefParseXML(ctxt->node, &txt, def->name, def, ¬Add) < 0) goto cleanup; for (foundIdx = 0; foundIdx < dns->ntxts; foundIdx++) { diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index a7e6b7a2..f5720e5e 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -125,9 +125,9 @@ struct _virNetworkDHCPHostDef { }; typedef struct _virNetworkDNSTxtDef virNetworkDNSTxtDef; -struct _virNetworkDNSTxtDef { - char *name; - char *value; +struct _virNetworkDNSTxtDef { /* genparse */ + char *name; /* xmlattr, required */ + char *value; /* xmlattr */ }; typedef struct _virNetworkDNSSrvDef virNetworkDNSSrvDef; @@ -428,3 +428,6 @@ virNetworkDefUpdateSection(virNetworkDef *def, unsigned int flags); /* virNetworkUpdateFlags */ VIR_ENUM_DECL(virNetworkTaint); + +#define ENABLE_VIR_NETWORK_DNSTXT_DEF_PARSE_HOOK +#include "network_conf.generated.h" -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 4 ++-- src/conf/network_conf.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index a6c2f11a..bb976a78 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -2199,8 +2199,8 @@ virNetworkDNSDefFormat(virBuffer *buf, } for (i = 0; i < def->ntxts; i++) { - virBufferEscapeString(buf, "<txt name='%s' ", def->txts[i].name); - virBufferEscapeString(buf, "value='%s'/>\n", def->txts[i].value); + if (virNetworkDNSTxtDefFormatBuf(buf, "txt", &def->txts[i], def, NULL) < 0) + return -1; } for (i = 0; i < def->nsrvs; i++) { diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index f5720e5e..a4c83b46 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -125,7 +125,7 @@ struct _virNetworkDHCPHostDef { }; typedef struct _virNetworkDNSTxtDef virNetworkDNSTxtDef; -struct _virNetworkDNSTxtDef { /* genparse */ +struct _virNetworkDNSTxtDef { /* genparse, genformat */ char *name; /* xmlattr, required */ char *value; /* xmlattr */ }; -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 107 +++++++++++++++++++++++++++++++--------- 1 file changed, 83 insertions(+), 24 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index bb976a78..20128af0 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -768,23 +768,26 @@ virNetworkDNSHostDefParseXML(const char *networkName, "_-+/*" static int -virNetworkDNSSrvDefParseXML(const char *networkName, - xmlNodePtr node, - xmlXPathContextPtr ctxt, - virNetworkDNSSrvDef *def, - bool partialOkay) +virNetworkDNSSrvDefParseHook(xmlNodePtr node G_GNUC_UNUSED, + virNetworkDNSSrvDef *def, + const char *networkName, + void *parent G_GNUC_UNUSED, + void *opaque, + const char *portStr, + const char *priorityStr, + const char *weightStr) { - int ret; - VIR_XPATH_NODE_AUTORESTORE(ctxt) - - ctxt->node = node; + bool partialOkay = false; + if (opaque) + partialOkay = *((bool *) opaque); - if (!(def->service = virXMLPropString(node, "service")) && !partialOkay) { + if (!def->service && !partialOkay) { virReportError(VIR_ERR_XML_DETAIL, _("missing required service attribute in DNS SRV record " "of network '%s'"), networkName); goto error; } + if (def->service) { if (strlen(def->service) > DNS_RECORD_LENGTH_SRV) { virReportError(VIR_ERR_XML_DETAIL, @@ -802,13 +805,14 @@ virNetworkDNSSrvDefParseXML(const char *networkName, } } - if (!(def->protocol = virXMLPropString(node, "protocol")) && !partialOkay) { + if (!def->protocol && !partialOkay) { virReportError(VIR_ERR_XML_DETAIL, _("missing required protocol attribute " "in DNS SRV record '%s' of network '%s'"), def->service, networkName); goto error; } + if (def->protocol && strspn(def->protocol, PROTOCOL_CHARS) < strlen(def->protocol)) { virReportError(VIR_ERR_XML_DETAIL, @@ -818,19 +822,14 @@ virNetworkDNSSrvDefParseXML(const char *networkName, goto error; } - /* Following attributes are optional */ - def->domain = virXMLPropString(node, "domain"); - def->target = virXMLPropString(node, "target"); - - ret = virXPathUInt("string(./@port)", ctxt, &def->port); - if (ret >= 0 && !def->target) { + if (portStr && !def->target) { virReportError(VIR_ERR_XML_DETAIL, _("DNS SRV port attribute not permitted without " "target for service '%s' in network '%s'"), def->service, networkName); goto error; } - if (ret == -2 || (ret >= 0 && (def->port < 1 || def->port > 65535))) { + if (portStr && (def->port < 1 || def->port > 65535)) { virReportError(VIR_ERR_XML_DETAIL, _("invalid DNS SRV port attribute " "for service '%s' in network '%s'"), @@ -838,15 +837,14 @@ virNetworkDNSSrvDefParseXML(const char *networkName, goto error; } - ret = virXPathUInt("string(./@priority)", ctxt, &def->priority); - if (ret >= 0 && !def->target) { + if (priorityStr && !def->target) { virReportError(VIR_ERR_XML_DETAIL, _("DNS SRV priority attribute not permitted without " "target for service '%s' in network '%s'"), def->service, networkName); goto error; } - if (ret == -2 || (ret >= 0 && def->priority > 65535)) { + if (priorityStr && def->priority > 65535) { virReportError(VIR_ERR_XML_DETAIL, _("Invalid DNS SRV priority attribute " "for service '%s' in network '%s'"), @@ -854,15 +852,14 @@ virNetworkDNSSrvDefParseXML(const char *networkName, goto error; } - ret = virXPathUInt("string(./@weight)", ctxt, &def->weight); - if (ret >= 0 && !def->target) { + if (weightStr && !def->target) { virReportError(VIR_ERR_XML_DETAIL, _("DNS SRV weight attribute not permitted without " "target for service '%s' in network '%s'"), def->service, networkName); goto error; } - if (ret == -2 || (ret >= 0 && def->weight > 65535)) { + if (weightStr && def->weight > 65535) { virReportError(VIR_ERR_XML_DETAIL, _("invalid DNS SRV weight attribute " "for service '%s' in network '%s'"), @@ -872,6 +869,68 @@ virNetworkDNSSrvDefParseXML(const char *networkName, return 0; + error: + return -1; +} + + +static int +virNetworkDNSSrvDefParseXML(const char *networkName, + xmlNodePtr node, + xmlXPathContextPtr ctxt G_GNUC_UNUSED, + virNetworkDNSSrvDef *def, + bool partialOkay) +{ + g_autofree char *portStr = NULL; + g_autofree char *priorityStr = NULL; + g_autofree char *weightStr = NULL; + + def->service = virXMLPropString(node, "service"); + def->protocol = virXMLPropString(node, "protocol"); + + /* Following attributes are optional */ + def->domain = virXMLPropString(node, "domain"); + def->target = virXMLPropString(node, "target"); + + portStr = virXMLPropString(node, "port"); + if (portStr) { + if (virStrToLong_uip(portStr, NULL, 0, &def->port) < 0) { + virReportError(VIR_ERR_XML_DETAIL, + _("invalid DNS SRV port attribute " + "for service '%s' in network '%s'"), + def->service, networkName); + goto error; + } + } + + priorityStr = virXMLPropString(node, "priority"); + if (priorityStr) { + if (virStrToLong_uip(priorityStr, NULL, 0, &def->priority) < 0) { + virReportError(VIR_ERR_XML_DETAIL, + _("Invalid DNS SRV priority attribute " + "for service '%s' in network '%s'"), + def->service, networkName); + goto error; + } + } + + weightStr = virXMLPropString(node, "weight"); + if (weightStr) { + if (virStrToLong_uip(weightStr, NULL, 0, &def->weight) < 0) { + virReportError(VIR_ERR_XML_DETAIL, + _("invalid DNS SRV weight attribute " + "for service '%s' in network '%s'"), + def->service, networkName); + goto error; + } + } + + if (virNetworkDNSSrvDefParseHook(node, def, networkName, def, &partialOkay, + portStr, priorityStr, weightStr) < 0) + goto error; + + return 0; + error: virNetworkDNSSrvDefClear(def); return -1; -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 83 +++-------------------------------------- src/conf/network_conf.h | 17 +++++---- 2 files changed, 15 insertions(+), 85 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 20128af0..cb2f3163 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -183,16 +183,6 @@ virNetworkDNSHostDefClear(virNetworkDNSHostDef *def) } -static void -virNetworkDNSSrvDefClear(virNetworkDNSSrvDef *def) -{ - VIR_FREE(def->domain); - VIR_FREE(def->service); - VIR_FREE(def->protocol); - VIR_FREE(def->target); -} - - static void virNetworkDNSForwarderClear(virNetworkDNSForwarder *def) { @@ -767,7 +757,7 @@ virNetworkDNSHostDefParseXML(const char *networkName, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" \ "_-+/*" -static int +int virNetworkDNSSrvDefParseHook(xmlNodePtr node G_GNUC_UNUSED, virNetworkDNSSrvDef *def, const char *networkName, @@ -874,69 +864,6 @@ virNetworkDNSSrvDefParseHook(xmlNodePtr node G_GNUC_UNUSED, } -static int -virNetworkDNSSrvDefParseXML(const char *networkName, - xmlNodePtr node, - xmlXPathContextPtr ctxt G_GNUC_UNUSED, - virNetworkDNSSrvDef *def, - bool partialOkay) -{ - g_autofree char *portStr = NULL; - g_autofree char *priorityStr = NULL; - g_autofree char *weightStr = NULL; - - def->service = virXMLPropString(node, "service"); - def->protocol = virXMLPropString(node, "protocol"); - - /* Following attributes are optional */ - def->domain = virXMLPropString(node, "domain"); - def->target = virXMLPropString(node, "target"); - - portStr = virXMLPropString(node, "port"); - if (portStr) { - if (virStrToLong_uip(portStr, NULL, 0, &def->port) < 0) { - virReportError(VIR_ERR_XML_DETAIL, - _("invalid DNS SRV port attribute " - "for service '%s' in network '%s'"), - def->service, networkName); - goto error; - } - } - - priorityStr = virXMLPropString(node, "priority"); - if (priorityStr) { - if (virStrToLong_uip(priorityStr, NULL, 0, &def->priority) < 0) { - virReportError(VIR_ERR_XML_DETAIL, - _("Invalid DNS SRV priority attribute " - "for service '%s' in network '%s'"), - def->service, networkName); - goto error; - } - } - - weightStr = virXMLPropString(node, "weight"); - if (weightStr) { - if (virStrToLong_uip(weightStr, NULL, 0, &def->weight) < 0) { - virReportError(VIR_ERR_XML_DETAIL, - _("invalid DNS SRV weight attribute " - "for service '%s' in network '%s'"), - def->service, networkName); - goto error; - } - } - - if (virNetworkDNSSrvDefParseHook(node, def, networkName, def, &partialOkay, - portStr, priorityStr, weightStr) < 0) - goto error; - - return 0; - - error: - virNetworkDNSSrvDefClear(def); - return -1; -} - - int virNetworkDNSTxtDefParseHook(xmlNodePtr node G_GNUC_UNUSED, virNetworkDNSTxtDef *def, @@ -1082,8 +1009,8 @@ virNetworkDNSDefParseXML(const char *networkName, def->srvs = g_new0(virNetworkDNSSrvDef, nsrvs); for (i = 0; i < nsrvs; i++) { - if (virNetworkDNSSrvDefParseXML(networkName, srvNodes[i], ctxt, - &def->srvs[def->nsrvs], false) < 0) { + if (virNetworkDNSSrvDefParseXML(srvNodes[i], &def->srvs[def->nsrvs], + networkName, def, NULL) < 0) { return -1; } def->nsrvs++; @@ -3553,6 +3480,7 @@ virNetworkDefUpdateDNSSrv(virNetworkDef *def, bool isAdd = (command == VIR_NETWORK_UPDATE_COMMAND_ADD_FIRST || command == VIR_NETWORK_UPDATE_COMMAND_ADD_LAST); int foundCt = 0; + bool notAdd; memset(&srv, 0, sizeof(srv)); @@ -3566,7 +3494,8 @@ virNetworkDefUpdateDNSSrv(virNetworkDef *def, if (virNetworkDefUpdateCheckElementName(def, ctxt->node, "srv") < 0) goto cleanup; - if (virNetworkDNSSrvDefParseXML(def->name, ctxt->node, ctxt, &srv, !isAdd) < 0) + notAdd = !isAdd; + if (virNetworkDNSSrvDefParseXML(ctxt->node, &srv, def->name, def, ¬Add) < 0) goto cleanup; for (i = 0; i < dns->nsrvs; i++) { diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index a4c83b46..a58d8953 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -131,14 +131,14 @@ struct _virNetworkDNSTxtDef { /* genparse, genformat */ }; typedef struct _virNetworkDNSSrvDef virNetworkDNSSrvDef; -struct _virNetworkDNSSrvDef { - char *domain; - char *service; - char *protocol; - char *target; - unsigned int port; - unsigned int priority; - unsigned int weight; +struct _virNetworkDNSSrvDef { /* genparse */ + char *service; /* xmlattr */ + char *protocol; /* xmlattr */ + char *domain; /* xmlattr */ + char *target; /* xmlattr */ + unsigned int port; /* xmlattr */ + unsigned int priority; /* xmlattr */ + unsigned int weight; /* xmlattr */ }; typedef struct _virNetworkDNSHostDef virNetworkDNSHostDef; @@ -429,5 +429,6 @@ virNetworkDefUpdateSection(virNetworkDef *def, VIR_ENUM_DECL(virNetworkTaint); +#define ENABLE_VIR_NETWORK_DNSSRV_DEF_PARSE_HOOK #define ENABLE_VIR_NETWORK_DNSTXT_DEF_PARSE_HOOK #include "network_conf.generated.h" -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 18 ++---------------- src/conf/network_conf.h | 2 +- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index cb2f3163..146c4977 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -2191,22 +2191,8 @@ virNetworkDNSDefFormat(virBuffer *buf, for (i = 0; i < def->nsrvs; i++) { if (def->srvs[i].service && def->srvs[i].protocol) { - virBufferEscapeString(buf, "<srv service='%s' ", - def->srvs[i].service); - virBufferEscapeString(buf, "protocol='%s'", def->srvs[i].protocol); - - if (def->srvs[i].domain) - virBufferEscapeString(buf, " domain='%s'", def->srvs[i].domain); - if (def->srvs[i].target) - virBufferEscapeString(buf, " target='%s'", def->srvs[i].target); - if (def->srvs[i].port) - virBufferAsprintf(buf, " port='%d'", def->srvs[i].port); - if (def->srvs[i].priority) - virBufferAsprintf(buf, " priority='%d'", def->srvs[i].priority); - if (def->srvs[i].weight) - virBufferAsprintf(buf, " weight='%d'", def->srvs[i].weight); - - virBufferAddLit(buf, "/>\n"); + if (virNetworkDNSSrvDefFormatBuf(buf, "srv", &def->srvs[i], def, NULL) < 0) + return -1; } } diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index a58d8953..052ccb58 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -131,7 +131,7 @@ struct _virNetworkDNSTxtDef { /* genparse, genformat */ }; typedef struct _virNetworkDNSSrvDef virNetworkDNSSrvDef; -struct _virNetworkDNSSrvDef { /* genparse */ +struct _virNetworkDNSSrvDef { /* genparse, genformat */ char *service; /* xmlattr */ char *protocol; /* xmlattr */ char *domain; /* xmlattr */ -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 63 +++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 146c4977..b326ef5f 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -676,21 +676,57 @@ virNetworkDHCPDefParseXML(const char *networkName, static int -virNetworkDNSHostDefParseXML(const char *networkName, - xmlNodePtr node, - virNetworkDNSHostDef *def, - bool partialOkay) +virNetworkDNSHostDefParseHook(xmlNodePtr node G_GNUC_UNUSED, + virNetworkDNSHostDef *def, + const char *networkName, + void *parent G_GNUC_UNUSED, + void *opaque, + const char *ip, + int nHostnameNodes G_GNUC_UNUSED) { - xmlNodePtr cur; - g_autofree char *ip = NULL; + bool partialOkay = false; - if (!(ip = virXMLPropString(node, "ip")) && !partialOkay) { + if (opaque) + partialOkay = *((bool *) opaque); + + if (!ip && !partialOkay) { virReportError(VIR_ERR_XML_DETAIL, _("Missing IP address in network '%s' DNS HOST record"), networkName); goto error; } + if (def->nnames == 0 && !partialOkay) { + virReportError(VIR_ERR_XML_DETAIL, + _("Missing hostname in network '%s' DNS HOST record"), + networkName); + goto error; + } + + if (!VIR_SOCKET_ADDR_VALID(&def->ip) && def->nnames == 0) { + virReportError(VIR_ERR_XML_DETAIL, + _("Missing ip and hostname in network '%s' DNS HOST record"), + networkName); + goto error; + } + + return 0; + + error: + return -1; +} + + +static int +virNetworkDNSHostDefParseXML(const char *networkName, + xmlNodePtr node, + virNetworkDNSHostDef *def, + bool partialOkay) +{ + xmlNodePtr cur; + g_autofree char *ip = NULL; + + ip = virXMLPropString(node, "ip"); if (ip && (virSocketAddrParse(&def->ip, ip, AF_UNSPEC) < 0)) { virReportError(VIR_ERR_XML_DETAIL, _("Invalid IP address in network '%s' DNS HOST record"), @@ -720,19 +756,10 @@ virNetworkDNSHostDefParseXML(const char *networkName, } cur = cur->next; } - if (def->nnames == 0 && !partialOkay) { - virReportError(VIR_ERR_XML_DETAIL, - _("Missing hostname in network '%s' DNS HOST record"), - networkName); - goto error; - } - if (!VIR_SOCKET_ADDR_VALID(&def->ip) && def->nnames == 0) { - virReportError(VIR_ERR_XML_DETAIL, - _("Missing ip and hostname in network '%s' DNS HOST record"), - networkName); + if (virNetworkDNSHostDefParseHook(node, def, networkName, def, &partialOkay, + ip, def->nnames) < 0) goto error; - } return 0; -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 71 ++++------------------------------------- src/conf/network_conf.h | 7 ++-- 2 files changed, 10 insertions(+), 68 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index b326ef5f..90b1e0ee 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -174,15 +174,6 @@ virNetworkIPDefClear(virNetworkIPDef *def) } -static void -virNetworkDNSHostDefClear(virNetworkDNSHostDef *def) -{ - while (def->nnames) - VIR_FREE(def->names[--def->nnames]); - VIR_FREE(def->names); -} - - static void virNetworkDNSForwarderClear(virNetworkDNSForwarder *def) { @@ -675,7 +666,7 @@ virNetworkDHCPDefParseXML(const char *networkName, } -static int +int virNetworkDNSHostDefParseHook(xmlNodePtr node G_GNUC_UNUSED, virNetworkDNSHostDef *def, const char *networkName, @@ -717,58 +708,6 @@ virNetworkDNSHostDefParseHook(xmlNodePtr node G_GNUC_UNUSED, } -static int -virNetworkDNSHostDefParseXML(const char *networkName, - xmlNodePtr node, - virNetworkDNSHostDef *def, - bool partialOkay) -{ - xmlNodePtr cur; - g_autofree char *ip = NULL; - - ip = virXMLPropString(node, "ip"); - if (ip && (virSocketAddrParse(&def->ip, ip, AF_UNSPEC) < 0)) { - virReportError(VIR_ERR_XML_DETAIL, - _("Invalid IP address in network '%s' DNS HOST record"), - networkName); - goto error; - } - - cur = node->children; - while (cur != NULL) { - if (cur->type == XML_ELEMENT_NODE && - virXMLNodeNameEqual(cur, "hostname")) { - if (cur->children != NULL) { - g_autofree char *name = virXMLNodeContentString(cur); - - if (!name) - goto error; - - if (!name[0]) { - virReportError(VIR_ERR_XML_DETAIL, - _("Missing hostname in network '%s' DNS HOST record"), - networkName); - goto error; - } - if (VIR_APPEND_ELEMENT(def->names, def->nnames, name) < 0) - goto error; - } - } - cur = cur->next; - } - - if (virNetworkDNSHostDefParseHook(node, def, networkName, def, &partialOkay, - ip, def->nnames) < 0) - goto error; - - return 0; - - error: - virNetworkDNSHostDefClear(def); - return -1; -} - - /* This includes all characters used in the names of current * /etc/services and /etc/protocols files (on Fedora 20), except ".", * which we can't allow because it would conflict with the use of "." @@ -1017,8 +956,8 @@ virNetworkDNSDefParseXML(const char *networkName, def->hosts = g_new0(virNetworkDNSHostDef, nhosts); for (i = 0; i < nhosts; i++) { - if (virNetworkDNSHostDefParseXML(networkName, hostNodes[i], - &def->hosts[def->nhosts], false) < 0) { + if (virNetworkDNSHostDefParseXML(hostNodes[i], &def->hosts[def->nhosts], + networkName, def, NULL) < 0) { return -1; } def->nhosts++; @@ -3398,6 +3337,7 @@ virNetworkDefUpdateDNSHost(virNetworkDef *def, bool isAdd = (command == VIR_NETWORK_UPDATE_COMMAND_ADD_FIRST || command == VIR_NETWORK_UPDATE_COMMAND_ADD_LAST); int foundCt = 0; + bool notAdd; memset(&host, 0, sizeof(host)); @@ -3411,7 +3351,8 @@ virNetworkDefUpdateDNSHost(virNetworkDef *def, if (virNetworkDefUpdateCheckElementName(def, ctxt->node, "host") < 0) goto cleanup; - if (virNetworkDNSHostDefParseXML(def->name, ctxt->node, &host, !isAdd) < 0) + notAdd = !isAdd; + if (virNetworkDNSHostDefParseXML(ctxt->node, &host, def->name, def, ¬Add) < 0) goto cleanup; for (i = 0; i < dns->nhosts; i++) { diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 052ccb58..998411be 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -142,10 +142,10 @@ struct _virNetworkDNSSrvDef { /* genparse, genformat */ }; typedef struct _virNetworkDNSHostDef virNetworkDNSHostDef; -struct _virNetworkDNSHostDef { - virSocketAddr ip; +struct _virNetworkDNSHostDef { /* genparse */ + virSocketAddr ip; /* xmlattr */ size_t nnames; - char **names; + char **names; /* xmlelem:hostname, array */ }; @@ -429,6 +429,7 @@ virNetworkDefUpdateSection(virNetworkDef *def, VIR_ENUM_DECL(virNetworkTaint); +#define ENABLE_VIR_NETWORK_DNSHOST_DEF_PARSE_HOOK #define ENABLE_VIR_NETWORK_DNSSRV_DEF_PARSE_HOOK #define ENABLE_VIR_NETWORK_DNSTXT_DEF_PARSE_HOOK #include "network_conf.generated.h" -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 14 +++----------- src/conf/network_conf.h | 2 +- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 90b1e0ee..ba67eab1 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -2095,7 +2095,7 @@ static int virNetworkDNSDefFormat(virBuffer *buf, const virNetworkDNSDef *def) { - size_t i, j; + size_t i; if (!(def->enable || def->forwardPlainNames || def->nfwds || def->nhosts || def->nsrvs || def->ntxts)) @@ -2164,16 +2164,8 @@ virNetworkDNSDefFormat(virBuffer *buf, if (def->nhosts) { for (i = 0; i < def->nhosts; i++) { - g_autofree char *ip = virSocketAddrFormat(&def->hosts[i].ip); - - virBufferAsprintf(buf, "<host ip='%s'>\n", ip); - virBufferAdjustIndent(buf, 2); - for (j = 0; j < def->hosts[i].nnames; j++) - virBufferEscapeString(buf, "<hostname>%s</hostname>\n", - def->hosts[i].names[j]); - - virBufferAdjustIndent(buf, -2); - virBufferAddLit(buf, "</host>\n"); + if (virNetworkDNSHostDefFormatBuf(buf, "host", &def->hosts[i], def, NULL) < 0) + return -1; } } virBufferAdjustIndent(buf, -2); diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 998411be..836d088d 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -142,7 +142,7 @@ struct _virNetworkDNSSrvDef { /* genparse, genformat */ }; typedef struct _virNetworkDNSHostDef virNetworkDNSHostDef; -struct _virNetworkDNSHostDef { /* genparse */ +struct _virNetworkDNSHostDef { /* genparse, genformat */ virSocketAddr ip; /* xmlattr */ size_t nnames; char **names; /* xmlelem:hostname, array */ -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 68 +++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index ba67eab1..cf9e77d3 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -871,6 +871,52 @@ virNetworkDNSTxtDefParseHook(xmlNodePtr node G_GNUC_UNUSED, } +static int +virNetworkDNSForwarderParseHook(xmlNodePtr node G_GNUC_UNUSED, + virNetworkDNSForwarder *def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED, + const char *addr) +{ + if (!(addr || def->domain)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Invalid forwarder element, must contain " + "at least one of addr or domain")); + return -1; + } + + return 0; +} + + +static int +virNetworkDNSForwarderParseXML(xmlNodePtr node, + virNetworkDNSForwarder *def, + const char *networkName, + void *parent G_GNUC_UNUSED, + void *opaque) +{ + g_autofree char *addr = virXMLPropString(node, "addr"); + + if (addr && virSocketAddrParse(&def->addr, addr, AF_UNSPEC) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid forwarder IP address '%s' " + "in network '%s'"), + addr, networkName); + return -1; + } + + def->domain = virXMLPropString(node, "domain"); + + if (virNetworkDNSForwarderParseHook(node, def, networkName, def, opaque, + addr) < 0) + return -1; + + return 0; +} + + static int virNetworkDNSDefParseXML(const char *networkName, xmlNodePtr node, @@ -924,23 +970,13 @@ virNetworkDNSDefParseXML(const char *networkName, def->forwarders = g_new0(virNetworkDNSForwarder, nfwds); for (i = 0; i < nfwds; i++) { - g_autofree char *addr = virXMLPropString(fwdNodes[i], "addr"); - - if (addr && virSocketAddrParse(&def->forwarders[i].addr, - addr, AF_UNSPEC) < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("Invalid forwarder IP address '%s' " - "in network '%s'"), - addr, networkName); + if (virNetworkDNSForwarderParseXML(fwdNodes[i], + &def->forwarders[i], + networkName, + def, + NULL) < 0) return -1; - } - def->forwarders[i].domain = virXMLPropString(fwdNodes[i], "domain"); - if (!(addr || def->forwarders[i].domain)) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Invalid forwarder element, must contain " - "at least one of addr or domain")); - return -1; - } + def->nfwds++; } } -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 36 +----------------------------------- src/conf/network_conf.h | 7 ++++--- 2 files changed, 5 insertions(+), 38 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index cf9e77d3..ef28bb4d 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -174,13 +174,6 @@ virNetworkIPDefClear(virNetworkIPDef *def) } -static void -virNetworkDNSForwarderClear(virNetworkDNSForwarder *def) -{ - VIR_FREE(def->domain); -} - - static void virNetworkDNSDefClear(virNetworkDNSDef *def) { @@ -871,7 +864,7 @@ virNetworkDNSTxtDefParseHook(xmlNodePtr node G_GNUC_UNUSED, } -static int +int virNetworkDNSForwarderParseHook(xmlNodePtr node G_GNUC_UNUSED, virNetworkDNSForwarder *def, const char *instname G_GNUC_UNUSED, @@ -890,33 +883,6 @@ virNetworkDNSForwarderParseHook(xmlNodePtr node G_GNUC_UNUSED, } -static int -virNetworkDNSForwarderParseXML(xmlNodePtr node, - virNetworkDNSForwarder *def, - const char *networkName, - void *parent G_GNUC_UNUSED, - void *opaque) -{ - g_autofree char *addr = virXMLPropString(node, "addr"); - - if (addr && virSocketAddrParse(&def->addr, addr, AF_UNSPEC) < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("Invalid forwarder IP address '%s' " - "in network '%s'"), - addr, networkName); - return -1; - } - - def->domain = virXMLPropString(node, "domain"); - - if (virNetworkDNSForwarderParseHook(node, def, networkName, def, opaque, - addr) < 0) - return -1; - - return 0; -} - - static int virNetworkDNSDefParseXML(const char *networkName, xmlNodePtr node, diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 836d088d..17f6c309 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -150,9 +150,9 @@ struct _virNetworkDNSHostDef { /* genparse, genformat */ typedef struct _virNetworkDNSForwarder virNetworkDNSForwarder; -struct _virNetworkDNSForwarder { - virSocketAddr addr; - char *domain; +struct _virNetworkDNSForwarder { /* genparse */ + virSocketAddr addr; /* xmlattr */ + char *domain; /* xmlattr */ }; typedef struct _virNetworkDNSDef virNetworkDNSDef; @@ -429,6 +429,7 @@ virNetworkDefUpdateSection(virNetworkDef *def, VIR_ENUM_DECL(virNetworkTaint); +#define ENABLE_VIR_NETWORK_DNSFORWARDER_PARSE_HOOK #define ENABLE_VIR_NETWORK_DNSHOST_DEF_PARSE_HOOK #define ENABLE_VIR_NETWORK_DNSSRV_DEF_PARSE_HOOK #define ENABLE_VIR_NETWORK_DNSTXT_DEF_PARSE_HOOK -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 18 +++--------------- src/conf/network_conf.h | 4 ++-- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index ef28bb4d..be639500 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -2135,21 +2135,9 @@ virNetworkDNSDefFormat(virBuffer *buf, virBufferAdjustIndent(buf, 2); for (i = 0; i < def->nfwds; i++) { - - virBufferAddLit(buf, "<forwarder"); - if (def->forwarders[i].domain) { - virBufferEscapeString(buf, " domain='%s'", - def->forwarders[i].domain); - } - if (VIR_SOCKET_ADDR_VALID(&def->forwarders[i].addr)) { - g_autofree char *addr = virSocketAddrFormat(&def->forwarders[i].addr); - - if (!addr) - return -1; - - virBufferAsprintf(buf, " addr='%s'", addr); - } - virBufferAddLit(buf, "/>\n"); + if (virNetworkDNSForwarderFormatBuf(buf, "forwarder", + &def->forwarders[i], def, NULL) < 0) + return -1; } for (i = 0; i < def->ntxts; i++) { diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 17f6c309..577c1568 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -150,9 +150,9 @@ struct _virNetworkDNSHostDef { /* genparse, genformat */ typedef struct _virNetworkDNSForwarder virNetworkDNSForwarder; -struct _virNetworkDNSForwarder { /* genparse */ - virSocketAddr addr; /* xmlattr */ +struct _virNetworkDNSForwarder { /* genparse, genformat */ char *domain; /* xmlattr */ + virSocketAddr addr; /* xmlattr */ }; typedef struct _virNetworkDNSDef virNetworkDNSDef; -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index be639500..19408987 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -883,6 +883,31 @@ virNetworkDNSForwarderParseHook(xmlNodePtr node G_GNUC_UNUSED, } +static int +virNetworkDNSDefParseHook(xmlNodePtr node G_GNUC_UNUSED, + virNetworkDNSDef *def, + const char *networkName, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED, + const char *enable G_GNUC_UNUSED, + const char *forwardPlainNames G_GNUC_UNUSED, + int nfwds, + int ntxts, + int nsrvs, + int nhosts) +{ + if (def->enable == VIR_TRISTATE_BOOL_NO && + (nfwds || nhosts || nsrvs || ntxts)) { + virReportError(VIR_ERR_XML_ERROR, + _("Extra data in disabled network '%s'"), + networkName); + return -1; + } + + return 0; +} + + static int virNetworkDNSDefParseXML(const char *networkName, xmlNodePtr node, @@ -1004,13 +1029,10 @@ virNetworkDNSDefParseXML(const char *networkName, } } - if (def->enable == VIR_TRISTATE_BOOL_NO && - (nfwds || nhosts || nsrvs || ntxts)) { - virReportError(VIR_ERR_XML_ERROR, - _("Extra data in disabled network '%s'"), - networkName); + if (virNetworkDNSDefParseHook(node, def, networkName, def, NULL, + enable, forwardPlainNames, + nfwds, ntxts, nsrvs, nhosts) < 0) return -1; - } return 0; } -- 2.25.1

Reorder the members of virNetworkDNSDef according to their orders in format function. Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 160 +--------------------------------------- src/conf/network_conf.h | 19 ++--- 2 files changed, 12 insertions(+), 167 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 19408987..364c10e2 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -174,32 +174,6 @@ virNetworkIPDefClear(virNetworkIPDef *def) } -static void -virNetworkDNSDefClear(virNetworkDNSDef *def) -{ - if (def->forwarders) { - while (def->nfwds) - virNetworkDNSForwarderClear(&def->forwarders[--def->nfwds]); - VIR_FREE(def->forwarders); - } - if (def->txts) { - while (def->ntxts) - virNetworkDNSTxtDefClear(&def->txts[--def->ntxts]); - VIR_FREE(def->txts); - } - if (def->hosts) { - while (def->nhosts) - virNetworkDNSHostDefClear(&def->hosts[--def->nhosts]); - VIR_FREE(def->hosts); - } - if (def->srvs) { - while (def->nsrvs) - virNetworkDNSSrvDefClear(&def->srvs[--def->nsrvs]); - VIR_FREE(def->srvs); - } -} - - static void virNetworkForwardDefClear(virNetworkForwardDef *def) { @@ -883,7 +857,7 @@ virNetworkDNSForwarderParseHook(xmlNodePtr node G_GNUC_UNUSED, } -static int +int virNetworkDNSDefParseHook(xmlNodePtr node G_GNUC_UNUSED, virNetworkDNSDef *def, const char *networkName, @@ -908,136 +882,6 @@ virNetworkDNSDefParseHook(xmlNodePtr node G_GNUC_UNUSED, } -static int -virNetworkDNSDefParseXML(const char *networkName, - xmlNodePtr node, - xmlXPathContextPtr ctxt, - virNetworkDNSDef *def) -{ - g_autofree xmlNodePtr *hostNodes = NULL; - g_autofree xmlNodePtr *srvNodes = NULL; - g_autofree xmlNodePtr *txtNodes = NULL; - g_autofree xmlNodePtr *fwdNodes = NULL; - g_autofree char *forwardPlainNames = NULL; - g_autofree char *enable = NULL; - int nhosts, nsrvs, ntxts, nfwds; - size_t i; - VIR_XPATH_NODE_AUTORESTORE(ctxt) - - ctxt->node = node; - - enable = virXPathString("string(./@enable)", ctxt); - if (enable) { - def->enable = virTristateBoolTypeFromString(enable); - if (def->enable <= 0) { - virReportError(VIR_ERR_XML_ERROR, - _("Invalid dns enable setting '%s' " - "in network '%s'"), - enable, networkName); - return -1; - } - } - - forwardPlainNames = virXPathString("string(./@forwardPlainNames)", ctxt); - if (forwardPlainNames) { - def->forwardPlainNames = virTristateBoolTypeFromString(forwardPlainNames); - if (def->forwardPlainNames <= 0) { - virReportError(VIR_ERR_XML_ERROR, - _("Invalid dns forwardPlainNames setting '%s' " - "in network '%s'"), - forwardPlainNames, networkName); - return -1; - } - } - - nfwds = virXPathNodeSet("./forwarder", ctxt, &fwdNodes); - if (nfwds < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("invalid <forwarder> element found in <dns> of network %s"), - networkName); - return -1; - } - if (nfwds > 0) { - def->forwarders = g_new0(virNetworkDNSForwarder, nfwds); - - for (i = 0; i < nfwds; i++) { - if (virNetworkDNSForwarderParseXML(fwdNodes[i], - &def->forwarders[i], - networkName, - def, - NULL) < 0) - return -1; - - def->nfwds++; - } - } - - nhosts = virXPathNodeSet("./host", ctxt, &hostNodes); - if (nhosts < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("invalid <host> element found in <dns> of network %s"), - networkName); - return -1; - } - if (nhosts > 0) { - def->hosts = g_new0(virNetworkDNSHostDef, nhosts); - - for (i = 0; i < nhosts; i++) { - if (virNetworkDNSHostDefParseXML(hostNodes[i], &def->hosts[def->nhosts], - networkName, def, NULL) < 0) { - return -1; - } - def->nhosts++; - } - } - - nsrvs = virXPathNodeSet("./srv", ctxt, &srvNodes); - if (nsrvs < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("invalid <srv> element found in <dns> of network %s"), - networkName); - return -1; - } - if (nsrvs > 0) { - def->srvs = g_new0(virNetworkDNSSrvDef, nsrvs); - - for (i = 0; i < nsrvs; i++) { - if (virNetworkDNSSrvDefParseXML(srvNodes[i], &def->srvs[def->nsrvs], - networkName, def, NULL) < 0) { - return -1; - } - def->nsrvs++; - } - } - - ntxts = virXPathNodeSet("./txt", ctxt, &txtNodes); - if (ntxts < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("invalid <txt> element found in <dns> of network %s"), - networkName); - return -1; - } - if (ntxts > 0) { - def->txts = g_new0(virNetworkDNSTxtDef, ntxts); - - for (i = 0; i < ntxts; i++) { - if (virNetworkDNSTxtDefParseXML(txtNodes[i], &def->txts[def->ntxts], - networkName, def, NULL) < 0) { - return -1; - } - def->ntxts++; - } - } - - if (virNetworkDNSDefParseHook(node, def, networkName, def, NULL, - enable, forwardPlainNames, - nfwds, ntxts, nsrvs, nhosts) < 0) - return -1; - - return 0; -} - - static int virNetworkIPDefParseXML(const char *networkName, xmlNodePtr node, @@ -1795,7 +1639,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt, dnsNode = virXPathNode("./dns", ctxt); if (dnsNode != NULL && - virNetworkDNSDefParseXML(def->name, dnsNode, ctxt, &def->dns) < 0) { + virNetworkDNSDefParseXML(dnsNode, &def->dns, def->name, def, NULL) < 0) { return NULL; } diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 577c1568..9968d962 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -156,17 +156,17 @@ struct _virNetworkDNSForwarder { /* genparse, genformat */ }; typedef struct _virNetworkDNSDef virNetworkDNSDef; -struct _virNetworkDNSDef { - int enable; /* enum virTristateBool */ - int forwardPlainNames; /* enum virTristateBool */ +struct _virNetworkDNSDef { /* genparse */ + virTristateBool enable; /* xmlattr */ + virTristateBool forwardPlainNames; /* xmlattr */ + size_t nfwds; + virNetworkDNSForwarder *forwarders; /* xmlelem, array:nfwds */ size_t ntxts; - virNetworkDNSTxtDef *txts; - size_t nhosts; - virNetworkDNSHostDef *hosts; + virNetworkDNSTxtDef *txts; /* xmlelem, array */ size_t nsrvs; - virNetworkDNSSrvDef *srvs; - size_t nfwds; - virNetworkDNSForwarder *forwarders; + virNetworkDNSSrvDef *srvs; /* xmlelem, array */ + size_t nhosts; + virNetworkDNSHostDef *hosts; /* xmlelem, array */ }; typedef struct _virNetworkIPDef virNetworkIPDef; @@ -429,6 +429,7 @@ virNetworkDefUpdateSection(virNetworkDef *def, VIR_ENUM_DECL(virNetworkTaint); +#define ENABLE_VIR_NETWORK_DNSDEF_PARSE_HOOK #define ENABLE_VIR_NETWORK_DNSFORWARDER_PARSE_HOOK #define ENABLE_VIR_NETWORK_DNSHOST_DEF_PARSE_HOOK #define ENABLE_VIR_NETWORK_DNSSRV_DEF_PARSE_HOOK -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 73 +---------------------------------------- src/conf/network_conf.h | 2 +- 2 files changed, 2 insertions(+), 73 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 364c10e2..a652110c 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -1959,77 +1959,6 @@ virNetworkDefParseNode(xmlDocPtr xml, } -static int -virNetworkDNSDefFormat(virBuffer *buf, - const virNetworkDNSDef *def) -{ - size_t i; - - if (!(def->enable || def->forwardPlainNames || def->nfwds || def->nhosts || - def->nsrvs || def->ntxts)) - return 0; - - virBufferAddLit(buf, "<dns"); - if (def->enable) { - const char *fwd = virTristateBoolTypeToString(def->enable); - - if (!fwd) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Unknown enable type %d in network"), - def->enable); - return -1; - } - virBufferAsprintf(buf, " enable='%s'", fwd); - } - if (def->forwardPlainNames) { - const char *fwd = virTristateBoolTypeToString(def->forwardPlainNames); - - if (!fwd) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Unknown forwardPlainNames type %d in network"), - def->forwardPlainNames); - return -1; - } - virBufferAsprintf(buf, " forwardPlainNames='%s'", fwd); - } - if (!(def->nfwds || def->nhosts || def->nsrvs || def->ntxts)) { - virBufferAddLit(buf, "/>\n"); - return 0; - } - - virBufferAddLit(buf, ">\n"); - virBufferAdjustIndent(buf, 2); - - for (i = 0; i < def->nfwds; i++) { - if (virNetworkDNSForwarderFormatBuf(buf, "forwarder", - &def->forwarders[i], def, NULL) < 0) - return -1; - } - - for (i = 0; i < def->ntxts; i++) { - if (virNetworkDNSTxtDefFormatBuf(buf, "txt", &def->txts[i], def, NULL) < 0) - return -1; - } - - for (i = 0; i < def->nsrvs; i++) { - if (def->srvs[i].service && def->srvs[i].protocol) { - if (virNetworkDNSSrvDefFormatBuf(buf, "srv", &def->srvs[i], def, NULL) < 0) - return -1; - } - } - - if (def->nhosts) { - for (i = 0; i < def->nhosts; i++) { - if (virNetworkDNSHostDefFormatBuf(buf, "host", &def->hosts[i], def, NULL) < 0) - return -1; - } - } - virBufferAdjustIndent(buf, -2); - virBufferAddLit(buf, "</dns>\n"); - return 0; -} - - static int virNetworkIPDefFormat(virBuffer *buf, const virNetworkIPDef *def) @@ -2436,7 +2365,7 @@ virNetworkDefFormatBuf(virBuffer *buf, virBufferAddLit(buf, "/>\n"); } - if (virNetworkDNSDefFormat(buf, &def->dns) < 0) + if (virNetworkDNSDefFormatBuf(buf, "dns", &def->dns, def, NULL) < 0) return -1; if (virNetDevVlanFormat(&def->vlan, buf) < 0) diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 9968d962..b0675175 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -156,7 +156,7 @@ struct _virNetworkDNSForwarder { /* genparse, genformat */ }; typedef struct _virNetworkDNSDef virNetworkDNSDef; -struct _virNetworkDNSDef { /* genparse */ +struct _virNetworkDNSDef { /* genparse, genformat */ virTristateBool enable; /* xmlattr */ virTristateBool forwardPlainNames; /* xmlattr */ size_t nfwds; -- 2.25.1

Polite ping. On 2021-04-22 at 15:25, Shi Lei wrote:
V2 here: [https://listman.redhat.com/archives/libvir-list/2020-September/msg00204.html]
Differ from V2:
* Add tests for xmlgen to illustrate all the different features we can use and make sure its proper functions in the future.
* Add docs/xmlgen.rst to explain the usage of all directives and the tool itself.
* Now xmlgen can check whether the first item of enum ends with _NONE, _DEFAULT or _ABSENT and generate proper code. So we no longer need to add extra 'default' item for enum.
* Now xmlgen can provide extra [tips] when we execute its command-line to show generated code for preview.
* Enable/disable hooks by macros rather than by special directives.
* Add virStrToBoolYesNo/virStrToBoolTrueFalse/virStrToBoolOnOff and explicitly check both the true and false values.
* Stronger check for python3-clang and libclang.so to make sure it can work.
* Add python3-clang to the libvirt.spec.in and the mingw-libvirt.spec.in.
Thanks!
Shi Lei (25): scripts: Add a tool to generate xml parse/format functions maint: Check python3-clang and libclang maint: Call xmlgen automatically when c-head-files change docs: Add xmlgen.rst to explain how to use it build-aux: Only check *.[ch] for sc_prohibit_useless_translation tests: Add tests for xmlgen util: Add some xml-helper-functions to cooperate with xmlgen util: Add helper aliases and functions for 'bool' and 'time_t' to cooperate with xmlgen util: Add parsexml/formatbuf helper functions for virSocketAddr util: Add virUUID type and parse/format functions conf: Extract error-checking code from virNetworkDNSTxtDefParseXML conf: Replace virNetworkDNSTxtDefParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSTxtDefFormatBuf conf: Extract error-checking code from virNetworkDNSSrvDefParseXML conf: Replace virNetworkDNSSrvDefParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSSrvDefFormatBuf conf: Extract error-checking code from virNetworkDNSHostDefParseXML conf: Replace virNetworkDNSHostDefParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSHostDefFormatBuf conf: Extract virNetworkDNSForwarderParseXML from virNetworkDNSParseXML conf: Replace virNetworkDNSForwarderParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSForwarderFormatBuf conf: Extract error-checking code from virNetworkDNSDefParseXML conf: Replace virNetworkDNSDefParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSDefFormatBuf
build-aux/syntax-check.mk | 2 +- docs/meson.build | 1 + docs/xmlgen.rst | 684 +++++++++++++ libvirt.spec.in | 1 + meson.build | 10 + mingw-libvirt.spec.in | 1 + po/POTFILES.in | 2 + scripts/meson.build | 8 + scripts/xmlgen/directive.py | 1192 ++++++++++++++++++++++ scripts/xmlgen/go | 29 + scripts/xmlgen/main.py | 534 ++++++++++ scripts/xmlgen/utils.py | 121 +++ src/conf/meson.build | 37 + src/conf/network_conf.c | 463 ++------- src/conf/network_conf.h | 59 +- src/internal.h | 8 + src/libvirt_private.syms | 13 + src/meson.build | 6 + src/util/meson.build | 36 + src/util/virbuffer.c | 44 + src/util/virbuffer.h | 8 +- src/util/virsocketaddr.c | 42 + src/util/virsocketaddr.h | 23 +- src/util/virstring.c | 102 ++ src/util/virstring.h | 15 + src/util/viruuid.c | 31 + src/util/viruuid.h | 18 + src/util/virxml.c | 120 +++ src/util/virxml.h | 6 + tests/meson.build | 3 + tests/xmlgenin/conf/array.h | 17 + tests/xmlgenin/conf/empty.h | 7 + tests/xmlgenin/conf/enum-first-item.h | 12 + tests/xmlgenin/conf/external.h | 9 + tests/xmlgenin/conf/genformat-separate.h | 11 + tests/xmlgenin/conf/genformat.h | 11 + tests/xmlgenin/conf/genparse.h | 11 + tests/xmlgenin/conf/namespace.h | 12 + tests/xmlgenin/conf/required.h | 9 + tests/xmlgenin/conf/skipparse.h | 10 + tests/xmlgenin/conf/specify.h | 13 + tests/xmlgenin/conf/xmlattr.h | 10 + tests/xmlgenin/conf/xmlelem.h | 10 + tests/xmlgenin/conf/xmlgroup.h | 8 + tests/xmlgenin/conf/xmlswitch.h | 17 + tests/xmlgenin/util/enums.h | 58 ++ tests/xmlgenin/util/structs.h | 67 ++ tests/xmlgenout/array.txt | 364 +++++++ tests/xmlgenout/empty.txt | 181 ++++ tests/xmlgenout/enum-first-item.txt | 297 ++++++ tests/xmlgenout/external.txt | 205 ++++ tests/xmlgenout/genformat-separate.txt | 190 ++++ tests/xmlgenout/genformat.txt | 142 +++ tests/xmlgenout/genparse.txt | 154 +++ tests/xmlgenout/namespace.txt | 222 ++++ tests/xmlgenout/required.txt | 236 +++++ tests/xmlgenout/skipparse.txt | 223 ++++ tests/xmlgenout/specify.txt | 291 ++++++ tests/xmlgenout/xmlattr.txt | 252 +++++ tests/xmlgenout/xmlelem.txt | 243 +++++ tests/xmlgenout/xmlgroup.txt | 204 ++++ tests/xmlgenout/xmlswitch.txt | 470 +++++++++ tests/xmlgentest.c | 107 ++ tools/meson.build | 3 + 64 files changed, 7289 insertions(+), 406 deletions(-) create mode 100644 docs/xmlgen.rst create mode 100644 scripts/xmlgen/directive.py create mode 100755 scripts/xmlgen/go create mode 100755 scripts/xmlgen/main.py create mode 100644 scripts/xmlgen/utils.py create mode 100644 tests/xmlgenin/conf/array.h create mode 100644 tests/xmlgenin/conf/empty.h create mode 100644 tests/xmlgenin/conf/enum-first-item.h create mode 100644 tests/xmlgenin/conf/external.h create mode 100644 tests/xmlgenin/conf/genformat-separate.h create mode 100644 tests/xmlgenin/conf/genformat.h create mode 100644 tests/xmlgenin/conf/genparse.h create mode 100644 tests/xmlgenin/conf/namespace.h create mode 100644 tests/xmlgenin/conf/required.h create mode 100644 tests/xmlgenin/conf/skipparse.h create mode 100644 tests/xmlgenin/conf/specify.h create mode 100644 tests/xmlgenin/conf/xmlattr.h create mode 100644 tests/xmlgenin/conf/xmlelem.h create mode 100644 tests/xmlgenin/conf/xmlgroup.h create mode 100644 tests/xmlgenin/conf/xmlswitch.h create mode 100644 tests/xmlgenin/util/enums.h create mode 100644 tests/xmlgenin/util/structs.h create mode 100644 tests/xmlgenout/array.txt create mode 100644 tests/xmlgenout/empty.txt create mode 100644 tests/xmlgenout/enum-first-item.txt create mode 100644 tests/xmlgenout/external.txt create mode 100644 tests/xmlgenout/genformat-separate.txt create mode 100644 tests/xmlgenout/genformat.txt create mode 100644 tests/xmlgenout/genparse.txt create mode 100644 tests/xmlgenout/namespace.txt create mode 100644 tests/xmlgenout/required.txt create mode 100644 tests/xmlgenout/skipparse.txt create mode 100644 tests/xmlgenout/specify.txt create mode 100644 tests/xmlgenout/xmlattr.txt create mode 100644 tests/xmlgenout/xmlelem.txt create mode 100644 tests/xmlgenout/xmlgroup.txt create mode 100644 tests/xmlgenout/xmlswitch.txt create mode 100644 tests/xmlgentest.c
-- 2.25.1
participants (1)
-
Shi Lei