blob: e57567608861b571fd1a4f4c82b9fa68a4dce3c1 [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
Eric Andersena1d187a2000-07-17 19:14:41 +000028
Eric Andersen6a99aaf2000-07-27 00:15:20 +000029//#define BB_FEATURE_SH_BACKTICKS
Eric Andersenfad9c112000-07-25 18:06:52 +000030//#define BB_FEATURE_SH_IF_EXPRESSIONS
Eric Andersen6a99aaf2000-07-27 00:15:20 +000031//#define BB_FEATURE_SH_ENVIRONMENT
Eric Andersena1d187a2000-07-17 19:14:41 +000032
33
Erik Andersen3522eb12000-03-12 23:49:18 +000034#include "internal.h"
35#include <stdio.h>
36#include <stdlib.h>
37#include <ctype.h>
38#include <errno.h>
39#include <fcntl.h>
40#include <glob.h>
41#include <signal.h>
42#include <string.h>
43#include <sys/ioctl.h>
44#include <sys/wait.h>
45#include <unistd.h>
Erik Andersenf0657d32000-04-12 17:49:52 +000046#ifdef BB_FEATURE_SH_COMMAND_EDITING
47#include "cmdedit.h"
48#endif
Erik Andersen3522eb12000-03-12 23:49:18 +000049
Eric Andersen6efc48c2000-07-18 08:16:39 +000050#define MAX_LINE 256 /* size of input buffer for `read' builtin */
Eric Andersenb54833c2000-07-03 23:56:26 +000051#define MAX_READ 128 /* size of input buffer for `read' builtin */
Erik Andersen3522eb12000-03-12 23:49:18 +000052#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
53
Erik Andersend75af992000-03-16 08:09:09 +000054
55enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
Erik Andersen161220c2000-03-16 08:12:48 +000056 REDIRECT_APPEND
57};
Erik Andersen3522eb12000-03-12 23:49:18 +000058
Eric Andersene92108a2000-07-26 00:53:56 +000059static const unsigned int REGULAR_JOB_CONTEXT=0x1;
Eric Andersen6a99aaf2000-07-27 00:15:20 +000060static const unsigned int IF_TRUE_CONTEXT=0x2;
61static const unsigned int IF_FALSE_CONTEXT=0x4;
62static const unsigned int THEN_EXP_CONTEXT=0x8;
63static const unsigned int ELSE_EXP_CONTEXT=0x10;
Eric Andersene92108a2000-07-26 00:53:56 +000064
Eric Andersenfad9c112000-07-25 18:06:52 +000065
Erik Andersen3522eb12000-03-12 23:49:18 +000066struct jobSet {
Erik Andersen161220c2000-03-16 08:12:48 +000067 struct job *head; /* head of list of running jobs */
68 struct job *fg; /* current foreground job */
Erik Andersen3522eb12000-03-12 23:49:18 +000069};
70
71struct redirectionSpecifier {
Erik Andersen161220c2000-03-16 08:12:48 +000072 enum redirectionType type; /* type of redirection */
73 int fd; /* file descriptor being redirected */
74 char *filename; /* file to redirect fd to */
Erik Andersen3522eb12000-03-12 23:49:18 +000075};
76
77struct childProgram {
Erik Andersen161220c2000-03-16 08:12:48 +000078 pid_t pid; /* 0 if exited */
79 char **argv; /* program name and arguments */
80 int numRedirections; /* elements in redirection array */
81 struct redirectionSpecifier *redirections; /* I/O redirections */
82 glob_t globResult; /* result of parameter globbing */
83 int freeGlob; /* should we globfree(&globResult)? */
84 int isStopped; /* is the program currently running? */
Erik Andersen3522eb12000-03-12 23:49:18 +000085};
86
87struct job {
Erik Andersen161220c2000-03-16 08:12:48 +000088 int jobId; /* job number */
89 int numProgs; /* total number of programs in job */
90 int runningProgs; /* number of programs running */
91 char *text; /* name of job */
92 char *cmdBuf; /* buffer various argv's point into */
93 pid_t pgrp; /* process group ID for the job */
94 struct childProgram *progs; /* array of programs in job */
95 struct job *next; /* to track background commands */
96 int stoppedProgs; /* number of programs alive, but stopped */
Eric Andersenfad9c112000-07-25 18:06:52 +000097 int jobContext; /* bitmask defining current context */
Erik Andersen3522eb12000-03-12 23:49:18 +000098};
99
100struct builtInCommand {
Erik Andersen161220c2000-03-16 08:12:48 +0000101 char *cmd; /* name */
102 char *descr; /* description */
Erik Andersen161220c2000-03-16 08:12:48 +0000103 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
Erik Andersen3522eb12000-03-12 23:49:18 +0000104};
105
Eric Andersen34e19412000-07-10 18:47:24 +0000106/* function prototypes for builtins */
107static int builtin_cd(struct job *cmd, struct jobSet *junk);
108static int builtin_env(struct job *dummy, struct jobSet *junk);
109static int builtin_exit(struct job *cmd, struct jobSet *junk);
110static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
111static int builtin_help(struct job *cmd, struct jobSet *junk);
112static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
113static int builtin_pwd(struct job *dummy, struct jobSet *junk);
114static int builtin_export(struct job *cmd, struct jobSet *junk);
115static int builtin_source(struct job *cmd, struct jobSet *jobList);
116static int builtin_unset(struct job *cmd, struct jobSet *junk);
117static int builtin_read(struct job *cmd, struct jobSet *junk);
Eric Andersenfad9c112000-07-25 18:06:52 +0000118#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
119static int builtin_if(struct job *cmd, struct jobSet *junk);
120static int builtin_then(struct job *cmd, struct jobSet *junk);
121static int builtin_else(struct job *cmd, struct jobSet *junk);
122static int builtin_fi(struct job *cmd, struct jobSet *junk);
123#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000124
Eric Andersen34e19412000-07-10 18:47:24 +0000125
126/* function prototypes for shell stuff */
Erik Andersend75af992000-03-16 08:09:09 +0000127static void checkJobs(struct jobSet *jobList);
128static int getCommand(FILE * source, char *command);
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000129static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg);
Eric Andersena1d187a2000-07-17 19:14:41 +0000130static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
Erik Andersen3522eb12000-03-12 23:49:18 +0000131static int busy_loop(FILE * input);
132
Erik Andersend75af992000-03-16 08:09:09 +0000133
Mark Whitley37653aa2000-07-12 23:36:17 +0000134/* Table of built-in functions (these are non-forking builtins, meaning they
135 * can change global variables in the parent shell process but they will not
136 * work with pipes and redirects; 'unset foo | whatever' will not work) */
Erik Andersen3522eb12000-03-12 23:49:18 +0000137static struct builtInCommand bltins[] = {
Eric Andersenfad9c112000-07-25 18:06:52 +0000138 {"bg", "Resume a job in the background", builtin_fg_bg},
139 {"cd", "Change working directory", builtin_cd},
140 {"exit", "Exit from shell()", builtin_exit},
141 {"fg", "Bring job into the foreground", builtin_fg_bg},
142 {"jobs", "Lists the active jobs", builtin_jobs},
143 {"export", "Set environment variable", builtin_export},
144 {"unset", "Unset environment variable", builtin_unset},
145 {"read", "Input environment variable", builtin_read},
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000146#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
147 {"if", NULL, builtin_if},
148 {"then", NULL, builtin_then},
149 {"else", NULL, builtin_else},
150 {"fi", NULL, builtin_fi},
151#endif
Eric Andersenfad9c112000-07-25 18:06:52 +0000152 {NULL, NULL, NULL}
Erik Andersen330fd2b2000-05-19 05:35:19 +0000153};
154
Mark Whitley37653aa2000-07-12 23:36:17 +0000155/* Table of forking built-in functions (things that fork cannot change global
156 * variables in the parent process, such as the current working directory) */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000157static struct builtInCommand bltins_forking[] = {
Eric Andersenfad9c112000-07-25 18:06:52 +0000158 {"env", "Print all environment variables", builtin_env},
159 {"pwd", "Print current directory", builtin_pwd},
Eric Andersenfad9c112000-07-25 18:06:52 +0000160 {".", "Source-in and run commands in a file", builtin_source},
161 {"help", "List shell built-in commands", builtin_help},
162 {NULL, NULL, NULL}
Erik Andersen3522eb12000-03-12 23:49:18 +0000163};
164
Erik Andersen3522eb12000-03-12 23:49:18 +0000165static char *prompt = "# ";
Eric Andersen6efc48c2000-07-18 08:16:39 +0000166static char *cwd;
Eric Andersen1c314ad2000-06-28 16:56:25 +0000167static char *local_pending_command = NULL;
Eric Andersenfad9c112000-07-25 18:06:52 +0000168static char *promptStr = NULL;
169static struct jobSet jobList = { NULL, NULL };
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000170static int argc;
171static char **argv;
172#ifdef BB_FEATURE_SH_ENVIRONMENT
173static int lastBgPid=-1;
174static int lastReturnCode=-1;
175#endif
176
Erik Andersen3522eb12000-03-12 23:49:18 +0000177
Erik Andersenf0657d32000-04-12 17:49:52 +0000178#ifdef BB_FEATURE_SH_COMMAND_EDITING
Eric Andersenfad04fd2000-07-14 06:49:52 +0000179void win_changed(int junk)
Erik Andersenf0657d32000-04-12 17:49:52 +0000180{
Eric Andersenfad04fd2000-07-14 06:49:52 +0000181 struct winsize win = { 0, 0, 0, 0 };
Erik Andersenf0657d32000-04-12 17:49:52 +0000182 ioctl(0, TIOCGWINSZ, &win);
183 if (win.ws_col > 0) {
184 cmdedit_setwidth( win.ws_col - 1);
185 }
186}
187#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000188
Erik Andersen3522eb12000-03-12 23:49:18 +0000189
Erik Andersend75af992000-03-16 08:09:09 +0000190/* built-in 'cd <path>' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000191static int builtin_cd(struct job *cmd, struct jobSet *junk)
Erik Andersend75af992000-03-16 08:09:09 +0000192{
Erik Andersen161220c2000-03-16 08:12:48 +0000193 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000194
Erik Andersen161220c2000-03-16 08:12:48 +0000195 if (!cmd->progs[0].argv[1] == 1)
196 newdir = getenv("HOME");
197 else
198 newdir = cmd->progs[0].argv[1];
199 if (chdir(newdir)) {
200 printf("cd: %s: %s\n", newdir, strerror(errno));
201 return FALSE;
202 }
Eric Andersen6efc48c2000-07-18 08:16:39 +0000203 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen161220c2000-03-16 08:12:48 +0000204
205 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000206}
207
208/* built-in 'env' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000209static int builtin_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000210{
Erik Andersen161220c2000-03-16 08:12:48 +0000211 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000212
Erik Andersen161220c2000-03-16 08:12:48 +0000213 for (e = environ; *e; e++) {
214 fprintf(stdout, "%s\n", *e);
215 }
216 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000217}
218
219/* built-in 'exit' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000220static int builtin_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000221{
Erik Andersen161220c2000-03-16 08:12:48 +0000222 if (!cmd->progs[0].argv[1] == 1)
223 exit TRUE;
224
Eric Andersenb6106152000-06-19 17:25:40 +0000225 return(atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000226}
227
228/* built-in 'fg' and 'bg' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000229static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000230{
Erik Andersen161220c2000-03-16 08:12:48 +0000231 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000232 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000233
Erik Andersen161220c2000-03-16 08:12:48 +0000234 if (!jobList->head) {
235 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
Matt Kraaid537a952000-07-14 01:51:25 +0000236 errorMsg("%s: exactly one argument is expected\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000237 cmd->progs[0].argv[0]);
238 return FALSE;
239 }
240 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
Matt Kraaid537a952000-07-14 01:51:25 +0000241 errorMsg("%s: bad argument '%s'\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000242 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
243 return FALSE;
244 for (job = jobList->head; job; job = job->next) {
245 if (job->jobId == jobNum) {
246 break;
247 }
248 }
249 }
250 } else {
251 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000252 }
Erik Andersen161220c2000-03-16 08:12:48 +0000253
254 if (!job) {
Matt Kraaid537a952000-07-14 01:51:25 +0000255 errorMsg("%s: unknown job %d\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000256 cmd->progs[0].argv[0], jobNum);
257 return FALSE;
Erik Andersend75af992000-03-16 08:09:09 +0000258 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000259
Erik Andersen161220c2000-03-16 08:12:48 +0000260 if (*cmd->progs[0].argv[0] == 'f') {
261 /* Make this job the foreground job */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000262 /* suppress messages when run from /linuxrc mag@sysgo.de */
263 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
264 perror("tcsetpgrp");
Erik Andersen161220c2000-03-16 08:12:48 +0000265 jobList->fg = job;
266 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000267
Erik Andersen161220c2000-03-16 08:12:48 +0000268 /* Restart the processes in the job */
269 for (i = 0; i < job->numProgs; i++)
270 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000271
Erik Andersen161220c2000-03-16 08:12:48 +0000272 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000273
Erik Andersen161220c2000-03-16 08:12:48 +0000274 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000275
Erik Andersen161220c2000-03-16 08:12:48 +0000276 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000277}
278
279/* built-in 'help' handler */
Eric Andersenfad04fd2000-07-14 06:49:52 +0000280static int builtin_help(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000281{
Erik Andersen161220c2000-03-16 08:12:48 +0000282 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000283
Erik Andersen161220c2000-03-16 08:12:48 +0000284 fprintf(stdout, "\nBuilt-in commands:\n");
285 fprintf(stdout, "-------------------\n");
286 for (x = bltins; x->cmd; x++) {
Eric Andersenfad9c112000-07-25 18:06:52 +0000287 if (x->descr==NULL)
288 continue;
Erik Andersen161220c2000-03-16 08:12:48 +0000289 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
290 }
Erik Andersen330fd2b2000-05-19 05:35:19 +0000291 for (x = bltins_forking; x->cmd; x++) {
Eric Andersenfad9c112000-07-25 18:06:52 +0000292 if (x->descr==NULL)
293 continue;
Erik Andersen330fd2b2000-05-19 05:35:19 +0000294 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
295 }
Erik Andersen161220c2000-03-16 08:12:48 +0000296 fprintf(stdout, "\n\n");
297 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000298}
299
300/* built-in 'jobs' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000301static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000302{
Erik Andersen161220c2000-03-16 08:12:48 +0000303 struct job *job;
304 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000305
Erik Andersen161220c2000-03-16 08:12:48 +0000306 for (job = jobList->head; job; job = job->next) {
307 if (job->runningProgs == job->stoppedProgs)
308 statusString = "Stopped";
309 else
310 statusString = "Running";
311
312 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
313 }
314 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000315}
316
317
318/* built-in 'pwd' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000319static int builtin_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000320{
Eric Andersen6efc48c2000-07-18 08:16:39 +0000321 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen161220c2000-03-16 08:12:48 +0000322 fprintf(stdout, "%s\n", cwd);
323 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000324}
325
Erik Andersen6273f652000-03-17 01:12:41 +0000326/* built-in 'export VAR=value' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000327static int builtin_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000328{
Erik Andersen161220c2000-03-16 08:12:48 +0000329 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000330
Erik Andersen161220c2000-03-16 08:12:48 +0000331 if (!cmd->progs[0].argv[1] == 1) {
Eric Andersen34e19412000-07-10 18:47:24 +0000332 return (builtin_env(cmd, junk));
Erik Andersen161220c2000-03-16 08:12:48 +0000333 }
334 res = putenv(cmd->progs[0].argv[1]);
335 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000336 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000337 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000338}
339
Eric Andersenb54833c2000-07-03 23:56:26 +0000340/* built-in 'read VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000341static int builtin_read(struct job *cmd, struct jobSet *junk)
Eric Andersenb54833c2000-07-03 23:56:26 +0000342{
343 int res = 0, len, newlen;
344 char *s;
345 char string[MAX_READ];
346
347 if (cmd->progs[0].argv[1]) {
348 /* argument (VAR) given: put "VAR=" into buffer */
349 strcpy(string, cmd->progs[0].argv[1]);
350 len = strlen(string);
351 string[len++] = '=';
352 string[len] = '\0';
353 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
354 newlen = strlen(string);
355 if(newlen > len)
356 string[--newlen] = '\0'; /* chomp trailing newline */
357 /*
358 ** string should now contain "VAR=<value>"
359 ** copy it (putenv() won't do that, so we must make sure
360 ** the string resides in a static buffer!)
361 */
362 res = -1;
363 if((s = strdup(string)))
364 res = putenv(s);
365 if (res)
366 fprintf(stdout, "read: %s\n", strerror(errno));
367 }
368 else
369 fgets(string, sizeof(string), stdin);
370
371 return (res);
372}
373
Eric Andersenfad9c112000-07-25 18:06:52 +0000374#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
375/* Built-in handler for 'if' commands */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000376static int builtin_if(struct job *cmd, struct jobSet *jobList)
Eric Andersenfad9c112000-07-25 18:06:52 +0000377{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000378 int status;
379 char* charptr1=cmd->text+3; /* skip over the leading 'if ' */
380
381 /* Now run the 'if' command */
382 status=strlen(charptr1);
383 local_pending_command = xmalloc(status+1);
384 strncpy(local_pending_command, charptr1, status);
385 printf("'if' now running '%s'\n", charptr1);
386 status = busy_loop(NULL); /* Frees local_pending_command */
387 printf("if test returned ");
388 if (status == 0) {
389 printf("TRUE\n");
390 cmd->jobContext |= IF_TRUE_CONTEXT;
391 } else {
392 printf("FALSE\n");
393 cmd->jobContext |= IF_FALSE_CONTEXT;
394 }
395
396 return status;
Eric Andersenfad9c112000-07-25 18:06:52 +0000397}
398
399/* Built-in handler for 'then' (part of the 'if' command) */
400static int builtin_then(struct job *cmd, struct jobSet *junk)
401{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000402 int status;
403 char* charptr1=cmd->text+5; /* skip over the leading 'then ' */
404
405 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
406 errorMsg("unexpected token `then'\n");
Eric Andersenfad9c112000-07-25 18:06:52 +0000407 return FALSE;
408 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000409 /* If the if result was FALSE, skip the 'then' stuff */
410 if (cmd->jobContext & IF_TRUE_CONTEXT) {
411 return TRUE;
412 }
413
Eric Andersenfad9c112000-07-25 18:06:52 +0000414 cmd->jobContext |= THEN_EXP_CONTEXT;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000415 //printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
416
417 /* Now run the 'then' command */
418 status=strlen(charptr1);
419 local_pending_command = xmalloc(status+1);
420 strncpy(local_pending_command, charptr1, status);
421 printf("'then' now running '%s'\n", charptr1);
422 return( busy_loop(NULL));
Eric Andersenfad9c112000-07-25 18:06:52 +0000423}
424
425/* Built-in handler for 'else' (part of the 'if' command) */
426static int builtin_else(struct job *cmd, struct jobSet *junk)
427{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000428 int status;
429 char* charptr1=cmd->text+5; /* skip over the leading 'else ' */
430
431 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
432 errorMsg("unexpected token `else'\n");
433 return FALSE;
434 }
435 /* If the if result was TRUE, skip the 'else' stuff */
436 if (cmd->jobContext & IF_FALSE_CONTEXT) {
437 return TRUE;
438 }
439
Eric Andersenfad9c112000-07-25 18:06:52 +0000440 cmd->jobContext |= ELSE_EXP_CONTEXT;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000441 //printf("Hit an else -- jobContext=%d\n", cmd->jobContext);
442
443 /* Now run the 'else' command */
444 status=strlen(charptr1);
445 local_pending_command = xmalloc(status+1);
446 strncpy(local_pending_command, charptr1, status);
447 printf("'else' now running '%s'\n", charptr1);
448 return( busy_loop(NULL));
Eric Andersenfad9c112000-07-25 18:06:52 +0000449}
450
451/* Built-in handler for 'fi' (part of the 'if' command) */
452static int builtin_fi(struct job *cmd, struct jobSet *junk)
453{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000454 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
455 errorMsg("unexpected token `fi'\n");
456 return FALSE;
457 }
458 /* Clear out the if and then context bits */
459 cmd->jobContext &= ~(IF_TRUE_CONTEXT|IF_FALSE_CONTEXT|THEN_EXP_CONTEXT|ELSE_EXP_CONTEXT);
460 printf("Hit an fi -- jobContext=%d\n", cmd->jobContext);
Eric Andersenfad9c112000-07-25 18:06:52 +0000461 return TRUE;
462}
463#endif
464
Erik Andersen3522eb12000-03-12 23:49:18 +0000465/* Built-in '.' handler (read-in and execute commands from file) */
Eric Andersen34e19412000-07-10 18:47:24 +0000466static int builtin_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000467{
Erik Andersen161220c2000-03-16 08:12:48 +0000468 FILE *input;
469 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000470
Erik Andersen161220c2000-03-16 08:12:48 +0000471 if (!cmd->progs[0].argv[1] == 1)
472 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000473
Erik Andersen161220c2000-03-16 08:12:48 +0000474 input = fopen(cmd->progs[0].argv[1], "r");
475 if (!input) {
476 fprintf(stdout, "Couldn't open file '%s'\n",
477 cmd->progs[0].argv[1]);
478 return FALSE;
479 }
Erik Andersend75af992000-03-16 08:09:09 +0000480
Erik Andersen161220c2000-03-16 08:12:48 +0000481 /* Now run the file */
482 status = busy_loop(input);
483 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000484}
485
486/* built-in 'unset VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000487static int builtin_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000488{
Erik Andersen161220c2000-03-16 08:12:48 +0000489 if (!cmd->progs[0].argv[1] == 1) {
490 fprintf(stdout, "unset: parameter required.\n");
491 return FALSE;
492 }
493 unsetenv(cmd->progs[0].argv[1]);
494 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000495}
496
497/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000498static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000499{
Erik Andersen161220c2000-03-16 08:12:48 +0000500 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000501
Erik Andersen161220c2000-03-16 08:12:48 +0000502 for (i = 0; i < cmd->numProgs; i++) {
503 free(cmd->progs[i].argv);
504 if (cmd->progs[i].redirections)
505 free(cmd->progs[i].redirections);
506 if (cmd->progs[i].freeGlob)
507 globfree(&cmd->progs[i].globResult);
508 }
509 free(cmd->progs);
510 if (cmd->text)
511 free(cmd->text);
512 free(cmd->cmdBuf);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000513 memset(cmd, 0, sizeof(struct job));
Erik Andersen3522eb12000-03-12 23:49:18 +0000514}
515
516/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000517static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000518{
Erik Andersen161220c2000-03-16 08:12:48 +0000519 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000520
Erik Andersen161220c2000-03-16 08:12:48 +0000521 freeJob(job);
522 if (job == jobList->head) {
523 jobList->head = job->next;
524 } else {
525 prevJob = jobList->head;
526 while (prevJob->next != job)
527 prevJob = prevJob->next;
528 prevJob->next = job->next;
529 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000530
Erik Andersen161220c2000-03-16 08:12:48 +0000531 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000532}
533
534/* Checks to see if any background processes have exited -- if they
535 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000536static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000537{
Erik Andersen161220c2000-03-16 08:12:48 +0000538 struct job *job;
539 pid_t childpid;
540 int status;
541 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000542
Erik Andersen161220c2000-03-16 08:12:48 +0000543 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
544 for (job = jobList->head; job; job = job->next) {
545 progNum = 0;
546 while (progNum < job->numProgs &&
547 job->progs[progNum].pid != childpid) progNum++;
548 if (progNum < job->numProgs)
549 break;
550 }
551
Eric Andersena1d187a2000-07-17 19:14:41 +0000552 /* This happens on backticked commands */
553 if(job==NULL)
554 return;
555
Erik Andersen161220c2000-03-16 08:12:48 +0000556 if (WIFEXITED(status) || WIFSIGNALED(status)) {
557 /* child exited */
558 job->runningProgs--;
559 job->progs[progNum].pid = 0;
560
561 if (!job->runningProgs) {
562 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
563 removeJob(jobList, job);
564 }
565 } else {
566 /* child stopped */
567 job->stoppedProgs++;
568 job->progs[progNum].isStopped = 1;
569
570 if (job->stoppedProgs == job->numProgs) {
571 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
572 job->text);
573 }
574 }
Erik Andersend75af992000-03-16 08:09:09 +0000575 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000576
Erik Andersen161220c2000-03-16 08:12:48 +0000577 if (childpid == -1 && errno != ECHILD)
578 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000579}
580
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000581static int setupRedirections(struct childProgram *prog)
582{
583 int i;
584 int openfd;
585 int mode = O_RDONLY;
586 struct redirectionSpecifier *redir = prog->redirections;
587
588 for (i = 0; i < prog->numRedirections; i++, redir++) {
589 switch (redir->type) {
590 case REDIRECT_INPUT:
591 mode = O_RDONLY;
592 break;
593 case REDIRECT_OVERWRITE:
594 mode = O_RDWR | O_CREAT | O_TRUNC;
595 break;
596 case REDIRECT_APPEND:
597 mode = O_RDWR | O_CREAT | O_APPEND;
598 break;
599 }
600
601 openfd = open(redir->filename, mode, 0666);
602 if (openfd < 0) {
603 /* this could get lost if stderr has been redirected, but
604 bash and ash both lose it as well (though zsh doesn't!) */
605 errorMsg("error opening %s: %s\n", redir->filename,
606 strerror(errno));
607 return 1;
608 }
609
610 if (openfd != redir->fd) {
611 dup2(openfd, redir->fd);
612 close(openfd);
613 }
614 }
615
616 return 0;
617}
618
619
Erik Andersend75af992000-03-16 08:09:09 +0000620static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000621{
Eric Andersen1c314ad2000-06-28 16:56:25 +0000622 if (source == NULL) {
623 if (local_pending_command) {
624 /* a command specified (-c option): return it & mark it done */
625 strcpy(command, local_pending_command);
626 free(local_pending_command);
627 local_pending_command = NULL;
628 return 0;
629 }
630 return 1;
631 }
632
Erik Andersen161220c2000-03-16 08:12:48 +0000633 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000634#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000635 int len;
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000636 len=fprintf(stdout, "%s %s", cwd, prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000637 fflush(stdout);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000638 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000639 sprintf(promptStr, "%s %s", cwd, prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000640 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000641 free( promptStr);
Erik Andersen161220c2000-03-16 08:12:48 +0000642 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000643#else
644 fprintf(stdout, "%s %s", cwd, prompt);
645 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000646#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000647 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000648
Erik Andersen161220c2000-03-16 08:12:48 +0000649 if (!fgets(command, BUFSIZ - 2, source)) {
650 if (source == stdin)
651 printf("\n");
652 return 1;
653 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000654
Erik Andersen161220c2000-03-16 08:12:48 +0000655 /* remove trailing newline */
656 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000657
Erik Andersen161220c2000-03-16 08:12:48 +0000658 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000659}
660
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000661#ifdef BB_FEATURE_SH_ENVIRONMENT
662#define __MAX_INT_CHARS 7
663static char* itoa(register int i)
664{
665 static char a[__MAX_INT_CHARS];
666 register char *b = a + sizeof(a) - 1;
667 int sign = (i < 0);
668
669 if (sign)
670 i = -i;
671 *b = 0;
672 do
673 {
674 *--b = '0' + (i % 10);
675 i /= 10;
676 }
677 while (i);
678 if (sign)
679 *--b = '-';
680 return b;
681}
682#endif
683
Erik Andersend75af992000-03-16 08:09:09 +0000684static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000685 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000686{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000687 int argc_l = *argcPtr;
Erik Andersen161220c2000-03-16 08:12:48 +0000688 int argcAlloced = *argcAllocedPtr;
689 int rc;
690 int flags;
691 int i;
Eric Andersenb54833c2000-07-03 23:56:26 +0000692 char *src, *dst, *var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000693
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000694 if (argc_l > 1) { /* cmd->globResult is already initialized */
Erik Andersen161220c2000-03-16 08:12:48 +0000695 flags = GLOB_APPEND;
696 i = prog->globResult.gl_pathc;
697 } else {
698 prog->freeGlob = 1;
699 flags = 0;
700 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000701 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000702 /* do shell variable substitution */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000703 if(*prog->argv[argc_l - 1] == '$') {
704 if ((var = getenv(prog->argv[argc_l - 1] + 1))) {
705 prog->argv[argc_l - 1] = var;
706 }
707#ifdef BB_FEATURE_SH_ENVIRONMENT
708 else {
709 switch(*(prog->argv[argc_l - 1] + 1)) {
710 case '?':
711 prog->argv[argc_l - 1] = itoa(lastReturnCode);
712 break;
713 case '$':
714 prog->argv[argc_l - 1] = itoa(getpid());
715 break;
716 case '#':
717 prog->argv[argc_l - 1] = itoa(argc-1);
718 break;
719 case '!':
720 if (lastBgPid==-1)
721 *(prog->argv[argc_l - 1])='\0';
722 else
723 prog->argv[argc_l - 1] = itoa(lastBgPid);
724 break;
725 case '0':case '1':case '2':case '3':case '4':
726 case '5':case '6':case '7':case '8':case '9':
727 {
728 int index=*(prog->argv[argc_l - 1] + 1)-48;
729 if (index >= argc) {
730 *(prog->argv[argc_l - 1])='\0';
731 } else {
732 prog->argv[argc_l - 1] = argv[index];
733 }
734 }
735 break;
736 }
737 }
738#endif
739 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000740
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000741 rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
Erik Andersen161220c2000-03-16 08:12:48 +0000742 if (rc == GLOB_NOSPACE) {
Matt Kraaid537a952000-07-14 01:51:25 +0000743 errorMsg("out of space during glob operation\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000744 return;
745 } else if (rc == GLOB_NOMATCH ||
746 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000747 strcmp(prog->argv[argc_l - 1],
Eric Andersene92108a2000-07-26 00:53:56 +0000748 prog->globResult.gl_pathv[i]) == 0)) {
Erik Andersen161220c2000-03-16 08:12:48 +0000749 /* we need to remove whatever \ quoting is still present */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000750 src = dst = prog->argv[argc_l - 1];
Erik Andersen161220c2000-03-16 08:12:48 +0000751 while (*src) {
752 if (*src != '\\')
753 *dst++ = *src;
754 src++;
755 }
756 *dst = '\0';
757 } else if (!rc) {
758 argcAlloced += (prog->globResult.gl_pathc - i);
759 prog->argv =
760 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000761 memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
Erik Andersen161220c2000-03-16 08:12:48 +0000762 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000763 argc_l += (prog->globResult.gl_pathc - i - 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000764 }
765
766 *argcAllocedPtr = argcAlloced;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000767 *argcPtr = argc_l;
Erik Andersen3522eb12000-03-12 23:49:18 +0000768}
769
770/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
771 line). If a valid command is found, commandPtr is set to point to
772 the beginning of the next command (if the original command had more
773 then one job associated with it) or NULL if no more commands are
774 present. */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000775static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000776{
Erik Andersen161220c2000-03-16 08:12:48 +0000777 char *command;
778 char *returnCommand = NULL;
779 char *src, *buf, *chptr;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000780 int argc_l = 0;
Erik Andersen161220c2000-03-16 08:12:48 +0000781 int done = 0;
782 int argvAlloced;
783 int i;
784 char quote = '\0';
785 int count;
786 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000787
Erik Andersen161220c2000-03-16 08:12:48 +0000788 /* skip leading white space */
789 while (**commandPtr && isspace(**commandPtr))
790 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000791
Erik Andersen161220c2000-03-16 08:12:48 +0000792 /* this handles empty lines or leading '#' characters */
793 if (!**commandPtr || (**commandPtr == '#')) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000794 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000795 return 0;
796 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000797
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000798 *inBg = 0;
Erik Andersen161220c2000-03-16 08:12:48 +0000799 job->numProgs = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000800 job->progs = xmalloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000801
Erik Andersen161220c2000-03-16 08:12:48 +0000802 /* We set the argv elements to point inside of this string. The
Eric Andersenb54833c2000-07-03 23:56:26 +0000803 memory is freed by freeJob(). Allocate twice the original
804 length in case we need to quote every single character.
Erik Andersen3522eb12000-03-12 23:49:18 +0000805
Erik Andersen161220c2000-03-16 08:12:48 +0000806 Getting clean memory relieves us of the task of NULL
807 terminating things and makes the rest of this look a bit
808 cleaner (though it is, admittedly, a tad less efficient) */
Eric Andersenfad9c112000-07-25 18:06:52 +0000809 job->cmdBuf = command = calloc(2*strlen(*commandPtr) + 1, sizeof(char));
Erik Andersen161220c2000-03-16 08:12:48 +0000810 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000811
Erik Andersen161220c2000-03-16 08:12:48 +0000812 prog = job->progs;
813 prog->numRedirections = 0;
814 prog->redirections = NULL;
815 prog->freeGlob = 0;
816 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000817
Erik Andersen161220c2000-03-16 08:12:48 +0000818 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000819 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000820 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000821
Erik Andersen161220c2000-03-16 08:12:48 +0000822 buf = command;
823 src = *commandPtr;
824 while (*src && !done) {
825 if (quote == *src) {
826 quote = '\0';
827 } else if (quote) {
828 if (*src == '\\') {
829 src++;
830 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000831 errorMsg("character expected after \\\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000832 freeJob(job);
833 return 1;
834 }
835
836 /* in shell, "\'" should yield \' */
837 if (*src != quote)
838 *buf++ = '\\';
839 } else if (*src == '*' || *src == '?' || *src == '[' ||
840 *src == ']') *buf++ = '\\';
841 *buf++ = *src;
842 } else if (isspace(*src)) {
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000843 if (*prog->argv[argc_l]) {
844 buf++, argc_l++;
Erik Andersen161220c2000-03-16 08:12:48 +0000845 /* +1 here leaves room for the NULL which ends argv */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000846 if ((argc_l + 1) == argvAlloced) {
Erik Andersen161220c2000-03-16 08:12:48 +0000847 argvAlloced += 5;
848 prog->argv = realloc(prog->argv,
849 sizeof(*prog->argv) *
850 argvAlloced);
851 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000852 globLastArgument(prog, &argc_l, &argvAlloced);
853 prog->argv[argc_l] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000854 }
855 } else
856 switch (*src) {
857 case '"':
858 case '\'':
859 quote = *src;
860 break;
861
862 case '#': /* comment */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000863 if (*(src-1)== '$')
864 *buf++ = *src;
865 else
866 done = 1;
Erik Andersen161220c2000-03-16 08:12:48 +0000867 break;
868
869 case '>': /* redirections */
870 case '<':
871 i = prog->numRedirections++;
872 prog->redirections = realloc(prog->redirections,
873 sizeof(*prog->redirections) *
874 (i + 1));
875
876 prog->redirections[i].fd = -1;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000877 if (buf != prog->argv[argc_l]) {
Erik Andersen161220c2000-03-16 08:12:48 +0000878 /* the stuff before this character may be the file number
879 being redirected */
880 prog->redirections[i].fd =
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000881 strtol(prog->argv[argc_l], &chptr, 10);
Erik Andersen161220c2000-03-16 08:12:48 +0000882
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000883 if (*chptr && *prog->argv[argc_l]) {
884 buf++, argc_l++;
885 globLastArgument(prog, &argc_l, &argvAlloced);
886 prog->argv[argc_l] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000887 }
888 }
889
890 if (prog->redirections[i].fd == -1) {
891 if (*src == '>')
892 prog->redirections[i].fd = 1;
893 else
894 prog->redirections[i].fd = 0;
895 }
896
897 if (*src++ == '>') {
898 if (*src == '>')
899 prog->redirections[i].type =
900 REDIRECT_APPEND, src++;
901 else
902 prog->redirections[i].type = REDIRECT_OVERWRITE;
903 } else {
904 prog->redirections[i].type = REDIRECT_INPUT;
905 }
906
907 /* This isn't POSIX sh compliant. Oh well. */
908 chptr = src;
909 while (isspace(*chptr))
910 chptr++;
911
912 if (!*chptr) {
Matt Kraaid537a952000-07-14 01:51:25 +0000913 errorMsg("file name expected after %c\n", *src);
Erik Andersen161220c2000-03-16 08:12:48 +0000914 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000915 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000916 return 1;
917 }
918
919 prog->redirections[i].filename = buf;
920 while (*chptr && !isspace(*chptr))
921 *buf++ = *chptr++;
922
923 src = chptr - 1; /* we src++ later */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000924 prog->argv[argc_l] = ++buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000925 break;
926
927 case '|': /* pipe */
928 /* finish this command */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000929 if (*prog->argv[argc_l])
930 argc_l++;
931 if (!argc_l) {
Eric Andersena1d187a2000-07-17 19:14:41 +0000932 errorMsg("empty command in pipe\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000933 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000934 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000935 return 1;
936 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000937 prog->argv[argc_l] = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +0000938
939 /* and start the next */
940 job->numProgs++;
941 job->progs = realloc(job->progs,
942 sizeof(*job->progs) * job->numProgs);
943 prog = job->progs + (job->numProgs - 1);
944 prog->numRedirections = 0;
945 prog->redirections = NULL;
946 prog->freeGlob = 0;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000947 argc_l = 0;
Erik Andersen161220c2000-03-16 08:12:48 +0000948
949 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000950 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000951 prog->argv[0] = ++buf;
952
953 src++;
954 while (*src && isspace(*src))
955 src++;
956
957 if (!*src) {
Eric Andersena1d187a2000-07-17 19:14:41 +0000958 errorMsg("empty command in pipe\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +0000959 freeJob(job);
960 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000961 return 1;
962 }
963 src--; /* we'll ++ it at the end of the loop */
964
965 break;
966
967 case '&': /* background */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000968 *inBg = 1;
Erik Andersen161220c2000-03-16 08:12:48 +0000969 case ';': /* multiple commands */
970 done = 1;
971 returnCommand = *commandPtr + (src - *commandPtr) + 1;
972 break;
973
974 case '\\':
975 src++;
976 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000977 errorMsg("character expected after \\\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +0000978 freeJob(job);
Erik Andersen161220c2000-03-16 08:12:48 +0000979 return 1;
980 }
981 if (*src == '*' || *src == '[' || *src == ']'
982 || *src == '?') *buf++ = '\\';
983 /* fallthrough */
Eric Andersena1d187a2000-07-17 19:14:41 +0000984#ifdef BB_FEATURE_SH_BACKTICKS
Eric Andersenec10b9d2000-07-14 01:13:11 +0000985 case '`':
986 /* Exec a backtick-ed command */
987 {
Eric Andersena1d187a2000-07-17 19:14:41 +0000988 char* charptr1=NULL, *charptr2;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000989 char* ptr=NULL;
Eric Andersena1d187a2000-07-17 19:14:41 +0000990 struct job *newJob;
991 struct jobSet njobList = { NULL, NULL };
992 int pipefd[2];
993 int size;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000994
995 ptr=strchr(++src, '`');
996 if (ptr==NULL) {
997 fprintf(stderr, "Unmatched '`' in command\n");
998 freeJob(job);
999 return 1;
1000 }
1001
Eric Andersena1d187a2000-07-17 19:14:41 +00001002 /* Make some space to hold just the backticked command */
Eric Andersen6efc48c2000-07-18 08:16:39 +00001003 charptr1 = charptr2 = xmalloc(1+ptr-src);
Eric Andersena1d187a2000-07-17 19:14:41 +00001004 snprintf(charptr1, 1+ptr-src, src);
1005 newJob = xmalloc(sizeof(struct job));
1006 /* Now parse and run the backticked command */
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001007 if (!parseCommand(&charptr1, newJob, &njobList, inBg)
Eric Andersena1d187a2000-07-17 19:14:41 +00001008 && newJob->numProgs) {
1009 pipe(pipefd);
1010 runCommand(newJob, &njobList, 0, pipefd);
Eric Andersenec10b9d2000-07-14 01:13:11 +00001011 }
Eric Andersena1d187a2000-07-17 19:14:41 +00001012 checkJobs(jobList);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001013 freeJob(newJob);
1014 free(charptr2);
1015
1016 /* Make a copy of any stuff left over in the command
1017 * line after the second backtick */
1018 charptr2 = xmalloc(strlen(ptr)+1);
1019 memcpy(charptr2, ptr+1, strlen(ptr));
1020
Eric Andersenec10b9d2000-07-14 01:13:11 +00001021
Eric Andersena1d187a2000-07-17 19:14:41 +00001022 /* Copy the output from the backtick-ed command into the
1023 * command line, making extra room as needed */
1024 --src;
1025 charptr1 = xmalloc(BUFSIZ);
1026 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
1027 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1028 if (newSize > BUFSIZ) {
1029 *commandPtr=realloc(*commandPtr, src - *commandPtr +
1030 size + 1 + strlen(charptr2));
1031 }
1032 memcpy(src, charptr1, size);
1033 src+=size;
1034 }
1035 free(charptr1);
1036 close(pipefd[0]);
1037 if (*(src-1)=='\n')
1038 --src;
1039
1040 /* Now paste into the *commandPtr all the stuff
1041 * leftover after the second backtick */
1042 memcpy(src, charptr2, strlen(charptr2));
Eric Andersena1d187a2000-07-17 19:14:41 +00001043 free(charptr2);
1044
Eric Andersena1d187a2000-07-17 19:14:41 +00001045 /* Now recursively call parseCommand to deal with the new
1046 * and improved version of the command line with the backtick
1047 * results expanded in place... */
Eric Andersen6efc48c2000-07-18 08:16:39 +00001048 freeJob(job);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001049 return(parseCommand(commandPtr, job, jobList, inBg));
Eric Andersenec10b9d2000-07-14 01:13:11 +00001050 }
1051 break;
Eric Andersena1d187a2000-07-17 19:14:41 +00001052#endif // BB_FEATURE_SH_BACKTICKS
Erik Andersen161220c2000-03-16 08:12:48 +00001053 default:
1054 *buf++ = *src;
1055 }
1056
Erik Andersend75af992000-03-16 08:09:09 +00001057 src++;
Erik Andersen161220c2000-03-16 08:12:48 +00001058 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001059
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001060 if (*prog->argv[argc_l]) {
1061 argc_l++;
1062 globLastArgument(prog, &argc_l, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +00001063 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001064 if (!argc_l) {
Erik Andersen161220c2000-03-16 08:12:48 +00001065 freeJob(job);
1066 return 0;
1067 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001068 prog->argv[argc_l] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +00001069
Erik Andersen161220c2000-03-16 08:12:48 +00001070 if (!returnCommand) {
Eric Andersenec10b9d2000-07-14 01:13:11 +00001071 job->text = xmalloc(strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +00001072 strcpy(job->text, *commandPtr);
1073 } else {
1074 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +00001075 count = returnCommand - *commandPtr;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001076 job->text = xmalloc(count + 1);
Erik Andersen161220c2000-03-16 08:12:48 +00001077 strncpy(job->text, *commandPtr, count);
1078 job->text[count] = '\0';
1079 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001080
Erik Andersen161220c2000-03-16 08:12:48 +00001081 *commandPtr = returnCommand;
Erik Andersen3522eb12000-03-12 23:49:18 +00001082
Erik Andersend75af992000-03-16 08:09:09 +00001083 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001084}
1085
Eric Andersena1d187a2000-07-17 19:14:41 +00001086static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
Erik Andersen3522eb12000-03-12 23:49:18 +00001087{
Eric Andersenfad9c112000-07-25 18:06:52 +00001088 struct job *theJob;
Erik Andersen161220c2000-03-16 08:12:48 +00001089 int i;
Eric Andersen6efc48c2000-07-18 08:16:39 +00001090 int nextin, nextout;
Erik Andersen161220c2000-03-16 08:12:48 +00001091 int pipefds[2]; /* pipefd[0] is for reading */
1092 struct builtInCommand *x;
Eric Andersenb54833c2000-07-03 23:56:26 +00001093#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Erik Andersenbcd61772000-05-13 06:33:19 +00001094 const struct BB_applet *a = applets;
1095#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001096
Eric Andersen6efc48c2000-07-18 08:16:39 +00001097
1098 nextin = 0, nextout = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001099 for (i = 0; i < newJob->numProgs; i++) {
1100 if ((i + 1) < newJob->numProgs) {
Erik Andersen161220c2000-03-16 08:12:48 +00001101 pipe(pipefds);
1102 nextout = pipefds[1];
1103 } else {
Eric Andersen6efc48c2000-07-18 08:16:39 +00001104 if (outPipe[1]!=-1) {
1105 nextout = outPipe[1];
1106 } else {
1107 nextout = 1;
1108 }
Erik Andersen161220c2000-03-16 08:12:48 +00001109 }
1110
Eric Andersen34e19412000-07-10 18:47:24 +00001111 /* Check if the command matches any non-forking builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +00001112 for (x = bltins; x->cmd; x++) {
Eric Andersene92108a2000-07-26 00:53:56 +00001113 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
Eric Andersenec10b9d2000-07-14 01:13:11 +00001114 return (x->function(newJob, jobList));
Erik Andersen330fd2b2000-05-19 05:35:19 +00001115 }
1116 }
1117
Eric Andersenec10b9d2000-07-14 01:13:11 +00001118 if (!(newJob->progs[i].pid = fork())) {
Erik Andersen161220c2000-03-16 08:12:48 +00001119 signal(SIGTTOU, SIG_DFL);
1120
Eric Andersena1d187a2000-07-17 19:14:41 +00001121 if (outPipe[1]!=-1) {
1122 close(outPipe[0]);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001123 }
1124 if (nextin != 0) {
1125 dup2(nextin, 0);
1126 close(nextin);
1127 }
1128
1129 if (nextout != 1) {
Erik Andersen161220c2000-03-16 08:12:48 +00001130 dup2(nextout, 1);
Eric Andersena1d187a2000-07-17 19:14:41 +00001131 dup2(nextout, 2);
Erik Andersen161220c2000-03-16 08:12:48 +00001132 close(nextout);
1133 }
1134
1135 /* explicit redirections override pipes */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001136 setupRedirections(newJob->progs + i);
Erik Andersen161220c2000-03-16 08:12:48 +00001137
Eric Andersen34e19412000-07-10 18:47:24 +00001138 /* Check if the command matches any of the other builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +00001139 for (x = bltins_forking; x->cmd; x++) {
Eric Andersene92108a2000-07-26 00:53:56 +00001140 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
Eric Andersenec10b9d2000-07-14 01:13:11 +00001141 exit (x->function(newJob, jobList));
Erik Andersenbcd61772000-05-13 06:33:19 +00001142 }
1143 }
Eric Andersenb54833c2000-07-03 23:56:26 +00001144#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Eric Andersen34e19412000-07-10 18:47:24 +00001145 /* Check if the command matches any busybox internal commands here */
Eric Andersen6efc48c2000-07-18 08:16:39 +00001146 /* TODO: Add matching when paths are appended (i.e. 'cat' currently
1147 * works, but '/bin/cat' doesn't ) */
Erik Andersenbcd61772000-05-13 06:33:19 +00001148 while (a->name != 0) {
Eric Andersenec10b9d2000-07-14 01:13:11 +00001149 if (strcmp(newJob->progs[i].argv[0], a->name) == 0) {
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001150 int argc_l;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001151 char** argv=newJob->progs[i].argv;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001152 for(argc_l=0;*argv!=NULL; argv++, argc_l++);
1153 exit((*(a->main)) (argc_l, newJob->progs[i].argv));
Erik Andersenbcd61772000-05-13 06:33:19 +00001154 }
1155 a++;
1156 }
1157#endif
1158
Eric Andersenec10b9d2000-07-14 01:13:11 +00001159 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001160 fatalError("%s: %s\n", newJob->progs[i].argv[0],
Erik Andersen161220c2000-03-16 08:12:48 +00001161 strerror(errno));
1162 }
Eric Andersen6efc48c2000-07-18 08:16:39 +00001163 if (outPipe[1]!=-1) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001164 close(outPipe[1]);
1165 }
Erik Andersen161220c2000-03-16 08:12:48 +00001166
1167 /* put our child in the process group whose leader is the
1168 first process in this pipe */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001169 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
Erik Andersen161220c2000-03-16 08:12:48 +00001170 if (nextin != 0)
1171 close(nextin);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001172 if (nextout != 1)
Erik Andersen161220c2000-03-16 08:12:48 +00001173 close(nextout);
1174
1175 /* If there isn't another process, nextin is garbage
1176 but it doesn't matter */
1177 nextin = pipefds[0];
1178 }
1179
Eric Andersenec10b9d2000-07-14 01:13:11 +00001180 newJob->pgrp = newJob->progs[0].pid;
Erik Andersen161220c2000-03-16 08:12:48 +00001181
Eric Andersenfad9c112000-07-25 18:06:52 +00001182 /* find the ID for the theJob to use */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001183 newJob->jobId = 1;
Eric Andersenfad9c112000-07-25 18:06:52 +00001184 for (theJob = jobList->head; theJob; theJob = theJob->next)
1185 if (theJob->jobId >= newJob->jobId)
1186 newJob->jobId = theJob->jobId + 1;
Erik Andersen161220c2000-03-16 08:12:48 +00001187
Eric Andersenfad9c112000-07-25 18:06:52 +00001188 /* add the theJob to the list of running jobs */
Erik Andersen161220c2000-03-16 08:12:48 +00001189 if (!jobList->head) {
Eric Andersenfad9c112000-07-25 18:06:52 +00001190 theJob = jobList->head = malloc(sizeof(*theJob));
Erik Andersend75af992000-03-16 08:09:09 +00001191 } else {
Eric Andersenfad9c112000-07-25 18:06:52 +00001192 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
1193 theJob->next = malloc(sizeof(*theJob));
1194 theJob = theJob->next;
Erik Andersend75af992000-03-16 08:09:09 +00001195 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001196
Eric Andersenfad9c112000-07-25 18:06:52 +00001197 *theJob = *newJob;
1198 theJob->next = NULL;
1199 theJob->runningProgs = theJob->numProgs;
1200 theJob->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001201
Erik Andersen161220c2000-03-16 08:12:48 +00001202 if (inBg) {
Eric Andersenfad9c112000-07-25 18:06:52 +00001203 /* we don't wait for background theJobs to return -- append it
1204 to the list of backgrounded theJobs and leave it alone */
1205 printf("[%d] %d\n", theJob->jobId,
Eric Andersenec10b9d2000-07-14 01:13:11 +00001206 newJob->progs[newJob->numProgs - 1].pid);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001207#ifdef BB_FEATURE_SH_ENVIRONMENT
1208 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1209#endif
Erik Andersen161220c2000-03-16 08:12:48 +00001210 } else {
Eric Andersenfad9c112000-07-25 18:06:52 +00001211 jobList->fg = theJob;
Erik Andersen3522eb12000-03-12 23:49:18 +00001212
Erik Andersen161220c2000-03-16 08:12:48 +00001213 /* move the new process group into the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001214 /* suppress messages when run from /linuxrc mag@sysgo.de */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001215 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
Erik Andersen161220c2000-03-16 08:12:48 +00001216 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001217 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001218
Erik Andersen161220c2000-03-16 08:12:48 +00001219 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001220}
1221
Erik Andersend75af992000-03-16 08:09:09 +00001222static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +00001223{
Erik Andersen161220c2000-03-16 08:12:48 +00001224 char *command;
1225 char *nextCommand = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001226 struct job newJob;
Eric Andersen1c314ad2000-06-28 16:56:25 +00001227 pid_t parent_pgrp;
Erik Andersen161220c2000-03-16 08:12:48 +00001228 int i;
Erik Andersen161220c2000-03-16 08:12:48 +00001229 int inBg;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001230 int status;
1231 newJob.jobContext = REGULAR_JOB_CONTEXT;
Erik Andersen3522eb12000-03-12 23:49:18 +00001232
Eric Andersen1c314ad2000-06-28 16:56:25 +00001233 /* save current owner of TTY so we can restore it on exit */
1234 parent_pgrp = tcgetpgrp(0);
1235
Erik Andersen161220c2000-03-16 08:12:48 +00001236 command = (char *) calloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +00001237
Erik Andersen161220c2000-03-16 08:12:48 +00001238 /* don't pay any attention to this signal; it just confuses
1239 things and isn't really meant for shells anyway */
1240 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +00001241
Erik Andersen161220c2000-03-16 08:12:48 +00001242 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +00001243 if (!jobList.fg) {
1244 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +00001245
Erik Andersend75af992000-03-16 08:09:09 +00001246 /* see if any background processes have exited */
1247 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +00001248
Erik Andersend75af992000-03-16 08:09:09 +00001249 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +00001250 if (getCommand(input, command))
1251 break;
1252 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +00001253 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001254
Eric Andersenec10b9d2000-07-14 01:13:11 +00001255 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +00001256 newJob.numProgs) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001257 int pipefds[2] = {-1,-1};
1258 runCommand(&newJob, &jobList, inBg, pipefds);
Eric Andersenfad9c112000-07-25 18:06:52 +00001259 }
1260 else {
Eric Andersena1d187a2000-07-17 19:14:41 +00001261 free(command);
1262 command = (char *) calloc(BUFSIZ, sizeof(char));
Eric Andersen6efc48c2000-07-18 08:16:39 +00001263 nextCommand = NULL;
Erik Andersend75af992000-03-16 08:09:09 +00001264 }
1265 } else {
1266 /* a job is running in the foreground; wait for it */
1267 i = 0;
1268 while (!jobList.fg->progs[i].pid ||
Eric Andersenfad9c112000-07-25 18:06:52 +00001269 jobList.fg->progs[i].isStopped == 1) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +00001270
Erik Andersend75af992000-03-16 08:09:09 +00001271 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +00001272
Erik Andersend75af992000-03-16 08:09:09 +00001273 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +00001274 /* the child exited */
1275 jobList.fg->runningProgs--;
1276 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001277
Erik Andersen161220c2000-03-16 08:12:48 +00001278 if (!jobList.fg->runningProgs) {
1279 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +00001280
Erik Andersen161220c2000-03-16 08:12:48 +00001281 removeJob(&jobList, jobList.fg);
1282 jobList.fg = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001283 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001284#ifdef BB_FEATURE_SH_ENVIRONMENT
1285 lastReturnCode=WEXITSTATUS(status);
1286#endif
Erik Andersend75af992000-03-16 08:09:09 +00001287 } else {
Erik Andersen161220c2000-03-16 08:12:48 +00001288 /* the child was stopped */
1289 jobList.fg->stoppedProgs++;
1290 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +00001291
Erik Andersen161220c2000-03-16 08:12:48 +00001292 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1293 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1294 "Stopped", jobList.fg->text);
1295 jobList.fg = NULL;
1296 }
Erik Andersend75af992000-03-16 08:09:09 +00001297 }
1298
1299 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +00001300 /* move the shell to the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001301 /* suppress messages when run from /linuxrc mag@sysgo.de */
1302 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1303 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001304 }
1305 }
1306 }
Erik Andersen161220c2000-03-16 08:12:48 +00001307 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +00001308
Eric Andersen1c314ad2000-06-28 16:56:25 +00001309 /* return controlling TTY back to parent process group before exiting */
1310 if (tcsetpgrp(0, parent_pgrp))
Eric Andersenb54833c2000-07-03 23:56:26 +00001311 perror("tcsetpgrp");
1312
1313 /* return exit status if called with "-c" */
1314 if (input == NULL && WIFEXITED(status))
1315 return WEXITSTATUS(status);
1316
Erik Andersen161220c2000-03-16 08:12:48 +00001317 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001318}
1319
1320
Eric Andersenfad9c112000-07-25 18:06:52 +00001321#ifdef BB_FEATURE_CLEAN_UP
1322void free_memory(void)
1323{
1324 if (promptStr)
1325 free(promptStr);
1326 if (cwd)
1327 free(cwd);
1328 if (local_pending_command)
1329 free(local_pending_command);
1330
1331 if (jobList.fg && !jobList.fg->runningProgs) {
1332 removeJob(&jobList, jobList.fg);
1333 }
1334}
1335#endif
1336
Eric Andersen6efc48c2000-07-18 08:16:39 +00001337
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001338int shell_main(int argc_l, char **argv_l)
Erik Andersen3522eb12000-03-12 23:49:18 +00001339{
Erik Andersen161220c2000-03-16 08:12:48 +00001340 FILE *input = stdin;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001341 argc = argc_l;
1342 argv = argv_l;
Erik Andersen3522eb12000-03-12 23:49:18 +00001343
Eric Andersen6efc48c2000-07-18 08:16:39 +00001344 /* initialize the cwd -- this is never freed...*/
1345 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1346 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen3522eb12000-03-12 23:49:18 +00001347
Eric Andersenfad9c112000-07-25 18:06:52 +00001348#ifdef BB_FEATURE_CLEAN_UP
1349 atexit(free_memory);
1350#endif
1351
Erik Andersenf0657d32000-04-12 17:49:52 +00001352#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersen1d1d9502000-04-21 01:26:49 +00001353 cmdedit_init();
Erik Andersenf0657d32000-04-12 17:49:52 +00001354 signal(SIGWINCH, win_changed);
1355 win_changed(0);
1356#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001357
Erik Andersen161220c2000-03-16 08:12:48 +00001358 //if (argv[0] && argv[0][0] == '-') {
Eric Andersen34e19412000-07-10 18:47:24 +00001359 // builtin_source("/etc/profile");
Erik Andersen161220c2000-03-16 08:12:48 +00001360 //}
Erik Andersen3522eb12000-03-12 23:49:18 +00001361
Eric Andersen6efc48c2000-07-18 08:16:39 +00001362
Erik Andersen161220c2000-03-16 08:12:48 +00001363 if (argc < 2) {
Eric Andersen6efc48c2000-07-18 08:16:39 +00001364 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
Erik Andersenf0657d32000-04-12 17:49:52 +00001365 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Erik Andersen161220c2000-03-16 08:12:48 +00001366 } else {
Eric Andersen1c314ad2000-06-28 16:56:25 +00001367 if (argv[1][0]=='-' && argv[1][1]=='c') {
1368 int i;
1369 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1370 if (local_pending_command == 0) {
Matt Kraaibe84cd42000-07-12 17:02:35 +00001371 fatalError("out of memory\n");
Eric Andersen1c314ad2000-06-28 16:56:25 +00001372 }
1373 for(i=2; i<argc; i++)
1374 {
1375 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
Eric Andersenb54833c2000-07-03 23:56:26 +00001376 local_pending_command = realloc(local_pending_command,
1377 strlen(local_pending_command) + strlen(argv[i]));
1378 if (local_pending_command==NULL)
Matt Kraaibe84cd42000-07-12 17:02:35 +00001379 fatalError("commands for -c option too long\n");
Eric Andersen1c314ad2000-06-28 16:56:25 +00001380 }
1381 strcat(local_pending_command, argv[i]);
Eric Andersenb54833c2000-07-03 23:56:26 +00001382 if ( (i + 1) < argc)
Eric Andersen1c314ad2000-06-28 16:56:25 +00001383 strcat(local_pending_command, " ");
1384 }
1385 input = NULL;
1386
Erik Andersene5b6c7d2000-04-17 16:16:10 +00001387 }
Eric Andersen1c314ad2000-06-28 16:56:25 +00001388 else if (argv[1][0]=='-') {
1389 usage(shell_usage);
1390 }
1391 else {
1392 input = fopen(argv[1], "r");
1393 if (!input) {
Matt Kraaibe84cd42000-07-12 17:02:35 +00001394 fatalError("Couldn't open file '%s': %s\n", argv[1],
Eric Andersen1c314ad2000-06-28 16:56:25 +00001395 strerror(errno));
1396 }
Erik Andersenf0657d32000-04-12 17:49:52 +00001397 }
Erik Andersen161220c2000-03-16 08:12:48 +00001398 }
Erik Andersend75af992000-03-16 08:09:09 +00001399
Erik Andersen161220c2000-03-16 08:12:48 +00001400 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +00001401}