From 7d9a5b5561f2543be63fe0fd6ae04847f48571e6 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Thu, 9 Nov 2017 09:49:26 +0000 Subject: [PATCH] output: add mustach templating support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- THANKS | 1 + man/dhcpd-pools.1.in | 20 ++- src/Makemodule.am | 3 + src/dhcpd-pools.c | 8 +- src/dhcpd-pools.h | 4 + src/mustach-dhcpd-pools.c | 334 ++++++++++++++++++++++++++++++++++++++ src/mustach.c | 258 +++++++++++++++++++++++++++++ src/mustach.h | 112 +++++++++++++ src/output.c | 7 +- 9 files changed, 741 insertions(+), 6 deletions(-) create mode 100644 src/mustach-dhcpd-pools.c create mode 100644 src/mustach.c create mode 100644 src/mustach.h diff --git a/THANKS b/THANKS index f230fcd..bec03eb 100644 --- a/THANKS +++ b/THANKS @@ -42,3 +42,4 @@ Manuel Hachtkemper Klaus Slott Boris Lytochkin Jeff Bailey +José Bollo diff --git a/man/dhcpd-pools.1.in b/man/dhcpd-pools.1.in index 5541cfe..935b1c0 100644 --- a/man/dhcpd-pools.1.in +++ b/man/dhcpd-pools.1.in @@ -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 diff --git a/src/Makemodule.am b/src/Makemodule.am index 45e62b5..46ca5a3 100644 --- a/src/Makemodule.am +++ b/src/Makemodule.am @@ -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 diff --git a/src/dhcpd-pools.c b/src/dhcpd-pools.c index 2213ba8..4389e20 100644 --- a/src/dhcpd-pools.c +++ b/src/dhcpd-pools.c @@ -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) diff --git a/src/dhcpd-pools.h b/src/dhcpd-pools.h index d3b2d75..5778666 100644 --- a/src/dhcpd-pools.h +++ b/src/dhcpd-pools.h @@ -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 */ diff --git a/src/mustach-dhcpd-pools.c b/src/mustach-dhcpd-pools.c new file mode 100644 index 0000000..1cc8bfc --- /dev/null +++ b/src/mustach-dhcpd-pools.c @@ -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 + +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/mustach.c b/src/mustach.c new file mode 100644 index 0000000..9868cff --- /dev/null +++ b/src/mustach.c @@ -0,0 +1,258 @@ +/* + Author: José Bollo + Author: José Bollo + + 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 +#include +#include +#include +#include + +#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; +} + diff --git a/src/mustach.h b/src/mustach.h new file mode 100644 index 0000000..8196679 --- /dev/null +++ b/src/mustach.h @@ -0,0 +1,112 @@ +/* + Author: José Bollo + Author: José Bollo + + 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 + diff --git a/src/output.c b/src/output.c index ff1561d..28f2a57 100644 --- a/src/output.c +++ b/src/output.c @@ -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); }