blob: 60d67e9e8a529572d0cc0de0bd3af685129df63a [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
Eric Andersen34e19412000-07-10 18:47:24 +000092/* function prototypes for builtins */
93static int builtin_cd(struct job *cmd, struct jobSet *junk);
94static int builtin_env(struct job *dummy, struct jobSet *junk);
95static int builtin_exit(struct job *cmd, struct jobSet *junk);
96static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
97static int builtin_help(struct job *cmd, struct jobSet *junk);
98static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
99static int builtin_pwd(struct job *dummy, struct jobSet *junk);
100static int builtin_export(struct job *cmd, struct jobSet *junk);
101static int builtin_source(struct job *cmd, struct jobSet *jobList);
102static int builtin_unset(struct job *cmd, struct jobSet *junk);
103static int builtin_read(struct job *cmd, struct jobSet *junk);
Erik Andersen3522eb12000-03-12 23:49:18 +0000104
Eric Andersen34e19412000-07-10 18:47:24 +0000105
106/* function prototypes for shell stuff */
Erik Andersend75af992000-03-16 08:09:09 +0000107static void checkJobs(struct jobSet *jobList);
108static int getCommand(FILE * source, char *command);
109static int parseCommand(char **commandPtr, struct job *job, int *isBg);
110static int setupRedirections(struct childProgram *prog);
111static int runCommand(struct job newJob, struct jobSet *jobList, int inBg);
Erik Andersen3522eb12000-03-12 23:49:18 +0000112static int busy_loop(FILE * input);
113
Erik Andersend75af992000-03-16 08:09:09 +0000114
Erik Andersen3522eb12000-03-12 23:49:18 +0000115/* Table of built-in functions */
116static struct builtInCommand bltins[] = {
Eric Andersen34e19412000-07-10 18:47:24 +0000117 {"bg", "Resume a job in the background", "bg [%%job]", builtin_fg_bg},
118 {"cd", "Change working directory", "cd [dir]", builtin_cd},
119 {"exit", "Exit from shell()", "exit", builtin_exit},
120 {"fg", "Bring job into the foreground", "fg [%%job]", builtin_fg_bg},
121 {"jobs", "Lists the active jobs", "jobs", builtin_jobs},
122 {"export", "Set environment variable", "export [VAR=value]", builtin_export},
123 {"unset", "Unset environment variable", "unset VAR", builtin_unset},
124 {"read", "Input environment variable", "read [VAR]", builtin_read},
Erik Andersen330fd2b2000-05-19 05:35:19 +0000125 {NULL, NULL, NULL, NULL}
126};
127
128/* Table of built-in functions */
129static struct builtInCommand bltins_forking[] = {
Eric Andersen34e19412000-07-10 18:47:24 +0000130 {"env", "Print all environment variables", "env", builtin_env},
131 {"pwd", "Print current directory", "pwd", builtin_pwd},
132 {".", "Source-in and run commands in a file", ". filename", builtin_source},
133 {"help", "List shell built-in commands", "help", builtin_help},
Erik Andersen161220c2000-03-16 08:12:48 +0000134 {NULL, NULL, NULL, NULL}
Erik Andersen3522eb12000-03-12 23:49:18 +0000135};
136
137static const char shell_usage[] =
Eric Andersen1c314ad2000-06-28 16:56:25 +0000138 "sh [FILE]...\n"
139 " or: sh -c command [args]...\n"
Erik Andersen7ab9c7e2000-05-12 19:41:47 +0000140#ifndef BB_FEATURE_TRIVIAL_HELP
141 "\nlash: The BusyBox command interpreter (shell).\n\n"
142#endif
143 ;
Erik Andersen3522eb12000-03-12 23:49:18 +0000144
Erik Andersen3522eb12000-03-12 23:49:18 +0000145static char *prompt = "# ";
Eric Andersenb54833c2000-07-03 23:56:26 +0000146static char *cwd = NULL;
Eric Andersen1c314ad2000-06-28 16:56:25 +0000147static char *local_pending_command = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000148
Erik Andersenf0657d32000-04-12 17:49:52 +0000149#ifdef BB_FEATURE_SH_COMMAND_EDITING
150void win_changed(int sig)
151{
152 struct winsize win = { 0, 0 };
153 ioctl(0, TIOCGWINSZ, &win);
154 if (win.ws_col > 0) {
155 cmdedit_setwidth( win.ws_col - 1);
156 }
157}
158#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000159
Erik Andersen3522eb12000-03-12 23:49:18 +0000160
Erik Andersend75af992000-03-16 08:09:09 +0000161/* built-in 'cd <path>' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000162static int builtin_cd(struct job *cmd, struct jobSet *junk)
Erik Andersend75af992000-03-16 08:09:09 +0000163{
Erik Andersen161220c2000-03-16 08:12:48 +0000164 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000165
Erik Andersen161220c2000-03-16 08:12:48 +0000166 if (!cmd->progs[0].argv[1] == 1)
167 newdir = getenv("HOME");
168 else
169 newdir = cmd->progs[0].argv[1];
170 if (chdir(newdir)) {
171 printf("cd: %s: %s\n", newdir, strerror(errno));
172 return FALSE;
173 }
174 getcwd(cwd, sizeof(cwd));
175
176 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000177}
178
179/* built-in 'env' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000180static int builtin_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000181{
Erik Andersen161220c2000-03-16 08:12:48 +0000182 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000183
Erik Andersen161220c2000-03-16 08:12:48 +0000184 for (e = environ; *e; e++) {
185 fprintf(stdout, "%s\n", *e);
186 }
187 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000188}
189
190/* built-in 'exit' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000191static int builtin_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000192{
Erik Andersen161220c2000-03-16 08:12:48 +0000193 if (!cmd->progs[0].argv[1] == 1)
194 exit TRUE;
195
Eric Andersenb6106152000-06-19 17:25:40 +0000196 return(atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000197}
198
199/* built-in 'fg' and 'bg' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000200static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000201{
Erik Andersen161220c2000-03-16 08:12:48 +0000202 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000203 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000204
Erik Andersen161220c2000-03-16 08:12:48 +0000205 if (!jobList->head) {
206 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
207 fprintf(stderr, "%s: exactly one argument is expected\n",
208 cmd->progs[0].argv[0]);
209 return FALSE;
210 }
211 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
212 fprintf(stderr, "%s: bad argument '%s'\n",
213 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
214 return FALSE;
215 for (job = jobList->head; job; job = job->next) {
216 if (job->jobId == jobNum) {
217 break;
218 }
219 }
220 }
221 } else {
222 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000223 }
Erik Andersen161220c2000-03-16 08:12:48 +0000224
225 if (!job) {
226 fprintf(stderr, "%s: unknown job %d\n",
227 cmd->progs[0].argv[0], jobNum);
228 return FALSE;
Erik Andersend75af992000-03-16 08:09:09 +0000229 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000230
Erik Andersen161220c2000-03-16 08:12:48 +0000231 if (*cmd->progs[0].argv[0] == 'f') {
232 /* Make this job the foreground job */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000233 /* suppress messages when run from /linuxrc mag@sysgo.de */
234 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
235 perror("tcsetpgrp");
Erik Andersen161220c2000-03-16 08:12:48 +0000236 jobList->fg = job;
237 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000238
Erik Andersen161220c2000-03-16 08:12:48 +0000239 /* Restart the processes in the job */
240 for (i = 0; i < job->numProgs; i++)
241 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000242
Erik Andersen161220c2000-03-16 08:12:48 +0000243 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000244
Erik Andersen161220c2000-03-16 08:12:48 +0000245 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000246
Erik Andersen161220c2000-03-16 08:12:48 +0000247 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000248}
249
250/* built-in 'help' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000251static int builtin_help(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000252{
Erik Andersen161220c2000-03-16 08:12:48 +0000253 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000254
Erik Andersen161220c2000-03-16 08:12:48 +0000255 fprintf(stdout, "\nBuilt-in commands:\n");
256 fprintf(stdout, "-------------------\n");
257 for (x = bltins; x->cmd; x++) {
258 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
259 }
Erik Andersen330fd2b2000-05-19 05:35:19 +0000260 for (x = bltins_forking; x->cmd; x++) {
261 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
262 }
Erik Andersen161220c2000-03-16 08:12:48 +0000263 fprintf(stdout, "\n\n");
264 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000265}
266
267/* built-in 'jobs' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000268static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000269{
Erik Andersen161220c2000-03-16 08:12:48 +0000270 struct job *job;
271 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000272
Erik Andersen161220c2000-03-16 08:12:48 +0000273 for (job = jobList->head; job; job = job->next) {
274 if (job->runningProgs == job->stoppedProgs)
275 statusString = "Stopped";
276 else
277 statusString = "Running";
278
279 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
280 }
281 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000282}
283
284
285/* built-in 'pwd' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000286static int builtin_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000287{
Erik Andersen161220c2000-03-16 08:12:48 +0000288 getcwd(cwd, sizeof(cwd));
289 fprintf(stdout, "%s\n", cwd);
290 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000291}
292
Erik Andersen6273f652000-03-17 01:12:41 +0000293/* built-in 'export VAR=value' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000294static int builtin_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000295{
Erik Andersen161220c2000-03-16 08:12:48 +0000296 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000297
Erik Andersen161220c2000-03-16 08:12:48 +0000298 if (!cmd->progs[0].argv[1] == 1) {
Eric Andersen34e19412000-07-10 18:47:24 +0000299 return (builtin_env(cmd, junk));
Erik Andersen161220c2000-03-16 08:12:48 +0000300 }
301 res = putenv(cmd->progs[0].argv[1]);
302 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000303 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000304 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000305}
306
Eric Andersenb54833c2000-07-03 23:56:26 +0000307/* built-in 'read VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000308static int builtin_read(struct job *cmd, struct jobSet *junk)
Eric Andersenb54833c2000-07-03 23:56:26 +0000309{
310 int res = 0, len, newlen;
311 char *s;
312 char string[MAX_READ];
313
314 if (cmd->progs[0].argv[1]) {
315 /* argument (VAR) given: put "VAR=" into buffer */
316 strcpy(string, cmd->progs[0].argv[1]);
317 len = strlen(string);
318 string[len++] = '=';
319 string[len] = '\0';
320 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
321 newlen = strlen(string);
322 if(newlen > len)
323 string[--newlen] = '\0'; /* chomp trailing newline */
324 /*
325 ** string should now contain "VAR=<value>"
326 ** copy it (putenv() won't do that, so we must make sure
327 ** the string resides in a static buffer!)
328 */
329 res = -1;
330 if((s = strdup(string)))
331 res = putenv(s);
332 if (res)
333 fprintf(stdout, "read: %s\n", strerror(errno));
334 }
335 else
336 fgets(string, sizeof(string), stdin);
337
338 return (res);
339}
340
Erik Andersen3522eb12000-03-12 23:49:18 +0000341/* Built-in '.' handler (read-in and execute commands from file) */
Eric Andersen34e19412000-07-10 18:47:24 +0000342static int builtin_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000343{
Erik Andersen161220c2000-03-16 08:12:48 +0000344 FILE *input;
345 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000346
Erik Andersen161220c2000-03-16 08:12:48 +0000347 if (!cmd->progs[0].argv[1] == 1)
348 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000349
Erik Andersen161220c2000-03-16 08:12:48 +0000350 input = fopen(cmd->progs[0].argv[1], "r");
351 if (!input) {
352 fprintf(stdout, "Couldn't open file '%s'\n",
353 cmd->progs[0].argv[1]);
354 return FALSE;
355 }
Erik Andersend75af992000-03-16 08:09:09 +0000356
Erik Andersen161220c2000-03-16 08:12:48 +0000357 /* Now run the file */
358 status = busy_loop(input);
359 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000360}
361
362/* built-in 'unset VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000363static int builtin_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000364{
Erik Andersen161220c2000-03-16 08:12:48 +0000365 if (!cmd->progs[0].argv[1] == 1) {
366 fprintf(stdout, "unset: parameter required.\n");
367 return FALSE;
368 }
369 unsetenv(cmd->progs[0].argv[1]);
370 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000371}
372
373/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000374static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000375{
Erik Andersen161220c2000-03-16 08:12:48 +0000376 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000377
Erik Andersen161220c2000-03-16 08:12:48 +0000378 for (i = 0; i < cmd->numProgs; i++) {
379 free(cmd->progs[i].argv);
380 if (cmd->progs[i].redirections)
381 free(cmd->progs[i].redirections);
382 if (cmd->progs[i].freeGlob)
383 globfree(&cmd->progs[i].globResult);
384 }
385 free(cmd->progs);
386 if (cmd->text)
387 free(cmd->text);
388 free(cmd->cmdBuf);
Erik Andersen3522eb12000-03-12 23:49:18 +0000389}
390
391/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000392static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000393{
Erik Andersen161220c2000-03-16 08:12:48 +0000394 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000395
Erik Andersen161220c2000-03-16 08:12:48 +0000396 freeJob(job);
397 if (job == jobList->head) {
398 jobList->head = job->next;
399 } else {
400 prevJob = jobList->head;
401 while (prevJob->next != job)
402 prevJob = prevJob->next;
403 prevJob->next = job->next;
404 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000405
Erik Andersen161220c2000-03-16 08:12:48 +0000406 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000407}
408
409/* Checks to see if any background processes have exited -- if they
410 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000411static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000412{
Erik Andersen161220c2000-03-16 08:12:48 +0000413 struct job *job;
414 pid_t childpid;
415 int status;
416 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000417
Erik Andersen161220c2000-03-16 08:12:48 +0000418 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
419 for (job = jobList->head; job; job = job->next) {
420 progNum = 0;
421 while (progNum < job->numProgs &&
422 job->progs[progNum].pid != childpid) progNum++;
423 if (progNum < job->numProgs)
424 break;
425 }
426
427 if (WIFEXITED(status) || WIFSIGNALED(status)) {
428 /* child exited */
429 job->runningProgs--;
430 job->progs[progNum].pid = 0;
431
432 if (!job->runningProgs) {
433 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
434 removeJob(jobList, job);
435 }
436 } else {
437 /* child stopped */
438 job->stoppedProgs++;
439 job->progs[progNum].isStopped = 1;
440
441 if (job->stoppedProgs == job->numProgs) {
442 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
443 job->text);
444 }
445 }
Erik Andersend75af992000-03-16 08:09:09 +0000446 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000447
Erik Andersen161220c2000-03-16 08:12:48 +0000448 if (childpid == -1 && errno != ECHILD)
449 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000450}
451
Erik Andersend75af992000-03-16 08:09:09 +0000452static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000453{
Eric Andersen1c314ad2000-06-28 16:56:25 +0000454 if (source == NULL) {
455 if (local_pending_command) {
456 /* a command specified (-c option): return it & mark it done */
457 strcpy(command, local_pending_command);
458 free(local_pending_command);
459 local_pending_command = NULL;
460 return 0;
461 }
462 return 1;
463 }
464
Erik Andersen161220c2000-03-16 08:12:48 +0000465 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000466#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000467 int len;
468 char *promptStr;
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000469 len=fprintf(stdout, "%s %s", cwd, prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000470 fflush(stdout);
471 promptStr=(char*)malloc(sizeof(char)*(len+1));
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000472 sprintf(promptStr, "%s %s", cwd, prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000473 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000474 free( promptStr);
Erik Andersen161220c2000-03-16 08:12:48 +0000475 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000476#else
477 fprintf(stdout, "%s %s", cwd, prompt);
478 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000479#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000480 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000481
Erik Andersen161220c2000-03-16 08:12:48 +0000482 if (!fgets(command, BUFSIZ - 2, source)) {
483 if (source == stdin)
484 printf("\n");
485 return 1;
486 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000487
Erik Andersen161220c2000-03-16 08:12:48 +0000488 /* remove trailing newline */
489 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000490
Erik Andersen161220c2000-03-16 08:12:48 +0000491 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000492}
493
Erik Andersend75af992000-03-16 08:09:09 +0000494static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000495 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000496{
Erik Andersen161220c2000-03-16 08:12:48 +0000497 int argc = *argcPtr;
498 int argcAlloced = *argcAllocedPtr;
499 int rc;
500 int flags;
501 int i;
Eric Andersenb54833c2000-07-03 23:56:26 +0000502 char *src, *dst, *var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000503
Erik Andersen161220c2000-03-16 08:12:48 +0000504 if (argc > 1) { /* cmd->globResult is already initialized */
505 flags = GLOB_APPEND;
506 i = prog->globResult.gl_pathc;
507 } else {
508 prog->freeGlob = 1;
509 flags = 0;
510 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000511 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000512 /* do shell variable substitution */
513 if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
514 prog->argv[argc - 1] = var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000515
Erik Andersen161220c2000-03-16 08:12:48 +0000516 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
517 if (rc == GLOB_NOSPACE) {
518 fprintf(stderr, "out of space during glob operation\n");
519 return;
520 } else if (rc == GLOB_NOMATCH ||
521 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
522 !strcmp(prog->argv[argc - 1],
523 prog->globResult.gl_pathv[i]))) {
524 /* we need to remove whatever \ quoting is still present */
525 src = dst = prog->argv[argc - 1];
526 while (*src) {
527 if (*src != '\\')
528 *dst++ = *src;
529 src++;
530 }
531 *dst = '\0';
532 } else if (!rc) {
533 argcAlloced += (prog->globResult.gl_pathc - i);
534 prog->argv =
535 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
536 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
537 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
538 argc += (prog->globResult.gl_pathc - i - 1);
539 }
540
541 *argcAllocedPtr = argcAlloced;
542 *argcPtr = argc;
Erik Andersen3522eb12000-03-12 23:49:18 +0000543}
544
545/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
546 line). If a valid command is found, commandPtr is set to point to
547 the beginning of the next command (if the original command had more
548 then one job associated with it) or NULL if no more commands are
549 present. */
Erik Andersend75af992000-03-16 08:09:09 +0000550static int parseCommand(char **commandPtr, struct job *job, int *isBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000551{
Erik Andersen161220c2000-03-16 08:12:48 +0000552 char *command;
553 char *returnCommand = NULL;
554 char *src, *buf, *chptr;
555 int argc = 0;
556 int done = 0;
557 int argvAlloced;
558 int i;
559 char quote = '\0';
560 int count;
561 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000562
Erik Andersen161220c2000-03-16 08:12:48 +0000563 /* skip leading white space */
564 while (**commandPtr && isspace(**commandPtr))
565 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000566
Erik Andersen161220c2000-03-16 08:12:48 +0000567 /* this handles empty lines or leading '#' characters */
568 if (!**commandPtr || (**commandPtr == '#')) {
569 job->numProgs = 0;
570 *commandPtr = NULL;
571 return 0;
572 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000573
Erik Andersen161220c2000-03-16 08:12:48 +0000574 *isBg = 0;
575 job->numProgs = 1;
576 job->progs = malloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000577
Erik Andersen161220c2000-03-16 08:12:48 +0000578 /* We set the argv elements to point inside of this string. The
Eric Andersenb54833c2000-07-03 23:56:26 +0000579 memory is freed by freeJob(). Allocate twice the original
580 length in case we need to quote every single character.
Erik Andersen3522eb12000-03-12 23:49:18 +0000581
Erik Andersen161220c2000-03-16 08:12:48 +0000582 Getting clean memory relieves us of the task of NULL
583 terminating things and makes the rest of this look a bit
584 cleaner (though it is, admittedly, a tad less efficient) */
Eric Andersenb54833c2000-07-03 23:56:26 +0000585 job->cmdBuf = command = calloc(1, 2*strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000586 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000587
Erik Andersen161220c2000-03-16 08:12:48 +0000588 prog = job->progs;
589 prog->numRedirections = 0;
590 prog->redirections = NULL;
591 prog->freeGlob = 0;
592 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000593
Erik Andersen161220c2000-03-16 08:12:48 +0000594 argvAlloced = 5;
595 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
596 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000597
Erik Andersen161220c2000-03-16 08:12:48 +0000598 buf = command;
599 src = *commandPtr;
600 while (*src && !done) {
601 if (quote == *src) {
602 quote = '\0';
603 } else if (quote) {
604 if (*src == '\\') {
605 src++;
606 if (!*src) {
607 fprintf(stderr, "character expected after \\\n");
608 freeJob(job);
609 return 1;
610 }
611
612 /* in shell, "\'" should yield \' */
613 if (*src != quote)
614 *buf++ = '\\';
615 } else if (*src == '*' || *src == '?' || *src == '[' ||
616 *src == ']') *buf++ = '\\';
617 *buf++ = *src;
618 } else if (isspace(*src)) {
619 if (*prog->argv[argc]) {
620 buf++, argc++;
621 /* +1 here leaves room for the NULL which ends argv */
622 if ((argc + 1) == argvAlloced) {
623 argvAlloced += 5;
624 prog->argv = realloc(prog->argv,
625 sizeof(*prog->argv) *
626 argvAlloced);
627 }
Erik Andersen161220c2000-03-16 08:12:48 +0000628 globLastArgument(prog, &argc, &argvAlloced);
Eric Andersenb54833c2000-07-03 23:56:26 +0000629 prog->argv[argc] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000630 }
631 } else
632 switch (*src) {
633 case '"':
634 case '\'':
635 quote = *src;
636 break;
637
638 case '#': /* comment */
639 done = 1;
640 break;
641
642 case '>': /* redirections */
643 case '<':
644 i = prog->numRedirections++;
645 prog->redirections = realloc(prog->redirections,
646 sizeof(*prog->redirections) *
647 (i + 1));
648
649 prog->redirections[i].fd = -1;
650 if (buf != prog->argv[argc]) {
651 /* the stuff before this character may be the file number
652 being redirected */
653 prog->redirections[i].fd =
654 strtol(prog->argv[argc], &chptr, 10);
655
656 if (*chptr && *prog->argv[argc]) {
657 buf++, argc++;
658 globLastArgument(prog, &argc, &argvAlloced);
Eric Andersenb54833c2000-07-03 23:56:26 +0000659 prog->argv[argc] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000660 }
661 }
662
663 if (prog->redirections[i].fd == -1) {
664 if (*src == '>')
665 prog->redirections[i].fd = 1;
666 else
667 prog->redirections[i].fd = 0;
668 }
669
670 if (*src++ == '>') {
671 if (*src == '>')
672 prog->redirections[i].type =
673 REDIRECT_APPEND, src++;
674 else
675 prog->redirections[i].type = REDIRECT_OVERWRITE;
676 } else {
677 prog->redirections[i].type = REDIRECT_INPUT;
678 }
679
680 /* This isn't POSIX sh compliant. Oh well. */
681 chptr = src;
682 while (isspace(*chptr))
683 chptr++;
684
685 if (!*chptr) {
686 fprintf(stderr, "file name expected after %c\n", *src);
687 freeJob(job);
688 return 1;
689 }
690
691 prog->redirections[i].filename = buf;
692 while (*chptr && !isspace(*chptr))
693 *buf++ = *chptr++;
694
695 src = chptr - 1; /* we src++ later */
696 prog->argv[argc] = ++buf;
697 break;
698
699 case '|': /* pipe */
700 /* finish this command */
701 if (*prog->argv[argc])
702 argc++;
703 if (!argc) {
704 fprintf(stderr, "empty command in pipe\n");
705 freeJob(job);
706 return 1;
707 }
708 prog->argv[argc] = NULL;
709
710 /* and start the next */
711 job->numProgs++;
712 job->progs = realloc(job->progs,
713 sizeof(*job->progs) * job->numProgs);
714 prog = job->progs + (job->numProgs - 1);
715 prog->numRedirections = 0;
716 prog->redirections = NULL;
717 prog->freeGlob = 0;
718 argc = 0;
719
720 argvAlloced = 5;
721 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
722 prog->argv[0] = ++buf;
723
724 src++;
725 while (*src && isspace(*src))
726 src++;
727
728 if (!*src) {
729 fprintf(stderr, "empty command in pipe\n");
730 return 1;
731 }
732 src--; /* we'll ++ it at the end of the loop */
733
734 break;
735
736 case '&': /* background */
737 *isBg = 1;
738 case ';': /* multiple commands */
739 done = 1;
740 returnCommand = *commandPtr + (src - *commandPtr) + 1;
741 break;
742
743 case '\\':
744 src++;
745 if (!*src) {
746 freeJob(job);
747 fprintf(stderr, "character expected after \\\n");
748 return 1;
749 }
750 if (*src == '*' || *src == '[' || *src == ']'
751 || *src == '?') *buf++ = '\\';
752 /* fallthrough */
753 default:
754 *buf++ = *src;
755 }
756
Erik Andersend75af992000-03-16 08:09:09 +0000757 src++;
Erik Andersen161220c2000-03-16 08:12:48 +0000758 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000759
Erik Andersen161220c2000-03-16 08:12:48 +0000760 if (*prog->argv[argc]) {
761 argc++;
Erik Andersend75af992000-03-16 08:09:09 +0000762 globLastArgument(prog, &argc, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000763 }
764 if (!argc) {
765 freeJob(job);
766 return 0;
767 }
768 prog->argv[argc] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000769
Erik Andersen161220c2000-03-16 08:12:48 +0000770 if (!returnCommand) {
771 job->text = malloc(strlen(*commandPtr) + 1);
772 strcpy(job->text, *commandPtr);
773 } else {
774 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +0000775 count = returnCommand - *commandPtr;
776 job->text = malloc(count + 1);
777 strncpy(job->text, *commandPtr, count);
778 job->text[count] = '\0';
779 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000780
Erik Andersen161220c2000-03-16 08:12:48 +0000781 *commandPtr = returnCommand;
Erik Andersen3522eb12000-03-12 23:49:18 +0000782
Erik Andersend75af992000-03-16 08:09:09 +0000783 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000784}
785
Erik Andersenbcd61772000-05-13 06:33:19 +0000786
Erik Andersend75af992000-03-16 08:09:09 +0000787static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000788{
Erik Andersen161220c2000-03-16 08:12:48 +0000789 struct job *job;
790 int i;
791 int nextin, nextout;
792 int pipefds[2]; /* pipefd[0] is for reading */
793 struct builtInCommand *x;
Eric Andersenb54833c2000-07-03 23:56:26 +0000794#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Erik Andersenbcd61772000-05-13 06:33:19 +0000795 const struct BB_applet *a = applets;
796#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000797
Erik Andersen3522eb12000-03-12 23:49:18 +0000798
Erik Andersen161220c2000-03-16 08:12:48 +0000799 nextin = 0, nextout = 1;
800 for (i = 0; i < newJob.numProgs; i++) {
801 if ((i + 1) < newJob.numProgs) {
802 pipe(pipefds);
803 nextout = pipefds[1];
804 } else {
805 nextout = 1;
806 }
807
Eric Andersen34e19412000-07-10 18:47:24 +0000808 /* Check if the command matches any non-forking builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000809 for (x = bltins; x->cmd; x++) {
810 if (!strcmp(newJob.progs[i].argv[0], x->cmd)) {
811 return (x->function(&newJob, jobList));
812 }
813 }
814
Erik Andersen161220c2000-03-16 08:12:48 +0000815 if (!(newJob.progs[i].pid = fork())) {
816 signal(SIGTTOU, SIG_DFL);
817
818 if (nextin != 0) {
819 dup2(nextin, 0);
820 close(nextin);
821 }
822
823 if (nextout != 1) {
824 dup2(nextout, 1);
825 close(nextout);
826 }
827
828 /* explicit redirections override pipes */
829 setupRedirections(newJob.progs + i);
830
Eric Andersen34e19412000-07-10 18:47:24 +0000831 /* Check if the command matches any of the other builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000832 for (x = bltins_forking; x->cmd; x++) {
Erik Andersenbcd61772000-05-13 06:33:19 +0000833 if (!strcmp(newJob.progs[i].argv[0], x->cmd)) {
834 exit (x->function(&newJob, jobList));
835 }
836 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000837#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Eric Andersen34e19412000-07-10 18:47:24 +0000838 /* Check if the command matches any busybox internal commands here */
839 /* TODO: Add matching when paths are appended (i.e. 'cat' currently
840 * works, but '/bin/cat' doesn't ) */
Erik Andersenbcd61772000-05-13 06:33:19 +0000841 while (a->name != 0) {
842 if (strcmp(newJob.progs[i].argv[0], a->name) == 0) {
843 int argc;
844 char** argv=newJob.progs[i].argv;
Erik Andersenc3f5c9c2000-05-13 19:00:07 +0000845 for(argc=0;*argv!=NULL; argv++, argc++);
Erik Andersenbcd61772000-05-13 06:33:19 +0000846 exit((*(a->main)) (argc, newJob.progs[i].argv));
847 }
848 a++;
849 }
850#endif
851
Erik Andersen161220c2000-03-16 08:12:48 +0000852 execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
853 fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
854 strerror(errno));
855 }
856
857 /* put our child in the process group whose leader is the
858 first process in this pipe */
859 setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
860
861 if (nextin != 0)
862 close(nextin);
863 if (nextout != 1)
864 close(nextout);
865
866 /* If there isn't another process, nextin is garbage
867 but it doesn't matter */
868 nextin = pipefds[0];
869 }
870
871 newJob.pgrp = newJob.progs[0].pid;
872
873 /* find the ID for the job to use */
874 newJob.jobId = 1;
875 for (job = jobList->head; job; job = job->next)
876 if (job->jobId >= newJob.jobId)
877 newJob.jobId = job->jobId + 1;
878
879 /* add the job to the list of running jobs */
880 if (!jobList->head) {
881 job = jobList->head = malloc(sizeof(*job));
Erik Andersend75af992000-03-16 08:09:09 +0000882 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000883 for (job = jobList->head; job->next; job = job->next);
884 job->next = malloc(sizeof(*job));
885 job = job->next;
Erik Andersend75af992000-03-16 08:09:09 +0000886 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000887
Erik Andersen161220c2000-03-16 08:12:48 +0000888 *job = newJob;
889 job->next = NULL;
890 job->runningProgs = job->numProgs;
891 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000892
Erik Andersen161220c2000-03-16 08:12:48 +0000893 if (inBg) {
894 /* we don't wait for background jobs to return -- append it
895 to the list of backgrounded jobs and leave it alone */
Erik Andersen161220c2000-03-16 08:12:48 +0000896 printf("[%d] %d\n", job->jobId,
897 newJob.progs[newJob.numProgs - 1].pid);
898 } else {
899 jobList->fg = job;
Erik Andersen3522eb12000-03-12 23:49:18 +0000900
Erik Andersen161220c2000-03-16 08:12:48 +0000901 /* move the new process group into the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000902 /* suppress messages when run from /linuxrc mag@sysgo.de */
903 if (tcsetpgrp(0, newJob.pgrp) && errno != ENOTTY)
Erik Andersen161220c2000-03-16 08:12:48 +0000904 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000905 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000906
Erik Andersen161220c2000-03-16 08:12:48 +0000907 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000908}
909
Erik Andersend75af992000-03-16 08:09:09 +0000910static int setupRedirections(struct childProgram *prog)
Erik Andersen3522eb12000-03-12 23:49:18 +0000911{
Erik Andersen161220c2000-03-16 08:12:48 +0000912 int i;
913 int openfd;
914 int mode = O_RDONLY;
915 struct redirectionSpecifier *redir = prog->redirections;
Erik Andersen3522eb12000-03-12 23:49:18 +0000916
Erik Andersen161220c2000-03-16 08:12:48 +0000917 for (i = 0; i < prog->numRedirections; i++, redir++) {
918 switch (redir->type) {
919 case REDIRECT_INPUT:
920 mode = O_RDONLY;
921 break;
922 case REDIRECT_OVERWRITE:
923 mode = O_RDWR | O_CREAT | O_TRUNC;
924 break;
925 case REDIRECT_APPEND:
926 mode = O_RDWR | O_CREAT | O_APPEND;
927 break;
928 }
929
930 openfd = open(redir->filename, mode, 0666);
931 if (openfd < 0) {
932 /* this could get lost if stderr has been redirected, but
933 bash and ash both lose it as well (though zsh doesn't!) */
934 fprintf(stderr, "error opening %s: %s\n", redir->filename,
935 strerror(errno));
936 return 1;
937 }
938
939 if (openfd != redir->fd) {
940 dup2(openfd, redir->fd);
941 close(openfd);
942 }
Erik Andersend75af992000-03-16 08:09:09 +0000943 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000944
Erik Andersen161220c2000-03-16 08:12:48 +0000945 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000946}
947
948
Erik Andersend75af992000-03-16 08:09:09 +0000949static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +0000950{
Erik Andersen161220c2000-03-16 08:12:48 +0000951 char *command;
952 char *nextCommand = NULL;
953 struct jobSet jobList = { NULL, NULL };
954 struct job newJob;
Eric Andersen1c314ad2000-06-28 16:56:25 +0000955 pid_t parent_pgrp;
Erik Andersen161220c2000-03-16 08:12:48 +0000956 int i;
957 int status;
958 int inBg;
Erik Andersen3522eb12000-03-12 23:49:18 +0000959
Eric Andersen1c314ad2000-06-28 16:56:25 +0000960 /* save current owner of TTY so we can restore it on exit */
961 parent_pgrp = tcgetpgrp(0);
962
Erik Andersen161220c2000-03-16 08:12:48 +0000963 command = (char *) calloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +0000964
Erik Andersen161220c2000-03-16 08:12:48 +0000965 /* don't pay any attention to this signal; it just confuses
966 things and isn't really meant for shells anyway */
967 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +0000968
Erik Andersen161220c2000-03-16 08:12:48 +0000969 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +0000970 if (!jobList.fg) {
971 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +0000972
Erik Andersend75af992000-03-16 08:09:09 +0000973 /* see if any background processes have exited */
974 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +0000975
Erik Andersend75af992000-03-16 08:09:09 +0000976 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +0000977 if (getCommand(input, command))
978 break;
979 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +0000980 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000981
Erik Andersend75af992000-03-16 08:09:09 +0000982 if (!parseCommand(&nextCommand, &newJob, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +0000983 newJob.numProgs) {
984 runCommand(newJob, &jobList, inBg);
Erik Andersend75af992000-03-16 08:09:09 +0000985 }
986 } else {
987 /* a job is running in the foreground; wait for it */
988 i = 0;
989 while (!jobList.fg->progs[i].pid ||
Erik Andersen161220c2000-03-16 08:12:48 +0000990 jobList.fg->progs[i].isStopped) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000991
Erik Andersend75af992000-03-16 08:09:09 +0000992 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +0000993
Erik Andersend75af992000-03-16 08:09:09 +0000994 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +0000995 /* the child exited */
996 jobList.fg->runningProgs--;
997 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000998
Erik Andersen161220c2000-03-16 08:12:48 +0000999 if (!jobList.fg->runningProgs) {
1000 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +00001001
Erik Andersen161220c2000-03-16 08:12:48 +00001002 removeJob(&jobList, jobList.fg);
1003 jobList.fg = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001004 }
Erik Andersend75af992000-03-16 08:09:09 +00001005 } else {
Erik Andersen161220c2000-03-16 08:12:48 +00001006 /* the child was stopped */
1007 jobList.fg->stoppedProgs++;
1008 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +00001009
Erik Andersen161220c2000-03-16 08:12:48 +00001010 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1011 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1012 "Stopped", jobList.fg->text);
1013 jobList.fg = NULL;
1014 }
Erik Andersend75af992000-03-16 08:09:09 +00001015 }
1016
1017 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +00001018 /* move the shell to the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001019 /* suppress messages when run from /linuxrc mag@sysgo.de */
1020 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1021 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001022 }
1023 }
1024 }
Erik Andersen161220c2000-03-16 08:12:48 +00001025 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +00001026
Eric Andersen1c314ad2000-06-28 16:56:25 +00001027 /* return controlling TTY back to parent process group before exiting */
1028 if (tcsetpgrp(0, parent_pgrp))
Eric Andersenb54833c2000-07-03 23:56:26 +00001029 perror("tcsetpgrp");
1030
1031 /* return exit status if called with "-c" */
1032 if (input == NULL && WIFEXITED(status))
1033 return WEXITSTATUS(status);
1034
Erik Andersen161220c2000-03-16 08:12:48 +00001035 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001036}
1037
1038
Erik Andersend75af992000-03-16 08:09:09 +00001039int shell_main(int argc, char **argv)
Erik Andersen3522eb12000-03-12 23:49:18 +00001040{
Erik Andersen161220c2000-03-16 08:12:48 +00001041 FILE *input = stdin;
Erik Andersen3522eb12000-03-12 23:49:18 +00001042
Erik Andersen161220c2000-03-16 08:12:48 +00001043 /* initialize the cwd */
Eric Andersenb54833c2000-07-03 23:56:26 +00001044 cwd = (char *) calloc(BUFSIZ, sizeof(char));
1045 if (cwd == 0) {
1046 fatalError("sh: out of memory\n");
1047 }
1048 getcwd(cwd, sizeof(char)*BUFSIZ);
Erik Andersen3522eb12000-03-12 23:49:18 +00001049
Erik Andersenf0657d32000-04-12 17:49:52 +00001050#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersen1d1d9502000-04-21 01:26:49 +00001051 cmdedit_init();
Erik Andersenf0657d32000-04-12 17:49:52 +00001052 signal(SIGWINCH, win_changed);
1053 win_changed(0);
1054#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001055
Erik Andersen161220c2000-03-16 08:12:48 +00001056 //if (argv[0] && argv[0][0] == '-') {
Eric Andersen34e19412000-07-10 18:47:24 +00001057 // builtin_source("/etc/profile");
Erik Andersen161220c2000-03-16 08:12:48 +00001058 //}
Erik Andersen3522eb12000-03-12 23:49:18 +00001059
Erik Andersen161220c2000-03-16 08:12:48 +00001060 if (argc < 2) {
Erik Andersenf0657d32000-04-12 17:49:52 +00001061 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1062 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Erik Andersen161220c2000-03-16 08:12:48 +00001063 } else {
Eric Andersen1c314ad2000-06-28 16:56:25 +00001064 if (argv[1][0]=='-' && argv[1][1]=='c') {
1065 int i;
1066 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1067 if (local_pending_command == 0) {
1068 fatalError("sh: out of memory\n");
1069 }
1070 for(i=2; i<argc; i++)
1071 {
1072 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
Eric Andersenb54833c2000-07-03 23:56:26 +00001073 local_pending_command = realloc(local_pending_command,
1074 strlen(local_pending_command) + strlen(argv[i]));
1075 if (local_pending_command==NULL)
1076 fatalError("sh: commands for -c option too long\n");
Eric Andersen1c314ad2000-06-28 16:56:25 +00001077 }
1078 strcat(local_pending_command, argv[i]);
Eric Andersenb54833c2000-07-03 23:56:26 +00001079 if ( (i + 1) < argc)
Eric Andersen1c314ad2000-06-28 16:56:25 +00001080 strcat(local_pending_command, " ");
1081 }
1082 input = NULL;
1083
Erik Andersene5b6c7d2000-04-17 16:16:10 +00001084 }
Eric Andersen1c314ad2000-06-28 16:56:25 +00001085 else if (argv[1][0]=='-') {
1086 usage(shell_usage);
1087 }
1088 else {
1089 input = fopen(argv[1], "r");
1090 if (!input) {
1091 fatalError("sh: Couldn't open file '%s': %s\n", argv[1],
1092 strerror(errno));
1093 }
Erik Andersenf0657d32000-04-12 17:49:52 +00001094 }
Erik Andersen161220c2000-03-16 08:12:48 +00001095 }
Erik Andersend75af992000-03-16 08:09:09 +00001096
Erik Andersen161220c2000-03-16 08:12:48 +00001097 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +00001098}