diff options
Diffstat (limited to 'toys')
-rw-r--r-- | toys/pending/sh.c | 212 |
1 files changed, 79 insertions, 133 deletions
diff --git a/toys/pending/sh.c b/toys/pending/sh.c index e221960a..76b4e13b 100644 --- a/toys/pending/sh.c +++ b/toys/pending/sh.c @@ -19,7 +19,23 @@ * * Things like the bash man page are good to read too. * + * TODO: "make sh" doesn't work (nofork builtins need to be included) + * TODO: test that $PS1 color changes work without stupid \[ \] hack + * TODO: make fake pty wrapper for test infrastructure * TODO: // Handle embedded NUL bytes in the command line. + * TODO: var=val command + * existing but considered builtins: false kill pwd true + * buitins: alias bg command fc fg getopts jobs newgrp read umask unalias wait + * "special" builtins: break continue : . eval exec export readonly return set + * shift times trap unset + * | & ; < > ( ) $ ` \ " ' <space> <tab> <newline> + * * ? [ # ~ = % + * ! { } case do done elif else esac fi for if in then until while + * [[ ]] function select + * $@ $* $# $? $- $$ $! $0 + * ENV HOME IFS LANG LC_ALL LINENO PATH PPID PS1 PS2 PS4 PWD + * label: + * TODO: test exit from "trap EXIT" doesn't recurse USE_SH(NEWTOY(cd, NULL, TOYFLAG_NOFORK)) USE_SH(NEWTOY(exit, NULL, TOYFLAG_NOFORK)) @@ -42,16 +58,6 @@ config SH -c command line to execute -i interactive mode (default when STDIN is a tty) -config EXIT - bool - default n - depends on SH - help - usage: exit [status] - - Exit shell. If no return value supplied on command line, use value - of most recent command, or 0 if none. - config CD bool default n @@ -63,112 +69,16 @@ config CD -P Physical path: resolve symlinks in path. -L Local path: .. trims directories off $PWD (default). -*/ - -/* -This level of micromanagement is silly, it adds more complexity than it's -worth. (Not just to the code, but decision fatigue configuring it.) - -That said, the following list is kept for the moment as a todo list of -features I need to implement. - -config SH_PROFILE - bool "Profile support" - default n - depends on SH_TTY - help - Read /etc/profile and ~/.profile when running interactively. - - Also enables the built-in command "source". - -config SH_JOBCTL - bool "Job Control (fg, bg, jobs)" - default n - depends on SH_TTY - help - Add job control to toysh. This lets toysh handle CTRL-Z, and enables - the built-in commands "fg", "bg", and "jobs". - - With pipe support, enable use of "&" to run background processes. - -config SH_FLOWCTL - bool "Flow control (if, while, for, functions)" - default n - depends on SH - help - Add flow control to toysh. This enables the if/then/else/fi, - while/do/done, and for/do/done constructs. - - With pipe support, this enables the ability to define functions - using the "function name" or "name()" syntax, plus curly brackets - "{ }" to group commands. - -config SH_QUOTES - bool "Smarter argument parsing (quotes)" - default n - depends on SH - help - Add support for parsing "" and '' style quotes to the toysh command - parser, with lets arguments have spaces in them. - -config SH_WILDCARDS - bool "Wildcards ( ?*{,} )" - default n - depends on SH_QUOTES - help - Expand wildcards in argument names, ala "ls -l *.t?z" and - "rm subdir/{one,two,three}.txt". - -config SH_PROCARGS - bool "Executable arguments ( `` and $() )" - default n - depends on SH_QUOTES - help - Add support for executing arguments contianing $() and ``, using - the output of the command as the new argument value(s). - - (Bash calls this "command substitution".) - -config SH_ENVVARS - bool "Environment variable support" - default n - depends on SH_QUOTES - help - Substitute environment variable values for $VARNAME or ${VARNAME}, - and enable the built-in command "export". - -config SH_LOCALS - bool "Local variables" - default n - depends on SH_ENVVARS - help - Support for local variables, fancy prompts ($PS1), the "set" command, - and $?. - -config SH_ARRAYS - bool "Array variables" - default n - depends on SH_LOCALS - help - Support for ${blah[blah]} style array variables. -config SH_PIPES - bool "Pipes and redirects ( | > >> < << & && | || () ; )" +config EXIT + bool default n depends on SH help - Support multiple commands on the same command line. This includes - | pipes, > >> < redirects, << here documents, || && conditional - execution, () subshells, ; sequential execution, and (with job - control) & background processes. + usage: exit [status] -config SH_BUILTINS - bool "Builtin commands" - default n - depends on SH - help - Adds the commands exec, fg, bg, help, jobs, pwd, export, source, set, - unset, read, alias. + Exit shell. If no return value supplied on command line, use value + of most recent command, or 0 if none. */ #define FOR_sh @@ -176,17 +86,9 @@ config SH_BUILTINS GLOBALS( char *command; -) -// A single executable, its arguments, and other information we know about it. -#define SH_FLAG_EXIT 1 -#define SH_FLAG_SUSPEND 2 -#define SH_FLAG_PIPE 4 -#define SH_FLAG_AND 8 -#define SH_FLAG_OR 16 -#define SH_FLAG_AMP 32 -#define SH_FLAG_SEMI 64 -#define SH_FLAG_PAREN 128 + long lineno; +) // What we know about a single process. struct command { @@ -206,6 +108,18 @@ struct pipeline { int cmdlinelen; // How long is cmdline? }; +void cd_main(void) +{ + char *dest = *toys.optargs ? *toys.optargs : getenv("HOME"); + + xchdir(dest ? dest : "/"); +} + +void exit_main(void) +{ + exit(*toys.optargs ? atoi(*toys.optargs) : 0); +} + // Parse one word from the command line, appending one or more argv[] entries // to struct command. Handles environment variable substitution and // substrings. Returns pointer to next used byte, or NULL if it @@ -287,6 +201,7 @@ static void run_pipeline(struct pipeline *line) if (!cmd || !cmd->argc) return; tl = toy_find(cmd->argv[0]); + // Is this command a builtin that should run in this process? if (tl && (tl->flags & TOYFLAG_NOFORK)) { struct toy_context temp; @@ -351,34 +266,65 @@ static void handle(char *command) } } -void cd_main(void) +static void do_prompt(void) { - char *dest = *toys.optargs ? *toys.optargs : getenv("HOME"); + char *prompt = getenv("PS1"), *s, c, cc; - xchdir(dest ? dest : "/"); -} + if (!prompt) prompt = "\\$ "; + while (*prompt) { + c = *(prompt++); -void exit_main(void) -{ - exit(*toys.optargs ? atoi(*toys.optargs) : 0); + if (c=='!') { + if (*prompt=='!') prompt++; + else { + printf("%ld", TT.lineno); + continue; + } + } else if (c=='\\') { + cc = *(prompt++); + if (!cc) goto down; + + // \nnn \dD{}hHjlstT@AuvVwW!#$ + // Ignore bash's "nonprintable" hack; query our cursor position instead. + if (cc=='[' || cc==']') continue; + else if (cc=='$') putchar(getuid() ? '$' : '#'); + else if (cc=='h' || cc=='H') { + *toybuf = 0; + gethostname(toybuf, sizeof(toybuf)-1); + if (cc=='h' && (s = strchr(toybuf, '.'))) *s = 0; + fputs(toybuf, stdout); + } else if (cc=='s') fputs(getbasename(*toys.argv), stdout); + else { + if (!(c = unescape(cc))) { + c = '\\'; + prompt--; + } + + goto down; + } + continue; + } +down: + putchar(c); + } } void sh_main(void) { - FILE *f; + FILE *f = 0; // Set up signal handlers and grab control of this tty. if (isatty(0)) toys.optflags |= FLAG_i; - f = *toys.optargs ? xfopen(*toys.optargs, "r") : NULL; - if (TT.command) handle(TT.command); + if (*toys.optargs) f = xfopen(*toys.optargs, "r"); + if (TT.command) handle(xstrdup(TT.command)); else { size_t cmdlen = 0; for (;;) { - char *prompt = getenv("PS1"), *command = 0; + char *command = 0; // TODO: parse escapes in prompt - if (!f) printf("%s", prompt ? prompt : "$ "); + if (!f) do_prompt(); if (1 > getline(&command, &cmdlen, f ? f : stdin)) break; handle(command); free(command); |