Add ddsconf to generate md, rnc and xsd configuration documentation
Signed-off-by: Jeroen Koekkoek <jeroen@koekkoek.nl>
This commit is contained in:
parent
9c04099937
commit
b25f10ff33
20 changed files with 5596 additions and 5087 deletions
|
@ -12,6 +12,7 @@
|
|||
set(CMAKE_INSTALL_TOOLSDIR "${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/tools")
|
||||
add_subdirectory(pubsub)
|
||||
add_subdirectory(ddsls)
|
||||
add_subdirectory(ddsconf)
|
||||
if(BUILD_IDLC)
|
||||
add_subdirectory(ddsperf)
|
||||
endif()
|
||||
|
|
17
src/tools/ddsconf/CMakeLists.txt
Normal file
17
src/tools/ddsconf/CMakeLists.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
#
|
||||
# Copyright(c) 2020 ADLINK Technology Limited and others
|
||||
#
|
||||
# This program and the accompanying materials are made available under the
|
||||
# terms of the Eclipse Public License v. 2.0 which is available at
|
||||
# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
# v. 1.0 which is available at
|
||||
# http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
#
|
||||
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
#
|
||||
add_executable(ddsconf ddsconf.c rnc.c md.c xsd.c)
|
||||
target_link_libraries(ddsconf PRIVATE ddsc)
|
||||
target_include_directories(ddsconf
|
||||
PRIVATE
|
||||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:ddsc,INCLUDE_DIRECTORIES>>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
|
653
src/tools/ddsconf/ddsconf.c
Normal file
653
src/tools/ddsconf/ddsconf.c
Normal file
|
@ -0,0 +1,653 @@
|
|||
/*
|
||||
* Copyright(c) 2020 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dds/ddsrt/heap.h"
|
||||
#include "dds/ddsrt/io.h"
|
||||
#include "dds/ddsrt/string.h"
|
||||
#include "dds/ddsrt/misc.h"
|
||||
|
||||
#include "ddsconf.h"
|
||||
|
||||
|
||||
/* configuration units */
|
||||
#define UNIT(str, ...) { .name = str, __VA_ARGS__ }
|
||||
#define DESCRIPTION(str) .description = str
|
||||
#define PATTERN(str) .pattern = str
|
||||
#define END_MARKER { NULL, NULL, NULL }
|
||||
|
||||
#include "dds/ddsi/ddsi_cfgunits.h"
|
||||
/* undefine unit macros */
|
||||
#undef UNIT
|
||||
#undef DESCRIPTION
|
||||
#undef PATTERN
|
||||
#undef END_MARKER
|
||||
|
||||
|
||||
/* configuration elements */
|
||||
#define DEPRECATED(name) "|" name
|
||||
#define MEMBER(name) /* drop */
|
||||
#define MEMBEROF(parent,name) /* drop */
|
||||
#define FUNCTIONS(...) /* drop */
|
||||
#define DESCRIPTION(str) .description = str
|
||||
#define RANGE(str) .range = str
|
||||
#define UNIT(str) .unit = str
|
||||
#define VALUES(...) .values = (const char *[]){ __VA_ARGS__, NULL }
|
||||
|
||||
#define NOMEMBER /* drop */
|
||||
#define NOFUNCTIONS /* drop */
|
||||
#define NOMETADATA { NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL }
|
||||
#define END_MARKER { NULL, NULL, NULL, 0, NULL, NULL, NOMETADATA }
|
||||
|
||||
#define ELEMENT(name, elems, attrs, multip, dflt, desc, ...) \
|
||||
{ name, elems, attrs, multip, dflt, desc, { __VA_ARGS__ } }
|
||||
|
||||
#define MOVED(name, whereto) \
|
||||
{ ">" name, NULL, NULL, 0, whereto, NULL, NOMETADATA }
|
||||
|
||||
#define EXPAND(macro, args) macro args /* Visual Studio */
|
||||
|
||||
#define NOP(name) \
|
||||
EXPAND(ELEMENT, (name, NULL, NULL, 1, NULL, NULL, .type = "nop"))
|
||||
#define BOOL(name, attrs, multip, dflt, ofst, funcs, desc, ...) \
|
||||
EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, desc, .type = "bool", __VA_ARGS__))
|
||||
#define INT(name, attrs, multip, dflt, ofst, funcs, desc, ...) \
|
||||
EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, desc, .type = "int", __VA_ARGS__))
|
||||
#define ENUM(name, attrs, multip, dflt, ofst, funcs, desc, ...) \
|
||||
EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, desc, .type = "enum", __VA_ARGS__))
|
||||
#define STRING(name, attrs, multip, dflt, ofst, funcs, desc, ...) \
|
||||
EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, desc, .type = "string", __VA_ARGS__))
|
||||
#define LIST(name, attrs, multip, dflt, ofst, funcs, desc, ...) \
|
||||
EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, desc, .type = "list", __VA_ARGS__))
|
||||
#define GROUP(name, elems, attrs, multip, ofst, funcs, desc) \
|
||||
EXPAND(ELEMENT, (name, elems, attrs, multip, NULL, desc, .type = "group"))
|
||||
|
||||
#include "dds/ddsi/ddsi_cfgelems.h"
|
||||
/* undefine element macros */
|
||||
#undef DEPRECATED
|
||||
#undef MEMBER
|
||||
#undef MEMBEROF
|
||||
#undef FUNCTIONS
|
||||
#undef DESCRIPTION
|
||||
#undef RANGE
|
||||
#undef UNIT
|
||||
#undef VALUES
|
||||
#undef NOMEMBER
|
||||
#undef NOFUNCTIONS
|
||||
#undef NOMETADATA
|
||||
#undef END_MARKER
|
||||
#undef ELEMENT
|
||||
#undef MOVED
|
||||
#undef NOP
|
||||
#undef BOOL
|
||||
#undef INT
|
||||
#undef ENUM
|
||||
#undef STRING
|
||||
#undef LIST
|
||||
#undef GROUP
|
||||
|
||||
|
||||
const char *schema(void) { return "config"; }
|
||||
|
||||
const char *url(void) { return "https://cdds.io/config"; }
|
||||
|
||||
const char *name(const struct cfgelem *elem)
|
||||
{
|
||||
if (elem->meta.name)
|
||||
return (const char *)elem->meta.name;
|
||||
return elem->name;
|
||||
}
|
||||
|
||||
static char spaces[32];
|
||||
|
||||
void print(FILE *out, unsigned int cols, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
assert((size_t)cols < sizeof(spaces));
|
||||
spaces[cols] = '\0';
|
||||
fprintf(out, "%s", spaces);
|
||||
spaces[cols] = ' ';
|
||||
va_start(ap, fmt);
|
||||
vfprintf(out, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void usage(const char *prog)
|
||||
{
|
||||
static const char fmt[] = "usage: %s [OPTIONS] -f FORMAT\n";
|
||||
fprintf(stderr, fmt, prog);
|
||||
}
|
||||
|
||||
static void help(const char *prog)
|
||||
{
|
||||
static const char fmt[] = "\
|
||||
usage: %s -f FORMAT\n\
|
||||
\n\
|
||||
OPTIONS:\n\
|
||||
-h print help message\n\
|
||||
-f FORMAT output format. one of md, rnc or xsd\n\
|
||||
-o FILENAME output file. specify - to use stdout\n\
|
||||
";
|
||||
|
||||
fprintf(stdout, fmt, prog);
|
||||
}
|
||||
|
||||
struct cfgelem *nextelem(const struct cfgelem *list, const struct cfgelem *elem)
|
||||
{
|
||||
const struct cfgelem *next = NULL;
|
||||
if (list) {
|
||||
const struct cfgelem *ce = list;
|
||||
/* find next lexicographic element in list */
|
||||
for (; ce && ce->name; ce++) {
|
||||
if (ismoved(ce) || isdeprecated(ce) || isnop(ce))
|
||||
continue;
|
||||
if ((!elem || strcmp(ce->name, elem->name) > 0) &&
|
||||
(!next || strcmp(ce->name, next->name) < 0))
|
||||
next = ce;
|
||||
}
|
||||
}
|
||||
return (struct cfgelem *)next;
|
||||
}
|
||||
|
||||
struct cfgelem *firstelem(const struct cfgelem *list)
|
||||
{
|
||||
return nextelem(list, NULL);
|
||||
}
|
||||
|
||||
const struct cfgunit *findunit(const struct cfgunit *units, const char *name)
|
||||
{
|
||||
for (const struct cfgunit *cu = units; cu->name; cu++) {
|
||||
if (strcmp(cu->name, name) == 0) {
|
||||
return cu;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ismoved(const struct cfgelem *elem)
|
||||
{
|
||||
return elem && elem->name && elem->name[0] == '>';
|
||||
}
|
||||
|
||||
int isdeprecated(const struct cfgelem *elem)
|
||||
{
|
||||
return elem && elem->name && elem->name[0] == '|';
|
||||
}
|
||||
|
||||
int isgroup(const struct cfgelem *elem)
|
||||
{
|
||||
return strcmp(elem->meta.type, "group") == 0;
|
||||
}
|
||||
|
||||
int isnop(const struct cfgelem *elem)
|
||||
{
|
||||
return strcmp(elem->meta.type, "nop") == 0;
|
||||
}
|
||||
|
||||
int isbool(const struct cfgelem *elem)
|
||||
{
|
||||
return strcmp(elem->meta.type, "bool") == 0;
|
||||
}
|
||||
|
||||
int isint(const struct cfgelem *elem)
|
||||
{
|
||||
return strcmp(elem->meta.type, "int") == 0;
|
||||
}
|
||||
|
||||
int isstring(const struct cfgelem *elem)
|
||||
{
|
||||
return strcmp(elem->meta.type, "string") == 0;
|
||||
}
|
||||
|
||||
int isenum(const struct cfgelem *elem)
|
||||
{
|
||||
return strcmp(elem->meta.type, "enum") == 0;
|
||||
}
|
||||
|
||||
int islist(const struct cfgelem *elem)
|
||||
{
|
||||
return strcmp(elem->meta.type, "list") == 0;
|
||||
}
|
||||
|
||||
int minimum(const struct cfgelem *elem)
|
||||
{
|
||||
switch (elem->multiplicity) {
|
||||
case 0: /* special case, treat as-if 1 */
|
||||
case 1: /* required if there is no default */
|
||||
if (isgroup(elem))
|
||||
return 0;
|
||||
return (!elem->value);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int maximum(const struct cfgelem *elem)
|
||||
{
|
||||
switch (elem->multiplicity) {
|
||||
case INT_MAX:
|
||||
return 0;
|
||||
case 0:
|
||||
case 1:
|
||||
return 1;
|
||||
default:
|
||||
return elem->multiplicity;
|
||||
}
|
||||
}
|
||||
|
||||
int haschildren(const struct cfgelem *elem)
|
||||
{
|
||||
int cnt = 0;
|
||||
if (elem->children != NULL) {
|
||||
for (const struct cfgelem *e = elem->children; e->name; e++) {
|
||||
cnt += !(ismoved(e) || isdeprecated(e));
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int hasattributes(const struct cfgelem *elem)
|
||||
{
|
||||
int cnt = 0;
|
||||
if (elem->attributes != NULL) {
|
||||
for (const struct cfgelem *e = elem->attributes; e->name; e++) {
|
||||
cnt += !(ismoved(e) || isdeprecated(e));
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* remove element aliases "s/\|.*$//" */
|
||||
static int sanitize_names(struct cfgelem *elem)
|
||||
{
|
||||
char *end;
|
||||
if (!elem->name || ismoved(elem) || isdeprecated(elem))
|
||||
return 0;
|
||||
if ((end = strchr(elem->name, '|'))) {
|
||||
assert(!elem->meta.name);
|
||||
size_t len = (uintptr_t)end - (uintptr_t)elem->name;
|
||||
if (!(elem->meta.name = ddsrt_malloc(len + 1)))
|
||||
return -1;
|
||||
memcpy(elem->meta.name, elem->name, len);
|
||||
elem->meta.name[len] = '\0';
|
||||
}
|
||||
if (elem->children) {
|
||||
for (struct cfgelem *ce = elem->children; ce->name; ce++) {
|
||||
if (sanitize_names(ce) != 0)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (elem->attributes) {
|
||||
for (struct cfgelem *ce = elem->attributes; ce->name; ce++) {
|
||||
if (sanitize_names(ce) != 0)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generate_enum_pattern(struct cfgelem *elem)
|
||||
{
|
||||
char *pat;
|
||||
const char **vals;
|
||||
size_t cnt = 0, len = 0, pos = 0, size = 0;
|
||||
assert(!elem->meta.pattern);
|
||||
assert(elem->meta.values);
|
||||
for (vals = elem->meta.values; *vals; vals++) {
|
||||
cnt++;
|
||||
size += strlen(*vals) + 2;
|
||||
}
|
||||
size += (cnt - 1) + 2 + 1;
|
||||
if (!(pat = ddsrt_malloc(size)))
|
||||
return -1;
|
||||
pat[pos++] = '(';
|
||||
for (vals = elem->meta.values; *vals; vals++) {
|
||||
if (vals != elem->meta.values)
|
||||
pat[pos++] = '|';
|
||||
pat[pos++] = '"';
|
||||
len = strlen(*vals);
|
||||
assert(pos < size - len);
|
||||
memcpy(pat+pos, *vals, len);
|
||||
pos += len;
|
||||
pat[pos++] = '"';
|
||||
}
|
||||
pat[pos++] = ')';
|
||||
pat[pos++] = '\0';
|
||||
assert(pos == size);
|
||||
elem->meta.pattern = pat;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generate_list_pattern(struct cfgelem *elem)
|
||||
{
|
||||
char *pat = NULL, *lst;
|
||||
const char **vals, *val = (elem->value ? elem->value : "");
|
||||
size_t cnt = 0, len = 0, pos = 0, size = 0;
|
||||
assert(elem->meta.values != NULL);
|
||||
for (vals = elem->meta.values; *vals != NULL; vals++) {
|
||||
cnt++;
|
||||
size += strlen(*vals);
|
||||
}
|
||||
size += (cnt - 1) + 2 + 1;
|
||||
if (!(lst = ddsrt_malloc(size)))
|
||||
return -1;
|
||||
lst[pos++] = '(';
|
||||
for (vals = elem->meta.values; *vals != NULL; vals++) {
|
||||
if (vals != elem->meta.values)
|
||||
lst[pos++] = '|';
|
||||
len = strlen(*vals);
|
||||
assert(pos < size - len);
|
||||
memcpy(lst+pos, *vals, len);
|
||||
pos += len;
|
||||
}
|
||||
lst[pos++] = ')';
|
||||
lst[pos++] = '\0';
|
||||
assert(pos == size);
|
||||
if (strlen(val) != 0)
|
||||
ddsrt_asprintf(&pat, "%s|(%s(,%s)*)", val, lst, lst);
|
||||
else
|
||||
ddsrt_asprintf(&pat, "(%s(,%s)*)|", lst, lst);
|
||||
free(lst);
|
||||
elem->meta.pattern = pat;
|
||||
return pat ? 0 : -1;
|
||||
}
|
||||
|
||||
/* generate patterns for enum and list elements */
|
||||
int makepattern(struct cfgelem *elem, const struct cfgunit *units)
|
||||
{
|
||||
(void)units;
|
||||
if (!elem->meta.pattern) {
|
||||
if (isenum(elem))
|
||||
return generate_enum_pattern(elem);
|
||||
if (islist(elem))
|
||||
return generate_list_pattern(elem);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int compare(struct cfgelem *a, struct cfgelem *b)
|
||||
{
|
||||
/* shallow compare (just pointer) is good enough for now */
|
||||
if (a->children != b->children)
|
||||
return -1;
|
||||
if (a->attributes != b->attributes)
|
||||
return -1;
|
||||
if (strcmp(a->meta.type, b->meta.type) != 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int duplicate(struct cfgelem *curs, struct cfgelem *elem)
|
||||
{
|
||||
if (curs == elem)
|
||||
return 0;
|
||||
if (curs->meta.flags & FLAG_DUPLICATE)
|
||||
return 0;
|
||||
if (elem->meta.flags & FLAG_DUPLICATE)
|
||||
return 0;
|
||||
if (strcmp(curs->name, elem->name) != 0)
|
||||
return 0;
|
||||
if (compare(curs, elem) != 0)
|
||||
return -1;
|
||||
curs->meta.flags |= FLAG_DUPLICATE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int expand(struct cfgelem *curs, struct cfgelem *elem)
|
||||
{
|
||||
if (curs == elem)
|
||||
return 0;
|
||||
if (strcmp(curs->name, elem->name) != 0)
|
||||
return 0;
|
||||
curs->meta.flags &= ~FLAG_DUPLICATE;
|
||||
curs->meta.flags |= FLAG_EXPAND;
|
||||
elem->meta.flags |= FLAG_EXPAND;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int walk(
|
||||
struct cfgelem *root,
|
||||
struct cfgelem *elem,
|
||||
int(*func)(struct cfgelem *, struct cfgelem *))
|
||||
{
|
||||
struct cfgelem *ce;
|
||||
|
||||
if (func(root, elem) == -1)
|
||||
return -1;
|
||||
|
||||
ce = firstelem(root->children);
|
||||
while (ce) {
|
||||
if (walk(ce, elem, func) == -1)
|
||||
return -1;
|
||||
ce = nextelem(root->children, ce);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mark(struct cfgelem *elem, struct cfgelem *root)
|
||||
{
|
||||
struct cfgelem *ce;
|
||||
if (walk(root, elem, &duplicate) != 0)
|
||||
walk(root, elem, &expand);
|
||||
ce = firstelem(elem->children);
|
||||
while (ce) {
|
||||
mark(ce, root);
|
||||
ce = nextelem(elem->children, ce);
|
||||
}
|
||||
}
|
||||
|
||||
#define BLOCK (1024)
|
||||
|
||||
static int
|
||||
format(
|
||||
char **strp,
|
||||
size_t *lenp,
|
||||
size_t *posp,
|
||||
const char *src,
|
||||
const char *(*xlat)(const char *, const char **))
|
||||
{
|
||||
char buf[2] = { '\0', '\0' };
|
||||
size_t nonspc, pos;
|
||||
const char *alt, *end, *ptr, *sub;
|
||||
|
||||
nonspc = pos = *posp;
|
||||
for (ptr = src, end = ptr; *ptr; ptr = end) {
|
||||
if (xlat && (alt = xlat(ptr, &end))) {
|
||||
sub = alt;
|
||||
} else {
|
||||
buf[0] = *ptr;
|
||||
end = ptr + 1;
|
||||
sub = (const char *)buf;
|
||||
}
|
||||
for (size_t cnt = 0; sub[cnt]; cnt++) {
|
||||
if (pos == *lenp) {
|
||||
char *str;
|
||||
size_t len = *lenp + BLOCK;
|
||||
if (!(str = ddsrt_realloc(*strp, (len + 1) * sizeof(char)))) {
|
||||
if (*strp)
|
||||
ddsrt_free(*strp);
|
||||
return -1;
|
||||
}
|
||||
*strp = str;
|
||||
*lenp = len;
|
||||
}
|
||||
(*strp)[pos++] = sub[cnt];
|
||||
}
|
||||
}
|
||||
if (nonspc != *posp && nonspc < pos)
|
||||
pos = ++nonspc;
|
||||
(*strp)[pos] = '\0';
|
||||
(*posp) = pos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define DFLTFMT "<p>The default value is: \"%s\".</p>"
|
||||
|
||||
int makedescription(
|
||||
struct cfgelem *elem,
|
||||
const struct cfgunit *units,
|
||||
const char *(*xlat)(const char *, const char **))
|
||||
{
|
||||
if (elem->description) {
|
||||
char *src = NULL;
|
||||
char *dest = elem->meta.description;
|
||||
const char *dflt = "";
|
||||
size_t len = 0, pos = 0;
|
||||
const struct cfgunit *unit;
|
||||
|
||||
if (isgroup(elem)) {
|
||||
src = ddsrt_strdup(elem->description);
|
||||
} else {
|
||||
if (elem->value)
|
||||
dflt = elem->value;
|
||||
if (elem->meta.unit) {
|
||||
unit = findunit(units, elem->meta.unit);
|
||||
assert(unit);
|
||||
ddsrt_asprintf(
|
||||
&src, "%s\n%s\n"DFLTFMT, elem->description, unit->description, dflt);
|
||||
} else {
|
||||
ddsrt_asprintf(
|
||||
&src, "%s\n"DFLTFMT, elem->description, dflt);
|
||||
}
|
||||
}
|
||||
|
||||
if (!src)
|
||||
return -1;
|
||||
format(&dest, &len, &pos, src, xlat);
|
||||
ddsrt_free(src);
|
||||
if (!dest)
|
||||
return -1;
|
||||
elem->meta.description = dest;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init(struct cfgelem *root)
|
||||
{
|
||||
if (sanitize_names(root) != 0)
|
||||
return -1;
|
||||
mark(root, root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fini(struct cfgelem *elem)
|
||||
{
|
||||
struct cfgelem *ce;
|
||||
if (!elem)
|
||||
return;
|
||||
if (elem->meta.name)
|
||||
ddsrt_free(elem->meta.name);
|
||||
if (elem->meta.title)
|
||||
ddsrt_free(elem->meta.title);
|
||||
if (elem->meta.pattern)
|
||||
ddsrt_free(elem->meta.pattern);
|
||||
if (elem->meta.description)
|
||||
ddsrt_free(elem->meta.description);
|
||||
elem->meta.name = NULL;
|
||||
elem->meta.title = NULL;
|
||||
elem->meta.pattern = NULL;
|
||||
elem->meta.description = NULL;
|
||||
ce = firstelem(elem->children);
|
||||
while (ce) {
|
||||
fini(ce);
|
||||
ce = nextelem(elem->children, ce);
|
||||
}
|
||||
ce = firstelem(elem->attributes);
|
||||
while (ce) {
|
||||
fini(ce);
|
||||
ce = nextelem(elem->attributes, ce);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int opt;
|
||||
int code = EXIT_FAILURE;
|
||||
FILE *out = NULL;
|
||||
const char *file = "-";
|
||||
enum { rnc, xsd, md } format = rnc;
|
||||
|
||||
while ((opt = getopt(argc, argv, "f:o:h")) != -1) {
|
||||
switch (opt) {
|
||||
case 'f':
|
||||
if (strcmp(optarg, "rnc") == 0) {
|
||||
format = rnc;
|
||||
} else if (strcmp(optarg, "xsd") == 0) {
|
||||
format = xsd;
|
||||
} else if (strcmp(optarg, "md") == 0) {
|
||||
format = md;
|
||||
} else {
|
||||
fprintf(stderr, "illegal output format: %s\n", optarg);
|
||||
usage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
help(argv[0]);
|
||||
exit(0);
|
||||
break;
|
||||
case 'o':
|
||||
file = (const char *)optarg;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (init(cyclonedds_root_cfgelems) == -1) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
goto exit_failure;
|
||||
}
|
||||
|
||||
DDSRT_WARNING_MSVC_OFF(4996)
|
||||
if (strcmp(file, "-") == 0) {
|
||||
out = stdout;
|
||||
} else if ((out = fopen(file, "wb")) == NULL) {
|
||||
fprintf(stderr, "cannot open %s for writing\n", file);
|
||||
goto exit_failure;
|
||||
}
|
||||
DDSRT_WARNING_MSVC_ON(4996)
|
||||
|
||||
memset(spaces, ' ', sizeof(spaces));
|
||||
|
||||
switch (format) {
|
||||
case rnc:
|
||||
if (printrnc(out, cyclonedds_root_cfgelems, cfgunits) == 0)
|
||||
code = EXIT_SUCCESS;
|
||||
break;
|
||||
case xsd:
|
||||
if (printxsd(out, cyclonedds_root_cfgelems, cfgunits) == 0)
|
||||
code = EXIT_SUCCESS;
|
||||
break;
|
||||
case md:
|
||||
if (printmd(out, cyclonedds_root_cfgelems, cfgunits) == 0)
|
||||
code = EXIT_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
exit_failure:
|
||||
fini(cyclonedds_root_cfgelems);
|
||||
if (out != NULL && out != stdout)
|
||||
fclose(out);
|
||||
|
||||
return code;
|
||||
}
|
80
src/tools/ddsconf/ddsconf.h
Normal file
80
src/tools/ddsconf/ddsconf.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright(c) 2020 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct cfgunit {
|
||||
const char *name;
|
||||
const char *description;
|
||||
const char *pattern;
|
||||
};
|
||||
|
||||
#define FLAG_DUPLICATE (1u<<0) /* exact same element exists, print other */
|
||||
#define FLAG_EXPAND (1u<<1) /* element with same name exists, expand */
|
||||
#define FLAG_REFERENCE (1u<<2)
|
||||
#define FLAG_NOMIN (1u<<3)
|
||||
#define FLAG_NOMAX (1u<<4)
|
||||
|
||||
struct cfgmeta {
|
||||
char *name;
|
||||
char *title;
|
||||
char *pattern;
|
||||
char *description;
|
||||
unsigned int flags;
|
||||
const char *type;
|
||||
const char *unit;
|
||||
const char *range;
|
||||
const char **values;
|
||||
};
|
||||
|
||||
struct cfgelem {
|
||||
const char *name;
|
||||
struct cfgelem *children;
|
||||
struct cfgelem *attributes;
|
||||
int multiplicity;
|
||||
const char *value;
|
||||
const char *description;
|
||||
struct cfgmeta meta;
|
||||
};
|
||||
|
||||
int makedescription(
|
||||
struct cfgelem *elem,
|
||||
const struct cfgunit *units,
|
||||
const char *(*xlat)(const char *, const char **));
|
||||
int makepattern(
|
||||
struct cfgelem *elem,
|
||||
const struct cfgunit *units);
|
||||
|
||||
const char *schema(void);
|
||||
const char *url(void);
|
||||
const char *name(const struct cfgelem *elem);
|
||||
int ismoved(const struct cfgelem *elem);
|
||||
int isdeprecated(const struct cfgelem *elem);
|
||||
int isgroup(const struct cfgelem *elem);
|
||||
int isnop(const struct cfgelem *elem);
|
||||
int isbool(const struct cfgelem *elem);
|
||||
int isint(const struct cfgelem *elem);
|
||||
int isstring(const struct cfgelem *elem);
|
||||
int isenum(const struct cfgelem *elem);
|
||||
int islist(const struct cfgelem *elem);
|
||||
int minimum(const struct cfgelem *elem);
|
||||
int maximum(const struct cfgelem *elem);
|
||||
int haschildren(const struct cfgelem *elem);
|
||||
int hasattributes(const struct cfgelem *elem);
|
||||
struct cfgelem *firstelem(const struct cfgelem *list);
|
||||
struct cfgelem *nextelem(const struct cfgelem *list, const struct cfgelem *elem);
|
||||
const struct cfgunit *findunit(const struct cfgunit *units, const char *name);
|
||||
void print(FILE *out, unsigned int cols, const char *fmt, ...);
|
||||
int printrnc(FILE *out, struct cfgelem *elem, const struct cfgunit *units);
|
||||
int printxsd(FILE *out, struct cfgelem *elem, const struct cfgunit *units);
|
||||
int printmd(FILE *out, struct cfgelem *elem, const struct cfgunit *units);
|
290
src/tools/ddsconf/md.c
Normal file
290
src/tools/ddsconf/md.c
Normal file
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* Copyright(c) 2020 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ddsconf.h"
|
||||
|
||||
#include "dds/ddsrt/heap.h"
|
||||
|
||||
static const char *xlatmd(const char *str, const char **end)
|
||||
{
|
||||
static struct { const char *search; const char *replace; } tr[] = {
|
||||
{ "<p>", "" }, { "</p>", "" },
|
||||
{ "<b>", "" }, { "</b>", "" },
|
||||
{ "<i>", "" }, { "</i>", "" },
|
||||
{ "<li>", " * " }, { "</li>", "\n" },
|
||||
{ "<ul>", "" }, { "</ul>", "" },
|
||||
{ "<sup>", "^" }, { "</sup>", "" },
|
||||
{ "*", "\\*" }, { "_", "\\_" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* replace </p>\s*<p> by double newline */
|
||||
if (strncmp(str, "</p>", 4) == 0) {
|
||||
const char *ptr = str + 4;
|
||||
while (*ptr == '\n' || *ptr == ' ') ptr++;
|
||||
if (strncmp(ptr, "<p>", 3) == 0) {
|
||||
*end = ptr;
|
||||
return "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t cnt = 0; tr[cnt].search; cnt++) {
|
||||
size_t len = strlen(tr[cnt].search);
|
||||
if (strncmp(str, tr[cnt].search, len) == 0) {
|
||||
*end = str + len;
|
||||
return tr[cnt].replace;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char hashes[16];
|
||||
|
||||
static void printhead(
|
||||
FILE *out,
|
||||
unsigned int level,
|
||||
unsigned int flags,
|
||||
struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
(void)level;
|
||||
(void)flags;
|
||||
(void)units;
|
||||
assert(level < sizeof(hashes));
|
||||
hashes[level+1] = '\0';
|
||||
fprintf(out, "%s %s\n", hashes, elem->meta.title);
|
||||
hashes[level+1] = '#';
|
||||
}
|
||||
|
||||
static void printlink(
|
||||
FILE *out,
|
||||
unsigned int level,
|
||||
unsigned int flags,
|
||||
struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
int chr;
|
||||
(void)level;
|
||||
(void)flags;
|
||||
(void)units;
|
||||
assert(elem->meta.title);
|
||||
fputc('#', out);
|
||||
for (const char *ptr = elem->meta.title; *ptr; ptr++) {
|
||||
chr = (unsigned char)*ptr;
|
||||
if (chr >= 'A' && chr <= 'Z')
|
||||
chr = (chr - 'A') + 'a';
|
||||
if (chr >= 'a' && chr <= 'z')
|
||||
fputc(chr, out);
|
||||
}
|
||||
}
|
||||
|
||||
static void printtype(
|
||||
FILE *out,
|
||||
unsigned int level,
|
||||
unsigned int flags,
|
||||
struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
(void)level;
|
||||
(void)flags;
|
||||
(void)units;
|
||||
assert(!isgroup(elem));
|
||||
if (isbool(elem)) {
|
||||
fprintf(out, "Boolean\n");
|
||||
} else if (islist(elem)) {
|
||||
assert(elem->meta.values);
|
||||
fprintf(out, "One of:\n");
|
||||
if (elem->value && strlen(elem->value))
|
||||
fprintf(out, "* Keyword: %s\n", elem->value);
|
||||
fprintf(out, "* Comma-separated list of: ");
|
||||
for (const char **v = elem->meta.values; *v; v++) {
|
||||
fprintf(out, "%s%s", v == elem->meta.values ? "" : ", ", *v);
|
||||
}
|
||||
fprintf(out, "\n");
|
||||
if (!elem->value || !strlen(elem->value))
|
||||
fprintf(out, "* Or empty\n");
|
||||
} else if (isenum(elem)) {
|
||||
assert(elem->meta.values);
|
||||
fprintf(out, "One of: ");
|
||||
for (const char **v = elem->meta.values; *v; v++) {
|
||||
fprintf(out, "%s%s", v == elem->meta.values ? "" : ", ", *v);
|
||||
}
|
||||
fprintf(out, "\n");
|
||||
} else if (isint(elem)) {
|
||||
fprintf(out, "Integer\n");
|
||||
} else if (elem->meta.unit) {
|
||||
fprintf(out, "Number-with-unit\n");
|
||||
} else if (isstring(elem)) {
|
||||
fprintf(out, "Text\n");
|
||||
}
|
||||
}
|
||||
|
||||
#define FLAG_LF (1u<<0)
|
||||
|
||||
static void printattr(
|
||||
FILE *out,
|
||||
unsigned int level,
|
||||
unsigned int flags,
|
||||
struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
if (flags & FLAG_LF)
|
||||
fputs("\n\n\n", out);
|
||||
printhead(out, level, flags, elem, units);
|
||||
printtype(out, level, flags, elem, units);
|
||||
fputs("\n", out);
|
||||
if (elem->description)
|
||||
fputs(elem->meta.description, out);
|
||||
}
|
||||
|
||||
static void printelem(
|
||||
FILE *out,
|
||||
unsigned int level,
|
||||
unsigned int flags,
|
||||
struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
if (flags & FLAG_LF)
|
||||
fprintf(out, "\n\n\n");
|
||||
printhead(out, level, flags, elem, units);
|
||||
flags &= ~FLAG_LF;
|
||||
if (hasattributes(elem)) {
|
||||
int cnt = 0;
|
||||
const char *sep = "Attributes: ";
|
||||
struct cfgelem *ce = firstelem(elem->attributes);
|
||||
while (ce) {
|
||||
if (!isnop(ce)) {
|
||||
fprintf(out, "%s[%s](", sep, name(ce));
|
||||
printlink(out, level, flags, ce, units);
|
||||
fprintf(out, ")");
|
||||
sep = ", ";
|
||||
cnt++;
|
||||
}
|
||||
ce = nextelem(elem->attributes, ce);
|
||||
}
|
||||
if (cnt != 0) {
|
||||
fprintf(out, "\n");
|
||||
flags |= FLAG_LF;
|
||||
}
|
||||
}
|
||||
if (haschildren(elem)) {
|
||||
const char *sep = "Children: ";
|
||||
struct cfgelem *ce = firstelem(elem->children);
|
||||
while (ce) {
|
||||
fprintf(out, "%s[%s](", sep, name(ce));
|
||||
printlink(out, level, flags, ce, units);
|
||||
fprintf(out, ")");
|
||||
sep = ", ";
|
||||
ce = nextelem(elem->children, ce);
|
||||
}
|
||||
fprintf(out, "\n");
|
||||
flags |= FLAG_LF;
|
||||
} else if (!isgroup(elem)) {
|
||||
if (flags & FLAG_LF)
|
||||
fprintf(out, "\n");
|
||||
printtype(out, level+1, flags, elem, units);
|
||||
flags |= FLAG_LF;
|
||||
}
|
||||
if (elem->description) {
|
||||
if (flags & FLAG_LF)
|
||||
fprintf(out, "\n");
|
||||
fputs(elem->meta.description, out);
|
||||
}
|
||||
if (hasattributes(elem)) {
|
||||
struct cfgelem *ce = firstelem(elem->attributes);
|
||||
while (ce) {
|
||||
if (!isnop(ce))
|
||||
printattr(out, level, flags, ce, units);
|
||||
ce = nextelem(elem->attributes, ce);
|
||||
}
|
||||
}
|
||||
if (isgroup(elem)) {
|
||||
struct cfgelem *ce = firstelem(elem->children);
|
||||
while (ce) {
|
||||
printelem(out, level+1, flags, ce, units);
|
||||
ce = nextelem(elem->children, ce);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int maketitles(
|
||||
struct cfgelem *elem, int attr, const char *path, size_t pathlen)
|
||||
{
|
||||
char *str;
|
||||
size_t namelen = 0, len = 0;
|
||||
struct cfgelem *ce;
|
||||
if (ismoved(elem) || isdeprecated(elem) || elem->meta.title)
|
||||
return 0;
|
||||
namelen = strlen(name(elem));
|
||||
if (!(str = ddsrt_malloc(pathlen + namelen + (attr ? 4 : 2))))
|
||||
return -1;
|
||||
memcpy(str, path, pathlen);
|
||||
len += pathlen;
|
||||
if (attr) {
|
||||
str[len++] = '[';
|
||||
str[len++] = '@';
|
||||
} else {
|
||||
str[len++] = '/';
|
||||
}
|
||||
memcpy(str+len, name(elem), namelen);
|
||||
len += namelen;
|
||||
if (attr) {
|
||||
str[len++] = ']';
|
||||
}
|
||||
str[len] = '\0';
|
||||
elem->meta.title = str;
|
||||
ce = firstelem(elem->children);
|
||||
while (ce) {
|
||||
if (maketitles(ce, 0, str, len) == -1)
|
||||
return -1;
|
||||
ce = nextelem(elem->children, ce);
|
||||
}
|
||||
ce = firstelem(elem->attributes);
|
||||
while (ce) {
|
||||
if (maketitles(ce, 1, str, len) == -1)
|
||||
return -1;
|
||||
ce = nextelem(elem->attributes, ce);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int initmd(struct cfgelem *elem, const struct cfgunit *units)
|
||||
{
|
||||
if (ismoved(elem) || isdeprecated(elem))
|
||||
return 0;
|
||||
if (makedescription(elem, units, xlatmd) == -1)
|
||||
return -1;
|
||||
for (struct cfgelem *ce = elem->children; ce && ce->name; ce++) {
|
||||
if (initmd(ce, units) == -1)
|
||||
return -1;
|
||||
}
|
||||
for (struct cfgelem *ce = elem->attributes; ce && ce->name; ce++) {
|
||||
if (initmd(ce, units) == - 1)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int printmd(FILE *out, struct cfgelem *elem, const struct cfgunit *units)
|
||||
{
|
||||
if (initmd(elem, units) == -1)
|
||||
return -1;
|
||||
if (maketitles(elem, 0, "/", 1) == -1)
|
||||
return -1;
|
||||
memset(hashes, '#', sizeof(hashes));
|
||||
printelem(out, 0u, 0u, elem, units);
|
||||
return 0;
|
||||
}
|
163
src/tools/ddsconf/rnc.c
Normal file
163
src/tools/ddsconf/rnc.c
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright(c) 2020 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ddsconf.h"
|
||||
#include "dds/ddsrt/heap.h"
|
||||
|
||||
#define FLAG_AMP (1u<<0)
|
||||
#define FLAG_ROOT (1u<<1)
|
||||
|
||||
static const char *amp[] = { "", "& " };
|
||||
static const char docfmt[] = "%s[ a:documentation [ xml:lang=\"en\" \"\"\"\n";
|
||||
static const char elemfmt[] = "%selement %s {\n";
|
||||
static const char attrfmt[] = "%sattribute %s {\n";
|
||||
|
||||
static const char *suffix(const struct cfgelem *elem)
|
||||
{
|
||||
if (minimum(elem) == 0)
|
||||
return maximum(elem) == 1 ? "?" : "*";
|
||||
else
|
||||
return maximum(elem) == 1 ? "" : "+";
|
||||
}
|
||||
|
||||
static void
|
||||
printtype(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
(void)flags;
|
||||
(void)units;
|
||||
if (strcmp(elem->meta.type, "string") == 0) {
|
||||
if (elem->meta.unit != NULL) {
|
||||
print(out, cols, "%s%s\n", amp[(flags & FLAG_AMP)], elem->meta.unit);
|
||||
} else {
|
||||
print(out, cols, "%stext\n", amp[(flags & FLAG_AMP)]);
|
||||
}
|
||||
} else if (strcmp(elem->meta.type, "bool") == 0) {
|
||||
print(out, cols, "%sxsd:boolean\n", amp[(flags & FLAG_AMP)]);
|
||||
} else if (strcmp(elem->meta.type, "int") == 0) {
|
||||
print(out, cols, "%sxsd:integer\n", amp[(flags & FLAG_AMP)]);
|
||||
} else if (strcmp(elem->meta.type, "enum") == 0) {
|
||||
assert(elem->meta.pattern != NULL);
|
||||
print(out, cols, "%s%s\n", amp[(flags & FLAG_AMP)], elem->meta.pattern);
|
||||
} else if (strcmp(elem->meta.type, "list") == 0) {
|
||||
assert(elem->meta.pattern != NULL);
|
||||
print(out, cols, "%sxsd:token { pattern = \"%s\" }\n", amp[(flags & FLAG_AMP)], elem->meta.pattern);
|
||||
} else {
|
||||
print(out, cols, "%sempty\n", amp[(flags & FLAG_AMP)]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
printattr(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
assert(!ismoved(elem) && !isdeprecated(elem));
|
||||
if (elem->description != NULL) {
|
||||
print(out, cols, docfmt, amp[(flags & FLAG_AMP)]);
|
||||
fputs(elem->meta.description, out);
|
||||
print(out, 0, "\"\"\" ] ]\n");
|
||||
flags &= ~FLAG_AMP;
|
||||
}
|
||||
print(out, cols, attrfmt, amp[(flags & FLAG_AMP)], name(elem));
|
||||
printtype(out, cols+2, flags, elem, units);
|
||||
print(out, cols, "}%s\n", suffix(elem));
|
||||
}
|
||||
|
||||
static void printelem(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
struct cfgelem *ce;
|
||||
|
||||
assert(!ismoved(elem) && !isdeprecated(elem));
|
||||
|
||||
if (elem->description != NULL) {
|
||||
print(out, cols, docfmt, amp[(flags & FLAG_AMP)]);
|
||||
fputs(elem->meta.description, out);
|
||||
print(out, 0, "\"\"\" ] ]\n");
|
||||
flags &= ~FLAG_AMP;
|
||||
}
|
||||
print(out, cols, elemfmt, amp[(flags & FLAG_AMP)], name(elem));
|
||||
flags &= ~FLAG_AMP;
|
||||
ce = firstelem(elem->attributes);
|
||||
while (ce) {
|
||||
if (!isnop(ce)) {
|
||||
printattr(out, cols+2, flags & ~FLAG_ROOT, ce, units);
|
||||
flags |= FLAG_AMP;
|
||||
}
|
||||
ce = nextelem(elem->attributes, ce);
|
||||
}
|
||||
if (haschildren(elem)) {
|
||||
ce = firstelem(elem->children);
|
||||
while (ce) {
|
||||
printelem(out, cols+2, flags & ~FLAG_ROOT, ce, units);
|
||||
flags |= FLAG_AMP;
|
||||
ce = nextelem(elem->children, ce);
|
||||
}
|
||||
} else if (!hasattributes(elem) ||
|
||||
!(isgroup(elem) || (isstring(elem) && !elem->meta.unit)))
|
||||
{
|
||||
printtype(out, cols+2, flags, elem, units);
|
||||
}
|
||||
print(out, cols, "}%s\n", (flags & FLAG_ROOT) ? "" : suffix(elem));
|
||||
}
|
||||
|
||||
static int initrnc(struct cfgelem *elem, const struct cfgunit *units)
|
||||
{
|
||||
if (ismoved(elem) || isdeprecated(elem))
|
||||
return 0;
|
||||
if (makedescription(elem, units, 0) == -1)
|
||||
return -1;
|
||||
if (makepattern(elem, units) == -1)
|
||||
return -1;
|
||||
for (struct cfgelem *ce = elem->children; ce && ce->name; ce++) {
|
||||
if (initrnc(ce, units) == -1)
|
||||
return -1;
|
||||
}
|
||||
for (struct cfgelem *ce = elem->attributes; ce && ce->name; ce++) {
|
||||
if (initrnc(ce, units) == -1)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int printrnc(FILE *out, struct cfgelem *elem, const struct cfgunit *units)
|
||||
{
|
||||
if (initrnc(elem, units) == -1)
|
||||
return -1;
|
||||
print(out, 0, "default namespace = \"%s\"\n", url());
|
||||
print(out, 0, "namespace a = \"http://relaxng.org/ns/compatibility/annotations/1.0\"\n");
|
||||
print(out, 0, "grammar {\n");
|
||||
print(out, 0, " start =\n");
|
||||
printelem(out, 2, FLAG_ROOT, elem, units);
|
||||
for(const struct cfgunit *cu = units; cu->name; cu++) {
|
||||
static const char *fmt = " %s = xsd:token { pattern = \"%s\" }\n";
|
||||
print(out, 0, fmt, cu->name, cu->pattern);
|
||||
}
|
||||
print(out, 0, "}");
|
||||
return 0;
|
||||
}
|
421
src/tools/ddsconf/xsd.c
Normal file
421
src/tools/ddsconf/xsd.c
Normal file
|
@ -0,0 +1,421 @@
|
|||
/*
|
||||
* Copyright(c) 2020 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ddsconf.h"
|
||||
|
||||
static const char *xlatxsd(const char *str, const char **end)
|
||||
{
|
||||
const char *sub;
|
||||
|
||||
if (*str == '&') /* ampersand */
|
||||
sub = "&";
|
||||
else if (*str == '<') /* less-than sign */
|
||||
sub = "<";
|
||||
else if (*str == '>') /* greater-than sign */
|
||||
sub =">";
|
||||
else
|
||||
return NULL;
|
||||
|
||||
*end = ++str;
|
||||
return sub;
|
||||
}
|
||||
|
||||
static const char *isbuiltintype(const struct cfgelem *elem)
|
||||
{
|
||||
if (strcmp(elem->meta.type, "bool") == 0) {
|
||||
return "boolean";
|
||||
} else if (strcmp(elem->meta.type, "int") == 0) {
|
||||
return "integer";
|
||||
} else if (strcmp(elem->meta.type, "string") == 0) {
|
||||
return "string";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int iscomplextype(const struct cfgelem *elem)
|
||||
{
|
||||
return haschildren(elem) != 0 || hasattributes(elem) != 0 || isgroup(elem);
|
||||
}
|
||||
|
||||
static void
|
||||
printdesc(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
(void)flags;
|
||||
(void)units;
|
||||
if (!elem->description)
|
||||
return;
|
||||
assert(elem->meta.description);
|
||||
print(out, cols+0, "<xs:annotation>\n");
|
||||
print(out, cols+2, "<xs:documentation>\n");
|
||||
fputs(elem->meta.description, out);
|
||||
fputs("</xs:documentation>\n", out);
|
||||
print(out, cols+0, "</xs:annotation>\n");
|
||||
}
|
||||
|
||||
static void
|
||||
printenum(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
(void)flags;
|
||||
(void)units;
|
||||
print(out, cols+0, "<xs:simpleType>\n");
|
||||
print(out, cols+2, "<xs:restriction base=\"xs:token\">\n");
|
||||
for(const char **v = elem->meta.values; v && *v; v++) {
|
||||
print(out, cols+4, "<xs:enumeration value=\"%s\"/>\n", *v);
|
||||
}
|
||||
print(out, cols+2, "</xs:restriction>\n");
|
||||
print(out, cols+0, "</xs:simpleType>\n");
|
||||
}
|
||||
|
||||
static void
|
||||
printlist(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
(void)flags;
|
||||
(void)units;
|
||||
print(out, cols+0, "<xs:simpleType>\n");
|
||||
print(out, cols+2, "<xs:restriction base=\"xs:token\">\n");
|
||||
print(out, cols+4, "<xs:pattern value=\"%s\"/>\n", elem->meta.pattern);
|
||||
print(out, cols+2, "</xs:restriction>\n");
|
||||
print(out, cols+0, "</xs:simpleType>\n");
|
||||
}
|
||||
|
||||
static void
|
||||
printattr(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
const char fmt[] = "<xs:attribute name=\"%s\"%s>\n";
|
||||
char type[64], required[32];
|
||||
|
||||
(void)flags;
|
||||
(void)units;
|
||||
assert(elem != NULL);
|
||||
assert(!haschildren(elem));
|
||||
if (ismoved(elem) || isdeprecated(elem) || isnop(elem))
|
||||
return;
|
||||
|
||||
type[0] = '\0';
|
||||
if (elem->meta.unit)
|
||||
snprintf(type, sizeof(type), " type=\"config:%s\"", elem->meta.unit);
|
||||
else if (!isstring(elem))
|
||||
snprintf(type, sizeof(type), " type=\"xs:%s\"", isbuiltintype(elem));
|
||||
|
||||
required[0] = '\0';
|
||||
if (minimum(elem))
|
||||
snprintf(type, sizeof(type), " use=\"required\"");
|
||||
|
||||
print(out, cols, fmt, name(elem), type, required);
|
||||
printdesc(out, cols+2, flags, elem, units);
|
||||
print(out, cols, "</xs:attribute>\n");
|
||||
}
|
||||
|
||||
static void printelem(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units);
|
||||
|
||||
static void
|
||||
printref(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
if (elem->meta.flags & FLAG_EXPAND) {
|
||||
printelem(out, cols, flags | FLAG_REFERENCE, elem, units);
|
||||
} else {
|
||||
char minattr[32] = "";
|
||||
char maxattr[32] = "";
|
||||
const char fmt[] = "<xs:element %s%sref=\"%s:%s\"/>\n";
|
||||
if (!(flags & FLAG_NOMIN) && minimum(elem) != 1)
|
||||
snprintf(minattr, sizeof(minattr), "minOccurs=\"%d\" ", minimum(elem));
|
||||
if (!(flags & FLAG_NOMAX) &&maximum(elem) == 0)
|
||||
snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"unbounded\" ");
|
||||
else if (!(FLAG_NOMAX) && maximum(elem) != 1)
|
||||
snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"%d\" ", maximum(elem));
|
||||
print(out, cols, fmt, minattr, maxattr, schema(), name(elem));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
printcomplextype(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
char minattr[32] = "";
|
||||
char maxattr[32] = "";
|
||||
|
||||
assert(!ismoved(elem) && !isdeprecated(elem));
|
||||
|
||||
if (flags & FLAG_REFERENCE) {
|
||||
if (minimum(elem) != 1)
|
||||
snprintf(minattr, sizeof(minattr), "minOccurs=\"%d\" ", minimum(elem));
|
||||
if (maximum(elem) == 0)
|
||||
snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"unbounded\" ");
|
||||
else if (maximum(elem) != 1)
|
||||
snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"%d\" ", maximum(elem));
|
||||
}
|
||||
|
||||
print(out, cols, "<xs:element %s%sname=\"%s\">\n", minattr, maxattr, name(elem));
|
||||
printdesc(out, cols+2, flags, elem, units);
|
||||
|
||||
flags &= ~(FLAG_NOMIN | FLAG_NOMAX);
|
||||
if (!haschildren(elem) && !hasattributes(elem)) {
|
||||
/* special case, group has only deprecated children and/or attributes */
|
||||
print(out, cols+2, "<xs:complexType/>\n");
|
||||
} else {
|
||||
int cnt;
|
||||
unsigned int ofst = 0;
|
||||
print(out, cols+2, "<xs:complexType>\n");
|
||||
|
||||
if ((cnt = haschildren(elem))) {
|
||||
const char *cont = NULL;
|
||||
struct cfgelem *ce;
|
||||
int min[2], max[2], eq[2] = { 1, 1 };
|
||||
assert(isgroup(elem));
|
||||
|
||||
minattr[0] = '\0';
|
||||
maxattr[0] = '\0';
|
||||
|
||||
if (cnt == 1) {
|
||||
/* minOccurs and maxOccurs placed in element */
|
||||
cont = "sequence";
|
||||
} else {
|
||||
assert(cnt > 1);
|
||||
ce = firstelem(elem->children);
|
||||
min[0] = min[1] = minimum(ce);
|
||||
max[0] = max[1] = maximum(ce);
|
||||
assert(min[1] <= max[1] || max[1] == 0);
|
||||
ce = nextelem(elem->children, ce);
|
||||
assert(ce);
|
||||
while (ce) {
|
||||
min[1] = minimum(ce);
|
||||
max[1] = maximum(ce);
|
||||
assert(min[1] <= max[1] || max[1] == 0);
|
||||
if (min[1] != min[0]) {
|
||||
eq[0] = 0;
|
||||
if (min[1] < min[0])
|
||||
min[0] = min[1];
|
||||
}
|
||||
if (max[1] != max[0]) {
|
||||
eq[1] = 0;
|
||||
if ((max[0] != 0 && max[1] > max[0]) || max[1] == 0)
|
||||
max[0] = max[1];
|
||||
}
|
||||
ce = nextelem(elem->children, ce);
|
||||
}
|
||||
if (min[0] > 1 || max[0] != 1 /* unbounded or >1 */) {
|
||||
cont = "choice";
|
||||
if (eq[0]) {
|
||||
if (min[0] != 1)
|
||||
snprintf(minattr, sizeof(minattr), " minOccurs=\"%d\"", min[0]);
|
||||
flags |= FLAG_NOMIN;
|
||||
}
|
||||
if (eq[1]) {
|
||||
if (max[0] == 0)
|
||||
snprintf(maxattr, sizeof(maxattr), " maxOccurs=\"unbounded\"");
|
||||
else if (max[0] != 1)
|
||||
snprintf(maxattr, sizeof(maxattr), " maxOccurs=\"%d\"", max[0]);
|
||||
flags |= FLAG_NOMAX;
|
||||
}
|
||||
} else {
|
||||
/* any order, each zero or one time */
|
||||
/* minOccurs placed in element maxOccurs always one */
|
||||
cont = "all";
|
||||
}
|
||||
}
|
||||
|
||||
print(out, cols+4, "<xs:%s%s%s>\n", cont, minattr, maxattr);
|
||||
ce = firstelem(elem->children);
|
||||
while (ce) {
|
||||
printref(out, cols+6, flags, ce, units);
|
||||
ce = nextelem(elem->children, ce);
|
||||
}
|
||||
print(out, cols+4, "</xs:%s>\n", cont);
|
||||
flags &= ~(FLAG_NOMIN | FLAG_NOMAX);
|
||||
} else if (!isgroup(elem) && (!isstring(elem) || elem->meta.unit)) {
|
||||
ofst = 4;
|
||||
print(out, cols+4, "<xs:simpleContent>\n");
|
||||
if (isenum(elem) || islist(elem)) {
|
||||
print(out, cols+6, "<xs:restriction base=\"xs:anyType\">\n");
|
||||
if (isenum(elem))
|
||||
printenum(out, cols+8, flags, elem, units);
|
||||
else
|
||||
printlist(out, cols+8, flags, elem, units);
|
||||
} else {
|
||||
const char extfmt[] = "<xs:extension base=\"%s:%s\">\n";
|
||||
if (elem->meta.unit)
|
||||
print(out, cols+6, extfmt, schema(), elem->meta.unit);
|
||||
else
|
||||
print(out, cols+6, extfmt, "xs", isbuiltintype(elem));
|
||||
}
|
||||
}
|
||||
if (hasattributes(elem)) {
|
||||
struct cfgelem *ce;
|
||||
ce = firstelem(elem->attributes);
|
||||
while (ce) {
|
||||
printattr(out, cols+ofst+4, flags, ce, units);
|
||||
ce = nextelem(elem->attributes, ce);
|
||||
}
|
||||
}
|
||||
if (!isgroup(elem) && (!isstring(elem) || elem->meta.unit)) {
|
||||
if (isenum(elem) || islist(elem))
|
||||
print(out, cols+6, "</xs:restriction>\n");
|
||||
else
|
||||
print(out, cols+6, "</xs:extension>\n");
|
||||
print(out, cols+4, "</xs:simpleContent>\n");
|
||||
}
|
||||
print(out, cols+2, "</xs:complexType>\n");
|
||||
}
|
||||
print(out, cols, "</xs:element>\n");
|
||||
}
|
||||
|
||||
static void
|
||||
printsimpletype(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
char min[32] = "";
|
||||
char max[32] = "";
|
||||
const char *type;
|
||||
const char fmt[] = "<xs:element %s%sname=\"%s\">\n";
|
||||
const char builtinfmt[] = "<xs:element %s%sname=\"%s\" type=\"%s:%s\">\n";
|
||||
|
||||
assert(!ismoved(elem) && !isdeprecated(elem));
|
||||
|
||||
if (flags & FLAG_REFERENCE) {
|
||||
if (minimum(elem) != 1)
|
||||
snprintf(min, sizeof(min), "minOccurs=\"%d\" ", minimum(elem));
|
||||
if (maximum(elem) == 0)
|
||||
snprintf(max, sizeof(max), "maxOccurs=\"unbounded\" ");
|
||||
else if (maximum(elem) != 1)
|
||||
snprintf(max, sizeof(max), "maxOccurs=\"%d\" ", maximum(elem));
|
||||
}
|
||||
|
||||
if (!(type = isbuiltintype(elem)))
|
||||
print(out, cols, fmt, min, max, name(elem));
|
||||
else if (elem->meta.unit)
|
||||
print(out, cols, builtinfmt, min, max, name(elem), schema(), elem->meta.unit);
|
||||
else
|
||||
print(out, cols, builtinfmt, min, max, name(elem), "xs", type);
|
||||
printdesc(out, cols+2, flags, elem, units);
|
||||
|
||||
if (isenum(elem))
|
||||
printenum(out, cols+2, flags, elem, units);
|
||||
else if (islist(elem))
|
||||
printlist(out, cols+2, flags, elem, units);
|
||||
|
||||
print(out, cols, "</xs:element>\n");
|
||||
}
|
||||
|
||||
static void
|
||||
printelem(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
struct cfgelem *ce;
|
||||
|
||||
assert(!(ismoved(elem) || isdeprecated(elem)));
|
||||
|
||||
if (!(elem->meta.flags & FLAG_DUPLICATE) && (flags & FLAG_EXPAND)) {
|
||||
if (iscomplextype(elem))
|
||||
printcomplextype(out, cols, flags, elem, units);
|
||||
else
|
||||
printsimpletype(out, cols, flags, elem, units);
|
||||
}
|
||||
|
||||
if (!(flags & FLAG_REFERENCE)) {
|
||||
ce = firstelem(elem->children);
|
||||
while (ce) {
|
||||
if (ce->meta.flags & FLAG_EXPAND)
|
||||
flags &= ~FLAG_EXPAND;
|
||||
else
|
||||
flags |= FLAG_EXPAND;
|
||||
printelem(out, cols, flags, ce, units);
|
||||
ce = nextelem(elem->children, ce);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int initxsd(struct cfgelem *elem, const struct cfgunit *units)
|
||||
{
|
||||
if (ismoved(elem) || isdeprecated(elem))
|
||||
return 0;
|
||||
if (makedescription(elem, units, &xlatxsd) == -1)
|
||||
return -1;
|
||||
if (makepattern(elem, units) == -1)
|
||||
return -1;
|
||||
for (struct cfgelem *ce = elem->children; ce && ce->name; ce++) {
|
||||
if (initxsd(ce, units) == -1)
|
||||
return -1;
|
||||
}
|
||||
for (struct cfgelem *ce = elem->attributes; ce && ce->name; ce++) {
|
||||
if (initxsd(ce, units) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int printxsd(FILE *out, struct cfgelem *elem, const struct cfgunit *units)
|
||||
{
|
||||
if (initxsd(elem, units) == -1)
|
||||
return -1;
|
||||
print(out, 0, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||
print(out, 0, "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" "
|
||||
"elementFormDefault=\"qualified\" targetNamespace=\"%s\" xmlns:%s=\"%s\">\n",
|
||||
url(), schema(), url());
|
||||
printelem(out, 2, FLAG_EXPAND, elem, units);
|
||||
for (const struct cfgunit *cu = units; cu->name; cu++) {
|
||||
print(out, 2, "<xs:simpleType name=\"%s\">\n", cu->name);
|
||||
print(out, 4, "<xs:restriction base=\"xs:token\">\n");
|
||||
print(out, 6, "<xs:pattern value=\"%s\"/>\n", cu->pattern);
|
||||
print(out, 4, "</xs:restriction>\n");
|
||||
print(out, 2, "</xs:simpleType>\n");
|
||||
}
|
||||
print(out, 0, "</xs:schema>");
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue