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