blob: 60c51f619449635be8ab5f3e0f6b35884aa9d828 [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 Andersen06f64b22000-09-19 07:16:39 +000029#define BB_FEATURE_SH_BACKTICKS
Eric Andersenfad9c112000-07-25 18:06:52 +000030//#define BB_FEATURE_SH_IF_EXPRESSIONS
Eric Andersen06f64b22000-09-19 07:16:39 +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},
Matt Kraaidd450a02000-09-13 03:43:36 +0000148 {".", "Source-in and run commands in a file", builtin_source},
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000149#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
150 {"if", NULL, builtin_if},
151 {"then", NULL, builtin_then},
152 {"else", NULL, builtin_else},
153 {"fi", NULL, builtin_fi},
154#endif
Eric Andersenfad9c112000-07-25 18:06:52 +0000155 {NULL, NULL, NULL}
Erik Andersen330fd2b2000-05-19 05:35:19 +0000156};
157
Mark Whitley37653aa2000-07-12 23:36:17 +0000158/* Table of forking built-in functions (things that fork cannot change global
159 * variables in the parent process, such as the current working directory) */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000160static struct builtInCommand bltins_forking[] = {
Eric Andersenfad9c112000-07-25 18:06:52 +0000161 {"env", "Print all environment variables", builtin_env},
162 {"pwd", "Print current directory", builtin_pwd},
Eric Andersenfad9c112000-07-25 18:06:52 +0000163 {"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
Pavel Roskin5f84fd72000-09-15 00:46:51 +0000228 exit (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);
Matt Kraaidd450a02000-09-13 03:43:36 +0000503 fclose(input);
Erik Andersen161220c2000-03-16 08:12:48 +0000504 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000505}
506
507/* built-in 'unset VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000508static int builtin_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000509{
Erik Andersen161220c2000-03-16 08:12:48 +0000510 if (!cmd->progs[0].argv[1] == 1) {
511 fprintf(stdout, "unset: parameter required.\n");
512 return FALSE;
513 }
514 unsetenv(cmd->progs[0].argv[1]);
515 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000516}
517
518/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000519static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000520{
Erik Andersen161220c2000-03-16 08:12:48 +0000521 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000522
Erik Andersen161220c2000-03-16 08:12:48 +0000523 for (i = 0; i < cmd->numProgs; i++) {
524 free(cmd->progs[i].argv);
525 if (cmd->progs[i].redirections)
526 free(cmd->progs[i].redirections);
527 if (cmd->progs[i].freeGlob)
528 globfree(&cmd->progs[i].globResult);
529 }
530 free(cmd->progs);
531 if (cmd->text)
532 free(cmd->text);
533 free(cmd->cmdBuf);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000534 memset(cmd, 0, sizeof(struct job));
Erik Andersen3522eb12000-03-12 23:49:18 +0000535}
536
537/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000538static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000539{
Erik Andersen161220c2000-03-16 08:12:48 +0000540 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000541
Erik Andersen161220c2000-03-16 08:12:48 +0000542 freeJob(job);
543 if (job == jobList->head) {
544 jobList->head = job->next;
545 } else {
546 prevJob = jobList->head;
547 while (prevJob->next != job)
548 prevJob = prevJob->next;
549 prevJob->next = job->next;
550 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000551
Erik Andersen161220c2000-03-16 08:12:48 +0000552 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000553}
554
555/* Checks to see if any background processes have exited -- if they
556 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000557static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000558{
Erik Andersen161220c2000-03-16 08:12:48 +0000559 struct job *job;
560 pid_t childpid;
561 int status;
562 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000563
Erik Andersen161220c2000-03-16 08:12:48 +0000564 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
565 for (job = jobList->head; job; job = job->next) {
566 progNum = 0;
567 while (progNum < job->numProgs &&
568 job->progs[progNum].pid != childpid) progNum++;
569 if (progNum < job->numProgs)
570 break;
571 }
572
Eric Andersena1d187a2000-07-17 19:14:41 +0000573 /* This happens on backticked commands */
574 if(job==NULL)
575 return;
576
Erik Andersen161220c2000-03-16 08:12:48 +0000577 if (WIFEXITED(status) || WIFSIGNALED(status)) {
578 /* child exited */
579 job->runningProgs--;
580 job->progs[progNum].pid = 0;
581
582 if (!job->runningProgs) {
583 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
584 removeJob(jobList, job);
585 }
586 } else {
587 /* child stopped */
588 job->stoppedProgs++;
589 job->progs[progNum].isStopped = 1;
590
591 if (job->stoppedProgs == job->numProgs) {
592 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
593 job->text);
594 }
595 }
Erik Andersend75af992000-03-16 08:09:09 +0000596 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000597
Erik Andersen161220c2000-03-16 08:12:48 +0000598 if (childpid == -1 && errno != ECHILD)
599 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000600}
601
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000602static int setupRedirections(struct childProgram *prog)
603{
604 int i;
605 int openfd;
606 int mode = O_RDONLY;
607 struct redirectionSpecifier *redir = prog->redirections;
608
609 for (i = 0; i < prog->numRedirections; i++, redir++) {
610 switch (redir->type) {
611 case REDIRECT_INPUT:
612 mode = O_RDONLY;
613 break;
614 case REDIRECT_OVERWRITE:
615 mode = O_RDWR | O_CREAT | O_TRUNC;
616 break;
617 case REDIRECT_APPEND:
618 mode = O_RDWR | O_CREAT | O_APPEND;
619 break;
620 }
621
622 openfd = open(redir->filename, mode, 0666);
623 if (openfd < 0) {
624 /* this could get lost if stderr has been redirected, but
625 bash and ash both lose it as well (though zsh doesn't!) */
626 errorMsg("error opening %s: %s\n", redir->filename,
627 strerror(errno));
628 return 1;
629 }
630
631 if (openfd != redir->fd) {
632 dup2(openfd, redir->fd);
633 close(openfd);
634 }
635 }
636
637 return 0;
638}
639
640
Erik Andersend75af992000-03-16 08:09:09 +0000641static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000642{
Eric Andersen1c314ad2000-06-28 16:56:25 +0000643 if (source == NULL) {
644 if (local_pending_command) {
645 /* a command specified (-c option): return it & mark it done */
646 strcpy(command, local_pending_command);
647 free(local_pending_command);
648 local_pending_command = NULL;
649 return 0;
650 }
651 return 1;
652 }
653
Erik Andersen161220c2000-03-16 08:12:48 +0000654 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000655#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000656 int len;
Eric Andersen501c88b2000-07-28 15:14:45 +0000657
658 /*
659 ** enable command line editing only while a command line
660 ** is actually being read; otherwise, we'll end up bequeathing
661 ** atexit() handlers and other unwanted stuff to our
662 ** child processes (rob@sysgo.de)
663 */
664 cmdedit_init();
665 signal(SIGWINCH, win_changed);
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000666 len=fprintf(stdout, "%s %s", cwd, prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000667 fflush(stdout);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000668 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000669 sprintf(promptStr, "%s %s", cwd, prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000670 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000671 free( promptStr);
Eric Andersen501c88b2000-07-28 15:14:45 +0000672 cmdedit_terminate();
673 signal(SIGWINCH, SIG_DFL);
Erik Andersen161220c2000-03-16 08:12:48 +0000674 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000675#else
676 fprintf(stdout, "%s %s", cwd, prompt);
677 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000678#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000679 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000680
Erik Andersen161220c2000-03-16 08:12:48 +0000681 if (!fgets(command, BUFSIZ - 2, source)) {
682 if (source == stdin)
683 printf("\n");
684 return 1;
685 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000686
Erik Andersen161220c2000-03-16 08:12:48 +0000687 /* remove trailing newline */
688 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000689
Erik Andersen161220c2000-03-16 08:12:48 +0000690 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000691}
692
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000693#ifdef BB_FEATURE_SH_ENVIRONMENT
694#define __MAX_INT_CHARS 7
695static char* itoa(register int i)
696{
697 static char a[__MAX_INT_CHARS];
698 register char *b = a + sizeof(a) - 1;
699 int sign = (i < 0);
700
701 if (sign)
702 i = -i;
703 *b = 0;
704 do
705 {
706 *--b = '0' + (i % 10);
707 i /= 10;
708 }
709 while (i);
710 if (sign)
711 *--b = '-';
712 return b;
713}
714#endif
715
Erik Andersend75af992000-03-16 08:09:09 +0000716static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000717 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000718{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000719 int argc_l = *argcPtr;
Erik Andersen161220c2000-03-16 08:12:48 +0000720 int argcAlloced = *argcAllocedPtr;
721 int rc;
722 int flags;
723 int i;
Eric Andersenb54833c2000-07-03 23:56:26 +0000724 char *src, *dst, *var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000725
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000726 if (argc_l > 1) { /* cmd->globResult is already initialized */
Erik Andersen161220c2000-03-16 08:12:48 +0000727 flags = GLOB_APPEND;
728 i = prog->globResult.gl_pathc;
729 } else {
730 prog->freeGlob = 1;
731 flags = 0;
732 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000733 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000734 /* do shell variable substitution */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000735 if(*prog->argv[argc_l - 1] == '$') {
736 if ((var = getenv(prog->argv[argc_l - 1] + 1))) {
737 prog->argv[argc_l - 1] = var;
738 }
739#ifdef BB_FEATURE_SH_ENVIRONMENT
740 else {
741 switch(*(prog->argv[argc_l - 1] + 1)) {
742 case '?':
743 prog->argv[argc_l - 1] = itoa(lastReturnCode);
744 break;
745 case '$':
746 prog->argv[argc_l - 1] = itoa(getpid());
747 break;
748 case '#':
749 prog->argv[argc_l - 1] = itoa(argc-1);
750 break;
751 case '!':
752 if (lastBgPid==-1)
753 *(prog->argv[argc_l - 1])='\0';
754 else
755 prog->argv[argc_l - 1] = itoa(lastBgPid);
756 break;
757 case '0':case '1':case '2':case '3':case '4':
758 case '5':case '6':case '7':case '8':case '9':
759 {
760 int index=*(prog->argv[argc_l - 1] + 1)-48;
761 if (index >= argc) {
762 *(prog->argv[argc_l - 1])='\0';
763 } else {
764 prog->argv[argc_l - 1] = argv[index];
765 }
766 }
767 break;
768 }
769 }
770#endif
771 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000772
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000773 rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
Erik Andersen161220c2000-03-16 08:12:48 +0000774 if (rc == GLOB_NOSPACE) {
Matt Kraaid537a952000-07-14 01:51:25 +0000775 errorMsg("out of space during glob operation\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000776 return;
777 } else if (rc == GLOB_NOMATCH ||
778 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000779 strcmp(prog->argv[argc_l - 1],
Eric Andersene92108a2000-07-26 00:53:56 +0000780 prog->globResult.gl_pathv[i]) == 0)) {
Erik Andersen161220c2000-03-16 08:12:48 +0000781 /* we need to remove whatever \ quoting is still present */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000782 src = dst = prog->argv[argc_l - 1];
Erik Andersen161220c2000-03-16 08:12:48 +0000783 while (*src) {
784 if (*src != '\\')
785 *dst++ = *src;
786 src++;
787 }
788 *dst = '\0';
789 } else if (!rc) {
790 argcAlloced += (prog->globResult.gl_pathc - i);
Matt Kraaib8907522000-09-13 02:08:21 +0000791 prog->argv = xrealloc(prog->argv, argcAlloced * sizeof(*prog->argv));
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000792 memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
Erik Andersen161220c2000-03-16 08:12:48 +0000793 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000794 argc_l += (prog->globResult.gl_pathc - i - 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000795 }
796
797 *argcAllocedPtr = argcAlloced;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000798 *argcPtr = argc_l;
Erik Andersen3522eb12000-03-12 23:49:18 +0000799}
800
801/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
802 line). If a valid command is found, commandPtr is set to point to
803 the beginning of the next command (if the original command had more
804 then one job associated with it) or NULL if no more commands are
805 present. */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000806static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000807{
Erik Andersen161220c2000-03-16 08:12:48 +0000808 char *command;
809 char *returnCommand = NULL;
810 char *src, *buf, *chptr;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000811 int argc_l = 0;
Erik Andersen161220c2000-03-16 08:12:48 +0000812 int done = 0;
813 int argvAlloced;
814 int i;
815 char quote = '\0';
816 int count;
817 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000818
Erik Andersen161220c2000-03-16 08:12:48 +0000819 /* skip leading white space */
820 while (**commandPtr && isspace(**commandPtr))
821 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000822
Erik Andersen161220c2000-03-16 08:12:48 +0000823 /* this handles empty lines or leading '#' characters */
824 if (!**commandPtr || (**commandPtr == '#')) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000825 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000826 return 0;
827 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000828
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000829 *inBg = 0;
Erik Andersen161220c2000-03-16 08:12:48 +0000830 job->numProgs = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000831 job->progs = xmalloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000832
Erik Andersen161220c2000-03-16 08:12:48 +0000833 /* We set the argv elements to point inside of this string. The
Eric Andersenb54833c2000-07-03 23:56:26 +0000834 memory is freed by freeJob(). Allocate twice the original
835 length in case we need to quote every single character.
Erik Andersen3522eb12000-03-12 23:49:18 +0000836
Erik Andersen161220c2000-03-16 08:12:48 +0000837 Getting clean memory relieves us of the task of NULL
838 terminating things and makes the rest of this look a bit
839 cleaner (though it is, admittedly, a tad less efficient) */
Matt Kraaib8907522000-09-13 02:08:21 +0000840 job->cmdBuf = command = xcalloc(2*strlen(*commandPtr) + 1, sizeof(char));
Erik Andersen161220c2000-03-16 08:12:48 +0000841 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000842
Erik Andersen161220c2000-03-16 08:12:48 +0000843 prog = job->progs;
844 prog->numRedirections = 0;
845 prog->redirections = NULL;
846 prog->freeGlob = 0;
847 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000848
Erik Andersen161220c2000-03-16 08:12:48 +0000849 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000850 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000851 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000852
Erik Andersen161220c2000-03-16 08:12:48 +0000853 buf = command;
854 src = *commandPtr;
855 while (*src && !done) {
856 if (quote == *src) {
857 quote = '\0';
858 } else if (quote) {
859 if (*src == '\\') {
860 src++;
861 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000862 errorMsg("character expected after \\\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000863 freeJob(job);
864 return 1;
865 }
866
867 /* in shell, "\'" should yield \' */
868 if (*src != quote)
869 *buf++ = '\\';
870 } else if (*src == '*' || *src == '?' || *src == '[' ||
871 *src == ']') *buf++ = '\\';
872 *buf++ = *src;
873 } else if (isspace(*src)) {
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000874 if (*prog->argv[argc_l]) {
875 buf++, argc_l++;
Erik Andersen161220c2000-03-16 08:12:48 +0000876 /* +1 here leaves room for the NULL which ends argv */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000877 if ((argc_l + 1) == argvAlloced) {
Erik Andersen161220c2000-03-16 08:12:48 +0000878 argvAlloced += 5;
Matt Kraaib8907522000-09-13 02:08:21 +0000879 prog->argv = xrealloc(prog->argv,
880 sizeof(*prog->argv) *
881 argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000882 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000883 globLastArgument(prog, &argc_l, &argvAlloced);
884 prog->argv[argc_l] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000885 }
886 } else
887 switch (*src) {
888 case '"':
889 case '\'':
890 quote = *src;
891 break;
892
893 case '#': /* comment */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000894 if (*(src-1)== '$')
895 *buf++ = *src;
896 else
897 done = 1;
Erik Andersen161220c2000-03-16 08:12:48 +0000898 break;
899
900 case '>': /* redirections */
901 case '<':
902 i = prog->numRedirections++;
Matt Kraaib8907522000-09-13 02:08:21 +0000903 prog->redirections = xrealloc(prog->redirections,
904 sizeof(*prog->redirections) *
905 (i + 1));
Erik Andersen161220c2000-03-16 08:12:48 +0000906
907 prog->redirections[i].fd = -1;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000908 if (buf != prog->argv[argc_l]) {
Erik Andersen161220c2000-03-16 08:12:48 +0000909 /* the stuff before this character may be the file number
910 being redirected */
911 prog->redirections[i].fd =
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000912 strtol(prog->argv[argc_l], &chptr, 10);
Erik Andersen161220c2000-03-16 08:12:48 +0000913
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000914 if (*chptr && *prog->argv[argc_l]) {
915 buf++, argc_l++;
916 globLastArgument(prog, &argc_l, &argvAlloced);
917 prog->argv[argc_l] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000918 }
919 }
920
921 if (prog->redirections[i].fd == -1) {
922 if (*src == '>')
923 prog->redirections[i].fd = 1;
924 else
925 prog->redirections[i].fd = 0;
926 }
927
928 if (*src++ == '>') {
929 if (*src == '>')
930 prog->redirections[i].type =
931 REDIRECT_APPEND, src++;
932 else
933 prog->redirections[i].type = REDIRECT_OVERWRITE;
934 } else {
935 prog->redirections[i].type = REDIRECT_INPUT;
936 }
937
938 /* This isn't POSIX sh compliant. Oh well. */
939 chptr = src;
940 while (isspace(*chptr))
941 chptr++;
942
943 if (!*chptr) {
Matt Kraaid537a952000-07-14 01:51:25 +0000944 errorMsg("file name expected after %c\n", *src);
Erik Andersen161220c2000-03-16 08:12:48 +0000945 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000946 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000947 return 1;
948 }
949
950 prog->redirections[i].filename = buf;
951 while (*chptr && !isspace(*chptr))
952 *buf++ = *chptr++;
953
954 src = chptr - 1; /* we src++ later */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000955 prog->argv[argc_l] = ++buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000956 break;
957
958 case '|': /* pipe */
959 /* finish this command */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000960 if (*prog->argv[argc_l])
961 argc_l++;
962 if (!argc_l) {
Eric Andersena1d187a2000-07-17 19:14:41 +0000963 errorMsg("empty command in pipe\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000964 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000965 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000966 return 1;
967 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000968 prog->argv[argc_l] = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +0000969
970 /* and start the next */
971 job->numProgs++;
Matt Kraaib8907522000-09-13 02:08:21 +0000972 job->progs = xrealloc(job->progs,
973 sizeof(*job->progs) * job->numProgs);
Erik Andersen161220c2000-03-16 08:12:48 +0000974 prog = job->progs + (job->numProgs - 1);
975 prog->numRedirections = 0;
976 prog->redirections = NULL;
977 prog->freeGlob = 0;
Eric Andersen501c88b2000-07-28 15:14:45 +0000978 prog->isStopped = 0;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000979 argc_l = 0;
Erik Andersen161220c2000-03-16 08:12:48 +0000980
981 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000982 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000983 prog->argv[0] = ++buf;
984
985 src++;
986 while (*src && isspace(*src))
987 src++;
988
989 if (!*src) {
Eric Andersena1d187a2000-07-17 19:14:41 +0000990 errorMsg("empty command in pipe\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +0000991 freeJob(job);
992 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000993 return 1;
994 }
995 src--; /* we'll ++ it at the end of the loop */
996
997 break;
998
999 case '&': /* background */
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001000 *inBg = 1;
Erik Andersen161220c2000-03-16 08:12:48 +00001001 case ';': /* multiple commands */
1002 done = 1;
1003 returnCommand = *commandPtr + (src - *commandPtr) + 1;
1004 break;
1005
Eric Andersena1d187a2000-07-17 19:14:41 +00001006#ifdef BB_FEATURE_SH_BACKTICKS
Eric Andersenec10b9d2000-07-14 01:13:11 +00001007 case '`':
1008 /* Exec a backtick-ed command */
1009 {
Eric Andersena1d187a2000-07-17 19:14:41 +00001010 char* charptr1=NULL, *charptr2;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001011 char* ptr=NULL;
Eric Andersena1d187a2000-07-17 19:14:41 +00001012 struct job *newJob;
1013 struct jobSet njobList = { NULL, NULL };
1014 int pipefd[2];
1015 int size;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001016
1017 ptr=strchr(++src, '`');
1018 if (ptr==NULL) {
1019 fprintf(stderr, "Unmatched '`' in command\n");
1020 freeJob(job);
1021 return 1;
1022 }
1023
Eric Andersena1d187a2000-07-17 19:14:41 +00001024 /* Make some space to hold just the backticked command */
Eric Andersen6efc48c2000-07-18 08:16:39 +00001025 charptr1 = charptr2 = xmalloc(1+ptr-src);
Matt Kraai0b2da462000-09-19 06:46:44 +00001026 memcpy(charptr1, src, ptr-src);
1027 charptr1[ptr-src] = '\0';
Eric Andersena1d187a2000-07-17 19:14:41 +00001028 newJob = xmalloc(sizeof(struct job));
1029 /* Now parse and run the backticked command */
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001030 if (!parseCommand(&charptr1, newJob, &njobList, inBg)
Eric Andersena1d187a2000-07-17 19:14:41 +00001031 && newJob->numProgs) {
1032 pipe(pipefd);
1033 runCommand(newJob, &njobList, 0, pipefd);
Eric Andersenec10b9d2000-07-14 01:13:11 +00001034 }
Eric Andersena1d187a2000-07-17 19:14:41 +00001035 checkJobs(jobList);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001036 freeJob(newJob);
1037 free(charptr2);
1038
1039 /* Make a copy of any stuff left over in the command
1040 * line after the second backtick */
1041 charptr2 = xmalloc(strlen(ptr)+1);
1042 memcpy(charptr2, ptr+1, strlen(ptr));
1043
Eric Andersenec10b9d2000-07-14 01:13:11 +00001044
Eric Andersena1d187a2000-07-17 19:14:41 +00001045 /* Copy the output from the backtick-ed command into the
1046 * command line, making extra room as needed */
1047 --src;
1048 charptr1 = xmalloc(BUFSIZ);
1049 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
1050 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1051 if (newSize > BUFSIZ) {
Matt Kraaib8907522000-09-13 02:08:21 +00001052 *commandPtr=xrealloc(*commandPtr, src - *commandPtr +
Eric Andersena1d187a2000-07-17 19:14:41 +00001053 size + 1 + strlen(charptr2));
1054 }
1055 memcpy(src, charptr1, size);
1056 src+=size;
1057 }
1058 free(charptr1);
1059 close(pipefd[0]);
1060 if (*(src-1)=='\n')
1061 --src;
1062
1063 /* Now paste into the *commandPtr all the stuff
1064 * leftover after the second backtick */
Matt Kraaicbbe4d62000-09-14 00:26:50 +00001065 memcpy(src, charptr2, strlen(charptr2)+1);
Eric Andersena1d187a2000-07-17 19:14:41 +00001066 free(charptr2);
1067
Eric Andersena1d187a2000-07-17 19:14:41 +00001068 /* Now recursively call parseCommand to deal with the new
1069 * and improved version of the command line with the backtick
1070 * results expanded in place... */
Eric Andersen6efc48c2000-07-18 08:16:39 +00001071 freeJob(job);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001072 return(parseCommand(commandPtr, job, jobList, inBg));
Eric Andersenec10b9d2000-07-14 01:13:11 +00001073 }
1074 break;
Eric Andersena1d187a2000-07-17 19:14:41 +00001075#endif // BB_FEATURE_SH_BACKTICKS
Matt Kraai131241f2000-09-14 00:43:20 +00001076
1077 case '\\':
1078 src++;
1079 if (!*src) {
1080 errorMsg("character expected after \\\n");
1081 freeJob(job);
1082 return 1;
1083 }
1084 if (*src == '*' || *src == '[' || *src == ']'
1085 || *src == '?') *buf++ = '\\';
1086 /* fallthrough */
Erik Andersen161220c2000-03-16 08:12:48 +00001087 default:
1088 *buf++ = *src;
1089 }
1090
Erik Andersend75af992000-03-16 08:09:09 +00001091 src++;
Erik Andersen161220c2000-03-16 08:12:48 +00001092 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001093
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001094 if (*prog->argv[argc_l]) {
1095 argc_l++;
1096 globLastArgument(prog, &argc_l, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +00001097 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001098 if (!argc_l) {
Erik Andersen161220c2000-03-16 08:12:48 +00001099 freeJob(job);
1100 return 0;
1101 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001102 prog->argv[argc_l] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +00001103
Erik Andersen161220c2000-03-16 08:12:48 +00001104 if (!returnCommand) {
Eric Andersenec10b9d2000-07-14 01:13:11 +00001105 job->text = xmalloc(strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +00001106 strcpy(job->text, *commandPtr);
1107 } else {
1108 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +00001109 count = returnCommand - *commandPtr;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001110 job->text = xmalloc(count + 1);
Erik Andersen161220c2000-03-16 08:12:48 +00001111 strncpy(job->text, *commandPtr, count);
1112 job->text[count] = '\0';
1113 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001114
Erik Andersen161220c2000-03-16 08:12:48 +00001115 *commandPtr = returnCommand;
Erik Andersen3522eb12000-03-12 23:49:18 +00001116
Erik Andersend75af992000-03-16 08:09:09 +00001117 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001118}
1119
Eric Andersena1d187a2000-07-17 19:14:41 +00001120static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
Erik Andersen3522eb12000-03-12 23:49:18 +00001121{
Eric Andersenfad9c112000-07-25 18:06:52 +00001122 struct job *theJob;
Erik Andersen161220c2000-03-16 08:12:48 +00001123 int i;
Eric Andersen6efc48c2000-07-18 08:16:39 +00001124 int nextin, nextout;
Erik Andersen161220c2000-03-16 08:12:48 +00001125 int pipefds[2]; /* pipefd[0] is for reading */
1126 struct builtInCommand *x;
Eric Andersenb54833c2000-07-03 23:56:26 +00001127#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Erik Andersenbcd61772000-05-13 06:33:19 +00001128 const struct BB_applet *a = applets;
1129#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001130
Eric Andersen6efc48c2000-07-18 08:16:39 +00001131
1132 nextin = 0, nextout = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001133 for (i = 0; i < newJob->numProgs; i++) {
1134 if ((i + 1) < newJob->numProgs) {
Erik Andersen161220c2000-03-16 08:12:48 +00001135 pipe(pipefds);
1136 nextout = pipefds[1];
1137 } else {
Eric Andersen6efc48c2000-07-18 08:16:39 +00001138 if (outPipe[1]!=-1) {
1139 nextout = outPipe[1];
1140 } else {
1141 nextout = 1;
1142 }
Erik Andersen161220c2000-03-16 08:12:48 +00001143 }
1144
Eric Andersen501c88b2000-07-28 15:14:45 +00001145#ifdef BB_FEATURE_SH_ENVIRONMENT
1146 if (showXtrace==TRUE) {
1147 int j;
1148 fprintf(stderr, "+ ");
1149 for (j = 0; newJob->progs[i].argv[j]; j++)
1150 fprintf(stderr, "%s ", newJob->progs[i].argv[j]);
1151 fprintf(stderr, "\n");
1152 }
1153#endif
1154
Eric Andersen34e19412000-07-10 18:47:24 +00001155 /* Check if the command matches any non-forking builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +00001156 for (x = bltins; x->cmd; x++) {
Eric Andersene92108a2000-07-26 00:53:56 +00001157 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001158 return(x->function(newJob, jobList));
Erik Andersen330fd2b2000-05-19 05:35:19 +00001159 }
1160 }
1161
Eric Andersenec10b9d2000-07-14 01:13:11 +00001162 if (!(newJob->progs[i].pid = fork())) {
Erik Andersen161220c2000-03-16 08:12:48 +00001163 signal(SIGTTOU, SIG_DFL);
1164
Eric Andersena1d187a2000-07-17 19:14:41 +00001165 if (outPipe[1]!=-1) {
1166 close(outPipe[0]);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001167 }
1168 if (nextin != 0) {
1169 dup2(nextin, 0);
1170 close(nextin);
1171 }
1172
1173 if (nextout != 1) {
Erik Andersen161220c2000-03-16 08:12:48 +00001174 dup2(nextout, 1);
Eric Andersena1d187a2000-07-17 19:14:41 +00001175 dup2(nextout, 2);
Erik Andersen161220c2000-03-16 08:12:48 +00001176 close(nextout);
Eric Andersen501c88b2000-07-28 15:14:45 +00001177 close(pipefds[0]);
Erik Andersen161220c2000-03-16 08:12:48 +00001178 }
1179
1180 /* explicit redirections override pipes */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001181 setupRedirections(newJob->progs + i);
Erik Andersen161220c2000-03-16 08:12:48 +00001182
Eric Andersen34e19412000-07-10 18:47:24 +00001183 /* Check if the command matches any of the other builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +00001184 for (x = bltins_forking; x->cmd; x++) {
Eric Andersene92108a2000-07-26 00:53:56 +00001185 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001186 applet_name=x->cmd;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001187 exit (x->function(newJob, jobList));
Erik Andersenbcd61772000-05-13 06:33:19 +00001188 }
1189 }
Eric Andersenb54833c2000-07-03 23:56:26 +00001190#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Eric Andersen34e19412000-07-10 18:47:24 +00001191 /* Check if the command matches any busybox internal commands here */
Erik Andersenbcd61772000-05-13 06:33:19 +00001192 while (a->name != 0) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001193 if (strcmp(get_last_path_component(newJob->progs[i].argv[0]), a->name) == 0) {
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001194 int argc_l;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001195 char** argv=newJob->progs[i].argv;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001196 for(argc_l=0;*argv!=NULL; argv++, argc_l++);
Eric Andersen501c88b2000-07-28 15:14:45 +00001197 applet_name=a->name;
Matt Kraai6085c722000-09-06 01:46:18 +00001198 optind = 1;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001199 exit((*(a->main)) (argc_l, newJob->progs[i].argv));
Erik Andersenbcd61772000-05-13 06:33:19 +00001200 }
1201 a++;
1202 }
1203#endif
1204
Eric Andersenec10b9d2000-07-14 01:13:11 +00001205 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001206 fatalError("%s: %s\n", newJob->progs[i].argv[0],
Erik Andersen161220c2000-03-16 08:12:48 +00001207 strerror(errno));
1208 }
Eric Andersen6efc48c2000-07-18 08:16:39 +00001209 if (outPipe[1]!=-1) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001210 close(outPipe[1]);
1211 }
Erik Andersen161220c2000-03-16 08:12:48 +00001212
1213 /* put our child in the process group whose leader is the
1214 first process in this pipe */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001215 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
Erik Andersen161220c2000-03-16 08:12:48 +00001216 if (nextin != 0)
1217 close(nextin);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001218 if (nextout != 1)
Erik Andersen161220c2000-03-16 08:12:48 +00001219 close(nextout);
1220
1221 /* If there isn't another process, nextin is garbage
1222 but it doesn't matter */
1223 nextin = pipefds[0];
1224 }
1225
Eric Andersenec10b9d2000-07-14 01:13:11 +00001226 newJob->pgrp = newJob->progs[0].pid;
Erik Andersen161220c2000-03-16 08:12:48 +00001227
Eric Andersenfad9c112000-07-25 18:06:52 +00001228 /* find the ID for the theJob to use */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001229 newJob->jobId = 1;
Eric Andersenfad9c112000-07-25 18:06:52 +00001230 for (theJob = jobList->head; theJob; theJob = theJob->next)
1231 if (theJob->jobId >= newJob->jobId)
1232 newJob->jobId = theJob->jobId + 1;
Erik Andersen161220c2000-03-16 08:12:48 +00001233
Eric Andersenfad9c112000-07-25 18:06:52 +00001234 /* add the theJob to the list of running jobs */
Erik Andersen161220c2000-03-16 08:12:48 +00001235 if (!jobList->head) {
Matt Kraaib8907522000-09-13 02:08:21 +00001236 theJob = jobList->head = xmalloc(sizeof(*theJob));
Erik Andersend75af992000-03-16 08:09:09 +00001237 } else {
Eric Andersenfad9c112000-07-25 18:06:52 +00001238 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
Matt Kraaib8907522000-09-13 02:08:21 +00001239 theJob->next = xmalloc(sizeof(*theJob));
Eric Andersenfad9c112000-07-25 18:06:52 +00001240 theJob = theJob->next;
Erik Andersend75af992000-03-16 08:09:09 +00001241 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001242
Eric Andersenfad9c112000-07-25 18:06:52 +00001243 *theJob = *newJob;
1244 theJob->next = NULL;
1245 theJob->runningProgs = theJob->numProgs;
1246 theJob->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001247
Erik Andersen161220c2000-03-16 08:12:48 +00001248 if (inBg) {
Eric Andersenfad9c112000-07-25 18:06:52 +00001249 /* we don't wait for background theJobs to return -- append it
1250 to the list of backgrounded theJobs and leave it alone */
1251 printf("[%d] %d\n", theJob->jobId,
Eric Andersenec10b9d2000-07-14 01:13:11 +00001252 newJob->progs[newJob->numProgs - 1].pid);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001253#ifdef BB_FEATURE_SH_ENVIRONMENT
1254 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1255#endif
Erik Andersen161220c2000-03-16 08:12:48 +00001256 } else {
Eric Andersenfad9c112000-07-25 18:06:52 +00001257 jobList->fg = theJob;
Erik Andersen3522eb12000-03-12 23:49:18 +00001258
Erik Andersen161220c2000-03-16 08:12:48 +00001259 /* move the new process group into the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001260 /* suppress messages when run from /linuxrc mag@sysgo.de */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001261 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
Erik Andersen161220c2000-03-16 08:12:48 +00001262 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001263 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001264
Erik Andersen161220c2000-03-16 08:12:48 +00001265 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001266}
1267
Erik Andersend75af992000-03-16 08:09:09 +00001268static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +00001269{
Erik Andersen161220c2000-03-16 08:12:48 +00001270 char *command;
1271 char *nextCommand = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001272 struct job newJob;
Eric Andersen1c314ad2000-06-28 16:56:25 +00001273 pid_t parent_pgrp;
Erik Andersen161220c2000-03-16 08:12:48 +00001274 int i;
Erik Andersen161220c2000-03-16 08:12:48 +00001275 int inBg;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001276 int status;
1277 newJob.jobContext = REGULAR_JOB_CONTEXT;
Erik Andersen3522eb12000-03-12 23:49:18 +00001278
Eric Andersen1c314ad2000-06-28 16:56:25 +00001279 /* save current owner of TTY so we can restore it on exit */
1280 parent_pgrp = tcgetpgrp(0);
1281
Matt Kraaib8907522000-09-13 02:08:21 +00001282 command = (char *) xcalloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +00001283
Erik Andersen161220c2000-03-16 08:12:48 +00001284 /* don't pay any attention to this signal; it just confuses
1285 things and isn't really meant for shells anyway */
1286 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +00001287
Erik Andersen161220c2000-03-16 08:12:48 +00001288 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +00001289 if (!jobList.fg) {
1290 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +00001291
Erik Andersend75af992000-03-16 08:09:09 +00001292 /* see if any background processes have exited */
1293 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +00001294
Erik Andersend75af992000-03-16 08:09:09 +00001295 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +00001296 if (getCommand(input, command))
1297 break;
1298 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +00001299 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001300
Eric Andersenec10b9d2000-07-14 01:13:11 +00001301 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +00001302 newJob.numProgs) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001303 int pipefds[2] = {-1,-1};
1304 runCommand(&newJob, &jobList, inBg, pipefds);
Eric Andersenfad9c112000-07-25 18:06:52 +00001305 }
1306 else {
Eric Andersena1d187a2000-07-17 19:14:41 +00001307 free(command);
Matt Kraaib8907522000-09-13 02:08:21 +00001308 command = (char *) xcalloc(BUFSIZ, sizeof(char));
Eric Andersen6efc48c2000-07-18 08:16:39 +00001309 nextCommand = NULL;
Erik Andersend75af992000-03-16 08:09:09 +00001310 }
1311 } else {
1312 /* a job is running in the foreground; wait for it */
1313 i = 0;
1314 while (!jobList.fg->progs[i].pid ||
Eric Andersenfad9c112000-07-25 18:06:52 +00001315 jobList.fg->progs[i].isStopped == 1) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +00001316
Erik Andersend75af992000-03-16 08:09:09 +00001317 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +00001318
Erik Andersend75af992000-03-16 08:09:09 +00001319 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +00001320 /* the child exited */
1321 jobList.fg->runningProgs--;
1322 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001323
Eric Andersen501c88b2000-07-28 15:14:45 +00001324#ifdef BB_FEATURE_SH_ENVIRONMENT
1325 lastReturnCode=WEXITSTATUS(status);
1326#endif
1327#if 0
1328 printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode);
1329#endif
Erik Andersen161220c2000-03-16 08:12:48 +00001330 if (!jobList.fg->runningProgs) {
1331 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +00001332
Erik Andersen161220c2000-03-16 08:12:48 +00001333 removeJob(&jobList, jobList.fg);
1334 jobList.fg = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001335 }
Erik Andersend75af992000-03-16 08:09:09 +00001336 } else {
Erik Andersen161220c2000-03-16 08:12:48 +00001337 /* the child was stopped */
1338 jobList.fg->stoppedProgs++;
1339 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +00001340
Erik Andersen161220c2000-03-16 08:12:48 +00001341 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1342 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1343 "Stopped", jobList.fg->text);
1344 jobList.fg = NULL;
1345 }
Erik Andersend75af992000-03-16 08:09:09 +00001346 }
1347
1348 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +00001349 /* move the shell to the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001350 /* suppress messages when run from /linuxrc mag@sysgo.de */
1351 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1352 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001353 }
1354 }
1355 }
Erik Andersen161220c2000-03-16 08:12:48 +00001356 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +00001357
Eric Andersen1c314ad2000-06-28 16:56:25 +00001358 /* return controlling TTY back to parent process group before exiting */
1359 if (tcsetpgrp(0, parent_pgrp))
Eric Andersenb54833c2000-07-03 23:56:26 +00001360 perror("tcsetpgrp");
1361
1362 /* return exit status if called with "-c" */
1363 if (input == NULL && WIFEXITED(status))
1364 return WEXITSTATUS(status);
1365
Erik Andersen161220c2000-03-16 08:12:48 +00001366 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001367}
1368
1369
Eric Andersenfad9c112000-07-25 18:06:52 +00001370#ifdef BB_FEATURE_CLEAN_UP
1371void free_memory(void)
1372{
1373 if (promptStr)
1374 free(promptStr);
1375 if (cwd)
1376 free(cwd);
1377 if (local_pending_command)
1378 free(local_pending_command);
1379
1380 if (jobList.fg && !jobList.fg->runningProgs) {
1381 removeJob(&jobList, jobList.fg);
1382 }
1383}
1384#endif
1385
Eric Andersen6efc48c2000-07-18 08:16:39 +00001386
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001387int shell_main(int argc_l, char **argv_l)
Erik Andersen3522eb12000-03-12 23:49:18 +00001388{
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001389 int opt, interactive=FALSE;
Erik Andersen161220c2000-03-16 08:12:48 +00001390 FILE *input = stdin;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001391 argc = argc_l;
1392 argv = argv_l;
Erik Andersen3522eb12000-03-12 23:49:18 +00001393
Eric Andersen501c88b2000-07-28 15:14:45 +00001394
1395 //if (argv[0] && argv[0][0] == '-') {
1396 // builtin_source("/etc/profile");
1397 //}
1398
Matt Kraai6085c722000-09-06 01:46:18 +00001399 while ((opt = getopt(argc_l, argv_l, "cx")) > 0) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001400 switch (opt) {
1401 case 'c':
1402 input = NULL;
Matt Kraai6085c722000-09-06 01:46:18 +00001403 if (local_pending_command != 0)
1404 fatalError("multiple -c arguments\n");
1405 local_pending_command = xstrdup(argv[optind]);
1406 optind++;
1407 argv = argv+optind;
Eric Andersen501c88b2000-07-28 15:14:45 +00001408 break;
Eric Andersen1428c4f2000-07-28 15:19:30 +00001409#ifdef BB_FEATURE_SH_ENVIRONMENT
Eric Andersen501c88b2000-07-28 15:14:45 +00001410 case 'x':
1411 showXtrace = TRUE;
1412 break;
Eric Andersen1428c4f2000-07-28 15:19:30 +00001413#endif
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001414 case 'i':
1415 interactive = TRUE;
1416 break;
Eric Andersen501c88b2000-07-28 15:14:45 +00001417 default:
1418 usage(shell_usage);
1419 }
1420 }
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001421 /* A shell is interactive if the `-i' flag was given, or if all of
1422 * the following conditions are met:
1423 * no -c command
1424 * no arguments remaining or the -s flag given
1425 * standard input is a terminal
1426 * standard output is a terminal
1427 * Refer to Posix.2, the description of the `sh' utility. */
1428 if (interactive==TRUE || ( argv[optind]==NULL && input==stdin && isatty(fileno(stdin)) && isatty(fileno(stdout)))) {
Eric Andersen851ce892000-08-21 22:34:23 +00001429 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
Eric Andersen501c88b2000-07-28 15:14:45 +00001430 /* Looks like they want an interactive shell */
1431 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1432 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001433 } else if (local_pending_command==NULL) {
Eric Andersen851ce892000-08-21 22:34:23 +00001434 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
Eric Andersen501c88b2000-07-28 15:14:45 +00001435 input = fopen(argv[optind], "r");
1436 if (!input) {
1437 fatalError("%s: %s\n", argv[optind], strerror(errno));
1438 }
Eric Andersen501c88b2000-07-28 15:14:45 +00001439 }
1440
Eric Andersen6efc48c2000-07-18 08:16:39 +00001441 /* initialize the cwd -- this is never freed...*/
1442 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1443 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen3522eb12000-03-12 23:49:18 +00001444
Eric Andersenfad9c112000-07-25 18:06:52 +00001445#ifdef BB_FEATURE_CLEAN_UP
1446 atexit(free_memory);
1447#endif
1448
Erik Andersenf0657d32000-04-12 17:49:52 +00001449#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenf0657d32000-04-12 17:49:52 +00001450 win_changed(0);
1451#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001452
Erik Andersen161220c2000-03-16 08:12:48 +00001453 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +00001454}