xref: /trueos/usr.bin/make/shell.c (revision a5cd5bc554f6b026dded052ccf6bc9f633b3b6de)
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.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Adam de Boor.
10  *
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.
26  *
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.
38  */
39 
40 #include <sys/cdefs.h>
41 __FBSDID("$FreeBSD$");
42 
43 #include <sys/queue.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 
48 #include "parse.h"
49 #include "pathnames.h"
50 #include "shell.h"
51 #include "util.h"
52 
53 /*
54  * Descriptions for various shells. What the list of builtins should contain
55  * is debatable: either all builtins or only those which may specified on
56  * a single line without use of meta-characters. For correct makefiles that
57  * contain only correct command lines there is no difference. But if a command
58  * line, for example, is: 'if -foo bar' and there is an executable named 'if'
59  * in the path, the first possibility would execute that 'if' while in the
60  * second case the shell would give an error. Histerically only a small
61  * subset of the builtins and no reserved words where given in the list which
62  * corresponds roughly to the first variant. So go with this but add missing
63  * words.
64  */
65 #define	CSH_BUILTINS						\
66 	"alias cd eval exec exit read set ulimit unalias "	\
67 	"umask unset wait"
68 
69 #define	SH_BUILTINS						\
70 	"alias cd eval exec exit read set ulimit unalias "	\
71 	"umask unset wait"
72 
73 #define	CSH_META	"#=|^(){};&<>*?[]:$`\\@\n"
74 #define	SH_META		"#=|^(){};&<>*?[]:$`\\\n"
75 
76 static const char *const shells_init[] = {
77 	/*
78 	 * CSH description. The csh can do echo control by playing
79 	 * with the setting of the 'echo' shell variable. Sadly,
80 	 * however, it is unable to do error control nicely.
81 	 */
82 	"name=csh path='" PATH_DEFSHELLDIR "/csh' "
83 	"quiet='unset verbose' echo='set verbose' filter='unset verbose' "
84 	"hasErrCtl=N check='echo \"%s\"\n' ignore='csh -c \"%s || exit 0\"' "
85 	"echoFlag=v errFlag=e "
86 	"meta='" CSH_META "' builtins='" CSH_BUILTINS "'",
87 
88 	/*
89 	 * SH description. Echo control is also possible and, under
90 	 * sun UNIX anyway, one can even control error checking.
91 	 */
92 	"name=sh path='" PATH_DEFSHELLDIR "/sh' "
93 	"quiet='set -' echo='set -v' filter='set -' "
94 	"hasErrCtl=Y check='set -e' ignore='set +e' "
95 	"echoFlag=v errFlag=e "
96 	"meta='" SH_META "' builtins='" SH_BUILTINS "'",
97 
98 	/*
99 	 * KSH description. The Korn shell has a superset of
100 	 * the Bourne shell's functionality. There are probably builtins
101 	 * missing here.
102 	 */
103 	"name=ksh path='" PATH_DEFSHELLDIR "/ksh' "
104 	"quiet='set -' echo='set -v' filter='set -' "
105 	"hasErrCtl=Y check='set -e' ignore='set +e' "
106 	"echoFlag=v errFlag=e "
107 	"meta='" SH_META "' builtins='" SH_BUILTINS "' unsetenv=T",
108 
109 	NULL
110 };
111 
112 /*
113  * This is the shell to which we pass all commands in the Makefile.
114  * It is set by the Job_ParseShell function.
115  */
116 struct Shell *commandShell;
117 
118 /*
119  * This is the list of all known shells.
120  */
121 static struct Shells shells = TAILQ_HEAD_INITIALIZER(shells);
122 
123 void ShellDump(const struct Shell *) __unused;
124 
125 /**
126  * Helper function for sorting the builtin list alphabetically.
127  */
128 static int
sort_builtins(const void * p1,const void * p2)129 sort_builtins(const void *p1, const void *p2)
130 {
131 
132 	return (strcmp(*(const char* const*)p1, *(const char* const*)p2));
133 }
134 
135 /**
136  * Free a shell structure and all associated strings.
137  */
138 static void
ShellFree(struct Shell * sh)139 ShellFree(struct Shell *sh)
140 {
141 
142 	if (sh != NULL) {
143 		free(sh->name);
144 		free(sh->path);
145 		free(sh->echoOff);
146 		free(sh->echoOn);
147 		free(sh->noPrint);
148 		free(sh->errCheck);
149 		free(sh->ignErr);
150 		free(sh->echo);
151 		free(sh->exit);
152 		ArgArray_Done(&sh->builtins);
153 		free(sh->meta);
154 		free(sh);
155 	}
156 }
157 
158 /**
159  * Dump a shell specification to stderr.
160  */
161 void
ShellDump(const struct Shell * sh)162 ShellDump(const struct Shell *sh)
163 {
164 	int i;
165 
166 	fprintf(stderr, "Shell %p:\n", sh);
167 	fprintf(stderr, "  name='%s' path='%s'\n", sh->name, sh->path);
168 	fprintf(stderr, "  hasEchoCtl=%d echoOff='%s' echoOn='%s'\n",
169 	    sh->hasEchoCtl, sh->echoOff, sh->echoOn);
170 	fprintf(stderr, "  noPrint='%s'\n", sh->noPrint);
171 	fprintf(stderr, "  hasErrCtl=%d errCheck='%s' ignErr='%s'\n",
172 	    sh->hasErrCtl, sh->errCheck, sh->ignErr);
173 	fprintf(stderr, "  echo='%s' exit='%s'\n", sh->echo, sh->exit);
174 	fprintf(stderr, "  builtins=%d\n", sh->builtins.argc - 1);
175 	for (i = 1; i < sh->builtins.argc; i++)
176 		fprintf(stderr, " '%s'", sh->builtins.argv[i]);
177 	fprintf(stderr, "\n  meta='%s'\n", sh->meta);
178 	fprintf(stderr, "  unsetenv=%d\n", sh->unsetenv);
179 }
180 
181 /**
182  * Parse a shell specification line and return the new Shell structure.
183  * In case of an error a message is printed and NULL is returned.
184  */
185 static struct Shell *
ShellParseSpec(const char * spec,Boolean * fullSpec)186 ShellParseSpec(const char *spec, Boolean *fullSpec)
187 {
188 	ArgArray	aa;
189 	struct Shell	*sh;
190 	char		*eq;
191 	char		*keyw;
192 	int		arg;
193 
194 	*fullSpec = FALSE;
195 
196 	sh = emalloc(sizeof(*sh));
197 	memset(sh, 0, sizeof(*sh));
198 	ArgArray_Init(&sh->builtins);
199 
200 	/*
201 	 * Parse the specification by keyword but skip the first word
202 	 */
203 	brk_string(&aa, spec, TRUE);
204 
205 	for (arg = 1; arg < aa.argc; arg++) {
206 		/*
207 		 * Split keyword and value
208 		 */
209 		keyw = aa.argv[arg];
210 		if ((eq = strchr(keyw, '=')) == NULL) {
211 			Parse_Error(PARSE_FATAL, "missing '=' in shell "
212 			    "specification keyword '%s'", keyw);
213 			ArgArray_Done(&aa);
214 			ShellFree(sh);
215 			return (NULL);
216 		}
217 		*eq++ = '\0';
218 
219 		if (strcmp(keyw, "path") == 0) {
220 			free(sh->path);
221 			sh->path = estrdup(eq);
222 		} else if (strcmp(keyw, "name") == 0) {
223 			free(sh->name);
224 			sh->name = estrdup(eq);
225 		} else if (strcmp(keyw, "quiet") == 0) {
226 			free(sh->echoOff);
227 			sh->echoOff = estrdup(eq);
228 			*fullSpec = TRUE;
229 		} else if (strcmp(keyw, "echo") == 0) {
230 			free(sh->echoOn);
231 			sh->echoOn = estrdup(eq);
232 			*fullSpec = TRUE;
233 		} else if (strcmp(keyw, "filter") == 0) {
234 			free(sh->noPrint);
235 			sh->noPrint = estrdup(eq);
236 			*fullSpec = TRUE;
237 		} else if (strcmp(keyw, "echoFlag") == 0) {
238 			free(sh->echo);
239 			sh->echo = estrdup(eq);
240 			*fullSpec = TRUE;
241 		} else if (strcmp(keyw, "errFlag") == 0) {
242 			free(sh->exit);
243 			sh->exit = estrdup(eq);
244 			*fullSpec = TRUE;
245 		} else if (strcmp(keyw, "hasErrCtl") == 0) {
246 			sh->hasErrCtl = (*eq == 'Y' || *eq == 'y' ||
247 			    *eq == 'T' || *eq == 't');
248 			*fullSpec = TRUE;
249 		} else if (strcmp(keyw, "check") == 0) {
250 			free(sh->errCheck);
251 			sh->errCheck = estrdup(eq);
252 			*fullSpec = TRUE;
253 		} else if (strcmp(keyw, "ignore") == 0) {
254 			free(sh->ignErr);
255 			sh->ignErr = estrdup(eq);
256 			*fullSpec = TRUE;
257 		} else if (strcmp(keyw, "builtins") == 0) {
258 			ArgArray_Done(&sh->builtins);
259 			brk_string(&sh->builtins, eq, TRUE);
260 			qsort(sh->builtins.argv + 1, sh->builtins.argc - 1,
261 			    sizeof(char *), sort_builtins);
262 			*fullSpec = TRUE;
263 		} else if (strcmp(keyw, "meta") == 0) {
264 			free(sh->meta);
265 			sh->meta = estrdup(eq);
266 			*fullSpec = TRUE;
267 		} else if (strcmp(keyw, "unsetenv") == 0) {
268 			sh->unsetenv = (*eq == 'Y' || *eq == 'y' ||
269 			    *eq == 'T' || *eq == 't');
270 			*fullSpec = TRUE;
271 		} else {
272 			Parse_Error(PARSE_FATAL, "unknown keyword in shell "
273 			    "specification '%s'", keyw);
274 			ArgArray_Done(&aa);
275 			ShellFree(sh);
276 			return (NULL);
277 		}
278 	}
279 	ArgArray_Done(&aa);
280 
281 	/*
282 	 * Some checks (could be more)
283 	 */
284 	if (*fullSpec) {
285 		if ((sh->echoOn != NULL) ^ (sh->echoOff != NULL)) {
286 			Parse_Error(PARSE_FATAL, "Shell must have either both "
287 			    "echoOff and echoOn or none of them");
288 			ShellFree(sh);
289 			return (NULL);
290 		}
291 
292 		if (sh->echoOn != NULL && sh->echoOff != NULL)
293 			sh->hasEchoCtl = TRUE;
294 	}
295 
296 	return (sh);
297 }
298 
299 /**
300  * Parse the builtin shell specifications and put them into the shell
301  * list. Then select the default shell to be the current shell. This
302  * is called from main() before any parsing (including MAKEFLAGS and
303  * command line) is done.
304  */
305 void
Shell_Init(void)306 Shell_Init(void)
307 {
308 	int i;
309 	struct Shell *sh;
310 	Boolean fullSpec;
311 
312 	for (i = 0; shells_init[i] != NULL; i++) {
313 		sh = ShellParseSpec(shells_init[i], &fullSpec);
314 		TAILQ_INSERT_TAIL(&shells, sh, link);
315 		if (strcmp(sh->name, DEFSHELLNAME) == 0)
316 			commandShell = sh;
317 	}
318 }
319 
320 /**
321  * Find a matching shell in 'shells' given its final component.
322  *
323  * Results:
324  *	A pointer to a freshly allocated Shell structure with the contents
325  *	from static description or NULL if no shell with the given name
326  *	is found.
327  */
328 static struct Shell *
ShellMatch(const char * name)329 ShellMatch(const char *name)
330 {
331 	struct Shell	*sh;
332 
333 	TAILQ_FOREACH(sh, &shells, link)
334 		if (strcmp(sh->name, name) == 0)
335 			return (sh);
336 
337 	return (NULL);
338 }
339 
340 /**
341  * Parse a shell specification and set up commandShell appropriately.
342  *
343  * Results:
344  *	TRUE if the specification was correct. FALSE otherwise.
345  *
346  * Side Effects:
347  *	commandShell points to a Shell structure.
348  *	created from the shell spec).
349  *
350  * Notes:
351  *	A shell specification consists of a .SHELL target, with dependency
352  *	operator, followed by a series of blank-separated words. Double
353  *	quotes can be used to use blanks in words. A backslash escapes
354  *	anything (most notably a double-quote and a space) and
355  *	provides the functionality it does in C. Each word consists of
356  *	keyword and value separated by an equal sign. There should be no
357  *	unnecessary spaces in the word. The keywords are as follows:
358  *	    name	    Name of shell.
359  *	    path	    Location of shell. Overrides "name" if given
360  *	    quiet	    Command to turn off echoing.
361  *	    echo	    Command to turn echoing on
362  *	    filter	    Result of turning off echoing that shouldn't be
363  *			    printed.
364  *	    echoFlag	    Flag to turn echoing on at the start
365  *	    errFlag	    Flag to turn error checking on at the start
366  *	    hasErrCtl	    True if shell has error checking control
367  *	    check	    Command to turn on error checking if hasErrCtl
368  *			    is TRUE or template of command to echo a command
369  *			    for which error checking is off if hasErrCtl is
370  *			    FALSE.
371  *	    ignore	    Command to turn off error checking if hasErrCtl
372  *			    is TRUE or template of command to execute a
373  *			    command so as to ignore any errors it returns if
374  *			    hasErrCtl is FALSE.
375  *	    builtins	    A space separated list of builtins. If one
376  *			    of these builtins is detected when make wants
377  *			    to execute a command line, the command line is
378  *			    handed to the shell. Otherwise make may try to
379  *			    execute the command directly. If this list is empty
380  *			    it is assumed, that the command must always be
381  *			    handed over to the shell.
382  *	    meta	    The shell meta characters. If this is not specified
383  *			    or empty, commands are alway passed to the shell.
384  *			    Otherwise they are not passed when they contain
385  *			    neither a meta character nor a builtin command.
386  *	    unsetenv	    Unsetenv("ENV") before executing anything.
387  */
388 Boolean
Shell_Parse(const char line[])389 Shell_Parse(const char line[])
390 {
391 	Boolean		fullSpec;
392 	struct Shell	*sh;
393 	struct Shell	*match;
394 
395 	/* parse the specification */
396 	if ((sh = ShellParseSpec(line, &fullSpec)) == NULL)
397 		return (FALSE);
398 
399 	if (sh->path == NULL) {
400 		/*
401 		 * If no path was given, the user wants one of the pre-defined
402 		 * shells, yes? So we find the one s/he wants with the help of
403 		 * JobMatchShell and set things up the right way.
404 		 */
405 		if (sh->name == NULL) {
406 			Parse_Error(PARSE_FATAL,
407 			    "Neither path nor name specified");
408 			ShellFree(sh);
409 			return (FALSE);
410 		}
411 		if (fullSpec) {
412 			/*
413 			 * XXX May want to merge sh into match. But this
414 			 * require ShellParseSpec to return information
415 			 * which attributes actuall have been specified.
416 			 */
417 			Parse_Error(PARSE_FATAL, "No path specified");
418 			ShellFree(sh);
419 			return (FALSE);
420 		}
421 		if ((match = ShellMatch(sh->name)) == NULL) {
422 			Parse_Error(PARSE_FATAL, "%s: no matching shell",
423 			    sh->name);
424 			ShellFree(sh);
425 			return (FALSE);
426 		}
427 		ShellFree(sh);
428 		commandShell = match;
429 
430 		return (TRUE);
431 	}
432 
433 	/*
434 	 * The user provided a path. If s/he gave nothing else
435 	 * (fullSpec is FALSE), try and find a matching shell in the
436 	 * ones we know of. Else we just take the specification at its
437 	 * word and copy it to a new location. In either case, we need
438 	 * to record the path the user gave for the shell.
439 	 */
440 	if (sh->name == NULL) {
441 		/* get the base name as the name */
442 		if ((sh->name = strrchr(sh->path, '/')) == NULL) {
443 			sh->name = estrdup(sh->path);
444 		} else {
445 			sh->name = estrdup(sh->name + 1);
446 		}
447 	}
448 
449 	if (!fullSpec) {
450 		if ((match = ShellMatch(sh->name)) == NULL) {
451 			Parse_Error(PARSE_FATAL,
452 			    "%s: no matching shell", sh->name);
453 			ShellFree(sh);
454 			return (FALSE);
455 		}
456 
457 		/* set the patch on the matching shell */
458 		free(match->path);
459 		match->path = sh->path;
460 		sh->path = NULL;
461 
462 		ShellFree(sh);
463 		commandShell = match;
464 		return (TRUE);
465 	}
466 
467 	TAILQ_INSERT_HEAD(&shells, sh, link);
468 
469 	/* set the new shell */
470 	commandShell = sh;
471 	return (TRUE);
472 }
473