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