blob: 9f67f1c9170ee34f1f7ff8b127ba96a378a3f82e [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 Andersen501c88b2000-07-28 15:14:45 +000032//#define DEBUG_SHELL
Eric Andersena1d187a2000-07-17 19:14:41 +000033
34
Erik Andersen3522eb12000-03-12 23:49:18 +000035#include "internal.h"
36#include <stdio.h>
37#include <stdlib.h>
38#include <ctype.h>
39#include <errno.h>
40#include <fcntl.h>
41#include <glob.h>
42#include <signal.h>
43#include <string.h>
44#include <sys/ioctl.h>
45#include <sys/wait.h>
46#include <unistd.h>
Eric Andersen501c88b2000-07-28 15:14:45 +000047#include <getopt.h>
Erik Andersenf0657d32000-04-12 17:49:52 +000048#ifdef BB_FEATURE_SH_COMMAND_EDITING
49#include "cmdedit.h"
50#endif
Erik Andersen3522eb12000-03-12 23:49:18 +000051
Eric Andersen6efc48c2000-07-18 08:16:39 +000052#define MAX_LINE 256 /* size of input buffer for `read' builtin */
Eric Andersenb54833c2000-07-03 23:56:26 +000053#define MAX_READ 128 /* size of input buffer for `read' builtin */
Erik Andersen3522eb12000-03-12 23:49:18 +000054#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
55
Erik Andersend75af992000-03-16 08:09:09 +000056
57enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
Erik Andersen161220c2000-03-16 08:12:48 +000058 REDIRECT_APPEND
59};
Erik Andersen3522eb12000-03-12 23:49:18 +000060
Eric Andersene92108a2000-07-26 00:53:56 +000061static const unsigned int REGULAR_JOB_CONTEXT=0x1;
Eric Andersen6a99aaf2000-07-27 00:15:20 +000062static const unsigned int IF_TRUE_CONTEXT=0x2;
63static const unsigned int IF_FALSE_CONTEXT=0x4;
64static const unsigned int THEN_EXP_CONTEXT=0x8;
65static const unsigned int ELSE_EXP_CONTEXT=0x10;
Eric Andersene92108a2000-07-26 00:53:56 +000066
Eric Andersenfad9c112000-07-25 18:06:52 +000067
Erik Andersen3522eb12000-03-12 23:49:18 +000068struct jobSet {
Erik Andersen161220c2000-03-16 08:12:48 +000069 struct job *head; /* head of list of running jobs */
70 struct job *fg; /* current foreground job */
Erik Andersen3522eb12000-03-12 23:49:18 +000071};
72
73struct redirectionSpecifier {
Erik Andersen161220c2000-03-16 08:12:48 +000074 enum redirectionType type; /* type of redirection */
75 int fd; /* file descriptor being redirected */
76 char *filename; /* file to redirect fd to */
Erik Andersen3522eb12000-03-12 23:49:18 +000077};
78
79struct childProgram {
Erik Andersen161220c2000-03-16 08:12:48 +000080 pid_t pid; /* 0 if exited */
81 char **argv; /* program name and arguments */
82 int numRedirections; /* elements in redirection array */
83 struct redirectionSpecifier *redirections; /* I/O redirections */
84 glob_t globResult; /* result of parameter globbing */
85 int freeGlob; /* should we globfree(&globResult)? */
86 int isStopped; /* is the program currently running? */
Erik Andersen3522eb12000-03-12 23:49:18 +000087};
88
89struct job {
Erik Andersen161220c2000-03-16 08:12:48 +000090 int jobId; /* job number */
91 int numProgs; /* total number of programs in job */
92 int runningProgs; /* number of programs running */
93 char *text; /* name of job */
94 char *cmdBuf; /* buffer various argv's point into */
95 pid_t pgrp; /* process group ID for the job */
96 struct childProgram *progs; /* array of programs in job */
97 struct job *next; /* to track background commands */
98 int stoppedProgs; /* number of programs alive, but stopped */
Eric Andersenfad9c112000-07-25 18:06:52 +000099 int jobContext; /* bitmask defining current context */
Erik Andersen3522eb12000-03-12 23:49:18 +0000100};
101
102struct builtInCommand {
Erik Andersen161220c2000-03-16 08:12:48 +0000103 char *cmd; /* name */
104 char *descr; /* description */
Erik Andersen161220c2000-03-16 08:12:48 +0000105 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
Erik Andersen3522eb12000-03-12 23:49:18 +0000106};
107
Eric Andersen34e19412000-07-10 18:47:24 +0000108/* function prototypes for builtins */
109static int builtin_cd(struct job *cmd, struct jobSet *junk);
110static int builtin_env(struct job *dummy, struct jobSet *junk);
111static int builtin_exit(struct job *cmd, struct jobSet *junk);
112static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
113static int builtin_help(struct job *cmd, struct jobSet *junk);
114static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
115static int builtin_pwd(struct job *dummy, struct jobSet *junk);
116static int builtin_export(struct job *cmd, struct jobSet *junk);
117static int builtin_source(struct job *cmd, struct jobSet *jobList);
118static int builtin_unset(struct job *cmd, struct jobSet *junk);
119static int builtin_read(struct job *cmd, struct jobSet *junk);
Eric Andersenfad9c112000-07-25 18:06:52 +0000120#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
121static int builtin_if(struct job *cmd, struct jobSet *junk);
122static int builtin_then(struct job *cmd, struct jobSet *junk);
123static int builtin_else(struct job *cmd, struct jobSet *junk);
124static int builtin_fi(struct job *cmd, struct jobSet *junk);
125#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000126
Eric Andersen34e19412000-07-10 18:47:24 +0000127
128/* function prototypes for shell stuff */
Erik Andersend75af992000-03-16 08:09:09 +0000129static void checkJobs(struct jobSet *jobList);
130static int getCommand(FILE * source, char *command);
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000131static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg);
Eric Andersena1d187a2000-07-17 19:14:41 +0000132static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
Erik Andersen3522eb12000-03-12 23:49:18 +0000133static int busy_loop(FILE * input);
134
Erik Andersend75af992000-03-16 08:09:09 +0000135
Mark Whitley37653aa2000-07-12 23:36:17 +0000136/* Table of built-in functions (these are non-forking builtins, meaning they
137 * can change global variables in the parent shell process but they will not
138 * work with pipes and redirects; 'unset foo | whatever' will not work) */
Erik Andersen3522eb12000-03-12 23:49:18 +0000139static struct builtInCommand bltins[] = {
Eric Andersenfad9c112000-07-25 18:06:52 +0000140 {"bg", "Resume a job in the background", builtin_fg_bg},
141 {"cd", "Change working directory", builtin_cd},
142 {"exit", "Exit from shell()", builtin_exit},
143 {"fg", "Bring job into the foreground", builtin_fg_bg},
144 {"jobs", "Lists the active jobs", builtin_jobs},
145 {"export", "Set environment variable", builtin_export},
146 {"unset", "Unset environment variable", builtin_unset},
147 {"read", "Input environment variable", builtin_read},
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000148#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
149 {"if", NULL, builtin_if},
150 {"then", NULL, builtin_then},
151 {"else", NULL, builtin_else},
152 {"fi", NULL, builtin_fi},
153#endif
Eric Andersenfad9c112000-07-25 18:06:52 +0000154 {NULL, NULL, NULL}
Erik Andersen330fd2b2000-05-19 05:35:19 +0000155};
156
Mark Whitley37653aa2000-07-12 23:36:17 +0000157/* Table of forking built-in functions (things that fork cannot change global
158 * variables in the parent process, such as the current working directory) */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000159static struct builtInCommand bltins_forking[] = {
Eric Andersenfad9c112000-07-25 18:06:52 +0000160 {"env", "Print all environment variables", builtin_env},
161 {"pwd", "Print current directory", builtin_pwd},
Eric Andersenfad9c112000-07-25 18:06:52 +0000162 {".", "Source-in and run commands in a file", builtin_source},
163 {"help", "List shell built-in commands", builtin_help},
164 {NULL, NULL, NULL}
Erik Andersen3522eb12000-03-12 23:49:18 +0000165};
166
Erik Andersen3522eb12000-03-12 23:49:18 +0000167static char *prompt = "# ";
Eric Andersen6efc48c2000-07-18 08:16:39 +0000168static char *cwd;
Eric Andersen1c314ad2000-06-28 16:56:25 +0000169static char *local_pending_command = NULL;
Eric Andersenfad9c112000-07-25 18:06:52 +0000170static char *promptStr = NULL;
171static struct jobSet jobList = { NULL, NULL };
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000172static int argc;
173static char **argv;
174#ifdef BB_FEATURE_SH_ENVIRONMENT
175static int lastBgPid=-1;
176static int lastReturnCode=-1;
Eric Andersen501c88b2000-07-28 15:14:45 +0000177static int showXtrace=FALSE;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000178#endif
Eric Andersen501c88b2000-07-28 15:14:45 +0000179
Erik Andersen3522eb12000-03-12 23:49:18 +0000180
Erik Andersenf0657d32000-04-12 17:49:52 +0000181#ifdef BB_FEATURE_SH_COMMAND_EDITING
Eric Andersenfad04fd2000-07-14 06:49:52 +0000182void win_changed(int junk)
Erik Andersenf0657d32000-04-12 17:49:52 +0000183{
Eric Andersenfad04fd2000-07-14 06:49:52 +0000184 struct winsize win = { 0, 0, 0, 0 };
Erik Andersenf0657d32000-04-12 17:49:52 +0000185 ioctl(0, TIOCGWINSZ, &win);
186 if (win.ws_col > 0) {
187 cmdedit_setwidth( win.ws_col - 1);
188 }
189}
190#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000191
Erik Andersen3522eb12000-03-12 23:49:18 +0000192
Erik Andersend75af992000-03-16 08:09:09 +0000193/* built-in 'cd <path>' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000194static int builtin_cd(struct job *cmd, struct jobSet *junk)
Erik Andersend75af992000-03-16 08:09:09 +0000195{
Erik Andersen161220c2000-03-16 08:12:48 +0000196 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000197
Erik Andersen161220c2000-03-16 08:12:48 +0000198 if (!cmd->progs[0].argv[1] == 1)
199 newdir = getenv("HOME");
200 else
201 newdir = cmd->progs[0].argv[1];
202 if (chdir(newdir)) {
203 printf("cd: %s: %s\n", newdir, strerror(errno));
204 return FALSE;
205 }
Eric Andersen6efc48c2000-07-18 08:16:39 +0000206 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen161220c2000-03-16 08:12:48 +0000207
208 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000209}
210
211/* built-in 'env' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000212static int builtin_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000213{
Erik Andersen161220c2000-03-16 08:12:48 +0000214 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000215
Erik Andersen161220c2000-03-16 08:12:48 +0000216 for (e = environ; *e; e++) {
217 fprintf(stdout, "%s\n", *e);
218 }
219 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000220}
221
222/* built-in 'exit' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000223static int builtin_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000224{
Erik Andersen161220c2000-03-16 08:12:48 +0000225 if (!cmd->progs[0].argv[1] == 1)
226 exit TRUE;
227
Eric Andersenb6106152000-06-19 17:25:40 +0000228 return(atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000229}
230
231/* built-in 'fg' and 'bg' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000232static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000233{
Erik Andersen161220c2000-03-16 08:12:48 +0000234 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000235 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000236
Erik Andersen161220c2000-03-16 08:12:48 +0000237 if (!jobList->head) {
238 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
Matt Kraaid537a952000-07-14 01:51:25 +0000239 errorMsg("%s: exactly one argument is expected\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000240 cmd->progs[0].argv[0]);
241 return FALSE;
242 }
243 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
Matt Kraaid537a952000-07-14 01:51:25 +0000244 errorMsg("%s: bad argument '%s'\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000245 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
246 return FALSE;
247 for (job = jobList->head; job; job = job->next) {
248 if (job->jobId == jobNum) {
249 break;
250 }
251 }
252 }
253 } else {
254 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000255 }
Erik Andersen161220c2000-03-16 08:12:48 +0000256
257 if (!job) {
Matt Kraaid537a952000-07-14 01:51:25 +0000258 errorMsg("%s: unknown job %d\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000259 cmd->progs[0].argv[0], jobNum);
260 return FALSE;
Erik Andersend75af992000-03-16 08:09:09 +0000261 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000262
Erik Andersen161220c2000-03-16 08:12:48 +0000263 if (*cmd->progs[0].argv[0] == 'f') {
264 /* Make this job the foreground job */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000265 /* suppress messages when run from /linuxrc mag@sysgo.de */
266 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
267 perror("tcsetpgrp");
Erik Andersen161220c2000-03-16 08:12:48 +0000268 jobList->fg = job;
269 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000270
Erik Andersen161220c2000-03-16 08:12:48 +0000271 /* Restart the processes in the job */
272 for (i = 0; i < job->numProgs; i++)
273 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000274
Erik Andersen161220c2000-03-16 08:12:48 +0000275 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000276
Erik Andersen161220c2000-03-16 08:12:48 +0000277 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000278
Erik Andersen161220c2000-03-16 08:12:48 +0000279 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000280}
281
282/* built-in 'help' handler */
Eric Andersenfad04fd2000-07-14 06:49:52 +0000283static int builtin_help(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000284{
Erik Andersen161220c2000-03-16 08:12:48 +0000285 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000286
Erik Andersen161220c2000-03-16 08:12:48 +0000287 fprintf(stdout, "\nBuilt-in commands:\n");
288 fprintf(stdout, "-------------------\n");
289 for (x = bltins; x->cmd; x++) {
Eric Andersenfad9c112000-07-25 18:06:52 +0000290 if (x->descr==NULL)
291 continue;
Erik Andersen161220c2000-03-16 08:12:48 +0000292 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
293 }
Erik Andersen330fd2b2000-05-19 05:35:19 +0000294 for (x = bltins_forking; x->cmd; x++) {
Eric Andersenfad9c112000-07-25 18:06:52 +0000295 if (x->descr==NULL)
296 continue;
Erik Andersen330fd2b2000-05-19 05:35:19 +0000297 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
298 }
Erik Andersen161220c2000-03-16 08:12:48 +0000299 fprintf(stdout, "\n\n");
300 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000301}
302
303/* built-in 'jobs' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000304static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000305{
Erik Andersen161220c2000-03-16 08:12:48 +0000306 struct job *job;
307 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000308
Erik Andersen161220c2000-03-16 08:12:48 +0000309 for (job = jobList->head; job; job = job->next) {
310 if (job->runningProgs == job->stoppedProgs)
311 statusString = "Stopped";
312 else
313 statusString = "Running";
314
315 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
316 }
317 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000318}
319
320
321/* built-in 'pwd' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000322static int builtin_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000323{
Eric Andersen6efc48c2000-07-18 08:16:39 +0000324 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen161220c2000-03-16 08:12:48 +0000325 fprintf(stdout, "%s\n", cwd);
326 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000327}
328
Erik Andersen6273f652000-03-17 01:12:41 +0000329/* built-in 'export VAR=value' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000330static int builtin_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000331{
Erik Andersen161220c2000-03-16 08:12:48 +0000332 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000333
Erik Andersen161220c2000-03-16 08:12:48 +0000334 if (!cmd->progs[0].argv[1] == 1) {
Eric Andersen34e19412000-07-10 18:47:24 +0000335 return (builtin_env(cmd, junk));
Erik Andersen161220c2000-03-16 08:12:48 +0000336 }
337 res = putenv(cmd->progs[0].argv[1]);
338 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000339 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000340 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000341}
342
Eric Andersenb54833c2000-07-03 23:56:26 +0000343/* built-in 'read VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000344static int builtin_read(struct job *cmd, struct jobSet *junk)
Eric Andersenb54833c2000-07-03 23:56:26 +0000345{
346 int res = 0, len, newlen;
347 char *s;
348 char string[MAX_READ];
349
350 if (cmd->progs[0].argv[1]) {
351 /* argument (VAR) given: put "VAR=" into buffer */
352 strcpy(string, cmd->progs[0].argv[1]);
353 len = strlen(string);
354 string[len++] = '=';
355 string[len] = '\0';
356 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
357 newlen = strlen(string);
358 if(newlen > len)
359 string[--newlen] = '\0'; /* chomp trailing newline */
360 /*
361 ** string should now contain "VAR=<value>"
362 ** copy it (putenv() won't do that, so we must make sure
363 ** the string resides in a static buffer!)
364 */
365 res = -1;
366 if((s = strdup(string)))
367 res = putenv(s);
368 if (res)
369 fprintf(stdout, "read: %s\n", strerror(errno));
370 }
371 else
372 fgets(string, sizeof(string), stdin);
373
374 return (res);
375}
376
Eric Andersenfad9c112000-07-25 18:06:52 +0000377#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
378/* Built-in handler for 'if' commands */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000379static int builtin_if(struct job *cmd, struct jobSet *jobList)
Eric Andersenfad9c112000-07-25 18:06:52 +0000380{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000381 int status;
382 char* charptr1=cmd->text+3; /* skip over the leading 'if ' */
383
384 /* Now run the 'if' command */
385 status=strlen(charptr1);
386 local_pending_command = xmalloc(status+1);
387 strncpy(local_pending_command, charptr1, status);
Eric Andersen501c88b2000-07-28 15:14:45 +0000388 local_pending_command[status]='\0';
389#ifdef DEBUG_SHELL
390 fprintf(stderr, "'if' now testing '%s'\n", local_pending_command);
391#endif
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000392 status = busy_loop(NULL); /* Frees local_pending_command */
Eric Andersen501c88b2000-07-28 15:14:45 +0000393#ifdef DEBUG_SHELL
394 fprintf(stderr, "if test returned ");
395#endif
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000396 if (status == 0) {
Eric Andersen501c88b2000-07-28 15:14:45 +0000397#ifdef DEBUG_SHELL
398 fprintf(stderr, "TRUE\n");
399#endif
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000400 cmd->jobContext |= IF_TRUE_CONTEXT;
401 } else {
Eric Andersen501c88b2000-07-28 15:14:45 +0000402#ifdef DEBUG_SHELL
403 fprintf(stderr, "FALSE\n");
404#endif
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000405 cmd->jobContext |= IF_FALSE_CONTEXT;
406 }
407
408 return status;
Eric Andersenfad9c112000-07-25 18:06:52 +0000409}
410
411/* Built-in handler for 'then' (part of the 'if' command) */
412static int builtin_then(struct job *cmd, struct jobSet *junk)
413{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000414 int status;
415 char* charptr1=cmd->text+5; /* skip over the leading 'then ' */
416
417 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
418 errorMsg("unexpected token `then'\n");
Eric Andersenfad9c112000-07-25 18:06:52 +0000419 return FALSE;
420 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000421 /* If the if result was FALSE, skip the 'then' stuff */
Eric Andersen501c88b2000-07-28 15:14:45 +0000422 if (cmd->jobContext & IF_FALSE_CONTEXT) {
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000423 return TRUE;
424 }
425
Eric Andersenfad9c112000-07-25 18:06:52 +0000426 cmd->jobContext |= THEN_EXP_CONTEXT;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000427 //printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
428
429 /* Now run the 'then' command */
430 status=strlen(charptr1);
431 local_pending_command = xmalloc(status+1);
432 strncpy(local_pending_command, charptr1, status);
Eric Andersen501c88b2000-07-28 15:14:45 +0000433 local_pending_command[status]='\0';
434#ifdef DEBUG_SHELL
435 fprintf(stderr, "'then' now running '%s'\n", charptr1);
436#endif
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000437 return( busy_loop(NULL));
Eric Andersenfad9c112000-07-25 18:06:52 +0000438}
439
440/* Built-in handler for 'else' (part of the 'if' command) */
441static int builtin_else(struct job *cmd, struct jobSet *junk)
442{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000443 int status;
444 char* charptr1=cmd->text+5; /* skip over the leading 'else ' */
445
446 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
447 errorMsg("unexpected token `else'\n");
448 return FALSE;
449 }
450 /* If the if result was TRUE, skip the 'else' stuff */
Eric Andersen501c88b2000-07-28 15:14:45 +0000451 if (cmd->jobContext & IF_TRUE_CONTEXT) {
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000452 return TRUE;
453 }
454
Eric Andersenfad9c112000-07-25 18:06:52 +0000455 cmd->jobContext |= ELSE_EXP_CONTEXT;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000456 //printf("Hit an else -- jobContext=%d\n", cmd->jobContext);
457
458 /* Now run the 'else' command */
459 status=strlen(charptr1);
460 local_pending_command = xmalloc(status+1);
461 strncpy(local_pending_command, charptr1, status);
Eric Andersen501c88b2000-07-28 15:14:45 +0000462 local_pending_command[status]='\0';
463#ifdef DEBUG_SHELL
464 fprintf(stderr, "'else' now running '%s'\n", charptr1);
465#endif
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000466 return( busy_loop(NULL));
Eric Andersenfad9c112000-07-25 18:06:52 +0000467}
468
469/* Built-in handler for 'fi' (part of the 'if' command) */
470static int builtin_fi(struct job *cmd, struct jobSet *junk)
471{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000472 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
473 errorMsg("unexpected token `fi'\n");
474 return FALSE;
475 }
476 /* Clear out the if and then context bits */
477 cmd->jobContext &= ~(IF_TRUE_CONTEXT|IF_FALSE_CONTEXT|THEN_EXP_CONTEXT|ELSE_EXP_CONTEXT);
Eric Andersen501c88b2000-07-28 15:14:45 +0000478#ifdef DEBUG_SHELL
479 fprintf(stderr, "Hit an fi -- jobContext=%d\n", cmd->jobContext);
480#endif
Eric Andersenfad9c112000-07-25 18:06:52 +0000481 return TRUE;
482}
483#endif
484
Erik Andersen3522eb12000-03-12 23:49:18 +0000485/* Built-in '.' handler (read-in and execute commands from file) */
Eric Andersen34e19412000-07-10 18:47:24 +0000486static int builtin_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000487{
Erik Andersen161220c2000-03-16 08:12:48 +0000488 FILE *input;
489 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000490
Erik Andersen161220c2000-03-16 08:12:48 +0000491 if (!cmd->progs[0].argv[1] == 1)
492 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000493
Erik Andersen161220c2000-03-16 08:12:48 +0000494 input = fopen(cmd->progs[0].argv[1], "r");
495 if (!input) {
496 fprintf(stdout, "Couldn't open file '%s'\n",
497 cmd->progs[0].argv[1]);
498 return FALSE;
499 }
Erik Andersend75af992000-03-16 08:09:09 +0000500
Erik Andersen161220c2000-03-16 08:12:48 +0000501 /* Now run the file */
502 status = busy_loop(input);
503 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000504}
505
506/* built-in 'unset VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000507static int builtin_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000508{
Erik Andersen161220c2000-03-16 08:12:48 +0000509 if (!cmd->progs[0].argv[1] == 1) {
510 fprintf(stdout, "unset: parameter required.\n");
511 return FALSE;
512 }
513 unsetenv(cmd->progs[0].argv[1]);
514 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000515}
516
517/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000518static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000519{
Erik Andersen161220c2000-03-16 08:12:48 +0000520 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000521
Erik Andersen161220c2000-03-16 08:12:48 +0000522 for (i = 0; i < cmd->numProgs; i++) {
523 free(cmd->progs[i].argv);
524 if (cmd->progs[i].redirections)
525 free(cmd->progs[i].redirections);
526 if (cmd->progs[i].freeGlob)
527 globfree(&cmd->progs[i].globResult);
528 }
529 free(cmd->progs);
530 if (cmd->text)
531 free(cmd->text);
532 free(cmd->cmdBuf);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000533 memset(cmd, 0, sizeof(struct job));
Erik Andersen3522eb12000-03-12 23:49:18 +0000534}
535
536/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000537static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000538{
Erik Andersen161220c2000-03-16 08:12:48 +0000539 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000540
Erik Andersen161220c2000-03-16 08:12:48 +0000541 freeJob(job);
542 if (job == jobList->head) {
543 jobList->head = job->next;
544 } else {
545 prevJob = jobList->head;
546 while (prevJob->next != job)
547 prevJob = prevJob->next;
548 prevJob->next = job->next;
549 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000550
Erik Andersen161220c2000-03-16 08:12:48 +0000551 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000552}
553
554/* Checks to see if any background processes have exited -- if they
555 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000556static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000557{
Erik Andersen161220c2000-03-16 08:12:48 +0000558 struct job *job;
559 pid_t childpid;
560 int status;
561 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000562
Erik Andersen161220c2000-03-16 08:12:48 +0000563 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
564 for (job = jobList->head; job; job = job->next) {
565 progNum = 0;
566 while (progNum < job->numProgs &&
567 job->progs[progNum].pid != childpid) progNum++;
568 if (progNum < job->numProgs)
569 break;
570 }
571
Eric Andersena1d187a2000-07-17 19:14:41 +0000572 /* This happens on backticked commands */
573 if(job==NULL)
574 return;
575
Erik Andersen161220c2000-03-16 08:12:48 +0000576 if (WIFEXITED(status) || WIFSIGNALED(status)) {
577 /* child exited */
578 job->runningProgs--;
579 job->progs[progNum].pid = 0;
580
581 if (!job->runningProgs) {
582 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
583 removeJob(jobList, job);
584 }
585 } else {
586 /* child stopped */
587 job->stoppedProgs++;
588 job->progs[progNum].isStopped = 1;
589
590 if (job->stoppedProgs == job->numProgs) {
591 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
592 job->text);
593 }
594 }
Erik Andersend75af992000-03-16 08:09:09 +0000595 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000596
Erik Andersen161220c2000-03-16 08:12:48 +0000597 if (childpid == -1 && errno != ECHILD)
598 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000599}
600
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000601static int setupRedirections(struct childProgram *prog)
602{
603 int i;
604 int openfd;
605 int mode = O_RDONLY;
606 struct redirectionSpecifier *redir = prog->redirections;
607
608 for (i = 0; i < prog->numRedirections; i++, redir++) {
609 switch (redir->type) {
610 case REDIRECT_INPUT:
611 mode = O_RDONLY;
612 break;
613 case REDIRECT_OVERWRITE:
614 mode = O_RDWR | O_CREAT | O_TRUNC;
615 break;
616 case REDIRECT_APPEND:
617 mode = O_RDWR | O_CREAT | O_APPEND;
618 break;
619 }
620
621 openfd = open(redir->filename, mode, 0666);
622 if (openfd < 0) {
623 /* this could get lost if stderr has been redirected, but
624 bash and ash both lose it as well (though zsh doesn't!) */
625 errorMsg("error opening %s: %s\n", redir->filename,
626 strerror(errno));
627 return 1;
628 }
629
630 if (openfd != redir->fd) {
631 dup2(openfd, redir->fd);
632 close(openfd);
633 }
634 }
635
636 return 0;
637}
638
639
Erik Andersend75af992000-03-16 08:09:09 +0000640static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000641{
Eric Andersen1c314ad2000-06-28 16:56:25 +0000642 if (source == NULL) {
643 if (local_pending_command) {
644 /* a command specified (-c option): return it & mark it done */
645 strcpy(command, local_pending_command);
646 free(local_pending_command);
647 local_pending_command = NULL;
648 return 0;
649 }
650 return 1;
651 }
652
Erik Andersen161220c2000-03-16 08:12:48 +0000653 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000654#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000655 int len;
Eric Andersen501c88b2000-07-28 15:14:45 +0000656
657 /*
658 ** enable command line editing only while a command line
659 ** is actually being read; otherwise, we'll end up bequeathing
660 ** atexit() handlers and other unwanted stuff to our
661 ** child processes (rob@sysgo.de)
662 */
663 cmdedit_init();
664 signal(SIGWINCH, win_changed);
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000665 len=fprintf(stdout, "%s %s", cwd, prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000666 fflush(stdout);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000667 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000668 sprintf(promptStr, "%s %s", cwd, prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000669 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000670 free( promptStr);
Eric Andersen501c88b2000-07-28 15:14:45 +0000671 cmdedit_terminate();
672 signal(SIGWINCH, SIG_DFL);
Erik Andersen161220c2000-03-16 08:12:48 +0000673 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000674#else
675 fprintf(stdout, "%s %s", cwd, prompt);
676 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000677#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000678 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000679
Erik Andersen161220c2000-03-16 08:12:48 +0000680 if (!fgets(command, BUFSIZ - 2, source)) {
681 if (source == stdin)
682 printf("\n");
683 return 1;
684 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000685
Erik Andersen161220c2000-03-16 08:12:48 +0000686 /* remove trailing newline */
687 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000688
Erik Andersen161220c2000-03-16 08:12:48 +0000689 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000690}
691
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000692#ifdef BB_FEATURE_SH_ENVIRONMENT
693#define __MAX_INT_CHARS 7
694static char* itoa(register int i)
695{
696 static char a[__MAX_INT_CHARS];
697 register char *b = a + sizeof(a) - 1;
698 int sign = (i < 0);
699
700 if (sign)
701 i = -i;
702 *b = 0;
703 do
704 {
705 *--b = '0' + (i % 10);
706 i /= 10;
707 }
708 while (i);
709 if (sign)
710 *--b = '-';
711 return b;
712}
713#endif
714
Erik Andersend75af992000-03-16 08:09:09 +0000715static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000716 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000717{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000718 int argc_l = *argcPtr;
Erik Andersen161220c2000-03-16 08:12:48 +0000719 int argcAlloced = *argcAllocedPtr;
720 int rc;
721 int flags;
722 int i;
Eric Andersenb54833c2000-07-03 23:56:26 +0000723 char *src, *dst, *var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000724
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000725 if (argc_l > 1) { /* cmd->globResult is already initialized */
Erik Andersen161220c2000-03-16 08:12:48 +0000726 flags = GLOB_APPEND;
727 i = prog->globResult.gl_pathc;
728 } else {
729 prog->freeGlob = 1;
730 flags = 0;
731 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000732 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000733 /* do shell variable substitution */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000734 if(*prog->argv[argc_l - 1] == '$') {
735 if ((var = getenv(prog->argv[argc_l - 1] + 1))) {
736 prog->argv[argc_l - 1] = var;
737 }
738#ifdef BB_FEATURE_SH_ENVIRONMENT
739 else {
740 switch(*(prog->argv[argc_l - 1] + 1)) {
741 case '?':
742 prog->argv[argc_l - 1] = itoa(lastReturnCode);
743 break;
744 case '$':
745 prog->argv[argc_l - 1] = itoa(getpid());
746 break;
747 case '#':
748 prog->argv[argc_l - 1] = itoa(argc-1);
749 break;
750 case '!':
751 if (lastBgPid==-1)
752 *(prog->argv[argc_l - 1])='\0';
753 else
754 prog->argv[argc_l - 1] = itoa(lastBgPid);
755 break;
756 case '0':case '1':case '2':case '3':case '4':
757 case '5':case '6':case '7':case '8':case '9':
758 {
759 int index=*(prog->argv[argc_l - 1] + 1)-48;
760 if (index >= argc) {
761 *(prog->argv[argc_l - 1])='\0';
762 } else {
763 prog->argv[argc_l - 1] = argv[index];
764 }
765 }
766 break;
767 }
768 }
769#endif
770 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000771
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000772 rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
Erik Andersen161220c2000-03-16 08:12:48 +0000773 if (rc == GLOB_NOSPACE) {
Matt Kraaid537a952000-07-14 01:51:25 +0000774 errorMsg("out of space during glob operation\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000775 return;
776 } else if (rc == GLOB_NOMATCH ||
777 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000778 strcmp(prog->argv[argc_l - 1],
Eric Andersene92108a2000-07-26 00:53:56 +0000779 prog->globResult.gl_pathv[i]) == 0)) {
Erik Andersen161220c2000-03-16 08:12:48 +0000780 /* we need to remove whatever \ quoting is still present */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000781 src = dst = prog->argv[argc_l - 1];
Erik Andersen161220c2000-03-16 08:12:48 +0000782 while (*src) {
783 if (*src != '\\')
784 *dst++ = *src;
785 src++;
786 }
787 *dst = '\0';
788 } else if (!rc) {
789 argcAlloced += (prog->globResult.gl_pathc - i);
Matt Kraaib8907522000-09-13 02:08:21 +0000790 prog->argv = xrealloc(prog->argv, argcAlloced * sizeof(*prog->argv));
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000791 memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
Erik Andersen161220c2000-03-16 08:12:48 +0000792 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000793 argc_l += (prog->globResult.gl_pathc - i - 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000794 }
795
796 *argcAllocedPtr = argcAlloced;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000797 *argcPtr = argc_l;
Erik Andersen3522eb12000-03-12 23:49:18 +0000798}
799
800/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
801 line). If a valid command is found, commandPtr is set to point to
802 the beginning of the next command (if the original command had more
803 then one job associated with it) or NULL if no more commands are
804 present. */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000805static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000806{
Erik Andersen161220c2000-03-16 08:12:48 +0000807 char *command;
808 char *returnCommand = NULL;
809 char *src, *buf, *chptr;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000810 int argc_l = 0;
Erik Andersen161220c2000-03-16 08:12:48 +0000811 int done = 0;
812 int argvAlloced;
813 int i;
814 char quote = '\0';
815 int count;
816 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000817
Erik Andersen161220c2000-03-16 08:12:48 +0000818 /* skip leading white space */
819 while (**commandPtr && isspace(**commandPtr))
820 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000821
Erik Andersen161220c2000-03-16 08:12:48 +0000822 /* this handles empty lines or leading '#' characters */
823 if (!**commandPtr || (**commandPtr == '#')) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000824 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000825 return 0;
826 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000827
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000828 *inBg = 0;
Erik Andersen161220c2000-03-16 08:12:48 +0000829 job->numProgs = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000830 job->progs = xmalloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000831
Erik Andersen161220c2000-03-16 08:12:48 +0000832 /* We set the argv elements to point inside of this string. The
Eric Andersenb54833c2000-07-03 23:56:26 +0000833 memory is freed by freeJob(). Allocate twice the original
834 length in case we need to quote every single character.
Erik Andersen3522eb12000-03-12 23:49:18 +0000835
Erik Andersen161220c2000-03-16 08:12:48 +0000836 Getting clean memory relieves us of the task of NULL
837 terminating things and makes the rest of this look a bit
838 cleaner (though it is, admittedly, a tad less efficient) */
Matt Kraaib8907522000-09-13 02:08:21 +0000839 job->cmdBuf = command = xcalloc(2*strlen(*commandPtr) + 1, sizeof(char));
Erik Andersen161220c2000-03-16 08:12:48 +0000840 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000841
Erik Andersen161220c2000-03-16 08:12:48 +0000842 prog = job->progs;
843 prog->numRedirections = 0;
844 prog->redirections = NULL;
845 prog->freeGlob = 0;
846 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000847
Erik Andersen161220c2000-03-16 08:12:48 +0000848 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000849 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000850 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000851
Erik Andersen161220c2000-03-16 08:12:48 +0000852 buf = command;
853 src = *commandPtr;
854 while (*src && !done) {
855 if (quote == *src) {
856 quote = '\0';
857 } else if (quote) {
858 if (*src == '\\') {
859 src++;
860 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000861 errorMsg("character expected after \\\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000862 freeJob(job);
863 return 1;
864 }
865
866 /* in shell, "\'" should yield \' */
867 if (*src != quote)
868 *buf++ = '\\';
869 } else if (*src == '*' || *src == '?' || *src == '[' ||
870 *src == ']') *buf++ = '\\';
871 *buf++ = *src;
872 } else if (isspace(*src)) {
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000873 if (*prog->argv[argc_l]) {
874 buf++, argc_l++;
Erik Andersen161220c2000-03-16 08:12:48 +0000875 /* +1 here leaves room for the NULL which ends argv */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000876 if ((argc_l + 1) == argvAlloced) {
Erik Andersen161220c2000-03-16 08:12:48 +0000877 argvAlloced += 5;
Matt Kraaib8907522000-09-13 02:08:21 +0000878 prog->argv = xrealloc(prog->argv,
879 sizeof(*prog->argv) *
880 argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000881 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000882 globLastArgument(prog, &argc_l, &argvAlloced);
883 prog->argv[argc_l] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000884 }
885 } else
886 switch (*src) {
887 case '"':
888 case '\'':
889 quote = *src;
890 break;
891
892 case '#': /* comment */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000893 if (*(src-1)== '$')
894 *buf++ = *src;
895 else
896 done = 1;
Erik Andersen161220c2000-03-16 08:12:48 +0000897 break;
898
899 case '>': /* redirections */
900 case '<':
901 i = prog->numRedirections++;
Matt Kraaib8907522000-09-13 02:08:21 +0000902 prog->redirections = xrealloc(prog->redirections,
903 sizeof(*prog->redirections) *
904 (i + 1));
Erik Andersen161220c2000-03-16 08:12:48 +0000905
906 prog->redirections[i].fd = -1;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000907 if (buf != prog->argv[argc_l]) {
Erik Andersen161220c2000-03-16 08:12:48 +0000908 /* the stuff before this character may be the file number
909 being redirected */
910 prog->redirections[i].fd =
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000911 strtol(prog->argv[argc_l], &chptr, 10);
Erik Andersen161220c2000-03-16 08:12:48 +0000912
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000913 if (*chptr && *prog->argv[argc_l]) {
914 buf++, argc_l++;
915 globLastArgument(prog, &argc_l, &argvAlloced);
916 prog->argv[argc_l] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000917 }
918 }
919
920 if (prog->redirections[i].fd == -1) {
921 if (*src == '>')
922 prog->redirections[i].fd = 1;
923 else
924 prog->redirections[i].fd = 0;
925 }
926
927 if (*src++ == '>') {
928 if (*src == '>')
929 prog->redirections[i].type =
930 REDIRECT_APPEND, src++;
931 else
932 prog->redirections[i].type = REDIRECT_OVERWRITE;
933 } else {
934 prog->redirections[i].type = REDIRECT_INPUT;
935 }
936
937 /* This isn't POSIX sh compliant. Oh well. */
938 chptr = src;
939 while (isspace(*chptr))
940 chptr++;
941
942 if (!*chptr) {
Matt Kraaid537a952000-07-14 01:51:25 +0000943 errorMsg("file name expected after %c\n", *src);
Erik Andersen161220c2000-03-16 08:12:48 +0000944 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000945 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000946 return 1;
947 }
948
949 prog->redirections[i].filename = buf;
950 while (*chptr && !isspace(*chptr))
951 *buf++ = *chptr++;
952
953 src = chptr - 1; /* we src++ later */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000954 prog->argv[argc_l] = ++buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000955 break;
956
957 case '|': /* pipe */
958 /* finish this command */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000959 if (*prog->argv[argc_l])
960 argc_l++;
961 if (!argc_l) {
Eric Andersena1d187a2000-07-17 19:14:41 +0000962 errorMsg("empty command in pipe\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000963 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000964 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000965 return 1;
966 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000967 prog->argv[argc_l] = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +0000968
969 /* and start the next */
970 job->numProgs++;
Matt Kraaib8907522000-09-13 02:08:21 +0000971 job->progs = xrealloc(job->progs,
972 sizeof(*job->progs) * job->numProgs);
Erik Andersen161220c2000-03-16 08:12:48 +0000973 prog = job->progs + (job->numProgs - 1);
974 prog->numRedirections = 0;
975 prog->redirections = NULL;
976 prog->freeGlob = 0;
Eric Andersen501c88b2000-07-28 15:14:45 +0000977 prog->isStopped = 0;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000978 argc_l = 0;
Erik Andersen161220c2000-03-16 08:12:48 +0000979
980 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000981 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000982 prog->argv[0] = ++buf;
983
984 src++;
985 while (*src && isspace(*src))
986 src++;
987
988 if (!*src) {
Eric Andersena1d187a2000-07-17 19:14:41 +0000989 errorMsg("empty command in pipe\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +0000990 freeJob(job);
991 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000992 return 1;
993 }
994 src--; /* we'll ++ it at the end of the loop */
995
996 break;
997
998 case '&': /* background */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000999 *inBg = 1;
Erik Andersen161220c2000-03-16 08:12:48 +00001000 case ';': /* multiple commands */
1001 done = 1;
1002 returnCommand = *commandPtr + (src - *commandPtr) + 1;
1003 break;
1004
1005 case '\\':
1006 src++;
1007 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +00001008 errorMsg("character expected after \\\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +00001009 freeJob(job);
Erik Andersen161220c2000-03-16 08:12:48 +00001010 return 1;
1011 }
1012 if (*src == '*' || *src == '[' || *src == ']'
1013 || *src == '?') *buf++ = '\\';
1014 /* fallthrough */
Eric Andersena1d187a2000-07-17 19:14:41 +00001015#ifdef BB_FEATURE_SH_BACKTICKS
Eric Andersenec10b9d2000-07-14 01:13:11 +00001016 case '`':
1017 /* Exec a backtick-ed command */
1018 {
Eric Andersena1d187a2000-07-17 19:14:41 +00001019 char* charptr1=NULL, *charptr2;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001020 char* ptr=NULL;
Eric Andersena1d187a2000-07-17 19:14:41 +00001021 struct job *newJob;
1022 struct jobSet njobList = { NULL, NULL };
1023 int pipefd[2];
1024 int size;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001025
1026 ptr=strchr(++src, '`');
1027 if (ptr==NULL) {
1028 fprintf(stderr, "Unmatched '`' in command\n");
1029 freeJob(job);
1030 return 1;
1031 }
1032
Eric Andersena1d187a2000-07-17 19:14:41 +00001033 /* Make some space to hold just the backticked command */
Eric Andersen6efc48c2000-07-18 08:16:39 +00001034 charptr1 = charptr2 = xmalloc(1+ptr-src);
Eric Andersena1d187a2000-07-17 19:14:41 +00001035 snprintf(charptr1, 1+ptr-src, src);
1036 newJob = xmalloc(sizeof(struct job));
1037 /* Now parse and run the backticked command */
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001038 if (!parseCommand(&charptr1, newJob, &njobList, inBg)
Eric Andersena1d187a2000-07-17 19:14:41 +00001039 && newJob->numProgs) {
1040 pipe(pipefd);
1041 runCommand(newJob, &njobList, 0, pipefd);
Eric Andersenec10b9d2000-07-14 01:13:11 +00001042 }
Eric Andersena1d187a2000-07-17 19:14:41 +00001043 checkJobs(jobList);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001044 freeJob(newJob);
1045 free(charptr2);
1046
1047 /* Make a copy of any stuff left over in the command
1048 * line after the second backtick */
1049 charptr2 = xmalloc(strlen(ptr)+1);
1050 memcpy(charptr2, ptr+1, strlen(ptr));
1051
Eric Andersenec10b9d2000-07-14 01:13:11 +00001052
Eric Andersena1d187a2000-07-17 19:14:41 +00001053 /* Copy the output from the backtick-ed command into the
1054 * command line, making extra room as needed */
1055 --src;
1056 charptr1 = xmalloc(BUFSIZ);
1057 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
1058 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1059 if (newSize > BUFSIZ) {
Matt Kraaib8907522000-09-13 02:08:21 +00001060 *commandPtr=xrealloc(*commandPtr, src - *commandPtr +
Eric Andersena1d187a2000-07-17 19:14:41 +00001061 size + 1 + strlen(charptr2));
1062 }
1063 memcpy(src, charptr1, size);
1064 src+=size;
1065 }
1066 free(charptr1);
1067 close(pipefd[0]);
1068 if (*(src-1)=='\n')
1069 --src;
1070
1071 /* Now paste into the *commandPtr all the stuff
1072 * leftover after the second backtick */
1073 memcpy(src, charptr2, strlen(charptr2));
Eric Andersena1d187a2000-07-17 19:14:41 +00001074 free(charptr2);
1075
Eric Andersena1d187a2000-07-17 19:14:41 +00001076 /* Now recursively call parseCommand to deal with the new
1077 * and improved version of the command line with the backtick
1078 * results expanded in place... */
Eric Andersen6efc48c2000-07-18 08:16:39 +00001079 freeJob(job);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001080 return(parseCommand(commandPtr, job, jobList, inBg));
Eric Andersenec10b9d2000-07-14 01:13:11 +00001081 }
1082 break;
Eric Andersena1d187a2000-07-17 19:14:41 +00001083#endif // BB_FEATURE_SH_BACKTICKS
Erik Andersen161220c2000-03-16 08:12:48 +00001084 default:
1085 *buf++ = *src;
1086 }
1087
Erik Andersend75af992000-03-16 08:09:09 +00001088 src++;
Erik Andersen161220c2000-03-16 08:12:48 +00001089 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001090
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001091 if (*prog->argv[argc_l]) {
1092 argc_l++;
1093 globLastArgument(prog, &argc_l, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +00001094 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001095 if (!argc_l) {
Erik Andersen161220c2000-03-16 08:12:48 +00001096 freeJob(job);
1097 return 0;
1098 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001099 prog->argv[argc_l] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +00001100
Erik Andersen161220c2000-03-16 08:12:48 +00001101 if (!returnCommand) {
Eric Andersenec10b9d2000-07-14 01:13:11 +00001102 job->text = xmalloc(strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +00001103 strcpy(job->text, *commandPtr);
1104 } else {
1105 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +00001106 count = returnCommand - *commandPtr;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001107 job->text = xmalloc(count + 1);
Erik Andersen161220c2000-03-16 08:12:48 +00001108 strncpy(job->text, *commandPtr, count);
1109 job->text[count] = '\0';
1110 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001111
Erik Andersen161220c2000-03-16 08:12:48 +00001112 *commandPtr = returnCommand;
Erik Andersen3522eb12000-03-12 23:49:18 +00001113
Erik Andersend75af992000-03-16 08:09:09 +00001114 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001115}
1116
Eric Andersena1d187a2000-07-17 19:14:41 +00001117static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
Erik Andersen3522eb12000-03-12 23:49:18 +00001118{
Eric Andersenfad9c112000-07-25 18:06:52 +00001119 struct job *theJob;
Erik Andersen161220c2000-03-16 08:12:48 +00001120 int i;
Eric Andersen6efc48c2000-07-18 08:16:39 +00001121 int nextin, nextout;
Erik Andersen161220c2000-03-16 08:12:48 +00001122 int pipefds[2]; /* pipefd[0] is for reading */
1123 struct builtInCommand *x;
Eric Andersenb54833c2000-07-03 23:56:26 +00001124#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Erik Andersenbcd61772000-05-13 06:33:19 +00001125 const struct BB_applet *a = applets;
1126#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001127
Eric Andersen6efc48c2000-07-18 08:16:39 +00001128
1129 nextin = 0, nextout = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001130 for (i = 0; i < newJob->numProgs; i++) {
1131 if ((i + 1) < newJob->numProgs) {
Erik Andersen161220c2000-03-16 08:12:48 +00001132 pipe(pipefds);
1133 nextout = pipefds[1];
1134 } else {
Eric Andersen6efc48c2000-07-18 08:16:39 +00001135 if (outPipe[1]!=-1) {
1136 nextout = outPipe[1];
1137 } else {
1138 nextout = 1;
1139 }
Erik Andersen161220c2000-03-16 08:12:48 +00001140 }
1141
Eric Andersen501c88b2000-07-28 15:14:45 +00001142#ifdef BB_FEATURE_SH_ENVIRONMENT
1143 if (showXtrace==TRUE) {
1144 int j;
1145 fprintf(stderr, "+ ");
1146 for (j = 0; newJob->progs[i].argv[j]; j++)
1147 fprintf(stderr, "%s ", newJob->progs[i].argv[j]);
1148 fprintf(stderr, "\n");
1149 }
1150#endif
1151
Eric Andersen34e19412000-07-10 18:47:24 +00001152 /* Check if the command matches any non-forking builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +00001153 for (x = bltins; x->cmd; x++) {
Eric Andersene92108a2000-07-26 00:53:56 +00001154 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001155 return(x->function(newJob, jobList));
Erik Andersen330fd2b2000-05-19 05:35:19 +00001156 }
1157 }
1158
Eric Andersenec10b9d2000-07-14 01:13:11 +00001159 if (!(newJob->progs[i].pid = fork())) {
Erik Andersen161220c2000-03-16 08:12:48 +00001160 signal(SIGTTOU, SIG_DFL);
1161
Eric Andersena1d187a2000-07-17 19:14:41 +00001162 if (outPipe[1]!=-1) {
1163 close(outPipe[0]);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001164 }
1165 if (nextin != 0) {
1166 dup2(nextin, 0);
1167 close(nextin);
1168 }
1169
1170 if (nextout != 1) {
Erik Andersen161220c2000-03-16 08:12:48 +00001171 dup2(nextout, 1);
Eric Andersena1d187a2000-07-17 19:14:41 +00001172 dup2(nextout, 2);
Erik Andersen161220c2000-03-16 08:12:48 +00001173 close(nextout);
Eric Andersen501c88b2000-07-28 15:14:45 +00001174 close(pipefds[0]);
Erik Andersen161220c2000-03-16 08:12:48 +00001175 }
1176
1177 /* explicit redirections override pipes */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001178 setupRedirections(newJob->progs + i);
Erik Andersen161220c2000-03-16 08:12:48 +00001179
Eric Andersen34e19412000-07-10 18:47:24 +00001180 /* Check if the command matches any of the other builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +00001181 for (x = bltins_forking; x->cmd; x++) {
Eric Andersene92108a2000-07-26 00:53:56 +00001182 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001183 applet_name=x->cmd;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001184 exit (x->function(newJob, jobList));
Erik Andersenbcd61772000-05-13 06:33:19 +00001185 }
1186 }
Eric Andersenb54833c2000-07-03 23:56:26 +00001187#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Eric Andersen34e19412000-07-10 18:47:24 +00001188 /* Check if the command matches any busybox internal commands here */
Erik Andersenbcd61772000-05-13 06:33:19 +00001189 while (a->name != 0) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001190 if (strcmp(get_last_path_component(newJob->progs[i].argv[0]), a->name) == 0) {
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001191 int argc_l;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001192 char** argv=newJob->progs[i].argv;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001193 for(argc_l=0;*argv!=NULL; argv++, argc_l++);
Eric Andersen501c88b2000-07-28 15:14:45 +00001194 applet_name=a->name;
Matt Kraai6085c722000-09-06 01:46:18 +00001195 optind = 1;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001196 exit((*(a->main)) (argc_l, newJob->progs[i].argv));
Erik Andersenbcd61772000-05-13 06:33:19 +00001197 }
1198 a++;
1199 }
1200#endif
1201
Eric Andersenec10b9d2000-07-14 01:13:11 +00001202 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001203 fatalError("%s: %s\n", newJob->progs[i].argv[0],
Erik Andersen161220c2000-03-16 08:12:48 +00001204 strerror(errno));
1205 }
Eric Andersen6efc48c2000-07-18 08:16:39 +00001206 if (outPipe[1]!=-1) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001207 close(outPipe[1]);
1208 }
Erik Andersen161220c2000-03-16 08:12:48 +00001209
1210 /* put our child in the process group whose leader is the
1211 first process in this pipe */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001212 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
Erik Andersen161220c2000-03-16 08:12:48 +00001213 if (nextin != 0)
1214 close(nextin);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001215 if (nextout != 1)
Erik Andersen161220c2000-03-16 08:12:48 +00001216 close(nextout);
1217
1218 /* If there isn't another process, nextin is garbage
1219 but it doesn't matter */
1220 nextin = pipefds[0];
1221 }
1222
Eric Andersenec10b9d2000-07-14 01:13:11 +00001223 newJob->pgrp = newJob->progs[0].pid;
Erik Andersen161220c2000-03-16 08:12:48 +00001224
Eric Andersenfad9c112000-07-25 18:06:52 +00001225 /* find the ID for the theJob to use */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001226 newJob->jobId = 1;
Eric Andersenfad9c112000-07-25 18:06:52 +00001227 for (theJob = jobList->head; theJob; theJob = theJob->next)
1228 if (theJob->jobId >= newJob->jobId)
1229 newJob->jobId = theJob->jobId + 1;
Erik Andersen161220c2000-03-16 08:12:48 +00001230
Eric Andersenfad9c112000-07-25 18:06:52 +00001231 /* add the theJob to the list of running jobs */
Erik Andersen161220c2000-03-16 08:12:48 +00001232 if (!jobList->head) {
Matt Kraaib8907522000-09-13 02:08:21 +00001233 theJob = jobList->head = xmalloc(sizeof(*theJob));
Erik Andersend75af992000-03-16 08:09:09 +00001234 } else {
Eric Andersenfad9c112000-07-25 18:06:52 +00001235 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
Matt Kraaib8907522000-09-13 02:08:21 +00001236 theJob->next = xmalloc(sizeof(*theJob));
Eric Andersenfad9c112000-07-25 18:06:52 +00001237 theJob = theJob->next;
Erik Andersend75af992000-03-16 08:09:09 +00001238 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001239
Eric Andersenfad9c112000-07-25 18:06:52 +00001240 *theJob = *newJob;
1241 theJob->next = NULL;
1242 theJob->runningProgs = theJob->numProgs;
1243 theJob->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001244
Erik Andersen161220c2000-03-16 08:12:48 +00001245 if (inBg) {
Eric Andersenfad9c112000-07-25 18:06:52 +00001246 /* we don't wait for background theJobs to return -- append it
1247 to the list of backgrounded theJobs and leave it alone */
1248 printf("[%d] %d\n", theJob->jobId,
Eric Andersenec10b9d2000-07-14 01:13:11 +00001249 newJob->progs[newJob->numProgs - 1].pid);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001250#ifdef BB_FEATURE_SH_ENVIRONMENT
1251 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1252#endif
Erik Andersen161220c2000-03-16 08:12:48 +00001253 } else {
Eric Andersenfad9c112000-07-25 18:06:52 +00001254 jobList->fg = theJob;
Erik Andersen3522eb12000-03-12 23:49:18 +00001255
Erik Andersen161220c2000-03-16 08:12:48 +00001256 /* move the new process group into the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001257 /* suppress messages when run from /linuxrc mag@sysgo.de */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001258 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
Erik Andersen161220c2000-03-16 08:12:48 +00001259 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001260 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001261
Erik Andersen161220c2000-03-16 08:12:48 +00001262 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001263}
1264
Erik Andersend75af992000-03-16 08:09:09 +00001265static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +00001266{
Erik Andersen161220c2000-03-16 08:12:48 +00001267 char *command;
1268 char *nextCommand = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001269 struct job newJob;
Eric Andersen1c314ad2000-06-28 16:56:25 +00001270 pid_t parent_pgrp;
Erik Andersen161220c2000-03-16 08:12:48 +00001271 int i;
Erik Andersen161220c2000-03-16 08:12:48 +00001272 int inBg;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001273 int status;
1274 newJob.jobContext = REGULAR_JOB_CONTEXT;
Erik Andersen3522eb12000-03-12 23:49:18 +00001275
Eric Andersen1c314ad2000-06-28 16:56:25 +00001276 /* save current owner of TTY so we can restore it on exit */
1277 parent_pgrp = tcgetpgrp(0);
1278
Matt Kraaib8907522000-09-13 02:08:21 +00001279 command = (char *) xcalloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +00001280
Erik Andersen161220c2000-03-16 08:12:48 +00001281 /* don't pay any attention to this signal; it just confuses
1282 things and isn't really meant for shells anyway */
1283 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +00001284
Erik Andersen161220c2000-03-16 08:12:48 +00001285 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +00001286 if (!jobList.fg) {
1287 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +00001288
Erik Andersend75af992000-03-16 08:09:09 +00001289 /* see if any background processes have exited */
1290 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +00001291
Erik Andersend75af992000-03-16 08:09:09 +00001292 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +00001293 if (getCommand(input, command))
1294 break;
1295 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +00001296 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001297
Eric Andersenec10b9d2000-07-14 01:13:11 +00001298 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +00001299 newJob.numProgs) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001300 int pipefds[2] = {-1,-1};
1301 runCommand(&newJob, &jobList, inBg, pipefds);
Eric Andersenfad9c112000-07-25 18:06:52 +00001302 }
1303 else {
Eric Andersena1d187a2000-07-17 19:14:41 +00001304 free(command);
Matt Kraaib8907522000-09-13 02:08:21 +00001305 command = (char *) xcalloc(BUFSIZ, sizeof(char));
Eric Andersen6efc48c2000-07-18 08:16:39 +00001306 nextCommand = NULL;
Erik Andersend75af992000-03-16 08:09:09 +00001307 }
1308 } else {
1309 /* a job is running in the foreground; wait for it */
1310 i = 0;
1311 while (!jobList.fg->progs[i].pid ||
Eric Andersenfad9c112000-07-25 18:06:52 +00001312 jobList.fg->progs[i].isStopped == 1) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +00001313
Erik Andersend75af992000-03-16 08:09:09 +00001314 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +00001315
Erik Andersend75af992000-03-16 08:09:09 +00001316 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +00001317 /* the child exited */
1318 jobList.fg->runningProgs--;
1319 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001320
Eric Andersen501c88b2000-07-28 15:14:45 +00001321#ifdef BB_FEATURE_SH_ENVIRONMENT
1322 lastReturnCode=WEXITSTATUS(status);
1323#endif
1324#if 0
1325 printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode);
1326#endif
Erik Andersen161220c2000-03-16 08:12:48 +00001327 if (!jobList.fg->runningProgs) {
1328 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +00001329
Erik Andersen161220c2000-03-16 08:12:48 +00001330 removeJob(&jobList, jobList.fg);
1331 jobList.fg = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001332 }
Erik Andersend75af992000-03-16 08:09:09 +00001333 } else {
Erik Andersen161220c2000-03-16 08:12:48 +00001334 /* the child was stopped */
1335 jobList.fg->stoppedProgs++;
1336 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +00001337
Erik Andersen161220c2000-03-16 08:12:48 +00001338 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1339 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1340 "Stopped", jobList.fg->text);
1341 jobList.fg = NULL;
1342 }
Erik Andersend75af992000-03-16 08:09:09 +00001343 }
1344
1345 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +00001346 /* move the shell to the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001347 /* suppress messages when run from /linuxrc mag@sysgo.de */
1348 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1349 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001350 }
1351 }
1352 }
Erik Andersen161220c2000-03-16 08:12:48 +00001353 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +00001354
Eric Andersen1c314ad2000-06-28 16:56:25 +00001355 /* return controlling TTY back to parent process group before exiting */
1356 if (tcsetpgrp(0, parent_pgrp))
Eric Andersenb54833c2000-07-03 23:56:26 +00001357 perror("tcsetpgrp");
1358
1359 /* return exit status if called with "-c" */
1360 if (input == NULL && WIFEXITED(status))
1361 return WEXITSTATUS(status);
1362
Erik Andersen161220c2000-03-16 08:12:48 +00001363 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001364}
1365
1366
Eric Andersenfad9c112000-07-25 18:06:52 +00001367#ifdef BB_FEATURE_CLEAN_UP
1368void free_memory(void)
1369{
1370 if (promptStr)
1371 free(promptStr);
1372 if (cwd)
1373 free(cwd);
1374 if (local_pending_command)
1375 free(local_pending_command);
1376
1377 if (jobList.fg && !jobList.fg->runningProgs) {
1378 removeJob(&jobList, jobList.fg);
1379 }
1380}
1381#endif
1382
Eric Andersen6efc48c2000-07-18 08:16:39 +00001383
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001384int shell_main(int argc_l, char **argv_l)
Erik Andersen3522eb12000-03-12 23:49:18 +00001385{
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001386 int opt, interactive=FALSE;
Erik Andersen161220c2000-03-16 08:12:48 +00001387 FILE *input = stdin;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001388 argc = argc_l;
1389 argv = argv_l;
Erik Andersen3522eb12000-03-12 23:49:18 +00001390
Eric Andersen501c88b2000-07-28 15:14:45 +00001391
1392 //if (argv[0] && argv[0][0] == '-') {
1393 // builtin_source("/etc/profile");
1394 //}
1395
Matt Kraai6085c722000-09-06 01:46:18 +00001396 while ((opt = getopt(argc_l, argv_l, "cx")) > 0) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001397 switch (opt) {
1398 case 'c':
1399 input = NULL;
Matt Kraai6085c722000-09-06 01:46:18 +00001400 if (local_pending_command != 0)
1401 fatalError("multiple -c arguments\n");
1402 local_pending_command = xstrdup(argv[optind]);
1403 optind++;
1404 argv = argv+optind;
Eric Andersen501c88b2000-07-28 15:14:45 +00001405 break;
Eric Andersen1428c4f2000-07-28 15:19:30 +00001406#ifdef BB_FEATURE_SH_ENVIRONMENT
Eric Andersen501c88b2000-07-28 15:14:45 +00001407 case 'x':
1408 showXtrace = TRUE;
1409 break;
Eric Andersen1428c4f2000-07-28 15:19:30 +00001410#endif
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001411 case 'i':
1412 interactive = TRUE;
1413 break;
Eric Andersen501c88b2000-07-28 15:14:45 +00001414 default:
1415 usage(shell_usage);
1416 }
1417 }
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001418 /* A shell is interactive if the `-i' flag was given, or if all of
1419 * the following conditions are met:
1420 * no -c command
1421 * no arguments remaining or the -s flag given
1422 * standard input is a terminal
1423 * standard output is a terminal
1424 * Refer to Posix.2, the description of the `sh' utility. */
1425 if (interactive==TRUE || ( argv[optind]==NULL && input==stdin && isatty(fileno(stdin)) && isatty(fileno(stdout)))) {
Eric Andersen851ce892000-08-21 22:34:23 +00001426 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
Eric Andersen501c88b2000-07-28 15:14:45 +00001427 /* Looks like they want an interactive shell */
1428 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1429 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001430 } else if (local_pending_command==NULL) {
Eric Andersen851ce892000-08-21 22:34:23 +00001431 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
Eric Andersen501c88b2000-07-28 15:14:45 +00001432 input = fopen(argv[optind], "r");
1433 if (!input) {
1434 fatalError("%s: %s\n", argv[optind], strerror(errno));
1435 }
Eric Andersen501c88b2000-07-28 15:14:45 +00001436 }
1437
Eric Andersen6efc48c2000-07-18 08:16:39 +00001438 /* initialize the cwd -- this is never freed...*/
1439 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1440 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen3522eb12000-03-12 23:49:18 +00001441
Eric Andersenfad9c112000-07-25 18:06:52 +00001442#ifdef BB_FEATURE_CLEAN_UP
1443 atexit(free_memory);
1444#endif
1445
Erik Andersenf0657d32000-04-12 17:49:52 +00001446#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenf0657d32000-04-12 17:49:52 +00001447 win_changed(0);
1448#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001449
Erik Andersen161220c2000-03-16 08:12:48 +00001450 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +00001451}