bbsh.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /* vi: set ts=4 :
  2. *
  3. * bbsh - busybox shell
  4. *
  5. * Copyright 2006 Rob Landley <rob@landley.net>
  6. *
  7. * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  8. */
  9. // A section of code that gets repeatedly or conditionally executed is stored
  10. // as a string and parsed each time it's run.
  11. // Wheee, debugging.
  12. // Terminal control
  13. #define ENABLE_BBSH_TTY 0
  14. // &, fg, bg, jobs. (ctrl-z with tty.)
  15. #define ENABLE_BBSH_JOBCTL 0
  16. // Flow control (if, while, for, functions { })
  17. #define ENABLE_BBSH_FLOWCTL 0
  18. #define ENABLE_BBSH_ENVVARS 0 // Environment variable support
  19. // Local and synthetic variables, fancy prompts, set, $?, etc.
  20. #define ENABLE_BBSH_LOCALVARS 0
  21. // Pipes and redirects: | > < >> << && || & () ;
  22. #define ENABLE_BBSH_PIPES 0
  23. /* Fun:
  24. echo `echo hello#comment " woot` and more
  25. */
  26. #include "busybox.h"
  27. // A single executable, its arguments, and other information we know about it.
  28. #define BBSH_FLAG_EXIT 1
  29. #define BBSH_FLAG_SUSPEND 2
  30. #define BBSH_FLAG_PIPE 4
  31. #define BBSH_FLAG_AND 8
  32. #define BBSH_FLAG_OR 16
  33. #define BBSH_FLAG_AMP 32
  34. #define BBSH_FLAG_SEMI 64
  35. #define BBSH_FLAG_PAREN 128
  36. // What we know about a single process.
  37. struct command {
  38. struct command *next;
  39. int flags; // exit, suspend, && ||
  40. int pid; // pid (or exit code)
  41. int argc;
  42. char *argv[0];
  43. };
  44. // A collection of processes piped into/waiting on each other.
  45. struct pipeline {
  46. struct pipeline *next;
  47. int job_id;
  48. struct command *cmd;
  49. char *cmdline;
  50. int cmdlinelen;
  51. };
  52. static void free_list(void *list, void (*freeit)(void *data))
  53. {
  54. while(list) {
  55. void **next = (void **)list;
  56. void *list_next = *next;
  57. freeit(list);
  58. free(list);
  59. list = list_next;
  60. }
  61. }
  62. // Parse one word from the command line, appending one or more argv[] entries
  63. // to struct command. Handles environment variable substitution and
  64. // substrings. Returns pointer to next used byte, or NULL if it
  65. // hit an ending token.
  66. static char *parse_word(char *start, struct command **cmd)
  67. {
  68. char *end;
  69. // Detect end of line (and truncate line at comment)
  70. if (ENABLE_BBSH_PIPES && strchr("><&|(;", *start)) return 0;
  71. // Grab next word. (Add dequote and envvar logic here)
  72. end = start;
  73. while (*end && !isspace(*end)) end++;
  74. (*cmd)->argv[(*cmd)->argc++] = xstrndup(start, end-start);
  75. // Allocate more space if there's no room for NULL terminator.
  76. if (!((*cmd)->argc & 7))
  77. *cmd = xrealloc(*cmd,
  78. sizeof(struct command) + ((*cmd)->argc+8)*sizeof(char *));
  79. (*cmd)->argv[(*cmd)->argc] = 0;
  80. return end;
  81. }
  82. // Parse a line of text into a pipeline.
  83. // Returns a pointer to the next line.
  84. static char *parse_pipeline(char *cmdline, struct pipeline *line)
  85. {
  86. struct command **cmd = &(line->cmd);
  87. char *start = line->cmdline = cmdline;
  88. if (!cmdline) return 0;
  89. if (ENABLE_BBSH_JOBCTL) line->cmdline = cmdline;
  90. // Parse command into argv[]
  91. for (;;) {
  92. char *end;
  93. // Skip leading whitespace and detect end of line.
  94. start = skip_whitespace(start);
  95. if (!*start || *start=='#') {
  96. if (ENABLE_BBSH_JOBCTL) line->cmdlinelen = start-cmdline;
  97. return 0;
  98. }
  99. // Allocate next command structure if necessary
  100. if (!*cmd) *cmd = xzalloc(sizeof(struct command)+8*sizeof(char *));
  101. // Parse next argument and add the results to argv[]
  102. end = parse_word(start, cmd);
  103. // If we hit the end of this command, how did it end?
  104. if (!end) {
  105. if (ENABLE_BBSH_PIPES && *start) {
  106. if (*start==';') {
  107. start++;
  108. break;
  109. }
  110. // handle | & < > >> << || &&
  111. }
  112. break;
  113. }
  114. start = end;
  115. }
  116. if (ENABLE_BBSH_JOBCTL) line->cmdlinelen = start-cmdline;
  117. return start;
  118. }
  119. // Execute the commands in a pipeline
  120. static int run_pipeline(struct pipeline *line)
  121. {
  122. struct command *cmd = line->cmd;
  123. if (!cmd || !cmd->argc) return 0;
  124. // Handle local commands. This is totally fake and plastic.
  125. if (cmd->argc==2 && !strcmp(cmd->argv[0],"cd"))
  126. chdir(cmd->argv[1]);
  127. else if(!strcmp(cmd->argv[0],"exit"))
  128. exit(cmd->argc>1 ? atoi(cmd->argv[1]) : 0);
  129. else {
  130. int status;
  131. pid_t pid=fork();
  132. if(!pid) {
  133. run_applet_by_name(cmd->argv[0],cmd->argc,cmd->argv);
  134. execvp(cmd->argv[0],cmd->argv);
  135. printf("No %s",cmd->argv[0]);
  136. exit(1);
  137. } else waitpid(pid, &status, 0);
  138. }
  139. return 0;
  140. }
  141. static void free_cmd(void *data)
  142. {
  143. struct command *cmd=(struct command *)data;
  144. while(cmd->argc) free(cmd->argv[--cmd->argc]);
  145. }
  146. static void handle(char *command)
  147. {
  148. struct pipeline line;
  149. char *start = command;
  150. for (;;) {
  151. memset(&line,0,sizeof(struct pipeline));
  152. start = parse_pipeline(start, &line);
  153. if (!line.cmd) break;
  154. run_pipeline(&line);
  155. free_list(line.cmd, free_cmd);
  156. }
  157. }
  158. int bbsh_main(int argc, char *argv[])
  159. {
  160. char *command=NULL;
  161. FILE *f;
  162. getopt32(argc, argv, "c:", &command);
  163. f = argv[optind] ? xfopen(argv[optind],"r") : NULL;
  164. if (command) handle(command);
  165. else {
  166. unsigned cmdlen=0;
  167. for (;;) {
  168. if(!f) putchar('$');
  169. if(1 > getline(&command, &cmdlen,f ? : stdin)) break;
  170. handle(command);
  171. }
  172. if (ENABLE_FEATURE_CLEAN_UP) free(command);
  173. }
  174. return 1;
  175. }