blob: 7203ba38ff1d56e5e3af762b5f91640f778f0571 [file] [log] [blame]
Erik Andersen3522eb12000-03-12 23:49:18 +00001/* vi: set sw=4 ts=4: */
2/*
Erik Andersen6acaa402000-03-26 14:03:20 +00003 * lash -- the BusyBox Lame-Ass SHell
Erik Andersen3522eb12000-03-12 23:49:18 +00004 *
5 * Copyright (C) 2000 by Lineo, inc.
6 * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
7 *
8 * Based in part on ladsh.c by Michael K. Johnson and Erik W. Troan, which is
9 * under the following liberal license: "We have placed this source code in the
10 * public domain. Use it in any project, free or commercial."
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 *
26 */
27
28#include "internal.h"
29#include <stdio.h>
30#include <stdlib.h>
31#include <ctype.h>
32#include <errno.h>
33#include <fcntl.h>
34#include <glob.h>
35#include <signal.h>
36#include <string.h>
37#include <sys/ioctl.h>
38#include <sys/wait.h>
39#include <unistd.h>
Erik Andersenf0657d32000-04-12 17:49:52 +000040#ifdef BB_FEATURE_SH_COMMAND_EDITING
41#include "cmdedit.h"
42#endif
Erik Andersen3522eb12000-03-12 23:49:18 +000043
Eric Andersenb54833c2000-07-03 23:56:26 +000044#define MAX_READ 128 /* size of input buffer for `read' builtin */
Erik Andersen3522eb12000-03-12 23:49:18 +000045#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
46
Erik Andersend75af992000-03-16 08:09:09 +000047
48enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
Erik Andersen161220c2000-03-16 08:12:48 +000049 REDIRECT_APPEND
50};
Erik Andersen3522eb12000-03-12 23:49:18 +000051
52struct jobSet {
Erik Andersen161220c2000-03-16 08:12:48 +000053 struct job *head; /* head of list of running jobs */
54 struct job *fg; /* current foreground job */
Erik Andersen3522eb12000-03-12 23:49:18 +000055};
56
57struct redirectionSpecifier {
Erik Andersen161220c2000-03-16 08:12:48 +000058 enum redirectionType type; /* type of redirection */
59 int fd; /* file descriptor being redirected */
60 char *filename; /* file to redirect fd to */
Erik Andersen3522eb12000-03-12 23:49:18 +000061};
62
63struct childProgram {
Erik Andersen161220c2000-03-16 08:12:48 +000064 pid_t pid; /* 0 if exited */
65 char **argv; /* program name and arguments */
66 int numRedirections; /* elements in redirection array */
67 struct redirectionSpecifier *redirections; /* I/O redirections */
68 glob_t globResult; /* result of parameter globbing */
69 int freeGlob; /* should we globfree(&globResult)? */
70 int isStopped; /* is the program currently running? */
Erik Andersen3522eb12000-03-12 23:49:18 +000071};
72
73struct job {
Erik Andersen161220c2000-03-16 08:12:48 +000074 int jobId; /* job number */
75 int numProgs; /* total number of programs in job */
76 int runningProgs; /* number of programs running */
77 char *text; /* name of job */
78 char *cmdBuf; /* buffer various argv's point into */
79 pid_t pgrp; /* process group ID for the job */
80 struct childProgram *progs; /* array of programs in job */
81 struct job *next; /* to track background commands */
82 int stoppedProgs; /* number of programs alive, but stopped */
Erik Andersen3522eb12000-03-12 23:49:18 +000083};
84
85struct builtInCommand {
Erik Andersen161220c2000-03-16 08:12:48 +000086 char *cmd; /* name */
87 char *descr; /* description */
88 char *usage; /* usage */
89 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
Erik Andersen3522eb12000-03-12 23:49:18 +000090};
91
92/* Some function prototypes */
Erik Andersend75af992000-03-16 08:09:09 +000093static int shell_cd(struct job *cmd, struct jobSet *junk);
94static int shell_env(struct job *dummy, struct jobSet *junk);
95static int shell_exit(struct job *cmd, struct jobSet *junk);
96static int shell_fg_bg(struct job *cmd, struct jobSet *jobList);
97static int shell_help(struct job *cmd, struct jobSet *junk);
98static int shell_jobs(struct job *dummy, struct jobSet *jobList);
99static int shell_pwd(struct job *dummy, struct jobSet *junk);
Erik Andersen6273f652000-03-17 01:12:41 +0000100static int shell_export(struct job *cmd, struct jobSet *junk);
Erik Andersend75af992000-03-16 08:09:09 +0000101static int shell_source(struct job *cmd, struct jobSet *jobList);
102static int shell_unset(struct job *cmd, struct jobSet *junk);
Eric Andersenb54833c2000-07-03 23:56:26 +0000103static int shell_read(struct job *cmd, struct jobSet *junk);
Erik Andersen3522eb12000-03-12 23:49:18 +0000104
Erik Andersend75af992000-03-16 08:09:09 +0000105static void checkJobs(struct jobSet *jobList);
106static int getCommand(FILE * source, char *command);
107static int parseCommand(char **commandPtr, struct job *job, int *isBg);
108static int setupRedirections(struct childProgram *prog);
109static int runCommand(struct job newJob, struct jobSet *jobList, int inBg);
Erik Andersen3522eb12000-03-12 23:49:18 +0000110static int busy_loop(FILE * input);
111
Erik Andersend75af992000-03-16 08:09:09 +0000112
Erik Andersen3522eb12000-03-12 23:49:18 +0000113/* Table of built-in functions */
114static struct builtInCommand bltins[] = {
Erik Andersen161220c2000-03-16 08:12:48 +0000115 {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg},
116 {"cd", "Change working directory", "cd [dir]", shell_cd},
Erik Andersen161220c2000-03-16 08:12:48 +0000117 {"exit", "Exit from shell()", "exit", shell_exit},
118 {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
119 {"jobs", "Lists the active jobs", "jobs", shell_jobs},
Erik Andersen6273f652000-03-17 01:12:41 +0000120 {"export", "Set environment variable", "export [VAR=value]", shell_export},
Erik Andersen161220c2000-03-16 08:12:48 +0000121 {"unset", "Unset environment variable", "unset VAR", shell_unset},
Eric Andersenb54833c2000-07-03 23:56:26 +0000122 {"read", "Input environment variable", "read [VAR]", shell_read},
Erik Andersen330fd2b2000-05-19 05:35:19 +0000123 {NULL, NULL, NULL, NULL}
124};
125
126/* Table of built-in functions */
127static struct builtInCommand bltins_forking[] = {
128 {"env", "Print all environment variables", "env", shell_env},
129 {"pwd", "Print current directory", "pwd", shell_pwd},
Erik Andersenc7c634b2000-03-19 05:28:55 +0000130 {".", "Source-in and run commands in a file", ". filename", shell_source},
Erik Andersen161220c2000-03-16 08:12:48 +0000131 {"help", "List shell built-in commands", "help", shell_help},
132 {NULL, NULL, NULL, NULL}
Erik Andersen3522eb12000-03-12 23:49:18 +0000133};
134
135static const char shell_usage[] =
Eric Andersen1c314ad2000-06-28 16:56:25 +0000136 "sh [FILE]...\n"
137 " or: sh -c command [args]...\n"
Erik Andersen7ab9c7e2000-05-12 19:41:47 +0000138#ifndef BB_FEATURE_TRIVIAL_HELP
139 "\nlash: The BusyBox command interpreter (shell).\n\n"
140#endif
141 ;
Erik Andersen3522eb12000-03-12 23:49:18 +0000142
Erik Andersen3522eb12000-03-12 23:49:18 +0000143static char *prompt = "# ";
Eric Andersenb54833c2000-07-03 23:56:26 +0000144static char *cwd = NULL;
Eric Andersen1c314ad2000-06-28 16:56:25 +0000145static char *local_pending_command = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000146
Erik Andersenf0657d32000-04-12 17:49:52 +0000147#ifdef BB_FEATURE_SH_COMMAND_EDITING
148void win_changed(int sig)
149{
150 struct winsize win = { 0, 0 };
151 ioctl(0, TIOCGWINSZ, &win);
152 if (win.ws_col > 0) {
153 cmdedit_setwidth( win.ws_col - 1);
154 }
155}
156#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000157
Erik Andersen3522eb12000-03-12 23:49:18 +0000158
Erik Andersend75af992000-03-16 08:09:09 +0000159/* built-in 'cd <path>' handler */
160static int shell_cd(struct job *cmd, struct jobSet *junk)
161{
Erik Andersen161220c2000-03-16 08:12:48 +0000162 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000163
Erik Andersen161220c2000-03-16 08:12:48 +0000164 if (!cmd->progs[0].argv[1] == 1)
165 newdir = getenv("HOME");
166 else
167 newdir = cmd->progs[0].argv[1];
168 if (chdir(newdir)) {
169 printf("cd: %s: %s\n", newdir, strerror(errno));
170 return FALSE;
171 }
172 getcwd(cwd, sizeof(cwd));
173
174 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000175}
176
177/* built-in 'env' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000178static int shell_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000179{
Erik Andersen161220c2000-03-16 08:12:48 +0000180 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000181
Erik Andersen161220c2000-03-16 08:12:48 +0000182 for (e = environ; *e; e++) {
183 fprintf(stdout, "%s\n", *e);
184 }
185 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000186}
187
188/* built-in 'exit' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000189static int shell_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000190{
Erik Andersen161220c2000-03-16 08:12:48 +0000191 if (!cmd->progs[0].argv[1] == 1)
192 exit TRUE;
193
Eric Andersenb6106152000-06-19 17:25:40 +0000194 return(atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000195}
196
197/* built-in 'fg' and 'bg' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000198static int shell_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000199{
Erik Andersen161220c2000-03-16 08:12:48 +0000200 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000201 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000202
Erik Andersen161220c2000-03-16 08:12:48 +0000203 if (!jobList->head) {
204 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
205 fprintf(stderr, "%s: exactly one argument is expected\n",
206 cmd->progs[0].argv[0]);
207 return FALSE;
208 }
209 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
210 fprintf(stderr, "%s: bad argument '%s'\n",
211 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
212 return FALSE;
213 for (job = jobList->head; job; job = job->next) {
214 if (job->jobId == jobNum) {
215 break;
216 }
217 }
218 }
219 } else {
220 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000221 }
Erik Andersen161220c2000-03-16 08:12:48 +0000222
223 if (!job) {
224 fprintf(stderr, "%s: unknown job %d\n",
225 cmd->progs[0].argv[0], jobNum);
226 return FALSE;
Erik Andersend75af992000-03-16 08:09:09 +0000227 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000228
Erik Andersen161220c2000-03-16 08:12:48 +0000229 if (*cmd->progs[0].argv[0] == 'f') {
230 /* Make this job the foreground job */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000231 /* suppress messages when run from /linuxrc mag@sysgo.de */
232 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
233 perror("tcsetpgrp");
Erik Andersen161220c2000-03-16 08:12:48 +0000234 jobList->fg = job;
235 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000236
Erik Andersen161220c2000-03-16 08:12:48 +0000237 /* Restart the processes in the job */
238 for (i = 0; i < job->numProgs; i++)
239 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000240
Erik Andersen161220c2000-03-16 08:12:48 +0000241 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000242
Erik Andersen161220c2000-03-16 08:12:48 +0000243 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000244
Erik Andersen161220c2000-03-16 08:12:48 +0000245 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000246}
247
248/* built-in 'help' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000249static int shell_help(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000250{
Erik Andersen161220c2000-03-16 08:12:48 +0000251 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000252
Erik Andersen161220c2000-03-16 08:12:48 +0000253 fprintf(stdout, "\nBuilt-in commands:\n");
254 fprintf(stdout, "-------------------\n");
255 for (x = bltins; x->cmd; x++) {
256 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
257 }
Erik Andersen330fd2b2000-05-19 05:35:19 +0000258 for (x = bltins_forking; x->cmd; x++) {
259 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
260 }
Erik Andersen161220c2000-03-16 08:12:48 +0000261 fprintf(stdout, "\n\n");
262 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000263}
264
265/* built-in 'jobs' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000266static int shell_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000267{
Erik Andersen161220c2000-03-16 08:12:48 +0000268 struct job *job;
269 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000270
Erik Andersen161220c2000-03-16 08:12:48 +0000271 for (job = jobList->head; job; job = job->next) {
272 if (job->runningProgs == job->stoppedProgs)
273 statusString = "Stopped";
274 else
275 statusString = "Running";
276
277 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
278 }
279 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000280}
281
282
283/* built-in 'pwd' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000284static int shell_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000285{
Erik Andersen161220c2000-03-16 08:12:48 +0000286 getcwd(cwd, sizeof(cwd));
287 fprintf(stdout, "%s\n", cwd);
288 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000289}
290
Erik Andersen6273f652000-03-17 01:12:41 +0000291/* built-in 'export VAR=value' handler */
292static int shell_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000293{
Erik Andersen161220c2000-03-16 08:12:48 +0000294 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000295
Erik Andersen161220c2000-03-16 08:12:48 +0000296 if (!cmd->progs[0].argv[1] == 1) {
297 return (shell_env(cmd, junk));
298 }
299 res = putenv(cmd->progs[0].argv[1]);
300 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000301 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000302 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000303}
304
Eric Andersenb54833c2000-07-03 23:56:26 +0000305/* built-in 'read VAR' handler */
306static int shell_read(struct job *cmd, struct jobSet *junk)
307{
308 int res = 0, len, newlen;
309 char *s;
310 char string[MAX_READ];
311
312 if (cmd->progs[0].argv[1]) {
313 /* argument (VAR) given: put "VAR=" into buffer */
314 strcpy(string, cmd->progs[0].argv[1]);
315 len = strlen(string);
316 string[len++] = '=';
317 string[len] = '\0';
318 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
319 newlen = strlen(string);
320 if(newlen > len)
321 string[--newlen] = '\0'; /* chomp trailing newline */
322 /*
323 ** string should now contain "VAR=<value>"
324 ** copy it (putenv() won't do that, so we must make sure
325 ** the string resides in a static buffer!)
326 */
327 res = -1;
328 if((s = strdup(string)))
329 res = putenv(s);
330 if (res)
331 fprintf(stdout, "read: %s\n", strerror(errno));
332 }
333 else
334 fgets(string, sizeof(string), stdin);
335
336 return (res);
337}
338
Erik Andersen3522eb12000-03-12 23:49:18 +0000339/* Built-in '.' handler (read-in and execute commands from file) */
Erik Andersend75af992000-03-16 08:09:09 +0000340static int shell_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000341{
Erik Andersen161220c2000-03-16 08:12:48 +0000342 FILE *input;
343 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000344
Erik Andersen161220c2000-03-16 08:12:48 +0000345 if (!cmd->progs[0].argv[1] == 1)
346 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000347
Erik Andersen161220c2000-03-16 08:12:48 +0000348 input = fopen(cmd->progs[0].argv[1], "r");
349 if (!input) {
350 fprintf(stdout, "Couldn't open file '%s'\n",
351 cmd->progs[0].argv[1]);
352 return FALSE;
353 }
Erik Andersend75af992000-03-16 08:09:09 +0000354
Erik Andersen161220c2000-03-16 08:12:48 +0000355 /* Now run the file */
356 status = busy_loop(input);
357 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000358}
359
360/* built-in 'unset VAR' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000361static int shell_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000362{
Erik Andersen161220c2000-03-16 08:12:48 +0000363 if (!cmd->progs[0].argv[1] == 1) {
364 fprintf(stdout, "unset: parameter required.\n");
365 return FALSE;
366 }
367 unsetenv(cmd->progs[0].argv[1]);
368 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000369}
370
371/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000372static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000373{
Erik Andersen161220c2000-03-16 08:12:48 +0000374 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000375
Erik Andersen161220c2000-03-16 08:12:48 +0000376 for (i = 0; i < cmd->numProgs; i++) {
377 free(cmd->progs[i].argv);
378 if (cmd->progs[i].redirections)
379 free(cmd->progs[i].redirections);
380 if (cmd->progs[i].freeGlob)
381 globfree(&cmd->progs[i].globResult);
382 }
383 free(cmd->progs);
384 if (cmd->text)
385 free(cmd->text);
386 free(cmd->cmdBuf);
Erik Andersen3522eb12000-03-12 23:49:18 +0000387}
388
389/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000390static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000391{
Erik Andersen161220c2000-03-16 08:12:48 +0000392 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000393
Erik Andersen161220c2000-03-16 08:12:48 +0000394 freeJob(job);
395 if (job == jobList->head) {
396 jobList->head = job->next;
397 } else {
398 prevJob = jobList->head;
399 while (prevJob->next != job)
400 prevJob = prevJob->next;
401 prevJob->next = job->next;
402 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000403
Erik Andersen161220c2000-03-16 08:12:48 +0000404 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000405}
406
407/* Checks to see if any background processes have exited -- if they
408 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000409static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000410{
Erik Andersen161220c2000-03-16 08:12:48 +0000411 struct job *job;
412 pid_t childpid;
413 int status;
414 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000415
Erik Andersen161220c2000-03-16 08:12:48 +0000416 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
417 for (job = jobList->head; job; job = job->next) {
418 progNum = 0;
419 while (progNum < job->numProgs &&
420 job->progs[progNum].pid != childpid) progNum++;
421 if (progNum < job->numProgs)
422 break;
423 }
424
425 if (WIFEXITED(status) || WIFSIGNALED(status)) {
426 /* child exited */
427 job->runningProgs--;
428 job->progs[progNum].pid = 0;
429
430 if (!job->runningProgs) {
431 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
432 removeJob(jobList, job);
433 }
434 } else {
435 /* child stopped */
436 job->stoppedProgs++;
437 job->progs[progNum].isStopped = 1;
438
439 if (job->stoppedProgs == job->numProgs) {
440 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
441 job->text);
442 }
443 }
Erik Andersend75af992000-03-16 08:09:09 +0000444 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000445
Erik Andersen161220c2000-03-16 08:12:48 +0000446 if (childpid == -1 && errno != ECHILD)
447 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000448}
449
Erik Andersend75af992000-03-16 08:09:09 +0000450static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000451{
Eric Andersen1c314ad2000-06-28 16:56:25 +0000452 if (source == NULL) {
453 if (local_pending_command) {
454 /* a command specified (-c option): return it & mark it done */
455 strcpy(command, local_pending_command);
456 free(local_pending_command);
457 local_pending_command = NULL;
458 return 0;
459 }
460 return 1;
461 }
462
Erik Andersen161220c2000-03-16 08:12:48 +0000463 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000464#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000465 int len;
466 char *promptStr;
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000467 len=fprintf(stdout, "%s %s", cwd, prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000468 fflush(stdout);
469 promptStr=(char*)malloc(sizeof(char)*(len+1));
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000470 sprintf(promptStr, "%s %s", cwd, prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000471 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000472 free( promptStr);
Erik Andersen161220c2000-03-16 08:12:48 +0000473 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000474#else
475 fprintf(stdout, "%s %s", cwd, prompt);
476 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000477#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000478 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000479
Erik Andersen161220c2000-03-16 08:12:48 +0000480 if (!fgets(command, BUFSIZ - 2, source)) {
481 if (source == stdin)
482 printf("\n");
483 return 1;
484 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000485
Erik Andersen161220c2000-03-16 08:12:48 +0000486 /* remove trailing newline */
487 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000488
Erik Andersen161220c2000-03-16 08:12:48 +0000489 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000490}
491
Erik Andersend75af992000-03-16 08:09:09 +0000492static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000493 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000494{
Erik Andersen161220c2000-03-16 08:12:48 +0000495 int argc = *argcPtr;
496 int argcAlloced = *argcAllocedPtr;
497 int rc;
498 int flags;
499 int i;
Eric Andersenb54833c2000-07-03 23:56:26 +0000500 char *src, *dst, *var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000501
Erik Andersen161220c2000-03-16 08:12:48 +0000502 if (argc > 1) { /* cmd->globResult is already initialized */
503 flags = GLOB_APPEND;
504 i = prog->globResult.gl_pathc;
505 } else {
506 prog->freeGlob = 1;
507 flags = 0;
508 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000509 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000510 /* do shell variable substitution */
511 if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
512 prog->argv[argc - 1] = var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000513
Erik Andersen161220c2000-03-16 08:12:48 +0000514 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
515 if (rc == GLOB_NOSPACE) {
516 fprintf(stderr, "out of space during glob operation\n");
517 return;
518 } else if (rc == GLOB_NOMATCH ||
519 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
520 !strcmp(prog->argv[argc - 1],
521 prog->globResult.gl_pathv[i]))) {
522 /* we need to remove whatever \ quoting is still present */
523 src = dst = prog->argv[argc - 1];
524 while (*src) {
525 if (*src != '\\')
526 *dst++ = *src;
527 src++;
528 }
529 *dst = '\0';
530 } else if (!rc) {
531 argcAlloced += (prog->globResult.gl_pathc - i);
532 prog->argv =
533 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
534 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
535 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
536 argc += (prog->globResult.gl_pathc - i - 1);
537 }
538
539 *argcAllocedPtr = argcAlloced;
540 *argcPtr = argc;
Erik Andersen3522eb12000-03-12 23:49:18 +0000541}
542
543/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
544 line). If a valid command is found, commandPtr is set to point to
545 the beginning of the next command (if the original command had more
546 then one job associated with it) or NULL if no more commands are
547 present. */
Erik Andersend75af992000-03-16 08:09:09 +0000548static int parseCommand(char **commandPtr, struct job *job, int *isBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000549{
Erik Andersen161220c2000-03-16 08:12:48 +0000550 char *command;
551 char *returnCommand = NULL;
552 char *src, *buf, *chptr;
553 int argc = 0;
554 int done = 0;
555 int argvAlloced;
556 int i;
557 char quote = '\0';
558 int count;
559 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000560
Erik Andersen161220c2000-03-16 08:12:48 +0000561 /* skip leading white space */
562 while (**commandPtr && isspace(**commandPtr))
563 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000564
Erik Andersen161220c2000-03-16 08:12:48 +0000565 /* this handles empty lines or leading '#' characters */
566 if (!**commandPtr || (**commandPtr == '#')) {
567 job->numProgs = 0;
568 *commandPtr = NULL;
569 return 0;
570 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000571
Erik Andersen161220c2000-03-16 08:12:48 +0000572 *isBg = 0;
573 job->numProgs = 1;
574 job->progs = malloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000575
Erik Andersen161220c2000-03-16 08:12:48 +0000576 /* We set the argv elements to point inside of this string. The
Eric Andersenb54833c2000-07-03 23:56:26 +0000577 memory is freed by freeJob(). Allocate twice the original
578 length in case we need to quote every single character.
Erik Andersen3522eb12000-03-12 23:49:18 +0000579
Erik Andersen161220c2000-03-16 08:12:48 +0000580 Getting clean memory relieves us of the task of NULL
581 terminating things and makes the rest of this look a bit
582 cleaner (though it is, admittedly, a tad less efficient) */
Eric Andersenb54833c2000-07-03 23:56:26 +0000583 job->cmdBuf = command = calloc(1, 2*strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000584 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000585
Erik Andersen161220c2000-03-16 08:12:48 +0000586 prog = job->progs;
587 prog->numRedirections = 0;
588 prog->redirections = NULL;
589 prog->freeGlob = 0;
590 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000591
Erik Andersen161220c2000-03-16 08:12:48 +0000592 argvAlloced = 5;
593 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
594 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000595
Erik Andersen161220c2000-03-16 08:12:48 +0000596 buf = command;
597 src = *commandPtr;
598 while (*src && !done) {
599 if (quote == *src) {
600 quote = '\0';
601 } else if (quote) {
602 if (*src == '\\') {
603 src++;
604 if (!*src) {
605 fprintf(stderr, "character expected after \\\n");
606 freeJob(job);
607 return 1;
608 }
609
610 /* in shell, "\'" should yield \' */
611 if (*src != quote)
612 *buf++ = '\\';
613 } else if (*src == '*' || *src == '?' || *src == '[' ||
614 *src == ']') *buf++ = '\\';
615 *buf++ = *src;
616 } else if (isspace(*src)) {
617 if (*prog->argv[argc]) {
618 buf++, argc++;
619 /* +1 here leaves room for the NULL which ends argv */
620 if ((argc + 1) == argvAlloced) {
621 argvAlloced += 5;
622 prog->argv = realloc(prog->argv,
623 sizeof(*prog->argv) *
624 argvAlloced);
625 }
Erik Andersen161220c2000-03-16 08:12:48 +0000626 globLastArgument(prog, &argc, &argvAlloced);
Eric Andersenb54833c2000-07-03 23:56:26 +0000627 prog->argv[argc] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000628 }
629 } else
630 switch (*src) {
631 case '"':
632 case '\'':
633 quote = *src;
634 break;
635
636 case '#': /* comment */
637 done = 1;
638 break;
639
640 case '>': /* redirections */
641 case '<':
642 i = prog->numRedirections++;
643 prog->redirections = realloc(prog->redirections,
644 sizeof(*prog->redirections) *
645 (i + 1));
646
647 prog->redirections[i].fd = -1;
648 if (buf != prog->argv[argc]) {
649 /* the stuff before this character may be the file number
650 being redirected */
651 prog->redirections[i].fd =
652 strtol(prog->argv[argc], &chptr, 10);
653
654 if (*chptr && *prog->argv[argc]) {
655 buf++, argc++;
656 globLastArgument(prog, &argc, &argvAlloced);
Eric Andersenb54833c2000-07-03 23:56:26 +0000657 prog->argv[argc] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000658 }
659 }
660
661 if (prog->redirections[i].fd == -1) {
662 if (*src == '>')
663 prog->redirections[i].fd = 1;
664 else
665 prog->redirections[i].fd = 0;
666 }
667
668 if (*src++ == '>') {
669 if (*src == '>')
670 prog->redirections[i].type =
671 REDIRECT_APPEND, src++;
672 else
673 prog->redirections[i].type = REDIRECT_OVERWRITE;
674 } else {
675 prog->redirections[i].type = REDIRECT_INPUT;
676 }
677
678 /* This isn't POSIX sh compliant. Oh well. */
679 chptr = src;
680 while (isspace(*chptr))
681 chptr++;
682
683 if (!*chptr) {
684 fprintf(stderr, "file name expected after %c\n", *src);
685 freeJob(job);
686 return 1;
687 }
688
689 prog->redirections[i].filename = buf;
690 while (*chptr && !isspace(*chptr))
691 *buf++ = *chptr++;
692
693 src = chptr - 1; /* we src++ later */
694 prog->argv[argc] = ++buf;
695 break;
696
697 case '|': /* pipe */
698 /* finish this command */
699 if (*prog->argv[argc])
700 argc++;
701 if (!argc) {
702 fprintf(stderr, "empty command in pipe\n");
703 freeJob(job);
704 return 1;
705 }
706 prog->argv[argc] = NULL;
707
708 /* and start the next */
709 job->numProgs++;
710 job->progs = realloc(job->progs,
711 sizeof(*job->progs) * job->numProgs);
712 prog = job->progs + (job->numProgs - 1);
713 prog->numRedirections = 0;
714 prog->redirections = NULL;
715 prog->freeGlob = 0;
716 argc = 0;
717
718 argvAlloced = 5;
719 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
720 prog->argv[0] = ++buf;
721
722 src++;
723 while (*src && isspace(*src))
724 src++;
725
726 if (!*src) {
727 fprintf(stderr, "empty command in pipe\n");
728 return 1;
729 }
730 src--; /* we'll ++ it at the end of the loop */
731
732 break;
733
734 case '&': /* background */
735 *isBg = 1;
736 case ';': /* multiple commands */
737 done = 1;
738 returnCommand = *commandPtr + (src - *commandPtr) + 1;
739 break;
740
741 case '\\':
742 src++;
743 if (!*src) {
744 freeJob(job);
745 fprintf(stderr, "character expected after \\\n");
746 return 1;
747 }
748 if (*src == '*' || *src == '[' || *src == ']'
749 || *src == '?') *buf++ = '\\';
750 /* fallthrough */
751 default:
752 *buf++ = *src;
753 }
754
Erik Andersend75af992000-03-16 08:09:09 +0000755 src++;
Erik Andersen161220c2000-03-16 08:12:48 +0000756 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000757
Erik Andersen161220c2000-03-16 08:12:48 +0000758 if (*prog->argv[argc]) {
759 argc++;
Erik Andersend75af992000-03-16 08:09:09 +0000760 globLastArgument(prog, &argc, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000761 }
762 if (!argc) {
763 freeJob(job);
764 return 0;
765 }
766 prog->argv[argc] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000767
Erik Andersen161220c2000-03-16 08:12:48 +0000768 if (!returnCommand) {
769 job->text = malloc(strlen(*commandPtr) + 1);
770 strcpy(job->text, *commandPtr);
771 } else {
772 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +0000773 count = returnCommand - *commandPtr;
774 job->text = malloc(count + 1);
775 strncpy(job->text, *commandPtr, count);
776 job->text[count] = '\0';
777 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000778
Erik Andersen161220c2000-03-16 08:12:48 +0000779 *commandPtr = returnCommand;
Erik Andersen3522eb12000-03-12 23:49:18 +0000780
Erik Andersend75af992000-03-16 08:09:09 +0000781 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000782}
783
Erik Andersenbcd61772000-05-13 06:33:19 +0000784
Erik Andersend75af992000-03-16 08:09:09 +0000785static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000786{
Erik Andersen161220c2000-03-16 08:12:48 +0000787 struct job *job;
788 int i;
789 int nextin, nextout;
790 int pipefds[2]; /* pipefd[0] is for reading */
791 struct builtInCommand *x;
Eric Andersenb54833c2000-07-03 23:56:26 +0000792#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Erik Andersenbcd61772000-05-13 06:33:19 +0000793 const struct BB_applet *a = applets;
794#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000795
Erik Andersen3522eb12000-03-12 23:49:18 +0000796
Erik Andersen161220c2000-03-16 08:12:48 +0000797 nextin = 0, nextout = 1;
798 for (i = 0; i < newJob.numProgs; i++) {
799 if ((i + 1) < newJob.numProgs) {
800 pipe(pipefds);
801 nextout = pipefds[1];
802 } else {
803 nextout = 1;
804 }
805
Erik Andersen330fd2b2000-05-19 05:35:19 +0000806 /* Match any built-ins here */
807 for (x = bltins; x->cmd; x++) {
808 if (!strcmp(newJob.progs[i].argv[0], x->cmd)) {
809 return (x->function(&newJob, jobList));
810 }
811 }
812
Erik Andersen161220c2000-03-16 08:12:48 +0000813 if (!(newJob.progs[i].pid = fork())) {
814 signal(SIGTTOU, SIG_DFL);
815
816 if (nextin != 0) {
817 dup2(nextin, 0);
818 close(nextin);
819 }
820
821 if (nextout != 1) {
822 dup2(nextout, 1);
823 close(nextout);
824 }
825
826 /* explicit redirections override pipes */
827 setupRedirections(newJob.progs + i);
828
Erik Andersenbcd61772000-05-13 06:33:19 +0000829 /* Match any built-ins here */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000830 for (x = bltins_forking; x->cmd; x++) {
Erik Andersenbcd61772000-05-13 06:33:19 +0000831 if (!strcmp(newJob.progs[i].argv[0], x->cmd)) {
832 exit (x->function(&newJob, jobList));
833 }
834 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000835#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Erik Andersenbcd61772000-05-13 06:33:19 +0000836 /* Handle busybox internals here */
837 while (a->name != 0) {
838 if (strcmp(newJob.progs[i].argv[0], a->name) == 0) {
839 int argc;
840 char** argv=newJob.progs[i].argv;
Erik Andersenc3f5c9c2000-05-13 19:00:07 +0000841 for(argc=0;*argv!=NULL; argv++, argc++);
Erik Andersenbcd61772000-05-13 06:33:19 +0000842 exit((*(a->main)) (argc, newJob.progs[i].argv));
843 }
844 a++;
845 }
846#endif
847
Erik Andersen161220c2000-03-16 08:12:48 +0000848 execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
849 fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
850 strerror(errno));
851 }
852
853 /* put our child in the process group whose leader is the
854 first process in this pipe */
855 setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
856
857 if (nextin != 0)
858 close(nextin);
859 if (nextout != 1)
860 close(nextout);
861
862 /* If there isn't another process, nextin is garbage
863 but it doesn't matter */
864 nextin = pipefds[0];
865 }
866
867 newJob.pgrp = newJob.progs[0].pid;
868
869 /* find the ID for the job to use */
870 newJob.jobId = 1;
871 for (job = jobList->head; job; job = job->next)
872 if (job->jobId >= newJob.jobId)
873 newJob.jobId = job->jobId + 1;
874
875 /* add the job to the list of running jobs */
876 if (!jobList->head) {
877 job = jobList->head = malloc(sizeof(*job));
Erik Andersend75af992000-03-16 08:09:09 +0000878 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000879 for (job = jobList->head; job->next; job = job->next);
880 job->next = malloc(sizeof(*job));
881 job = job->next;
Erik Andersend75af992000-03-16 08:09:09 +0000882 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000883
Erik Andersen161220c2000-03-16 08:12:48 +0000884 *job = newJob;
885 job->next = NULL;
886 job->runningProgs = job->numProgs;
887 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000888
Erik Andersen161220c2000-03-16 08:12:48 +0000889 if (inBg) {
890 /* we don't wait for background jobs to return -- append it
891 to the list of backgrounded jobs and leave it alone */
Erik Andersen161220c2000-03-16 08:12:48 +0000892 printf("[%d] %d\n", job->jobId,
893 newJob.progs[newJob.numProgs - 1].pid);
894 } else {
895 jobList->fg = job;
Erik Andersen3522eb12000-03-12 23:49:18 +0000896
Erik Andersen161220c2000-03-16 08:12:48 +0000897 /* move the new process group into the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000898 /* suppress messages when run from /linuxrc mag@sysgo.de */
899 if (tcsetpgrp(0, newJob.pgrp) && errno != ENOTTY)
Erik Andersen161220c2000-03-16 08:12:48 +0000900 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000901 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000902
Erik Andersen161220c2000-03-16 08:12:48 +0000903 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000904}
905
Erik Andersend75af992000-03-16 08:09:09 +0000906static int setupRedirections(struct childProgram *prog)
Erik Andersen3522eb12000-03-12 23:49:18 +0000907{
Erik Andersen161220c2000-03-16 08:12:48 +0000908 int i;
909 int openfd;
910 int mode = O_RDONLY;
911 struct redirectionSpecifier *redir = prog->redirections;
Erik Andersen3522eb12000-03-12 23:49:18 +0000912
Erik Andersen161220c2000-03-16 08:12:48 +0000913 for (i = 0; i < prog->numRedirections; i++, redir++) {
914 switch (redir->type) {
915 case REDIRECT_INPUT:
916 mode = O_RDONLY;
917 break;
918 case REDIRECT_OVERWRITE:
919 mode = O_RDWR | O_CREAT | O_TRUNC;
920 break;
921 case REDIRECT_APPEND:
922 mode = O_RDWR | O_CREAT | O_APPEND;
923 break;
924 }
925
926 openfd = open(redir->filename, mode, 0666);
927 if (openfd < 0) {
928 /* this could get lost if stderr has been redirected, but
929 bash and ash both lose it as well (though zsh doesn't!) */
930 fprintf(stderr, "error opening %s: %s\n", redir->filename,
931 strerror(errno));
932 return 1;
933 }
934
935 if (openfd != redir->fd) {
936 dup2(openfd, redir->fd);
937 close(openfd);
938 }
Erik Andersend75af992000-03-16 08:09:09 +0000939 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000940
Erik Andersen161220c2000-03-16 08:12:48 +0000941 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000942}
943
944
Erik Andersend75af992000-03-16 08:09:09 +0000945static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +0000946{
Erik Andersen161220c2000-03-16 08:12:48 +0000947 char *command;
948 char *nextCommand = NULL;
949 struct jobSet jobList = { NULL, NULL };
950 struct job newJob;
Eric Andersen1c314ad2000-06-28 16:56:25 +0000951 pid_t parent_pgrp;
Erik Andersen161220c2000-03-16 08:12:48 +0000952 int i;
953 int status;
954 int inBg;
Erik Andersen3522eb12000-03-12 23:49:18 +0000955
Eric Andersen1c314ad2000-06-28 16:56:25 +0000956 /* save current owner of TTY so we can restore it on exit */
957 parent_pgrp = tcgetpgrp(0);
958
Erik Andersen161220c2000-03-16 08:12:48 +0000959 command = (char *) calloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +0000960
Erik Andersen161220c2000-03-16 08:12:48 +0000961 /* don't pay any attention to this signal; it just confuses
962 things and isn't really meant for shells anyway */
963 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +0000964
Erik Andersen161220c2000-03-16 08:12:48 +0000965 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +0000966 if (!jobList.fg) {
967 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +0000968
Erik Andersend75af992000-03-16 08:09:09 +0000969 /* see if any background processes have exited */
970 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +0000971
Erik Andersend75af992000-03-16 08:09:09 +0000972 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +0000973 if (getCommand(input, command))
974 break;
975 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +0000976 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000977
Erik Andersend75af992000-03-16 08:09:09 +0000978 if (!parseCommand(&nextCommand, &newJob, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +0000979 newJob.numProgs) {
980 runCommand(newJob, &jobList, inBg);
Erik Andersend75af992000-03-16 08:09:09 +0000981 }
982 } else {
983 /* a job is running in the foreground; wait for it */
984 i = 0;
985 while (!jobList.fg->progs[i].pid ||
Erik Andersen161220c2000-03-16 08:12:48 +0000986 jobList.fg->progs[i].isStopped) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000987
Erik Andersend75af992000-03-16 08:09:09 +0000988 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +0000989
Erik Andersend75af992000-03-16 08:09:09 +0000990 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +0000991 /* the child exited */
992 jobList.fg->runningProgs--;
993 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000994
Erik Andersen161220c2000-03-16 08:12:48 +0000995 if (!jobList.fg->runningProgs) {
996 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +0000997
Erik Andersen161220c2000-03-16 08:12:48 +0000998 removeJob(&jobList, jobList.fg);
999 jobList.fg = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001000 }
Erik Andersend75af992000-03-16 08:09:09 +00001001 } else {
Erik Andersen161220c2000-03-16 08:12:48 +00001002 /* the child was stopped */
1003 jobList.fg->stoppedProgs++;
1004 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +00001005
Erik Andersen161220c2000-03-16 08:12:48 +00001006 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1007 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1008 "Stopped", jobList.fg->text);
1009 jobList.fg = NULL;
1010 }
Erik Andersend75af992000-03-16 08:09:09 +00001011 }
1012
1013 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +00001014 /* move the shell to the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001015 /* suppress messages when run from /linuxrc mag@sysgo.de */
1016 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1017 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001018 }
1019 }
1020 }
Erik Andersen161220c2000-03-16 08:12:48 +00001021 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +00001022
Eric Andersen1c314ad2000-06-28 16:56:25 +00001023 /* return controlling TTY back to parent process group before exiting */
1024 if (tcsetpgrp(0, parent_pgrp))
Eric Andersenb54833c2000-07-03 23:56:26 +00001025 perror("tcsetpgrp");
1026
1027 /* return exit status if called with "-c" */
1028 if (input == NULL && WIFEXITED(status))
1029 return WEXITSTATUS(status);
1030
Erik Andersen161220c2000-03-16 08:12:48 +00001031 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001032}
1033
1034
Erik Andersend75af992000-03-16 08:09:09 +00001035int shell_main(int argc, char **argv)
Erik Andersen3522eb12000-03-12 23:49:18 +00001036{
Erik Andersen161220c2000-03-16 08:12:48 +00001037 FILE *input = stdin;
Erik Andersen3522eb12000-03-12 23:49:18 +00001038
Erik Andersen161220c2000-03-16 08:12:48 +00001039 /* initialize the cwd */
Eric Andersenb54833c2000-07-03 23:56:26 +00001040 cwd = (char *) calloc(BUFSIZ, sizeof(char));
1041 if (cwd == 0) {
1042 fatalError("sh: out of memory\n");
1043 }
1044 getcwd(cwd, sizeof(char)*BUFSIZ);
Erik Andersen3522eb12000-03-12 23:49:18 +00001045
Erik Andersenf0657d32000-04-12 17:49:52 +00001046#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersen1d1d9502000-04-21 01:26:49 +00001047 cmdedit_init();
Erik Andersenf0657d32000-04-12 17:49:52 +00001048 signal(SIGWINCH, win_changed);
1049 win_changed(0);
1050#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001051
Erik Andersen161220c2000-03-16 08:12:48 +00001052 //if (argv[0] && argv[0][0] == '-') {
1053 // shell_source("/etc/profile");
1054 //}
Erik Andersen3522eb12000-03-12 23:49:18 +00001055
Erik Andersen161220c2000-03-16 08:12:48 +00001056 if (argc < 2) {
Erik Andersenf0657d32000-04-12 17:49:52 +00001057 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1058 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Erik Andersen161220c2000-03-16 08:12:48 +00001059 } else {
Eric Andersen1c314ad2000-06-28 16:56:25 +00001060 if (argv[1][0]=='-' && argv[1][1]=='c') {
1061 int i;
1062 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1063 if (local_pending_command == 0) {
1064 fatalError("sh: out of memory\n");
1065 }
1066 for(i=2; i<argc; i++)
1067 {
1068 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
Eric Andersenb54833c2000-07-03 23:56:26 +00001069 local_pending_command = realloc(local_pending_command,
1070 strlen(local_pending_command) + strlen(argv[i]));
1071 if (local_pending_command==NULL)
1072 fatalError("sh: commands for -c option too long\n");
Eric Andersen1c314ad2000-06-28 16:56:25 +00001073 }
1074 strcat(local_pending_command, argv[i]);
Eric Andersenb54833c2000-07-03 23:56:26 +00001075 if ( (i + 1) < argc)
Eric Andersen1c314ad2000-06-28 16:56:25 +00001076 strcat(local_pending_command, " ");
1077 }
1078 input = NULL;
1079
Erik Andersene5b6c7d2000-04-17 16:16:10 +00001080 }
Eric Andersen1c314ad2000-06-28 16:56:25 +00001081 else if (argv[1][0]=='-') {
1082 usage(shell_usage);
1083 }
1084 else {
1085 input = fopen(argv[1], "r");
1086 if (!input) {
1087 fatalError("sh: Couldn't open file '%s': %s\n", argv[1],
1088 strerror(errno));
1089 }
Erik Andersenf0657d32000-04-12 17:49:52 +00001090 }
Erik Andersen161220c2000-03-16 08:12:48 +00001091 }
Erik Andersend75af992000-03-16 08:09:09 +00001092
Erik Andersen161220c2000-03-16 08:12:48 +00001093 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +00001094}