Mercurial > projects > pwm
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); +}