Remove dhcpd and dhcrelay remainings.
[dragonfly.git] / usr.bin / make / shell.c
blob6471b21df8cd523920eafac9789fda53c262a52a
1 /*-
2 * Copyright (c) 1988, 1989, 1990, 1993
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1988, 1989 by Adam de Boor
5 * Copyright (c) 1989 by Berkeley Softworks
6 * All rights reserved.
8 * This code is derived from software contributed to Berkeley by
9 * Adam de Boor.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
39 * $DragonFly: src/usr.bin/make/shell.c,v 1.25 2005/09/22 09:13:38 okumoto Exp $
42 #include <string.h>
43 #include <stdlib.h>
45 #include "make.h"
46 #include "parse.h"
47 #include "pathnames.h"
48 #include "shell.h"
49 #include "str.h"
50 #include "util.h"
52 /**
53 * Helper function for sorting the builtin list alphabetically.
55 static int
56 sort_builtins(const void *p1, const void *p2)
58 return (strcmp(*(const char* const*)p1, *(const char* const*)p2));
61 /**
62 * Free a shell structure and all associated strings.
64 void
65 Shell_Destroy(Shell *sh)
67 if (sh != NULL) {
68 free(sh->name);
69 free(sh->path);
70 free(sh->echoOff);
71 free(sh->echoOn);
72 free(sh->noPrint);
73 free(sh->errCheck);
74 free(sh->ignErr);
75 free(sh->echo);
76 free(sh->exit);
77 ArgArray_Done(&sh->builtins);
78 free(sh->meta);
79 free(sh);
83 /**
84 * Dump a shell specification to stderr.
86 void
87 Shell_Dump(const Shell *sh)
89 int i;
91 fprintf(stderr, "Shell %p:\n", sh);
92 fprintf(stderr, " name='%s' path='%s'\n", sh->name, sh->path);
93 fprintf(stderr, " hasEchoCtl=%d echoOff='%s' echoOn='%s'\n",
94 sh->hasEchoCtl, sh->echoOff, sh->echoOn);
95 fprintf(stderr, " noPrint='%s'\n", sh->noPrint);
96 fprintf(stderr, " hasErrCtl=%d errCheck='%s' ignErr='%s'\n",
97 sh->hasErrCtl, sh->errCheck, sh->ignErr);
98 fprintf(stderr, " echo='%s' exit='%s'\n", sh->echo, sh->exit);
99 fprintf(stderr, " builtins=%d\n", sh->builtins.argc - 1);
100 for (i = 1; i < sh->builtins.argc; i++)
101 fprintf(stderr, " '%s'", sh->builtins.argv[i]);
102 fprintf(stderr, "\n meta='%s'\n", sh->meta);
103 fprintf(stderr, " unsetenv=%d\n", sh->unsetenv);
107 * Parse a shell specification line and return the new Shell structure.
108 * In case of an error a message is printed and NULL is returned.
110 * Notes:
111 * A shell specification consists of a .SHELL target, with dependency
112 * operator, followed by a series of blank-separated words. Double
113 * quotes can be used to use blanks in words. A backslash escapes
114 * anything (most notably a double-quote and a space) and
115 * provides the functionality it does in C. Each word consists of
116 * keyword and value separated by an equal sign. There should be no
117 * unnecessary spaces in the word. The keywords are as follows:
118 * name Name of shell.
119 * path Location of shell. Overrides "name" if given
120 * quiet Command to turn off echoing.
121 * echo Command to turn echoing on
122 * filter Result of turning off echoing that shouldn't be
123 * printed.
124 * echoFlag Flag to turn echoing on at the start
125 * errFlag Flag to turn error checking on at the start
126 * hasErrCtl True if shell has error checking control
127 * check Command to turn on error checking if hasErrCtl
128 * is true or template of command to echo a command
129 * for which error checking is off if hasErrCtl is
130 * false.
131 * ignore Command to turn off error checking if hasErrCtl
132 * is true or template of command to execute a
133 * command so as to ignore any errors it returns if
134 * hasErrCtl is false.
135 * builtins A space separated list of builtins. If one
136 * of these builtins is detected when make wants
137 * to execute a command line, the command line is
138 * handed to the shell. Otherwise make may try to
139 * execute the command directly. If this list is empty
140 * it is assumed, that the command must always be
141 * handed over to the shell.
142 * meta The shell meta characters. If this is not specified
143 * or empty, commands are alway passed to the shell.
144 * Otherwise they are not passed when they contain
145 * neither a meta character nor a builtin command.
147 static Shell *
148 ShellParseSpec(const char spec[], bool *fullSpec)
150 ArgArray aa;
151 Shell *sh;
152 char *eq;
153 char *keyw;
154 int arg;
156 *fullSpec = false;
158 sh = emalloc(sizeof(*sh));
159 memset(sh, 0, sizeof(*sh));
160 ArgArray_Init(&sh->builtins);
163 * Parse the specification by keyword but skip the first word
165 brk_string(&aa, spec, true);
167 for (arg = 1; arg < aa.argc; arg++) {
169 * Split keyword and value
171 keyw = aa.argv[arg];
172 if ((eq = strchr(keyw, '=')) == NULL) {
173 Parse_Error(PARSE_FATAL, "missing '=' in shell "
174 "specification keyword '%s'", keyw);
175 ArgArray_Done(&aa);
176 Shell_Destroy(sh);
177 return (NULL);
179 *eq++ = '\0';
181 if (strcmp(keyw, "path") == 0) {
182 free(sh->path);
183 sh->path = estrdup(eq);
184 } else if (strcmp(keyw, "name") == 0) {
185 free(sh->name);
186 sh->name = estrdup(eq);
187 } else if (strcmp(keyw, "quiet") == 0) {
188 free(sh->echoOff);
189 sh->echoOff = estrdup(eq);
190 *fullSpec = true;
191 } else if (strcmp(keyw, "echo") == 0) {
192 free(sh->echoOn);
193 sh->echoOn = estrdup(eq);
194 *fullSpec = true;
195 } else if (strcmp(keyw, "filter") == 0) {
196 free(sh->noPrint);
197 sh->noPrint = estrdup(eq);
198 *fullSpec = true;
199 } else if (strcmp(keyw, "echoFlag") == 0) {
200 free(sh->echo);
201 sh->echo = estrdup(eq);
202 *fullSpec = true;
203 } else if (strcmp(keyw, "errFlag") == 0) {
204 free(sh->exit);
205 sh->exit = estrdup(eq);
206 *fullSpec = true;
207 } else if (strcmp(keyw, "hasErrCtl") == 0) {
208 sh->hasErrCtl = (
209 *eq == 'Y' || *eq == 'y' ||
210 *eq == 'T' || *eq == 't');
211 *fullSpec = true;
212 } else if (strcmp(keyw, "check") == 0) {
213 free(sh->errCheck);
214 sh->errCheck = estrdup(eq);
215 *fullSpec = true;
216 } else if (strcmp(keyw, "ignore") == 0) {
217 free(sh->ignErr);
218 sh->ignErr = estrdup(eq);
219 *fullSpec = true;
220 } else if (strcmp(keyw, "builtins") == 0) {
221 ArgArray_Done(&sh->builtins);
222 brk_string(&sh->builtins, eq, true);
223 qsort(sh->builtins.argv + 1, sh->builtins.argc - 1,
224 sizeof(char *), sort_builtins);
225 *fullSpec = true;
226 } else if (strcmp(keyw, "meta") == 0) {
227 free(sh->meta);
228 sh->meta = estrdup(eq);
229 *fullSpec = true;
230 } else if (strcmp(keyw, "unsetenv") == 0) {
231 sh->unsetenv = (
232 *eq == 'Y' || *eq == 'y' ||
233 *eq == 'T' || *eq == 't');
234 *fullSpec = true;
235 } else {
236 Parse_Error(PARSE_FATAL, "unknown keyword in shell "
237 "specification '%s'", keyw);
238 ArgArray_Done(&aa);
239 Shell_Destroy(sh);
240 return (NULL);
243 ArgArray_Done(&aa);
246 * Some checks (could be more)
248 if (*fullSpec) {
249 if ((sh->echoOn != NULL) ^ (sh->echoOff != NULL)) {
250 Parse_Error(PARSE_FATAL, "Shell must have either both "
251 "echoOff and echoOn or none of them");
252 Shell_Destroy(sh);
253 return (NULL);
256 if (sh->echoOn != NULL && sh->echoOff != NULL)
257 sh->hasEchoCtl = true;
260 return (sh);
264 * Find a matching shell in 'shells' given its final component.
266 * Descriptions for various shells. What the list of builtins should contain
267 * is debatable: either all builtins or only those which may specified on
268 * a single line without use of meta-characters. For correct makefiles that
269 * contain only correct command lines there is no difference. But if a command
270 * line, for example, is: 'if -foo bar' and there is an executable named 'if'
271 * in the path, the first possibility would execute that 'if' while in the
272 * second case the shell would give an error. Histerically only a small
273 * subset of the builtins and no reserved words where given in the list which
274 * corresponds roughly to the first variant. So go with this but add missing
275 * words.
277 * @result
278 * A pointer to a Shell structure, or NULL if no shell with
279 * the given name is found.
281 Shell *
282 Shell_Match(const char name[])
284 Shell *shell;
285 const char *shellDir = PATH_DEFSHELLDIR;
287 shell = emalloc(sizeof(Shell));
289 if (strcmp(name, "csh") == 0) {
291 * CSH description. The csh can do echo control by playing
292 * with the setting of the 'echo' shell variable. Sadly,
293 * however, it is unable to do error control nicely.
295 shell->name = strdup(name);
296 shell->path = str_concat(shellDir, '/', name);
297 shell->hasEchoCtl = true;
298 shell->echoOff = strdup("unset verbose");
299 shell->echoOn = strdup("set verbose");
300 shell->noPrint = strdup("unset verbose");
301 shell->hasErrCtl = false;
302 shell->errCheck = strdup("echo \"%s\"\n");
303 shell->ignErr = strdup("csh -c \"%s || exit 0\"");
304 shell->echo = strdup("v");
305 shell->exit = strdup("e");
306 shell->meta = strdup("#=|^(){};&<>*?[]:$`\\@\n");
307 brk_string(&shell->builtins,
308 "alias cd eval exec exit read set ulimit unalias "
309 "umask unset wait", true);
310 shell->unsetenv = false;
312 } else if (strcmp(name, "sh") == 0) {
314 * SH description. Echo control is also possible and, under
315 * sun UNIX anyway, one can even control error checking.
318 shell->name = strdup(name);
319 shell->path = str_concat(shellDir, '/', name);
320 shell->hasEchoCtl = true;
321 shell->echoOff = strdup("set -");
322 shell->echoOn = strdup("set -v");
323 shell->noPrint = strdup("set -");
324 #ifdef OLDBOURNESHELL
325 shell->hasErrCtl = false;
326 shell->errCheck = strdup("echo \"%s\"\n");
327 shell->ignErr = strdup("sh -c '%s || exit 0'\n");
328 #else
329 shell->hasErrCtl = true;
330 shell->errCheck = strdup("set -e");
331 shell->ignErr = strdup("set +e");
332 #endif
333 shell->echo = strdup("v");
334 shell->exit = strdup("e");
335 shell->meta = strdup("#=|^(){};&<>*?[]:$`\\\n");
336 brk_string(&shell->builtins,
337 "alias cd eval exec exit read set ulimit unalias "
338 "umask unset wait", true);
339 shell->unsetenv = false;
341 } else if (strcmp(name, "ksh") == 0) {
343 * KSH description. The Korn shell has a superset of
344 * the Bourne shell's functionality. There are probably
345 * builtins missing here.
347 shell->name = strdup(name);
348 shell->path = str_concat(shellDir, '/', name);
349 shell->hasEchoCtl = true;
350 shell->echoOff = strdup("set -");
351 shell->echoOn = strdup("set -v");
352 shell->noPrint = strdup("set -");
353 shell->hasErrCtl = true;
354 shell->errCheck = strdup("set -e");
355 shell->ignErr = strdup("set +e");
356 shell->echo = strdup("v");
357 shell->exit = strdup("e");
358 shell->meta = strdup("#=|^(){};&<>*?[]:$`\\\n");
359 brk_string(&shell->builtins,
360 "alias cd eval exec exit read set ulimit unalias "
361 "umask unset wait", true);
362 shell->unsetenv = true;
364 } else {
365 free(shell);
366 shell = NULL;
369 return (shell);
373 * Given the line following a .SHELL target, parse it as a shell
374 * specification.
376 * Results:
377 * A pointer to a Shell structure, or NULL if no the spec was invalid.
379 Shell *
380 Shell_Parse(const char line[])
382 bool fullSpec;
383 Shell *sh;
385 /* parse the specification */
386 if ((sh = ShellParseSpec(line, &fullSpec)) == NULL)
387 return (NULL);
389 if (sh->path == NULL) {
390 Shell *match;
392 * If no path was given, the user wants one of the pre-defined
393 * shells, yes? So we find the one s/he wants with the help of
394 * ShellMatch and set things up the right way.
396 if (sh->name == NULL) {
397 Parse_Error(PARSE_FATAL,
398 "Neither path nor name specified");
399 Shell_Destroy(sh);
400 return (NULL);
402 if (fullSpec) {
403 Parse_Error(PARSE_FATAL, "No path specified");
404 Shell_Destroy(sh);
405 return (NULL);
407 if ((match = Shell_Match(sh->name)) == NULL) {
408 Parse_Error(PARSE_FATAL, "%s: no matching shell",
409 sh->name);
410 Shell_Destroy(sh);
411 return (NULL);
414 Shell_Destroy(sh);
415 return (match);
417 } else {
418 Shell *match;
421 * The user provided a path. If s/he gave nothing else
422 * (fullSpec is false), try and find a matching shell in the
423 * ones we know of. Else we just take the specification at its
424 * word and copy it to a new location. In either case, we need
425 * to record the path the user gave for the shell.
427 if (sh->name == NULL) {
428 /* get the base name as the name */
429 if ((sh->name = strrchr(sh->path, '/')) == NULL) {
430 sh->name = estrdup(sh->path);
431 } else {
432 sh->name = estrdup(sh->name + 1);
435 if (fullSpec) {
436 return (sh);
438 if ((match = Shell_Match(sh->name)) == NULL) {
439 Parse_Error(PARSE_FATAL,
440 "%s: no matching shell", sh->name);
441 Shell_Destroy(sh);
442 return (NULL);
445 free(match->path);
446 match->path = sh->path;
447 sh->path = NULL;
449 Shell_Destroy(sh);
450 return (match);