/** * Parse the builtin shell specifications and put them into the shell * list. Then select the default shell to be the current shell. This * is called from main() before any parsing (including MAKEFLAGS and * command line) is done. */ void Shell_Init(void) { int i; struct Shell *sh; Boolean fullSpec; for (i = 0; shells_init[i] != NULL; i++) { sh = ShellParseSpec(shells_init[i], &fullSpec); TAILQ_INSERT_TAIL(&shells, sh, link); if (strcmp(sh->name, DEFSHELLNAME) == 0) commandShell = sh; } }
/** * Given the line following a .SHELL target, parse it as a shell * specification. * * Results: * A pointer to a Shell structure, or NULL if no the spec was invalid. */ Shell * Shell_Parse(const char line[]) { bool fullSpec; Shell *sh; /* parse the specification */ if ((sh = ShellParseSpec(line, &fullSpec)) == NULL) return (NULL); if (sh->path == NULL) { Shell *match; /* * If no path was given, the user wants one of the pre-defined * shells, yes? So we find the one s/he wants with the help of * ShellMatch and set things up the right way. */ if (sh->name == NULL) { Parse_Error(PARSE_FATAL, "Neither path nor name specified"); Shell_Destroy(sh); return (NULL); } if (fullSpec) { Parse_Error(PARSE_FATAL, "No path specified"); Shell_Destroy(sh); return (NULL); } if ((match = Shell_Match(sh->name)) == NULL) { Parse_Error(PARSE_FATAL, "%s: no matching shell", sh->name); Shell_Destroy(sh); return (NULL); } Shell_Destroy(sh); return (match); } else { Shell *match; /* * The user provided a path. If s/he gave nothing else * (fullSpec is false), try and find a matching shell in the * ones we know of. Else we just take the specification at its * word and copy it to a new location. In either case, we need * to record the path the user gave for the shell. */ if (sh->name == NULL) { /* get the base name as the name */ if ((sh->name = strrchr(sh->path, '/')) == NULL) { sh->name = estrdup(sh->path); } else { sh->name = estrdup(sh->name + 1); } } if (fullSpec) { return (sh); } if ((match = Shell_Match(sh->name)) == NULL) { Parse_Error(PARSE_FATAL, "%s: no matching shell", sh->name); Shell_Destroy(sh); return (NULL); } free(match->path); match->path = sh->path; sh->path = NULL; Shell_Destroy(sh); return (match); } }
/** * Parse a shell specification and set up commandShell appropriately. * * Results: * TRUE if the specification was correct. FALSE otherwise. * * Side Effects: * commandShell points to a Shell structure. * created from the shell spec). * * Notes: * A shell specification consists of a .SHELL target, with dependency * operator, followed by a series of blank-separated words. Double * quotes can be used to use blanks in words. A backslash escapes * anything (most notably a double-quote and a space) and * provides the functionality it does in C. Each word consists of * keyword and value separated by an equal sign. There should be no * unnecessary spaces in the word. The keywords are as follows: * name Name of shell. * path Location of shell. Overrides "name" if given * quiet Command to turn off echoing. * echo Command to turn echoing on * filter Result of turning off echoing that shouldn't be * printed. * echoFlag Flag to turn echoing on at the start * errFlag Flag to turn error checking on at the start * hasErrCtl True if shell has error checking control * check Command to turn on error checking if hasErrCtl * is TRUE or template of command to echo a command * for which error checking is off if hasErrCtl is * FALSE. * ignore Command to turn off error checking if hasErrCtl * is TRUE or template of command to execute a * command so as to ignore any errors it returns if * hasErrCtl is FALSE. * builtins A space separated list of builtins. If one * of these builtins is detected when make wants * to execute a command line, the command line is * handed to the shell. Otherwise make may try to * execute the command directly. If this list is empty * it is assumed, that the command must always be * handed over to the shell. * meta The shell meta characters. If this is not specified * or empty, commands are alway passed to the shell. * Otherwise they are not passed when they contain * neither a meta character nor a builtin command. * unsetenv Unsetenv("ENV") before executing anything. */ Boolean Shell_Parse(const char line[]) { Boolean fullSpec; struct Shell *sh; struct Shell *match; /* parse the specification */ if ((sh = ShellParseSpec(line, &fullSpec)) == NULL) return (FALSE); if (sh->path == NULL) { /* * If no path was given, the user wants one of the pre-defined * shells, yes? So we find the one s/he wants with the help of * JobMatchShell and set things up the right way. */ if (sh->name == NULL) { Parse_Error(PARSE_FATAL, "Neither path nor name specified"); ShellFree(sh); return (FALSE); } if (fullSpec) { /* * XXX May want to merge sh into match. But this * require ShellParseSpec to return information * which attributes actuall have been specified. */ Parse_Error(PARSE_FATAL, "No path specified"); ShellFree(sh); return (FALSE); } if ((match = ShellMatch(sh->name)) == NULL) { Parse_Error(PARSE_FATAL, "%s: no matching shell", sh->name); ShellFree(sh); return (FALSE); } ShellFree(sh); commandShell = match; return (TRUE); } /* * The user provided a path. If s/he gave nothing else * (fullSpec is FALSE), try and find a matching shell in the * ones we know of. Else we just take the specification at its * word and copy it to a new location. In either case, we need * to record the path the user gave for the shell. */ if (sh->name == NULL) { /* get the base name as the name */ if ((sh->name = strrchr(sh->path, '/')) == NULL) { sh->name = estrdup(sh->path); } else { sh->name = estrdup(sh->name + 1); } } if (!fullSpec) { if ((match = ShellMatch(sh->name)) == NULL) { Parse_Error(PARSE_FATAL, "%s: no matching shell", sh->name); ShellFree(sh); return (FALSE); } /* set the patch on the matching shell */ free(match->path); match->path = sh->path; sh->path = NULL; ShellFree(sh); commandShell = match; return (TRUE); } TAILQ_INSERT_HEAD(&shells, sh, link); /* set the new shell */ commandShell = sh; return (TRUE); }