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