view macro.c @ 42:fb995e5d54e9 version-1

Release version 1
author Guido Berhoerster <guido+pwm@berhoerster.name>
date Tue, 20 Aug 2019 21:26:55 +0200
parents 722a45b4028b
children
line wrap: on
line source

/*
 * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name>
 *
 * 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.
 */

#include "compat.h"

#include <ctype.h>
#ifdef	HAVE_QUEUE_H
#include <sys/queue.h>
#endif /* HAVE_QUEUE_H */
#include <stdlib.h>
#include <string.h>

#include "macro.h"
#include "tok.h"
#include "util.h"

TAILQ_HEAD(macro_head, macro_entry);

struct macro_entry {
	TAILQ_ENTRY(macro_entry) entry;
	char		*name;
	int		argc;
	char		**argv;
};

static struct macro_entry *
match_macro(struct macro_head *head, const char *name)
{
	struct macro_entry *entry;

	TAILQ_FOREACH(entry, head, entry) {
		if (strcmp(entry->name, name) == 0) {
			return (entry);
		}
	}

	return (NULL);
}

void
macro_init(struct macro_head **headp)
{
	struct macro_head *head;

	head = xmalloc(sizeof (struct macro_head));
	TAILQ_INIT(head);
	*headp = head;
}

static void
free_macro_entry(struct macro_entry *entry)
{
	int	i;

	if (entry == NULL) {
		return;
	}

	free(entry->name);
	for (i = 0; i < entry->argc; i++) {
		free(entry->argv[i]);
	}
	free(entry->argv);
	free(entry);
}

void
macro_destroy(struct macro_head *head)
{
	struct macro_entry *entry;
	struct macro_entry *tmp_entry;

	if (head == NULL) {
		return;
	}

	TAILQ_FOREACH_SAFE(entry, head, entry, tmp_entry) {
		TAILQ_REMOVE(head, entry, entry);
		free_macro_entry(entry);
	}
	free(head);
}

enum macro_err
macro_parse(const char *name, size_t tokenc, union tok **tokenv,
    struct macro_head *head, struct macro_entry **macro_entryp)
{
	const char	*p = name;
	int		argc;
	char		**argv;
	struct macro_entry *entry;

	/* validate macro name */
	for (p = name; *p != '\0'; p++) {
		/*
		 * macro names must contain only ASCII alphanumeric characters
		 * and underscores
		 */
		if (!isascii(*p) || (!isalnum(*p) && (*p != '_'))) {
			return (MACRO_ERR_INVALID_NAME);
		}
	}

	/* expand macros */
	if (macro_expand_macros(head, tokenc, tokenv, &argc, &argv) != 0) {
		return (MACRO_ERR_UNDEFINED_MACRO);
	}

	/* create macro */
	entry = xmalloc(sizeof (struct macro_entry));
	entry->name = xstrdup(name);
	entry->argc = argc;
	entry->argv = argv;
	*macro_entryp = entry;

	return (MACRO_ERR_OK);
}

void
macro_add(struct macro_head *head, struct macro_entry *entry)
{
	struct macro_entry *old_entry;

	/* remove existing macro with the same name */
	old_entry = match_macro(head, entry->name);
	if (old_entry != NULL) {
		TAILQ_REMOVE(head, old_entry, entry);
		free_macro_entry(old_entry);
	}

	TAILQ_INSERT_TAIL(head, entry, entry);
}

int
macro_expand_macros(struct macro_head *head, size_t tokenc, union tok **tokenv,
    int *argcp, char ***argvp)
{
	int		retval = -1;
	char		**argv = NULL;
	int		argc = 0;
	size_t		argv_size = tokenc + 1;
	size_t		i;
	struct macro_entry *entry;
	int		j;

	argv = xmalloc(argv_size * sizeof (char *));
	argv[0] = NULL;

	for (i = 0; i < tokenc; i++) {
		switch (tokenv[i]->any.type) {
		case TOK_ARG:
			argv[argc++] = xstrdup(tokenv[i]->arg.value);
			argv[argc] = NULL;
			break;
		case TOK_MACRO:
			entry = match_macro(head, tokenv[i]->macro.name);
			if (entry == NULL) {
				goto out;
			}
			argv_size += entry->argc;
			argv = xrealloc(argv, argv_size * sizeof (char *));
			for (j = 0; j < entry->argc; j++) {
				argv[argc++] = xstrdup(entry->argv[j]);
				argv[argc] = NULL;
			}
			break;
		}
	}
	*argvp = argv;
	*argcp = argc;
	retval = 0;

out:
	if (retval != 0) {
		for (j = 0; j < argc; j++) {
			free(argv[j]);
		}
		free(argv);
	}

	return (retval);
}