diff macro.c @ 27:722a45b4028b

Add define command for defining macros Macros are parsed when they are defined with the D command and can subsequently be used as arguments for other commands. Handle out of memory errors directly in tok.c.
author Guido Berhoerster <guido+pwm@berhoerster.name>
date Mon, 25 Sep 2017 21:21:25 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/macro.c	Mon Sep 25 21:21:25 2017 +0200
@@ -0,0 +1,202 @@
+/*
+ * 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);
+}