output: add mustach templating support

Based on José Bollo's mustache C implementation.  This adaptation uses
project specific data structures to avoid overhead with json parsing.

Reference: https://gitlab.com/jobol/mustach.git
Commit: d84608a69033d38c81b8fcff0cb272e225dd5428
Signed-off-by: Sami Kerola <kerolasa@iki.fi>
This commit is contained in:
Sami Kerola 2017-11-09 09:49:26 +00:00
parent fe847bb9b1
commit 7d9a5b5561
No known key found for this signature in database
GPG key ID: A9553245FDE9B739
9 changed files with 741 additions and 6 deletions

1
THANKS
View file

@ -42,3 +42,4 @@ Manuel Hachtkemper
Klaus Slott
Boris Lytochkin
Jeff Bailey
José Bollo

View file

@ -8,6 +8,7 @@ dhcpd-pools \- ISC dhcpd pools usage analysis
.OP \-\-sort nimcptTe
.OP \-\-reverse
.OP \-\-format tHcxXjJ
.OP \-\-mustach template
.OP \-\-output file
.OP \-\-limit nr
.OP \-\-color when
@ -103,6 +104,14 @@ to include ethernet address.
The default format is
.IR @OUTPUT_FORMAT@ .
.TP
\fB\-\-mustach\fR=\fITEMPLATE\fR
Output using mustach
.I template
file. Mustache tags in the template are same as json output without IP
address information. When the native output formats controlled with
.B \-\-format
option do not provide what you need you should use mustach instead.
.TP
\fB\-o\fR, \fB\-\-output\fR=\fIFILE\fR
.I File
where output is written. Default is stdout.
@ -280,8 +289,11 @@ Original design by Sami Kerola.
XML support by Dominic Germain, Sogetel inc.
.br
IPv6 support by Cheer Xiao.
.PP
The software has FreeBSD License.
.br
Mustache templating support by José Bollo.
.SH LICENSE
The dhcpd-pools uses FreeBSD License, the mustache uses Apache License, and
the gnulib modules are Mostly, but not entirely, GPL.
.SH "REPORTING BUGS"
Report bugs to
.MT @PACKAGE_BUGREPORT@
@ -294,4 +306,6 @@ Home page
.SH "SEE ALSO"
.BR dhcpd.leases (5),
.BR dhcpd.conf (5),
.BR chmod (1)
.BR chmod (1),
.UR https://mustache.github.io/
.UE

View file

@ -13,6 +13,9 @@ dhcpd_pools_SOURCES = \
src/dhcpd-pools.h \
src/getdata.c \
src/hash.c \
src/mustach-dhcpd-pools.c \
src/mustach.c \
src/mustach.h \
src/other.c \
src/output.c \
src/sort.c

View file

@ -112,7 +112,8 @@ int main(int argc, char **argv)
OPT_CRIT_COUNT,
OPT_COLOR,
OPT_SKIP_OK,
OPT_SET_IPV
OPT_SET_IPV,
OPT_MUSTACH
};
int ret_val;
@ -126,6 +127,7 @@ int main(int argc, char **argv)
{"reverse", no_argument, NULL, 'r'},
{"output", required_argument, NULL, 'o'},
{"limit", required_argument, NULL, 'L'},
{"mustach", required_argument, NULL, OPT_MUSTACH},
{"version", no_argument, NULL, 'v'},
{"help", no_argument, NULL, 'h'},
{"snet-alarms", no_argument, NULL, OPT_SNET_ALARMS},
@ -225,6 +227,10 @@ int main(int argc, char **argv)
config.header_limit = return_limit(optarg[0]);
config.number_limit = return_limit(optarg[1]);
break;
case OPT_MUSTACH:
config.mustach_template = optarg;
output_format = 'm';
break;
case OPT_COLOR:
config.color_mode = parse_color_mode(optarg);
if (config.color_mode == color_unknown)

View file

@ -225,6 +225,7 @@ struct configuration_t {
int output_format;
struct output_sort *sorts;
char *output_file;
char *mustach_template;
double warning;
double critical;
double warn_count;
@ -342,7 +343,10 @@ extern void mergesort_ranges(struct range_t *restrict orig, int size,
struct range_t *restrict temp)
__attribute__ ((nonnull(1, 3)));
/* output function */
extern void range_output_helper(struct output_helper_t *oh, struct range_t *range_p);
extern void shnet_output_helper(struct output_helper_t *oh, struct shared_network_t *shared_p);
extern int output_analysis(const char);
extern int mustach_dhcpd_pools(void);
/* Memory release, file closing etc */
extern void clean_up(void);
/* Hash functions */

334
src/mustach-dhcpd-pools.c Normal file
View file

@ -0,0 +1,334 @@
/*
* The dhcpd-pools has BSD 2-clause license which also known as "Simplified
* BSD License" or "FreeBSD License".
*
* Copyright 2006- Sami Kerola. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are
* those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of Sami Kerola.
*/
/*! \file mustach-dhcpd-pools.c
* \brief Mustache templating specific functions.
*/
#include <config.h>
#include <fcntl.h>
#include <errno.h>
#include <error.h>
#include <sys/stat.h>
#include <unistd.h>
#include "close-stream.h"
#include "dhcpd-pools.h"
#include "error.h"
#include "mustach.h"
#include "xalloc.h"
struct expl {
struct range_t *range_p;
struct shared_network_t *shnet_p;
int current;
};
static int must_enter(void *closure, const char *name);
static int must_leave(void *closure);
/* Set mustach function pointers. */
static struct mustach_itf itf = {
.start = NULL,
.enter = must_enter,
.put = NULL,
.next = NULL,
.leave = must_leave
};
static int must_put_range(void *closure, const char *name, int escape
__attribute__ ((unused)), FILE *file)
{
struct expl *e = closure;
struct output_helper_t oh;
if (!strcmp(name, "location")) {
fprintf(file, "%s", e->range_p->shared_net->name);
return 0;
}
if (!strcmp(name, "range")) {
fprintf(file, "%s - %s", ntop_ipaddr(&e->range_p->first_ip),
ntop_ipaddr(&e->range_p->last_ip));
return 0;
}
if (!strcmp(name, "used")) {
fprintf(file, "%g", e->range_p->count);
return 0;
}
if (!strcmp(name, "touched")) {
fprintf(file, "%g", e->range_p->touched);
return 0;
}
range_output_helper(&oh, e->range_p);
if (!strcmp(name, "defined")) {
fprintf(file, "%g", oh.range_size);
return 0;
}
if (!strcmp(name, "free")) {
fprintf(file, "%g", oh.range_size - e->range_p->count);
return 0;
}
if (!strcmp(name, "percent")) {
fprintf(file, "%g", oh.percent);
return 0;
}
if (!strcmp(name, "touch_count")) {
fprintf(file, "%g", oh.tc);
return 0;
}
if (!strcmp(name, "touch_percent")) {
fprintf(file, "%g", oh.tcp);
return 0;
}
if (config.backups_found == 1) {
if (!strcmp(name, "backup_count")) {
fprintf(file, "%g", e->range_p->backups);
return 0;
}
if (!strcmp(name, "backup_percent")) {
fprintf(file, "%g", oh.bup);
return 0;
}
}
if (!strcmp(name, "status")) {
fprintf(file, "%d", oh.status);
return 0;
}
return 0;
}
static int must_put_shnet(void *closure, const char *name, int escape
__attribute__ ((unused)), FILE *file)
{
struct expl *e = closure;
struct output_helper_t oh;
if (!strcmp(name, "location")) {
fprintf(file, "%s", e->shnet_p->name);
return 0;
}
if (!strcmp(name, "defined")) {
fprintf(file, "%g", e->shnet_p->available);
return 0;
}
if (!strcmp(name, "used")) {
fprintf(file, "%g", e->shnet_p->used);
return 0;
}
if (!strcmp(name, "touched")) {
fprintf(file, "%g", e->shnet_p->touched);
return 0;
}
shnet_output_helper(&oh, e->shnet_p);
if (!strcmp(name, "free")) {
fprintf(file, "%g", e->shnet_p->available - e->shnet_p->used);
return 0;
}
if (!strcmp(name, "percent")) {
fprintf(file, "%g", oh.percent);
return 0;
}
if (!strcmp(name, "touch_count")) {
fprintf(file, "%g", oh.tc);
return 0;
}
if (!strcmp(name, "touch_percent")) {
fprintf(file, "%g", oh.tcp);
return 0;
}
if (config.backups_found == 1) {
if (!strcmp(name, "backup_count")) {
fprintf(file, "%g", e->shnet_p->backups);
return 0;
}
if (!strcmp(name, "backup_percent")) {
fprintf(file, "%g", oh.bup);
return 0;
}
}
if (!strcmp(name, "status")) {
fprintf(file, "%d", oh.status);
return 0;
}
return 0;
}
static int must_next_range(void *closure)
{
struct expl *e = closure;
struct output_helper_t oh;
do {
e->range_p++;
e->current--;
if (e->current <= 0)
return 0;
range_output_helper(&oh, e->range_p);
} while (config.skip_ok && oh.status == STATUS_OK);
return 1;
}
static int must_next_shnet(void *closure)
{
struct expl *e = closure;
struct output_helper_t oh;
do {
e->shnet_p++;
e->current--;
if (e->current <= 0)
return 0;
shnet_output_helper(&oh, e->shnet_p);
} while (config.skip_ok && oh.status == STATUS_OK);
return 1;
}
static int must_enter(void *closure, const char *name)
{
struct expl *e = closure;
if (!strcmp(name, "subnets")) {
itf.put = must_put_range;
itf.next = must_next_range;
e->current = num_ranges;
e->range_p = ranges;
/* must_next_range() will skip_ok when needed */
return must_next_range(closure);
}
if (!strcmp(name, "shared-networks")) {
itf.put = must_put_shnet;
itf.next = must_next_shnet;
e->shnet_p = shared_networks;
e->current = num_shared_networks + 1;
return must_next_shnet(closure);
}
if (!strcmp(name, "summary")) {
itf.put = must_put_shnet;
itf.next = must_next_shnet;
e->shnet_p = shared_networks;
e->current = 1;
return 1;
}
return 0;
}
static int must_leave(void *closure __attribute__ ((unused)))
{
struct expl *e = closure;
e->shnet_p = shared_networks;
e->range_p = ranges;
return 0;
}
static char *must_read_template(const char *filename)
{
int f;
struct stat s;
char *result;
if ((f = open(filename, O_RDONLY)) < 0) {
error(EXIT_FAILURE, errno, "must_read_template: open: %s", filename);
}
fstat(f, &s);
result = xmalloc(s.st_size + 1);
if (read(f, result, s.st_size) != s.st_size) {
error(EXIT_FAILURE, errno, "must_read_template: read: %s", filename);
}
close(f);
result[s.st_size] = 0;
return result;
}
int mustach_dhcpd_pools(void)
{
struct expl e;
char *template;
FILE *outfile;
int ret;
template = must_read_template(config.mustach_template);
if (config.output_file[0]) {
outfile = fopen(config.output_file, "w+");
if (outfile == NULL) {
error(EXIT_FAILURE, errno, "mustach_dhcpd_pools: fopen: %s",
config.output_file);
}
} else {
outfile = stdout;
}
ret = fmustach(template, &itf, &e, outfile);
free(template);
if (outfile == stdout) {
if (fflush(stdout))
error(EXIT_FAILURE, errno, "mustach_dhcpd_pools: fflush");
} else {
if (close_stream(outfile))
error(EXIT_FAILURE, errno, "mustach_dhcpd_pools: fclose");
}
switch (ret) {
case MUSTACH_OK:
return 0;
case MUSTACH_ERROR_SYSTEM:
error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: system error");
break;
case MUSTACH_ERROR_UNEXPECTED_END:
error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: unexpected end");
break;
case MUSTACH_ERROR_EMPTY_TAG:
error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: empty tag");
break;
case MUSTACH_ERROR_TAG_TOO_LONG:
error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: too long tag");
break;
case MUSTACH_ERROR_BAD_SEPARATORS:
error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: bad separator");
break;
case MUSTACH_ERROR_TOO_DEPTH:
error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: too deep");
break;
case MUSTACH_ERROR_CLOSING:
error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: closing");
break;
case MUSTACH_ERROR_BAD_UNESCAPE_TAG:
error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: bad escape tag");
break;
default:
error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: unknown error");
}
return 1;
}

258
src/mustach.c Normal file
View file

@ -0,0 +1,258 @@
/*
Author: José Bollo <jobol@nonadev.net>
Author: José Bollo <jose.bollo@iot.bzh>
https://gitlab.com/jobol/mustach
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include "mustach.h"
#define NAME_LENGTH_MAX 1024
#define DEPTH_MAX 256
static int getpartial(struct mustach_itf *itf, void *closure, const char *name, char **result)
{
int rc;
FILE *file;
size_t size;
*result = NULL;
file = open_memstream(result, &size);
if (file == NULL)
rc = MUSTACH_ERROR_SYSTEM;
else {
rc = itf->put(closure, name, 0, file);
if (rc == 0)
/* adds terminating null */
rc = fputc(0, file) ? MUSTACH_ERROR_SYSTEM : 0;
fclose(file);
if (rc < 0) {
free(*result);
*result = NULL;
}
}
return rc;
}
static int process(const char *template, struct mustach_itf *itf, void *closure, FILE *file, const char *opstr, const char *clstr)
{
char name[NAME_LENGTH_MAX + 1], *partial, c;
const char *beg, *term;
struct { const char *name, *again; size_t length; int emit, entered; } stack[DEPTH_MAX];
size_t oplen, cllen, len, l;
int depth, rc, emit;
emit = 1;
oplen = strlen(opstr);
cllen = strlen(clstr);
depth = 0;
for(;;) {
beg = strstr(template, opstr);
if (beg == NULL) {
/* no more mustach */
if (emit)
fwrite(template, strlen(template), 1, file);
return depth ? MUSTACH_ERROR_UNEXPECTED_END : 0;
}
if (emit)
fwrite(template, (size_t)(beg - template), 1, file);
beg += oplen;
term = strstr(beg, clstr);
if (term == NULL)
return MUSTACH_ERROR_UNEXPECTED_END;
template = term + cllen;
len = (size_t)(term - beg);
c = *beg;
switch(c) {
case '!':
case '=':
break;
case '{':
for (l = 0 ; clstr[l] == '}' ; l++);
if (clstr[l]) {
if (!len || beg[len-1] != '}')
return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
len--;
} else {
if (term[l] != '}')
return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
template++;
}
c = '&';
/*@fallthrough@*/
case '^':
case '#':
case '/':
case '&':
case '>':
#if !defined(NO_EXTENSION_FOR_MUSTACH) && !defined(NO_COLON_EXTENSION_FOR_MUSTACH)
case ':':
#endif
beg++; len--;
default:
while (len && isspace(beg[0])) { beg++; len--; }
while (len && isspace(beg[len-1])) len--;
if (len == 0)
return MUSTACH_ERROR_EMPTY_TAG;
if (len > NAME_LENGTH_MAX)
return MUSTACH_ERROR_TAG_TOO_LONG;
memcpy(name, beg, len);
name[len] = 0;
break;
}
switch(c) {
case '!':
/* comment */
/* nothing to do */
break;
case '=':
/* defines separators */
if (len < 5 || beg[len - 1] != '=')
return MUSTACH_ERROR_BAD_SEPARATORS;
beg++;
len -= 2;
for (l = 0; l < len && !isspace(beg[l]) ; l++);
if (l == len)
return MUSTACH_ERROR_BAD_SEPARATORS;
opstr = strndupa(beg, l);
while (l < len && isspace(beg[l])) l++;
if (l == len)
return MUSTACH_ERROR_BAD_SEPARATORS;
clstr = strndupa(beg + l, len - l);
oplen = strlen(opstr);
cllen = strlen(clstr);
break;
case '^':
case '#':
/* begin section */
if (depth == DEPTH_MAX)
return MUSTACH_ERROR_TOO_DEPTH;
rc = emit;
if (rc) {
rc = itf->enter(closure, name);
if (rc < 0)
return rc;
}
stack[depth].name = beg;
stack[depth].again = template;
stack[depth].length = len;
stack[depth].emit = emit;
stack[depth].entered = rc;
if ((c == '#') == (rc == 0))
emit = 0;
depth++;
break;
case '/':
/* end section */
if (depth-- == 0 || len != stack[depth].length || memcmp(stack[depth].name, name, len))
return MUSTACH_ERROR_CLOSING;
rc = emit && stack[depth].entered ? itf->next(closure) : 0;
if (rc < 0)
return rc;
if (rc) {
template = stack[depth++].again;
} else {
emit = stack[depth].emit;
if (emit && stack[depth].entered)
itf->leave(closure);
}
break;
case '>':
/* partials */
if (emit) {
rc = getpartial(itf, closure, name, &partial);
if (rc == 0) {
rc = process(partial, itf, closure, file, opstr, clstr);
free(partial);
}
if (rc < 0)
return rc;
}
break;
default:
/* replacement */
if (emit) {
rc = itf->put(closure, name, c != '&', file);
if (rc < 0)
return rc;
}
break;
}
}
}
int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE *file)
{
int rc = itf->start ? itf->start(closure) : 0;
if (rc == 0)
rc = process(template, itf, closure, file, "{{", "}}");
return rc;
}
int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int fd)
{
int rc;
FILE *file;
file = fdopen(fd, "w");
if (file == NULL) {
rc = MUSTACH_ERROR_SYSTEM;
errno = ENOMEM;
} else {
rc = fmustach(template, itf, closure, file);
fclose(file);
}
return rc;
}
int mustach(const char *template, struct mustach_itf *itf, void *closure, char **result, size_t *size)
{
int rc;
FILE *file;
size_t s;
*result = NULL;
if (size == NULL)
size = &s;
file = open_memstream(result, size);
if (file == NULL) {
rc = MUSTACH_ERROR_SYSTEM;
errno = ENOMEM;
} else {
rc = fmustach(template, itf, closure, file);
if (rc == 0)
/* adds terminating null */
rc = fputc(0, file) ? MUSTACH_ERROR_SYSTEM : 0;
fclose(file);
if (rc >= 0)
/* removes terminating null of the length */
(*size)--;
else {
free(*result);
*result = NULL;
*size = 0;
}
}
return rc;
}

112
src/mustach.h Normal file
View file

@ -0,0 +1,112 @@
/*
Author: José Bollo <jobol@nonadev.net>
Author: José Bollo <jose.bollo@iot.bzh>
https://gitlab.com/jobol/mustach
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef _mustach_h_included_
#define _mustach_h_included_
/**
* mustach_itf - interface for callbacks
*
* All of this function should return a negative value to stop
* the mustache processing. The returned negative value will be
* then returned to the caller of mustach as is.
*
* The functions enter and next should return 0 or 1.
*
* All other functions should normally return 0.
*
* @start: Starts the mustach processing of the closure
* 'start' is optional (can be NULL)
*
* @put: Writes the value of 'name' to 'file' with 'escape' or not
*
* @enter: Enters the section of 'name' if possible.
* Musts return 1 if entered or 0 if not entered.
* When 1 is returned, the function 'leave' will always be called.
* Conversely 'leave' is never called when enter returns 0 or
* a negative value.
* When 1 is returned, the function must activate the first
* item of the section.
*
* @next: Activates the next item of the section if it exists.
* Musts return 1 when the next item is activated.
* Musts return 0 when there is no item to activate.
*
* @leave: Leaves the last entered section
*/
struct mustach_itf {
int (*start)(void *closure);
int (*put)(void *closure, const char *name, int escape, FILE *file);
int (*enter)(void *closure, const char *name);
int (*next)(void *closure);
int (*leave)(void *closure);
};
#define MUSTACH_OK 0
#define MUSTACH_ERROR_SYSTEM -1
#define MUSTACH_ERROR_UNEXPECTED_END -2
#define MUSTACH_ERROR_EMPTY_TAG -3
#define MUSTACH_ERROR_TAG_TOO_LONG -4
#define MUSTACH_ERROR_BAD_SEPARATORS -5
#define MUSTACH_ERROR_TOO_DEPTH -6
#define MUSTACH_ERROR_CLOSING -7
#define MUSTACH_ERROR_BAD_UNESCAPE_TAG -8
/**
* fmustach - Renders the mustache 'template' in 'file' for 'itf' and 'closure'.
*
* @template: the template string to instanciate
* @itf: the interface to the functions that mustach calls
* @closure: the closure to pass to functions called
* @file: the file where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE *file);
/**
* fmustach - Renders the mustache 'template' in 'fd' for 'itf' and 'closure'.
*
* @template: the template string to instanciate
* @itf: the interface to the functions that mustach calls
* @closure: the closure to pass to functions called
* @fd: the file descriptor number where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int fd);
/**
* fmustach - Renders the mustache 'template' in 'result' for 'itf' and 'closure'.
*
* @template: the template string to instanciate
* @itf: the interface to the functions that mustach calls
* @closure: the closure to pass to functions called
* @result: the pointer receiving the result when 0 is returned
* @size: the size of the returned result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int mustach(const char *template, struct mustach_itf *itf, void *closure, char **result, size_t *size);
#endif

View file

@ -60,7 +60,7 @@
#include "dhcpd-pools.h"
/*! \brief Calculate range percentages and such. */
static void range_output_helper(struct output_helper_t *oh, struct range_t *range_p)
void range_output_helper(struct output_helper_t *oh, struct range_t *range_p)
{
/* counts and calculations */
oh->range_size = get_range_size(range_p);
@ -87,7 +87,7 @@ static void range_output_helper(struct output_helper_t *oh, struct range_t *rang
}
/*! \brief Calculate shared network percentages and such. */
static void shnet_output_helper(struct output_helper_t *oh, struct shared_network_t *shared_p)
void shnet_output_helper(struct output_helper_t *oh, struct shared_network_t *shared_p)
{
/* counts and calculations */
oh->tc = shared_p->touched + shared_p->used;
@ -1265,6 +1265,9 @@ int output_analysis(const char c)
case 'c':
ret = output_csv();
break;
case 'm':
ret = mustach_dhcpd_pools();
break;
default:
error(EXIT_FAILURE, 0, "unknown output format: '%c'", c);
}