view template.y @ 1125:9293874827bf

post: don't leak parsed tag and cat names Sadly, the recent commit (37044617c35deabfe8337a049d2da635bb14075a) did not fix all the reference leaks surrounding the tag and category name s-expression processing. Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Sat, 09 Jun 2018 20:06:30 -0400
parents d13f51a2d239
children 40e8a3bb0684
line wrap: on
line source

/*
 * Copyright (c) 2012-2018 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

%pure-parser
%lex-param {void *scanner}
%parse-param {struct parser_output *data}

%{
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>

#include <jeffpc/error.h>
#include <jeffpc/val.h>

#include "config.h"
#include "iter.h"
#include "vars.h"
#include "render.h"
#include "pipeline.h"
#include "utils.h"
#include "debug.h"

#include "parse.h"

#define scanner data->scanner

extern int tmpl_lex(void *, void *);

void yyerror(void *scan, char *e)
{
	DBG("Error: %s", e);
}

static char *tostr(char c)
{
	char *ret;

	ret = malloc(2);
	ASSERT(ret);

	ret[0] = c;
	ret[1] = '\0';

	return ret;
}

static char *condconcat(struct parser_output *data, char *a, char *b)
{
	if (!cond_value(data)) {
		free(b);
		return a;
	}

	return concat(a, b);
}

static char *__foreach(struct req *req, const struct nvpair *var, char *tmpl)
{
	struct val **items;
	size_t nitems;
	size_t i;
	char *out;
	int ret;

	out = NULL;

	ret = nvpair_value_array(var, &items, &nitems);
	ASSERT0(ret);

	for (i = 0; i < nitems; i++) {
		vars_scope_push(&req->vars);

		switch (items[i]->type) {
			case VT_NVL:
				vars_merge(&req->vars,
					   val_cast_to_nvl(items[i]));
				break;
			case VT_STR:
				vars_set_str(&req->vars, nvpair_name(var),
					     val_getref_str(items[i]));
				break;
			default:
				//vars_dump(&req->vars);
				panic("%s called with '%s' which has type %d",
				      __func__, nvpair_name(var),
				      nvpair_type(var));
		}

		out = concat(out, render_template(req, tmpl));

		vars_scope_pop(&req->vars);
	}

	return out;
}

static char *foreach(struct parser_output *data, struct req *req, char *varname,
                     char *tmpl)
{
	const struct nvpair *var;

	if (!cond_value(data))
		return xstrdup("");

	var = vars_lookup(&req->vars, varname);
	if (!var)
		return xstrdup("");

	return __foreach(req, var, tmpl);
}

static char *print_val(struct val *val)
{
	char buf[32];
	const char *tmp;

	tmp = NULL;

	switch (val->type) {
		case VT_STR:
			tmp = str_cstr(val_cast_to_str(val));
			break;
		case VT_INT:
			snprintf(buf, sizeof(buf), "%"PRIu64, val->i);
			tmp = buf;
			break;
		case VT_BLOB:
		case VT_NULL:
		case VT_SYM:
		case VT_CONS:
		case VT_BOOL:
		case VT_CHAR:
		case VT_ARRAY:
		case VT_NVL:
			panic("%s called with value of type %d", __func__,
			      val->type);
	}

	return xstrdup(tmp);
}

static char *print_var(const struct nvpair *var)
{
	struct str *str;
	char buf[32];
	char *ret;

	switch (nvpair_type(var)) {
		case VT_STR:
			str = nvpair_value_str(var);
			ret = xstrdup(str_cstr(str));
			str_putref(str);
			break;
		case VT_INT:
			snprintf(buf, sizeof(buf), "%"PRIu64, pair2int(var));
			ret = xstrdup(buf);
			break;
		default:
			panic("%s called with '%s' which has type %d", __func__,
			      nvpair_name(var), nvpair_type(var));
			break;
	}

	return ret;
}

static char *pipeline(struct parser_output *data, struct req *req,
		      char *varname, struct pipeline *line)
{
	const struct nvpair *var;
	struct pipestage *cur;
	struct val *val;
	char *out;

	if (!cond_value(data)) {
		pipeline_destroy(line);
		return xstrdup("");
	}

	var = vars_lookup(&req->vars, varname);
	if (!var) {
		pipeline_destroy(line);
		return xstrdup("");
	}

	switch (nvpair_type(var)) {
		case VT_STR:
			val = str_cast_to_val(nvpair_value_str(var));
			break;
		case VT_INT:
			val = VAL_ALLOC_INT(pair2int(var));
			break;
		default:
			vars_dump(&req->vars);
			panic("%s called with '%s' which has type %d", __func__,
			      varname, nvpair_type(var));
			break;
	}

	list_for_each(cur, &line->pipe)
		val = cur->stage->f(val);

	pipeline_destroy(line);

	out = print_val(val);

	val_putref(val);

	return out;
}

static char *variable(struct parser_output *data, struct req *req, char *name)
{
	const struct nvpair *var;

	if (!cond_value(data))
		return xstrdup("");

	var = vars_lookup(&req->vars, name);

	if (!var)
		return render_template(req, name);
	else
		return print_var(var);
}

enum if_fxns {
	IFFXN_GT,
	IFFXN_LT,
	IFFXN_EQ,
};

static uint64_t __function_get_arg(struct req *req, const char *arg)
{
	const struct nvpair *var;
	uint64_t val;

	if (!str2u64(arg, &val))
		return val;

	var = vars_lookup(&req->vars, arg);
	if (!var)
		return 0;

	switch (nvpair_type(var)) {
		case VT_INT:
			return pair2int(var);
		default:
			panic("unexpected nvpair type: %d",
			      nvpair_type(var));
	}
}

static char *__function(struct parser_output *data, struct req *req,
			enum if_fxns fxn, const char *sa1,
			const char *sa2)
{
	uint64_t ia1, ia2;		/* int value of saX */
	bool result = true;

	ia1 = __function_get_arg(req, sa1);
	ia2 = __function_get_arg(req, sa2);

	switch (fxn) {
		case IFFXN_GT:
			result = ia1 > ia2;
			break;
		case IFFXN_LT:
			result = ia1 < ia2;
			break;
		case IFFXN_EQ:
			result = ia1 == ia2;
			break;
	}

	cond_if(data, result);

	return xstrdup(NULL);
}

static char *__function_ifset(struct parser_output *data, struct req *req,
			      const char *arg)
{
	const struct nvpair *var;

	var = vars_lookup(&req->vars, arg);

	cond_if(data, var != NULL);

	return xstrdup(NULL);
}

static char *function(struct parser_output *data, struct req *req,
                      const char *fxn, const char *sa1, const char *sa2)
{
	if (!strcmp(fxn, "ifgt")) {
		return __function(data, req, IFFXN_GT, sa1, sa2);
	} else if (!strcmp(fxn, "iflt")) {
		return __function(data, req, IFFXN_LT, sa1, sa2);
	} else if (!strcmp(fxn, "ifeq")) {
		return __function(data, req, IFFXN_EQ, sa1, sa2);
	} else if (!strcmp(fxn, "ifset")) {
		return __function_ifset(data, req, sa1);
	} else if (!strcmp(fxn, "endif")) {
		cond_endif(data);
	} else if (!strcmp(fxn, "else")) {
		cond_else(data);
	} else {
		panic("unknown template function '%s'", fxn);
	}

	return xstrdup(NULL);
}
%}

%union {
	char c;
	char *ptr;
	struct pipestage *pipestage;
	struct pipeline *pipeline;
};

%token <ptr> WORD
%token <c> CHAR

%type <ptr> words cmd
%type <pipeline> pipeline
%type <pipestage> pipe

%%

page : words					{ data->output = $1; }
     ;

words : words CHAR				{ $$ = condconcat(data, $1, tostr($2)); }
      | words WORD				{ $$ = condconcat(data, $1, $2); }
      | words '|'				{ $$ = condconcat(data, $1, xstrdup("|")); }
      | words '%'				{ $$ = condconcat(data, $1, xstrdup("%")); }
      | words '('				{ $$ = condconcat(data, $1, xstrdup("(")); }
      | words ')'				{ $$ = condconcat(data, $1, xstrdup(")")); }
      | words ','				{ $$ = condconcat(data, $1, xstrdup(",")); }
      | words cmd				{ $$ = concat($1, $2); }
      |						{ $$ = xstrdup(""); }
      ;

cmd : '{' WORD pipeline '}'		{
						$$ = pipeline(data, data->req, $2, $3);
						free($2);
					}
    | '{' WORD '%' WORD '}'		{
						$$ = foreach(data, data->req, $2, $4);
						free($2);
						free($4);
					}
    | '{' WORD '(' WORD ',' WORD ')' '}'{
						$$ = function(data, data->req, $2, $4, $6);
						free($2);
						free($4);
						free($6);
					}
    | '{' WORD '(' WORD ')' '}'		{
						$$ = function(data, data->req, $2, $4, NULL);
						free($2);
						free($4);
					}
    | '{' WORD '(' ')' '}'		{
						$$ = function(data, data->req, $2, NULL, NULL);
						free($2);
					}
    | '{' WORD '}'			{
						$$ = variable(data, data->req, $2);
						free($2);
					}
    ;

pipeline : pipeline pipe		{
						pipeline_append($1, $2);
						$$ = $1;
					}
	 | pipe				{ $$ = pipestage_to_pipeline($1); }
         ;

pipe : '|' WORD				{ $$ = pipestage_alloc($2); free($2); }
     ;

%%