comparison 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
comparison
equal deleted inserted replaced
26:5bdea77d0c1d 27:722a45b4028b
1 /*
2 * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 #include "compat.h"
25
26 #include <ctype.h>
27 #ifdef HAVE_QUEUE_H
28 #include <sys/queue.h>
29 #endif /* HAVE_QUEUE_H */
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "macro.h"
34 #include "tok.h"
35 #include "util.h"
36
37 TAILQ_HEAD(macro_head, macro_entry);
38
39 struct macro_entry {
40 TAILQ_ENTRY(macro_entry) entry;
41 char *name;
42 int argc;
43 char **argv;
44 };
45
46 static struct macro_entry *
47 match_macro(struct macro_head *head, const char *name)
48 {
49 struct macro_entry *entry;
50
51 TAILQ_FOREACH(entry, head, entry) {
52 if (strcmp(entry->name, name) == 0) {
53 return (entry);
54 }
55 }
56
57 return (NULL);
58 }
59
60 void
61 macro_init(struct macro_head **headp)
62 {
63 struct macro_head *head;
64
65 head = xmalloc(sizeof (struct macro_head));
66 TAILQ_INIT(head);
67 *headp = head;
68 }
69
70 static void
71 free_macro_entry(struct macro_entry *entry)
72 {
73 int i;
74
75 if (entry == NULL) {
76 return;
77 }
78
79 free(entry->name);
80 for (i = 0; i < entry->argc; i++) {
81 free(entry->argv[i]);
82 }
83 free(entry->argv);
84 free(entry);
85 }
86
87 void
88 macro_destroy(struct macro_head *head)
89 {
90 struct macro_entry *entry;
91 struct macro_entry *tmp_entry;
92
93 if (head == NULL) {
94 return;
95 }
96
97 TAILQ_FOREACH_SAFE(entry, head, entry, tmp_entry) {
98 TAILQ_REMOVE(head, entry, entry);
99 free_macro_entry(entry);
100 }
101 free(head);
102 }
103
104 enum macro_err
105 macro_parse(const char *name, size_t tokenc, union tok **tokenv,
106 struct macro_head *head, struct macro_entry **macro_entryp)
107 {
108 const char *p = name;
109 int argc;
110 char **argv;
111 struct macro_entry *entry;
112
113 /* validate macro name */
114 for (p = name; *p != '\0'; p++) {
115 /*
116 * macro names must contain only ASCII alphanumeric characters
117 * and underscores
118 */
119 if (!isascii(*p) || (!isalnum(*p) && (*p != '_'))) {
120 return (MACRO_ERR_INVALID_NAME);
121 }
122 }
123
124 /* expand macros */
125 if (macro_expand_macros(head, tokenc, tokenv, &argc, &argv) != 0) {
126 return (MACRO_ERR_UNDEFINED_MACRO);
127 }
128
129 /* create macro */
130 entry = xmalloc(sizeof (struct macro_entry));
131 entry->name = xstrdup(name);
132 entry->argc = argc;
133 entry->argv = argv;
134 *macro_entryp = entry;
135
136 return (MACRO_ERR_OK);
137 }
138
139 void
140 macro_add(struct macro_head *head, struct macro_entry *entry)
141 {
142 struct macro_entry *old_entry;
143
144 /* remove existing macro with the same name */
145 old_entry = match_macro(head, entry->name);
146 if (old_entry != NULL) {
147 TAILQ_REMOVE(head, old_entry, entry);
148 free_macro_entry(old_entry);
149 }
150
151 TAILQ_INSERT_TAIL(head, entry, entry);
152 }
153
154 int
155 macro_expand_macros(struct macro_head *head, size_t tokenc, union tok **tokenv,
156 int *argcp, char ***argvp)
157 {
158 int retval = -1;
159 char **argv = NULL;
160 int argc = 0;
161 size_t argv_size = tokenc + 1;
162 size_t i;
163 struct macro_entry *entry;
164 int j;
165
166 argv = xmalloc(argv_size * sizeof (char *));
167 argv[0] = NULL;
168
169 for (i = 0; i < tokenc; i++) {
170 switch (tokenv[i]->any.type) {
171 case TOK_ARG:
172 argv[argc++] = xstrdup(tokenv[i]->arg.value);
173 argv[argc] = NULL;
174 break;
175 case TOK_MACRO:
176 entry = match_macro(head, tokenv[i]->macro.name);
177 if (entry == NULL) {
178 goto out;
179 }
180 argv_size += entry->argc;
181 argv = xrealloc(argv, argv_size * sizeof (char *));
182 for (j = 0; j < entry->argc; j++) {
183 argv[argc++] = xstrdup(entry->argv[j]);
184 argv[argc] = NULL;
185 }
186 break;
187 }
188 }
189 *argvp = argv;
190 *argcp = argc;
191 retval = 0;
192
193 out:
194 if (retval != 0) {
195 for (j = 0; j < argc; j++) {
196 free(argv[j]);
197 }
198 free(argv);
199 }
200
201 return (retval);
202 }