Mercurial > projects > pwm
view macro.c @ 32:b5ebed168e59
Only invoke pager in the help command for the summary of all commands
Display usage information for a single command directly and prefix it with
"usage:" like usage error messages.
author | Guido Berhoerster <guido+pwm@berhoerster.name> |
---|---|
date | Tue, 28 Nov 2017 17:16:24 +0100 |
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); }