Cleanup str_concat() there is no need for special flags. Just
[dragonfly.git] / usr.bin / make / shell.c
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  * $DragonFly: src/usr.bin/make/shell.c,v 1.11 2005/05/20 11:48:55 okumoto Exp $
40  */
41
42 #include <string.h>
43 #include <stdlib.h>
44
45 #include "make.h"
46 #include "parse.h"
47 #include "pathnames.h"
48 #include "shell.h"
49 #include "str.h"
50 #include "util.h"
51
52 struct Shell    *commandShell = NULL;
53
54 /**
55  * Find a matching shell in 'shells' given its final component.
56  *
57  * @result
58  *      A pointer to a Shell structure, or NULL if no shell with
59  *      the given name is found.
60  */
61 static struct Shell *
62 ShellMatch(const char name[])
63 {
64         struct Shell    *shell;
65         const char      *shellDir = PATH_DEFSHELLDIR;
66         shell = emalloc(sizeof(struct Shell));
67
68         if (strcmp(name, "csh") == 0) {
69                 /*
70                  * CSH description. The csh can do echo control by playing
71                  * with the setting of the 'echo' shell variable. Sadly,
72                  * however, it is unable to do error control nicely.
73                  */
74                 shell->name             = strdup(name);
75                 shell->path             = str_concat(shellDir, '/', name);
76                 shell->hasEchoCtl       = TRUE;
77                 shell->echoOff          = strdup("unset verbose");
78                 shell->echoOn           = strdup("set verbose");
79                 shell->noPrint          = strdup("unset verbose");
80                 shell->hasErrCtl        = FALSE;
81                 shell->errCheck         = strdup("echo \"%s\"\n");
82                 shell->ignErr           = strdup("csh -c \"%s || exit 0\"");
83                 shell->echo             = strdup("v");
84                 shell->exit             = strdup("e");
85
86         } else if (strcmp(name, "sh") == 0) {
87                 /*
88                  * SH description. Echo control is also possible and, under
89                  * sun UNIX anyway, one can even control error checking.
90                  */
91
92                 shell->name             = strdup(name);
93                 shell->path             = str_concat(shellDir, '/', name);
94                 shell->hasEchoCtl       = TRUE;
95                 shell->echoOff          = strdup("set -");
96                 shell->echoOn           = strdup("set -v");
97                 shell->noPrint          = strdup("set -");
98 #ifdef OLDBOURNESHELL
99                 shell->hasErrCtl        = FALSE;
100                 shell->errCheck         = strdup("echo \"%s\"\n");
101                 shell->ignErr           = strdup("sh -c '%s || exit 0'\n");
102 #else
103                 shell->hasErrCtl        = TRUE;
104                 shell->errCheck         = strdup("set -e");
105                 shell->ignErr           = strdup("set +e");
106 #endif
107                 shell->echo             = strdup("v");
108                 shell->exit             = strdup("e");
109         } else if (strcmp(name, "ksh") == 0) {
110                 /*
111                  * KSH description. The Korn shell has a superset of
112                  * the Bourne shell's functionality.
113                  */
114                 shell->name             = strdup(name);
115                 shell->path             = str_concat(shellDir, '/', name);
116                 shell->hasEchoCtl       = TRUE;
117                 shell->echoOff          = strdup("set -");
118                 shell->echoOn           = strdup("set -v");
119                 shell->noPrint          = strdup("set -");
120                 shell->hasErrCtl        = TRUE;
121                 shell->errCheck         = strdup("set -e");
122                 shell->ignErr           = strdup("set +e");
123                 shell->echo             = strdup("v");
124                 shell->exit             = strdup("e");
125         } else {
126                 free(shell);
127                 shell = NULL;
128         }
129
130         return (shell);
131 }
132
133 /**
134  * Make a new copy of the shell structure including a copy of the strings
135  * in it. This also defaults some fields in case they are NULL.
136  *
137  * Returns:
138  *      The function returns a pointer to the new shell structure.
139  */
140 static struct Shell *
141 ShellCopy(const struct Shell *o)
142 {
143         struct Shell *n;
144
145         n = emalloc(sizeof(struct Shell));
146         n->name         = estrdup(o->name);
147         n->path         = estrdup(o->path);
148         n->hasEchoCtl   = o->hasEchoCtl;
149         n->echoOff      = o->echoOff ? estrdup(o->echoOff) : NULL;
150         n->echoOn       = o->echoOn ? estrdup(o->echoOn) : NULL;
151         n->noPrint      = o->noPrint ? estrdup(o->noPrint) : NULL;
152         n->hasErrCtl    = o->hasErrCtl;
153         n->errCheck     = o->errCheck ? estrdup(o->errCheck) : estrdup("");
154         n->ignErr       = o->ignErr ? estrdup(o->ignErr) : estrdup("%s");
155         n->echo         = o->echo ? estrdup(o->echo) : estrdup("");
156         n->exit         = o->exit ? estrdup(o->exit) : estrdup("");
157
158         return (n);
159 }
160
161 /**
162  * Free a shell structure and all associated strings.
163  */
164 static void
165 ShellFree(struct Shell *sh)
166 {
167
168         if (sh != NULL) {
169                 free(sh->name);
170                 free(sh->echoOff);
171                 free(sh->echoOn);
172                 free(sh->noPrint);
173                 free(sh->errCheck);
174                 free(sh->ignErr);
175                 free(sh->echo);
176                 free(sh->exit);
177                 free(sh);
178         }
179 }
180
181 /**
182  * Given the line following a .SHELL target, parse the
183  * line as a shell specification. Returns FALSE if the
184  * spec was incorrect.
185  *
186  * Parse a shell specification and set up commandShell.
187  *
188  * Results:
189  *      TRUE if the specification was correct. FALSE otherwise.
190  *
191  * Side Effects:
192  *      commandShell points to a Shell structure (either predefined or
193  *      created from the shell spec)
194  *
195  * Notes:
196  *      A shell specification consists of a .SHELL target, with dependency
197  *      operator, followed by a series of blank-separated words. Double
198  *      quotes can be used to use blanks in words. A backslash escapes
199  *      anything (most notably a double-quote and a space) and
200  *      provides the functionality it does in C. Each word consists of
201  *      keyword and value separated by an equal sign. There should be no
202  *      unnecessary spaces in the word. The keywords are as follows:
203  *          name            Name of shell.
204  *          path            Location of shell. Overrides "name" if given
205  *          quiet           Command to turn off echoing.
206  *          echo            Command to turn echoing on
207  *          filter          Result of turning off echoing that shouldn't be
208  *                          printed.
209  *          echoFlag        Flag to turn echoing on at the start
210  *          errFlag         Flag to turn error checking on at the start
211  *          hasErrCtl       True if shell has error checking control
212  *          check           Command to turn on error checking if hasErrCtl
213  *                          is TRUE or template of command to echo a command
214  *                          for which error checking is off if hasErrCtl is
215  *                          FALSE.
216  *          ignore          Command to turn off error checking if hasErrCtl
217  *                          is TRUE or template of command to execute a
218  *                          command so as to ignore any errors it returns if
219  *                          hasErrCtl is FALSE.
220  */
221 Boolean
222 Shell_Parse(const char line[])
223 {
224         ArgArray        aa;
225         char            **argv;
226         int             argc;
227         Boolean         fullSpec = FALSE;
228         struct Shell    newShell;
229         struct Shell    *sh;
230
231         memset(&newShell, 0, sizeof(newShell));
232
233         /*
234          * Parse the specification by keyword but skip the first word
235          */
236         brk_string(&aa, line, TRUE);
237
238         for (argc = aa.argc - 1, argv = aa.argv + 1; argc != 0; argc--, argv++)
239         {
240                 char            *eq;
241
242                 /*
243                  * Split keyword and value
244                  */
245                 if ((eq = strchr(*argv, '=')) == NULL) {
246                         Parse_Error(PARSE_FATAL,
247                             "missing '=' in shell specification keyword '%s'",
248                             *argv);
249                         ArgArray_Done(&aa);
250                         return (FALSE);
251                 }
252                 *eq++ = '\0';
253
254                 if (strcmp(*argv, "path") == 0) {
255                         newShell.path = eq;
256                 } else if (strcmp(*argv, "name") == 0) {
257                         newShell.name = eq;
258                 } else if (strcmp(*argv, "quiet") == 0) {
259                         newShell.echoOff = eq;
260                         fullSpec = TRUE;
261                 } else if (strcmp(*argv, "echo") == 0) {
262                         newShell.echoOn = eq;
263                         fullSpec = TRUE;
264                 } else if (strcmp(*argv, "filter") == 0) {
265                         newShell.noPrint = eq;
266                         fullSpec = TRUE;
267                 } else if (strcmp(*argv, "echoFlag") == 0) {
268                         newShell.echo = eq;
269                         fullSpec = TRUE;
270                 } else if (strcmp(*argv, "errFlag") == 0) {
271                         newShell.exit = eq;
272                         fullSpec = TRUE;
273                 } else if (strcmp(*argv, "hasErrCtl") == 0) {
274                         newShell.hasErrCtl = (*eq == 'Y' || *eq == 'y' ||
275                             *eq == 'T' || *eq == 't');
276                         fullSpec = TRUE;
277                 } else if (strcmp(*argv, "check") == 0) {
278                         newShell.errCheck = eq;
279                         fullSpec = TRUE;
280                 } else if (strcmp(*argv, "ignore") == 0) {
281                         newShell.ignErr = eq;
282                         fullSpec = TRUE;
283                 } else {
284                         Parse_Error(PARSE_FATAL,
285                             "unknown keyword in shell specification '%s'",
286                             *argv);
287                         ArgArray_Done(&aa);
288                         return (FALSE);
289                 }
290         }
291
292         /*
293          * Some checks (could be more)
294          */
295         if (fullSpec) {
296                 if ((newShell.echoOn != NULL) ^ (newShell.echoOff != NULL))
297                         Parse_Error(PARSE_FATAL,
298                             "Shell must have either both "
299                             "echoOff and echoOn or none of them");
300
301                 if (newShell.echoOn != NULL && newShell.echoOff)
302                         newShell.hasEchoCtl = TRUE;
303         }
304
305         if (newShell.path == NULL) {
306                 /*
307                  * If no path was given, the user wants one of the pre-defined
308                  * shells, yes? So we find the one s/he wants with the help of
309                  * ShellMatch and set things up the right way.
310                  */
311                 if (newShell.name == NULL) {
312                         Parse_Error(PARSE_FATAL,
313                             "Neither path nor name specified");
314                         ArgArray_Done(&aa);
315                         return (FALSE);
316                 }
317                 if ((sh = ShellMatch(newShell.name)) == NULL) {
318                         Parse_Error(PARSE_FATAL, "%s: no matching shell",
319                             newShell.name);
320                         ArgArray_Done(&aa);
321                         return (FALSE);
322                 }
323
324         } else {
325                 /*
326                  * The user provided a path. If s/he gave nothing else
327                  * (fullSpec is FALSE), try and find a matching shell in the
328                  * ones we know of. Else we just take the specification at its
329                  * word and copy it to a new location. In either case, we need
330                  * to record the path the user gave for the shell.
331                  */
332                 if (newShell.name == NULL) {
333                         /* get the base name as the name */
334                         newShell.name = strrchr(newShell.path, '/');
335                         if (newShell.name == NULL) {
336                                 newShell.name = newShell.path;
337                         } else {
338                                 newShell.name += 1;
339                         }
340                 }
341
342                 if (!fullSpec) {
343                         if ((sh = ShellMatch(newShell.name)) == NULL) {
344                                 Parse_Error(PARSE_FATAL,
345                                     "%s: no matching shell", newShell.name);
346                                 ArgArray_Done(&aa);
347                                 return (FALSE);
348                         }
349                 } else {
350                         sh = ShellCopy(&newShell);
351                 }
352         }
353
354         /* Release the old shell and set the new shell */
355         ShellFree(commandShell);
356         commandShell = sh;
357
358         ArgArray_Done(&aa);
359         return (TRUE);
360 }
361
362 void
363 Shell_Init(void)
364 {
365         commandShell = ShellMatch(DEFSHELLNAME);
366 }
367