OSDN Git Service

Merge branch 'master' of https://android.googlesource.com/platform/external/toybox...
[android-x86/external-toybox.git] / toys / pending / sh.c
index e221960..76b4e13 100644 (file)
  *
  * 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);