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