blob: 7a8810aef258440bc2e897819915976e1491d2a2 [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 Andersend2f56772000-09-21 02:48:07 +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
Eric Andersen3570a342000-09-25 21:45:58 +000035#include "busybox.h"
Erik Andersen3522eb12000-03-12 23:49:18 +000036#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);
Eric Andersend2f56772000-09-21 02:48:07 +0000111static int builtin_exec(struct job *cmd, struct jobSet *junk);
Eric Andersen34e19412000-07-10 18:47:24 +0000112static int builtin_exit(struct job *cmd, struct jobSet *junk);
113static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
114static int builtin_help(struct job *cmd, struct jobSet *junk);
115static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
116static int builtin_pwd(struct job *dummy, struct jobSet *junk);
117static int builtin_export(struct job *cmd, struct jobSet *junk);
118static int builtin_source(struct job *cmd, struct jobSet *jobList);
119static int builtin_unset(struct job *cmd, struct jobSet *junk);
120static int builtin_read(struct job *cmd, struct jobSet *junk);
Eric Andersenfad9c112000-07-25 18:06:52 +0000121#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
122static int builtin_if(struct job *cmd, struct jobSet *junk);
123static int builtin_then(struct job *cmd, struct jobSet *junk);
124static int builtin_else(struct job *cmd, struct jobSet *junk);
125static int builtin_fi(struct job *cmd, struct jobSet *junk);
126#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000127
Eric Andersen34e19412000-07-10 18:47:24 +0000128
129/* function prototypes for shell stuff */
Erik Andersend75af992000-03-16 08:09:09 +0000130static void checkJobs(struct jobSet *jobList);
131static int getCommand(FILE * source, char *command);
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000132static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg);
Eric Andersena1d187a2000-07-17 19:14:41 +0000133static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
Erik Andersen3522eb12000-03-12 23:49:18 +0000134static int busy_loop(FILE * input);
135
Erik Andersend75af992000-03-16 08:09:09 +0000136
Mark Whitley37653aa2000-07-12 23:36:17 +0000137/* Table of built-in functions (these are non-forking builtins, meaning they
138 * can change global variables in the parent shell process but they will not
139 * work with pipes and redirects; 'unset foo | whatever' will not work) */
Erik Andersen3522eb12000-03-12 23:49:18 +0000140static struct builtInCommand bltins[] = {
Eric Andersenfad9c112000-07-25 18:06:52 +0000141 {"bg", "Resume a job in the background", builtin_fg_bg},
142 {"cd", "Change working directory", builtin_cd},
Eric Andersend2f56772000-09-21 02:48:07 +0000143 {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec},
Eric Andersenfad9c112000-07-25 18:06:52 +0000144 {"exit", "Exit from shell()", builtin_exit},
145 {"fg", "Bring job into the foreground", builtin_fg_bg},
146 {"jobs", "Lists the active jobs", builtin_jobs},
147 {"export", "Set environment variable", builtin_export},
148 {"unset", "Unset environment variable", builtin_unset},
149 {"read", "Input environment variable", builtin_read},
Matt Kraaidd450a02000-09-13 03:43:36 +0000150 {".", "Source-in and run commands in a file", builtin_source},
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000151#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
152 {"if", NULL, builtin_if},
153 {"then", NULL, builtin_then},
154 {"else", NULL, builtin_else},
155 {"fi", NULL, builtin_fi},
156#endif
Eric Andersenfad9c112000-07-25 18:06:52 +0000157 {NULL, NULL, NULL}
Erik Andersen330fd2b2000-05-19 05:35:19 +0000158};
159
Mark Whitley37653aa2000-07-12 23:36:17 +0000160/* Table of forking built-in functions (things that fork cannot change global
161 * variables in the parent process, such as the current working directory) */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000162static struct builtInCommand bltins_forking[] = {
Eric Andersenfad9c112000-07-25 18:06:52 +0000163 {"env", "Print all environment variables", builtin_env},
164 {"pwd", "Print current directory", builtin_pwd},
Eric Andersenfad9c112000-07-25 18:06:52 +0000165 {"help", "List shell built-in commands", builtin_help},
166 {NULL, NULL, NULL}
Erik Andersen3522eb12000-03-12 23:49:18 +0000167};
168
Erik Andersen3522eb12000-03-12 23:49:18 +0000169static char *prompt = "# ";
Eric Andersen6efc48c2000-07-18 08:16:39 +0000170static char *cwd;
Eric Andersen1c314ad2000-06-28 16:56:25 +0000171static char *local_pending_command = NULL;
Eric Andersenfad9c112000-07-25 18:06:52 +0000172static char *promptStr = NULL;
173static struct jobSet jobList = { NULL, NULL };
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000174static int argc;
175static char **argv;
176#ifdef BB_FEATURE_SH_ENVIRONMENT
177static int lastBgPid=-1;
178static int lastReturnCode=-1;
Eric Andersen501c88b2000-07-28 15:14:45 +0000179static int showXtrace=FALSE;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000180#endif
Eric Andersen501c88b2000-07-28 15:14:45 +0000181
Erik Andersen3522eb12000-03-12 23:49:18 +0000182
Erik Andersenf0657d32000-04-12 17:49:52 +0000183#ifdef BB_FEATURE_SH_COMMAND_EDITING
Eric Andersenfad04fd2000-07-14 06:49:52 +0000184void win_changed(int junk)
Erik Andersenf0657d32000-04-12 17:49:52 +0000185{
Eric Andersenfad04fd2000-07-14 06:49:52 +0000186 struct winsize win = { 0, 0, 0, 0 };
Erik Andersenf0657d32000-04-12 17:49:52 +0000187 ioctl(0, TIOCGWINSZ, &win);
188 if (win.ws_col > 0) {
189 cmdedit_setwidth( win.ws_col - 1);
190 }
191}
192#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000193
Erik Andersen3522eb12000-03-12 23:49:18 +0000194
Erik Andersend75af992000-03-16 08:09:09 +0000195/* built-in 'cd <path>' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000196static int builtin_cd(struct job *cmd, struct jobSet *junk)
Erik Andersend75af992000-03-16 08:09:09 +0000197{
Erik Andersen161220c2000-03-16 08:12:48 +0000198 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000199
Erik Andersen161220c2000-03-16 08:12:48 +0000200 if (!cmd->progs[0].argv[1] == 1)
201 newdir = getenv("HOME");
202 else
203 newdir = cmd->progs[0].argv[1];
204 if (chdir(newdir)) {
205 printf("cd: %s: %s\n", newdir, strerror(errno));
206 return FALSE;
207 }
Eric Andersen6efc48c2000-07-18 08:16:39 +0000208 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen161220c2000-03-16 08:12:48 +0000209
210 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000211}
212
213/* built-in 'env' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000214static int builtin_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000215{
Erik Andersen161220c2000-03-16 08:12:48 +0000216 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000217
Erik Andersen161220c2000-03-16 08:12:48 +0000218 for (e = environ; *e; e++) {
219 fprintf(stdout, "%s\n", *e);
220 }
221 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000222}
223
Eric Andersend2f56772000-09-21 02:48:07 +0000224/* built-in 'exec' handler */
225static int builtin_exec(struct job *cmd, struct jobSet *junk)
226{
227 if (cmd->progs[0].argv[1])
228 {
229 cmd->progs[0].argv++;
230 execvp(cmd->progs[0].argv[0], cmd->progs[0].argv);
231 fatalError("Exec to %s failed: %s\n", cmd->progs[0].argv[0],
232 strerror(errno));
233 }
234 return TRUE;
235}
236
Erik Andersen3522eb12000-03-12 23:49:18 +0000237/* built-in 'exit' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000238static int builtin_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000239{
Erik Andersen161220c2000-03-16 08:12:48 +0000240 if (!cmd->progs[0].argv[1] == 1)
241 exit TRUE;
242
Pavel Roskin5f84fd72000-09-15 00:46:51 +0000243 exit (atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000244}
245
246/* built-in 'fg' and 'bg' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000247static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000248{
Erik Andersen161220c2000-03-16 08:12:48 +0000249 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000250 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000251
Erik Andersen161220c2000-03-16 08:12:48 +0000252 if (!jobList->head) {
253 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
Matt Kraaid537a952000-07-14 01:51:25 +0000254 errorMsg("%s: exactly one argument is expected\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000255 cmd->progs[0].argv[0]);
256 return FALSE;
257 }
258 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
Matt Kraaid537a952000-07-14 01:51:25 +0000259 errorMsg("%s: bad argument '%s'\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000260 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
261 return FALSE;
262 for (job = jobList->head; job; job = job->next) {
263 if (job->jobId == jobNum) {
264 break;
265 }
266 }
267 }
268 } else {
269 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000270 }
Erik Andersen161220c2000-03-16 08:12:48 +0000271
272 if (!job) {
Matt Kraaid537a952000-07-14 01:51:25 +0000273 errorMsg("%s: unknown job %d\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000274 cmd->progs[0].argv[0], jobNum);
275 return FALSE;
Erik Andersend75af992000-03-16 08:09:09 +0000276 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000277
Erik Andersen161220c2000-03-16 08:12:48 +0000278 if (*cmd->progs[0].argv[0] == 'f') {
279 /* Make this job the foreground job */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000280 /* suppress messages when run from /linuxrc mag@sysgo.de */
281 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
282 perror("tcsetpgrp");
Erik Andersen161220c2000-03-16 08:12:48 +0000283 jobList->fg = job;
284 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000285
Erik Andersen161220c2000-03-16 08:12:48 +0000286 /* Restart the processes in the job */
287 for (i = 0; i < job->numProgs; i++)
288 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000289
Erik Andersen161220c2000-03-16 08:12:48 +0000290 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000291
Erik Andersen161220c2000-03-16 08:12:48 +0000292 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000293
Erik Andersen161220c2000-03-16 08:12:48 +0000294 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000295}
296
297/* built-in 'help' handler */
Eric Andersenfad04fd2000-07-14 06:49:52 +0000298static int builtin_help(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000299{
Erik Andersen161220c2000-03-16 08:12:48 +0000300 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000301
Erik Andersen161220c2000-03-16 08:12:48 +0000302 fprintf(stdout, "\nBuilt-in commands:\n");
303 fprintf(stdout, "-------------------\n");
304 for (x = bltins; x->cmd; x++) {
Eric Andersenfad9c112000-07-25 18:06:52 +0000305 if (x->descr==NULL)
306 continue;
Erik Andersen161220c2000-03-16 08:12:48 +0000307 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
308 }
Erik Andersen330fd2b2000-05-19 05:35:19 +0000309 for (x = bltins_forking; x->cmd; x++) {
Eric Andersenfad9c112000-07-25 18:06:52 +0000310 if (x->descr==NULL)
311 continue;
Erik Andersen330fd2b2000-05-19 05:35:19 +0000312 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
313 }
Erik Andersen161220c2000-03-16 08:12:48 +0000314 fprintf(stdout, "\n\n");
315 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000316}
317
318/* built-in 'jobs' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000319static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000320{
Erik Andersen161220c2000-03-16 08:12:48 +0000321 struct job *job;
322 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000323
Erik Andersen161220c2000-03-16 08:12:48 +0000324 for (job = jobList->head; job; job = job->next) {
325 if (job->runningProgs == job->stoppedProgs)
326 statusString = "Stopped";
327 else
328 statusString = "Running";
329
330 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
331 }
332 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000333}
334
335
336/* built-in 'pwd' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000337static int builtin_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000338{
Eric Andersen6efc48c2000-07-18 08:16:39 +0000339 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen161220c2000-03-16 08:12:48 +0000340 fprintf(stdout, "%s\n", cwd);
341 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000342}
343
Erik Andersen6273f652000-03-17 01:12:41 +0000344/* built-in 'export VAR=value' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000345static int builtin_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000346{
Erik Andersen161220c2000-03-16 08:12:48 +0000347 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000348
Erik Andersen161220c2000-03-16 08:12:48 +0000349 if (!cmd->progs[0].argv[1] == 1) {
Eric Andersen34e19412000-07-10 18:47:24 +0000350 return (builtin_env(cmd, junk));
Erik Andersen161220c2000-03-16 08:12:48 +0000351 }
352 res = putenv(cmd->progs[0].argv[1]);
353 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000354 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000355 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000356}
357
Eric Andersenb54833c2000-07-03 23:56:26 +0000358/* built-in 'read VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000359static int builtin_read(struct job *cmd, struct jobSet *junk)
Eric Andersenb54833c2000-07-03 23:56:26 +0000360{
361 int res = 0, len, newlen;
362 char *s;
363 char string[MAX_READ];
364
365 if (cmd->progs[0].argv[1]) {
366 /* argument (VAR) given: put "VAR=" into buffer */
367 strcpy(string, cmd->progs[0].argv[1]);
368 len = strlen(string);
369 string[len++] = '=';
370 string[len] = '\0';
371 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
372 newlen = strlen(string);
373 if(newlen > len)
374 string[--newlen] = '\0'; /* chomp trailing newline */
375 /*
376 ** string should now contain "VAR=<value>"
377 ** copy it (putenv() won't do that, so we must make sure
378 ** the string resides in a static buffer!)
379 */
380 res = -1;
381 if((s = strdup(string)))
382 res = putenv(s);
383 if (res)
384 fprintf(stdout, "read: %s\n", strerror(errno));
385 }
386 else
387 fgets(string, sizeof(string), stdin);
388
389 return (res);
390}
391
Eric Andersenfad9c112000-07-25 18:06:52 +0000392#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
393/* Built-in handler for 'if' commands */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000394static int builtin_if(struct job *cmd, struct jobSet *jobList)
Eric Andersenfad9c112000-07-25 18:06:52 +0000395{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000396 int status;
397 char* charptr1=cmd->text+3; /* skip over the leading 'if ' */
398
399 /* Now run the 'if' command */
400 status=strlen(charptr1);
401 local_pending_command = xmalloc(status+1);
402 strncpy(local_pending_command, charptr1, status);
Eric Andersen501c88b2000-07-28 15:14:45 +0000403 local_pending_command[status]='\0';
404#ifdef DEBUG_SHELL
405 fprintf(stderr, "'if' now testing '%s'\n", local_pending_command);
406#endif
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000407 status = busy_loop(NULL); /* Frees local_pending_command */
Eric Andersen501c88b2000-07-28 15:14:45 +0000408#ifdef DEBUG_SHELL
409 fprintf(stderr, "if test returned ");
410#endif
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000411 if (status == 0) {
Eric Andersen501c88b2000-07-28 15:14:45 +0000412#ifdef DEBUG_SHELL
413 fprintf(stderr, "TRUE\n");
414#endif
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000415 cmd->jobContext |= IF_TRUE_CONTEXT;
416 } else {
Eric Andersen501c88b2000-07-28 15:14:45 +0000417#ifdef DEBUG_SHELL
418 fprintf(stderr, "FALSE\n");
419#endif
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000420 cmd->jobContext |= IF_FALSE_CONTEXT;
421 }
422
423 return status;
Eric Andersenfad9c112000-07-25 18:06:52 +0000424}
425
426/* Built-in handler for 'then' (part of the 'if' command) */
427static int builtin_then(struct job *cmd, struct jobSet *junk)
428{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000429 int status;
430 char* charptr1=cmd->text+5; /* skip over the leading 'then ' */
431
432 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
433 errorMsg("unexpected token `then'\n");
Eric Andersenfad9c112000-07-25 18:06:52 +0000434 return FALSE;
435 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000436 /* If the if result was FALSE, skip the 'then' stuff */
Eric Andersen501c88b2000-07-28 15:14:45 +0000437 if (cmd->jobContext & IF_FALSE_CONTEXT) {
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000438 return TRUE;
439 }
440
Eric Andersenfad9c112000-07-25 18:06:52 +0000441 cmd->jobContext |= THEN_EXP_CONTEXT;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000442 //printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
443
444 /* Now run the 'then' command */
445 status=strlen(charptr1);
446 local_pending_command = xmalloc(status+1);
447 strncpy(local_pending_command, charptr1, status);
Eric Andersen501c88b2000-07-28 15:14:45 +0000448 local_pending_command[status]='\0';
449#ifdef DEBUG_SHELL
450 fprintf(stderr, "'then' now running '%s'\n", charptr1);
451#endif
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000452 return( busy_loop(NULL));
Eric Andersenfad9c112000-07-25 18:06:52 +0000453}
454
455/* Built-in handler for 'else' (part of the 'if' command) */
456static int builtin_else(struct job *cmd, struct jobSet *junk)
457{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000458 int status;
459 char* charptr1=cmd->text+5; /* skip over the leading 'else ' */
460
461 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
462 errorMsg("unexpected token `else'\n");
463 return FALSE;
464 }
465 /* If the if result was TRUE, skip the 'else' stuff */
Eric Andersen501c88b2000-07-28 15:14:45 +0000466 if (cmd->jobContext & IF_TRUE_CONTEXT) {
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000467 return TRUE;
468 }
469
Eric Andersenfad9c112000-07-25 18:06:52 +0000470 cmd->jobContext |= ELSE_EXP_CONTEXT;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000471 //printf("Hit an else -- jobContext=%d\n", cmd->jobContext);
472
473 /* Now run the 'else' command */
474 status=strlen(charptr1);
475 local_pending_command = xmalloc(status+1);
476 strncpy(local_pending_command, charptr1, status);
Eric Andersen501c88b2000-07-28 15:14:45 +0000477 local_pending_command[status]='\0';
478#ifdef DEBUG_SHELL
479 fprintf(stderr, "'else' now running '%s'\n", charptr1);
480#endif
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000481 return( busy_loop(NULL));
Eric Andersenfad9c112000-07-25 18:06:52 +0000482}
483
484/* Built-in handler for 'fi' (part of the 'if' command) */
485static int builtin_fi(struct job *cmd, struct jobSet *junk)
486{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000487 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
488 errorMsg("unexpected token `fi'\n");
489 return FALSE;
490 }
491 /* Clear out the if and then context bits */
492 cmd->jobContext &= ~(IF_TRUE_CONTEXT|IF_FALSE_CONTEXT|THEN_EXP_CONTEXT|ELSE_EXP_CONTEXT);
Eric Andersen501c88b2000-07-28 15:14:45 +0000493#ifdef DEBUG_SHELL
494 fprintf(stderr, "Hit an fi -- jobContext=%d\n", cmd->jobContext);
495#endif
Eric Andersenfad9c112000-07-25 18:06:52 +0000496 return TRUE;
497}
498#endif
499
Erik Andersen3522eb12000-03-12 23:49:18 +0000500/* Built-in '.' handler (read-in and execute commands from file) */
Eric Andersen34e19412000-07-10 18:47:24 +0000501static int builtin_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000502{
Erik Andersen161220c2000-03-16 08:12:48 +0000503 FILE *input;
504 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000505
Erik Andersen161220c2000-03-16 08:12:48 +0000506 if (!cmd->progs[0].argv[1] == 1)
507 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000508
Erik Andersen161220c2000-03-16 08:12:48 +0000509 input = fopen(cmd->progs[0].argv[1], "r");
510 if (!input) {
511 fprintf(stdout, "Couldn't open file '%s'\n",
512 cmd->progs[0].argv[1]);
513 return FALSE;
514 }
Erik Andersend75af992000-03-16 08:09:09 +0000515
Erik Andersen161220c2000-03-16 08:12:48 +0000516 /* Now run the file */
517 status = busy_loop(input);
Matt Kraaidd450a02000-09-13 03:43:36 +0000518 fclose(input);
Erik Andersen161220c2000-03-16 08:12:48 +0000519 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000520}
521
522/* built-in 'unset VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000523static int builtin_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000524{
Erik Andersen161220c2000-03-16 08:12:48 +0000525 if (!cmd->progs[0].argv[1] == 1) {
526 fprintf(stdout, "unset: parameter required.\n");
527 return FALSE;
528 }
529 unsetenv(cmd->progs[0].argv[1]);
530 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000531}
532
533/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000534static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000535{
Erik Andersen161220c2000-03-16 08:12:48 +0000536 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000537
Erik Andersen161220c2000-03-16 08:12:48 +0000538 for (i = 0; i < cmd->numProgs; i++) {
539 free(cmd->progs[i].argv);
540 if (cmd->progs[i].redirections)
541 free(cmd->progs[i].redirections);
542 if (cmd->progs[i].freeGlob)
543 globfree(&cmd->progs[i].globResult);
544 }
545 free(cmd->progs);
546 if (cmd->text)
547 free(cmd->text);
548 free(cmd->cmdBuf);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000549 memset(cmd, 0, sizeof(struct job));
Erik Andersen3522eb12000-03-12 23:49:18 +0000550}
551
552/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000553static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000554{
Erik Andersen161220c2000-03-16 08:12:48 +0000555 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000556
Erik Andersen161220c2000-03-16 08:12:48 +0000557 freeJob(job);
558 if (job == jobList->head) {
559 jobList->head = job->next;
560 } else {
561 prevJob = jobList->head;
562 while (prevJob->next != job)
563 prevJob = prevJob->next;
564 prevJob->next = job->next;
565 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000566
Erik Andersen161220c2000-03-16 08:12:48 +0000567 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000568}
569
570/* Checks to see if any background processes have exited -- if they
571 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000572static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000573{
Erik Andersen161220c2000-03-16 08:12:48 +0000574 struct job *job;
575 pid_t childpid;
576 int status;
577 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000578
Erik Andersen161220c2000-03-16 08:12:48 +0000579 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
580 for (job = jobList->head; job; job = job->next) {
581 progNum = 0;
582 while (progNum < job->numProgs &&
583 job->progs[progNum].pid != childpid) progNum++;
584 if (progNum < job->numProgs)
585 break;
586 }
587
Eric Andersena1d187a2000-07-17 19:14:41 +0000588 /* This happens on backticked commands */
589 if(job==NULL)
590 return;
591
Erik Andersen161220c2000-03-16 08:12:48 +0000592 if (WIFEXITED(status) || WIFSIGNALED(status)) {
593 /* child exited */
594 job->runningProgs--;
595 job->progs[progNum].pid = 0;
596
597 if (!job->runningProgs) {
598 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
599 removeJob(jobList, job);
600 }
601 } else {
602 /* child stopped */
603 job->stoppedProgs++;
604 job->progs[progNum].isStopped = 1;
605
606 if (job->stoppedProgs == job->numProgs) {
607 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
608 job->text);
609 }
610 }
Erik Andersend75af992000-03-16 08:09:09 +0000611 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000612
Erik Andersen161220c2000-03-16 08:12:48 +0000613 if (childpid == -1 && errno != ECHILD)
614 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000615}
616
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000617static int setupRedirections(struct childProgram *prog)
618{
619 int i;
620 int openfd;
621 int mode = O_RDONLY;
622 struct redirectionSpecifier *redir = prog->redirections;
623
624 for (i = 0; i < prog->numRedirections; i++, redir++) {
625 switch (redir->type) {
626 case REDIRECT_INPUT:
627 mode = O_RDONLY;
628 break;
629 case REDIRECT_OVERWRITE:
Eric Andersen46f0beb2000-11-14 21:59:22 +0000630 mode = O_WRONLY | O_CREAT | O_TRUNC;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000631 break;
632 case REDIRECT_APPEND:
Eric Andersen46f0beb2000-11-14 21:59:22 +0000633 mode = O_WRONLY | O_CREAT | O_APPEND;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000634 break;
635 }
636
637 openfd = open(redir->filename, mode, 0666);
638 if (openfd < 0) {
639 /* this could get lost if stderr has been redirected, but
640 bash and ash both lose it as well (though zsh doesn't!) */
641 errorMsg("error opening %s: %s\n", redir->filename,
642 strerror(errno));
643 return 1;
644 }
645
646 if (openfd != redir->fd) {
647 dup2(openfd, redir->fd);
648 close(openfd);
649 }
650 }
651
652 return 0;
653}
654
655
Erik Andersend75af992000-03-16 08:09:09 +0000656static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000657{
Eric Andersen1c314ad2000-06-28 16:56:25 +0000658 if (source == NULL) {
659 if (local_pending_command) {
660 /* a command specified (-c option): return it & mark it done */
661 strcpy(command, local_pending_command);
662 free(local_pending_command);
663 local_pending_command = NULL;
664 return 0;
665 }
666 return 1;
667 }
668
Erik Andersen161220c2000-03-16 08:12:48 +0000669 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000670#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000671 int len;
Eric Andersen501c88b2000-07-28 15:14:45 +0000672
673 /*
674 ** enable command line editing only while a command line
675 ** is actually being read; otherwise, we'll end up bequeathing
676 ** atexit() handlers and other unwanted stuff to our
677 ** child processes (rob@sysgo.de)
678 */
679 cmdedit_init();
680 signal(SIGWINCH, win_changed);
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000681 len=fprintf(stdout, "%s %s", cwd, prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000682 fflush(stdout);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000683 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000684 sprintf(promptStr, "%s %s", cwd, prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000685 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000686 free( promptStr);
Eric Andersen501c88b2000-07-28 15:14:45 +0000687 cmdedit_terminate();
688 signal(SIGWINCH, SIG_DFL);
Erik Andersen161220c2000-03-16 08:12:48 +0000689 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000690#else
691 fprintf(stdout, "%s %s", cwd, prompt);
692 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000693#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000694 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000695
Erik Andersen161220c2000-03-16 08:12:48 +0000696 if (!fgets(command, BUFSIZ - 2, source)) {
697 if (source == stdin)
698 printf("\n");
699 return 1;
700 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000701
Erik Andersen161220c2000-03-16 08:12:48 +0000702 /* remove trailing newline */
703 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000704
Erik Andersen161220c2000-03-16 08:12:48 +0000705 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000706}
707
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000708#ifdef BB_FEATURE_SH_ENVIRONMENT
709#define __MAX_INT_CHARS 7
710static char* itoa(register int i)
711{
712 static char a[__MAX_INT_CHARS];
713 register char *b = a + sizeof(a) - 1;
714 int sign = (i < 0);
715
716 if (sign)
717 i = -i;
718 *b = 0;
719 do
720 {
721 *--b = '0' + (i % 10);
722 i /= 10;
723 }
724 while (i);
725 if (sign)
726 *--b = '-';
727 return b;
728}
729#endif
730
Erik Andersend75af992000-03-16 08:09:09 +0000731static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000732 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000733{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000734 int argc_l = *argcPtr;
Erik Andersen161220c2000-03-16 08:12:48 +0000735 int argcAlloced = *argcAllocedPtr;
736 int rc;
737 int flags;
738 int i;
Eric Andersenb54833c2000-07-03 23:56:26 +0000739 char *src, *dst, *var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000740
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000741 if (argc_l > 1) { /* cmd->globResult is already initialized */
Erik Andersen161220c2000-03-16 08:12:48 +0000742 flags = GLOB_APPEND;
743 i = prog->globResult.gl_pathc;
744 } else {
745 prog->freeGlob = 1;
746 flags = 0;
747 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000748 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000749 /* do shell variable substitution */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000750 if(*prog->argv[argc_l - 1] == '$') {
751 if ((var = getenv(prog->argv[argc_l - 1] + 1))) {
752 prog->argv[argc_l - 1] = var;
753 }
754#ifdef BB_FEATURE_SH_ENVIRONMENT
755 else {
756 switch(*(prog->argv[argc_l - 1] + 1)) {
757 case '?':
758 prog->argv[argc_l - 1] = itoa(lastReturnCode);
759 break;
760 case '$':
761 prog->argv[argc_l - 1] = itoa(getpid());
762 break;
763 case '#':
764 prog->argv[argc_l - 1] = itoa(argc-1);
765 break;
766 case '!':
767 if (lastBgPid==-1)
768 *(prog->argv[argc_l - 1])='\0';
769 else
770 prog->argv[argc_l - 1] = itoa(lastBgPid);
771 break;
772 case '0':case '1':case '2':case '3':case '4':
773 case '5':case '6':case '7':case '8':case '9':
774 {
775 int index=*(prog->argv[argc_l - 1] + 1)-48;
776 if (index >= argc) {
777 *(prog->argv[argc_l - 1])='\0';
778 } else {
779 prog->argv[argc_l - 1] = argv[index];
780 }
781 }
782 break;
783 }
784 }
785#endif
786 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000787
Eric Andersen46f0beb2000-11-14 21:59:22 +0000788 if (strpbrk(prog->argv[argc_l - 1],"*[]?")!= NULL){
789 rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
790 if (rc == GLOB_NOSPACE) {
791 errorMsg("out of space during glob operation\n");
792 return;
793 } else if (rc == GLOB_NOMATCH ||
Erik Andersen161220c2000-03-16 08:12:48 +0000794 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000795 strcmp(prog->argv[argc_l - 1],
Eric Andersene92108a2000-07-26 00:53:56 +0000796 prog->globResult.gl_pathv[i]) == 0)) {
Eric Andersen46f0beb2000-11-14 21:59:22 +0000797 /* we need to remove whatever \ quoting is still present */
798 src = dst = prog->argv[argc_l - 1];
799 while (*src) {
800 if (*src != '\\')
801 *dst++ = *src;
802 src++;
803 }
804 *dst = '\0';
805 } else if (!rc) {
806 argcAlloced += (prog->globResult.gl_pathc - i);
807 prog->argv = xrealloc(prog->argv, argcAlloced * sizeof(*prog->argv));
808 memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
809 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
810 argc_l += (prog->globResult.gl_pathc - i - 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000811 }
Eric Andersen46f0beb2000-11-14 21:59:22 +0000812 }else{
813 src = dst = prog->argv[argc_l - 1];
814 while (*src) {
815 if (*src != '\\')
816 *dst++ = *src;
817 src++;
818 }
819 *dst = '\0';
820 prog->globResult.gl_pathc=0;
821 if (flags==0)
822 prog->globResult.gl_pathv=NULL;
Erik Andersen161220c2000-03-16 08:12:48 +0000823 }
Erik Andersen161220c2000-03-16 08:12:48 +0000824 *argcAllocedPtr = argcAlloced;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000825 *argcPtr = argc_l;
Erik Andersen3522eb12000-03-12 23:49:18 +0000826}
827
828/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
829 line). If a valid command is found, commandPtr is set to point to
830 the beginning of the next command (if the original command had more
831 then one job associated with it) or NULL if no more commands are
832 present. */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000833static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000834{
Erik Andersen161220c2000-03-16 08:12:48 +0000835 char *command;
836 char *returnCommand = NULL;
837 char *src, *buf, *chptr;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000838 int argc_l = 0;
Erik Andersen161220c2000-03-16 08:12:48 +0000839 int done = 0;
840 int argvAlloced;
841 int i;
842 char quote = '\0';
843 int count;
844 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000845
Erik Andersen161220c2000-03-16 08:12:48 +0000846 /* skip leading white space */
847 while (**commandPtr && isspace(**commandPtr))
848 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000849
Erik Andersen161220c2000-03-16 08:12:48 +0000850 /* this handles empty lines or leading '#' characters */
851 if (!**commandPtr || (**commandPtr == '#')) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000852 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000853 return 0;
854 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000855
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000856 *inBg = 0;
Erik Andersen161220c2000-03-16 08:12:48 +0000857 job->numProgs = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000858 job->progs = xmalloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000859
Erik Andersen161220c2000-03-16 08:12:48 +0000860 /* We set the argv elements to point inside of this string. The
Eric Andersenb54833c2000-07-03 23:56:26 +0000861 memory is freed by freeJob(). Allocate twice the original
862 length in case we need to quote every single character.
Erik Andersen3522eb12000-03-12 23:49:18 +0000863
Erik Andersen161220c2000-03-16 08:12:48 +0000864 Getting clean memory relieves us of the task of NULL
865 terminating things and makes the rest of this look a bit
866 cleaner (though it is, admittedly, a tad less efficient) */
Matt Kraaib8907522000-09-13 02:08:21 +0000867 job->cmdBuf = command = xcalloc(2*strlen(*commandPtr) + 1, sizeof(char));
Erik Andersen161220c2000-03-16 08:12:48 +0000868 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000869
Erik Andersen161220c2000-03-16 08:12:48 +0000870 prog = job->progs;
871 prog->numRedirections = 0;
872 prog->redirections = NULL;
873 prog->freeGlob = 0;
874 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000875
Erik Andersen161220c2000-03-16 08:12:48 +0000876 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000877 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000878 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000879
Erik Andersen161220c2000-03-16 08:12:48 +0000880 buf = command;
881 src = *commandPtr;
882 while (*src && !done) {
883 if (quote == *src) {
884 quote = '\0';
885 } else if (quote) {
886 if (*src == '\\') {
887 src++;
888 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000889 errorMsg("character expected after \\\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000890 freeJob(job);
891 return 1;
892 }
893
894 /* in shell, "\'" should yield \' */
895 if (*src != quote)
896 *buf++ = '\\';
897 } else if (*src == '*' || *src == '?' || *src == '[' ||
898 *src == ']') *buf++ = '\\';
899 *buf++ = *src;
900 } else if (isspace(*src)) {
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000901 if (*prog->argv[argc_l]) {
902 buf++, argc_l++;
Erik Andersen161220c2000-03-16 08:12:48 +0000903 /* +1 here leaves room for the NULL which ends argv */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000904 if ((argc_l + 1) == argvAlloced) {
Erik Andersen161220c2000-03-16 08:12:48 +0000905 argvAlloced += 5;
Matt Kraaib8907522000-09-13 02:08:21 +0000906 prog->argv = xrealloc(prog->argv,
907 sizeof(*prog->argv) *
908 argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000909 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000910 globLastArgument(prog, &argc_l, &argvAlloced);
911 prog->argv[argc_l] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000912 }
913 } else
914 switch (*src) {
915 case '"':
916 case '\'':
917 quote = *src;
918 break;
919
920 case '#': /* comment */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000921 if (*(src-1)== '$')
922 *buf++ = *src;
923 else
924 done = 1;
Erik Andersen161220c2000-03-16 08:12:48 +0000925 break;
926
927 case '>': /* redirections */
928 case '<':
929 i = prog->numRedirections++;
Matt Kraaib8907522000-09-13 02:08:21 +0000930 prog->redirections = xrealloc(prog->redirections,
931 sizeof(*prog->redirections) *
932 (i + 1));
Erik Andersen161220c2000-03-16 08:12:48 +0000933
934 prog->redirections[i].fd = -1;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000935 if (buf != prog->argv[argc_l]) {
Erik Andersen161220c2000-03-16 08:12:48 +0000936 /* the stuff before this character may be the file number
937 being redirected */
938 prog->redirections[i].fd =
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000939 strtol(prog->argv[argc_l], &chptr, 10);
Erik Andersen161220c2000-03-16 08:12:48 +0000940
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000941 if (*chptr && *prog->argv[argc_l]) {
942 buf++, argc_l++;
943 globLastArgument(prog, &argc_l, &argvAlloced);
944 prog->argv[argc_l] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000945 }
946 }
947
948 if (prog->redirections[i].fd == -1) {
949 if (*src == '>')
950 prog->redirections[i].fd = 1;
951 else
952 prog->redirections[i].fd = 0;
953 }
954
955 if (*src++ == '>') {
956 if (*src == '>')
957 prog->redirections[i].type =
958 REDIRECT_APPEND, src++;
959 else
960 prog->redirections[i].type = REDIRECT_OVERWRITE;
961 } else {
962 prog->redirections[i].type = REDIRECT_INPUT;
963 }
964
965 /* This isn't POSIX sh compliant. Oh well. */
966 chptr = src;
967 while (isspace(*chptr))
968 chptr++;
969
970 if (!*chptr) {
Matt Kraaid537a952000-07-14 01:51:25 +0000971 errorMsg("file name expected after %c\n", *src);
Erik Andersen161220c2000-03-16 08:12:48 +0000972 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000973 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000974 return 1;
975 }
976
977 prog->redirections[i].filename = buf;
978 while (*chptr && !isspace(*chptr))
979 *buf++ = *chptr++;
980
981 src = chptr - 1; /* we src++ later */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000982 prog->argv[argc_l] = ++buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000983 break;
984
985 case '|': /* pipe */
986 /* finish this command */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000987 if (*prog->argv[argc_l])
988 argc_l++;
989 if (!argc_l) {
Eric Andersena1d187a2000-07-17 19:14:41 +0000990 errorMsg("empty command in pipe\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000991 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000992 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000993 return 1;
994 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000995 prog->argv[argc_l] = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +0000996
997 /* and start the next */
998 job->numProgs++;
Matt Kraaib8907522000-09-13 02:08:21 +0000999 job->progs = xrealloc(job->progs,
1000 sizeof(*job->progs) * job->numProgs);
Erik Andersen161220c2000-03-16 08:12:48 +00001001 prog = job->progs + (job->numProgs - 1);
1002 prog->numRedirections = 0;
1003 prog->redirections = NULL;
1004 prog->freeGlob = 0;
Eric Andersen501c88b2000-07-28 15:14:45 +00001005 prog->isStopped = 0;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001006 argc_l = 0;
Erik Andersen161220c2000-03-16 08:12:48 +00001007
1008 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001009 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +00001010 prog->argv[0] = ++buf;
1011
1012 src++;
1013 while (*src && isspace(*src))
1014 src++;
1015
1016 if (!*src) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001017 errorMsg("empty command in pipe\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +00001018 freeJob(job);
1019 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +00001020 return 1;
1021 }
1022 src--; /* we'll ++ it at the end of the loop */
1023
1024 break;
1025
1026 case '&': /* background */
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001027 *inBg = 1;
Erik Andersen161220c2000-03-16 08:12:48 +00001028 case ';': /* multiple commands */
1029 done = 1;
1030 returnCommand = *commandPtr + (src - *commandPtr) + 1;
1031 break;
1032
Eric Andersena1d187a2000-07-17 19:14:41 +00001033#ifdef BB_FEATURE_SH_BACKTICKS
Eric Andersenec10b9d2000-07-14 01:13:11 +00001034 case '`':
1035 /* Exec a backtick-ed command */
1036 {
Eric Andersena1d187a2000-07-17 19:14:41 +00001037 char* charptr1=NULL, *charptr2;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001038 char* ptr=NULL;
Eric Andersena1d187a2000-07-17 19:14:41 +00001039 struct job *newJob;
1040 struct jobSet njobList = { NULL, NULL };
1041 int pipefd[2];
1042 int size;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001043
1044 ptr=strchr(++src, '`');
1045 if (ptr==NULL) {
1046 fprintf(stderr, "Unmatched '`' in command\n");
1047 freeJob(job);
1048 return 1;
1049 }
1050
Eric Andersena1d187a2000-07-17 19:14:41 +00001051 /* Make some space to hold just the backticked command */
Eric Andersen6efc48c2000-07-18 08:16:39 +00001052 charptr1 = charptr2 = xmalloc(1+ptr-src);
Matt Kraai0b2da462000-09-19 06:46:44 +00001053 memcpy(charptr1, src, ptr-src);
1054 charptr1[ptr-src] = '\0';
Eric Andersena1d187a2000-07-17 19:14:41 +00001055 newJob = xmalloc(sizeof(struct job));
1056 /* Now parse and run the backticked command */
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001057 if (!parseCommand(&charptr1, newJob, &njobList, inBg)
Eric Andersena1d187a2000-07-17 19:14:41 +00001058 && newJob->numProgs) {
1059 pipe(pipefd);
1060 runCommand(newJob, &njobList, 0, pipefd);
Eric Andersenec10b9d2000-07-14 01:13:11 +00001061 }
Eric Andersena1d187a2000-07-17 19:14:41 +00001062 checkJobs(jobList);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001063 freeJob(newJob);
1064 free(charptr2);
1065
1066 /* Make a copy of any stuff left over in the command
1067 * line after the second backtick */
1068 charptr2 = xmalloc(strlen(ptr)+1);
1069 memcpy(charptr2, ptr+1, strlen(ptr));
1070
Eric Andersenec10b9d2000-07-14 01:13:11 +00001071
Eric Andersena1d187a2000-07-17 19:14:41 +00001072 /* Copy the output from the backtick-ed command into the
1073 * command line, making extra room as needed */
1074 --src;
1075 charptr1 = xmalloc(BUFSIZ);
1076 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
1077 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1078 if (newSize > BUFSIZ) {
Matt Kraaib8907522000-09-13 02:08:21 +00001079 *commandPtr=xrealloc(*commandPtr, src - *commandPtr +
Eric Andersena1d187a2000-07-17 19:14:41 +00001080 size + 1 + strlen(charptr2));
1081 }
1082 memcpy(src, charptr1, size);
1083 src+=size;
1084 }
1085 free(charptr1);
1086 close(pipefd[0]);
1087 if (*(src-1)=='\n')
1088 --src;
1089
1090 /* Now paste into the *commandPtr all the stuff
1091 * leftover after the second backtick */
Matt Kraaicbbe4d62000-09-14 00:26:50 +00001092 memcpy(src, charptr2, strlen(charptr2)+1);
Eric Andersena1d187a2000-07-17 19:14:41 +00001093 free(charptr2);
1094
Eric Andersena1d187a2000-07-17 19:14:41 +00001095 /* Now recursively call parseCommand to deal with the new
1096 * and improved version of the command line with the backtick
1097 * results expanded in place... */
Eric Andersen6efc48c2000-07-18 08:16:39 +00001098 freeJob(job);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001099 return(parseCommand(commandPtr, job, jobList, inBg));
Eric Andersenec10b9d2000-07-14 01:13:11 +00001100 }
1101 break;
Eric Andersena1d187a2000-07-17 19:14:41 +00001102#endif // BB_FEATURE_SH_BACKTICKS
Matt Kraai131241f2000-09-14 00:43:20 +00001103
1104 case '\\':
1105 src++;
1106 if (!*src) {
1107 errorMsg("character expected after \\\n");
1108 freeJob(job);
1109 return 1;
1110 }
1111 if (*src == '*' || *src == '[' || *src == ']'
1112 || *src == '?') *buf++ = '\\';
1113 /* fallthrough */
Erik Andersen161220c2000-03-16 08:12:48 +00001114 default:
1115 *buf++ = *src;
1116 }
1117
Erik Andersend75af992000-03-16 08:09:09 +00001118 src++;
Erik Andersen161220c2000-03-16 08:12:48 +00001119 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001120
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001121 if (*prog->argv[argc_l]) {
1122 argc_l++;
1123 globLastArgument(prog, &argc_l, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +00001124 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001125 if (!argc_l) {
Erik Andersen161220c2000-03-16 08:12:48 +00001126 freeJob(job);
1127 return 0;
1128 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001129 prog->argv[argc_l] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +00001130
Erik Andersen161220c2000-03-16 08:12:48 +00001131 if (!returnCommand) {
Eric Andersenec10b9d2000-07-14 01:13:11 +00001132 job->text = xmalloc(strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +00001133 strcpy(job->text, *commandPtr);
1134 } else {
1135 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +00001136 count = returnCommand - *commandPtr;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001137 job->text = xmalloc(count + 1);
Erik Andersen161220c2000-03-16 08:12:48 +00001138 strncpy(job->text, *commandPtr, count);
1139 job->text[count] = '\0';
1140 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001141
Erik Andersen161220c2000-03-16 08:12:48 +00001142 *commandPtr = returnCommand;
Eric Andersen46f0beb2000-11-14 21:59:22 +00001143
Erik Andersend75af992000-03-16 08:09:09 +00001144 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001145}
1146
Eric Andersena1d187a2000-07-17 19:14:41 +00001147static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
Erik Andersen3522eb12000-03-12 23:49:18 +00001148{
Eric Andersenfad9c112000-07-25 18:06:52 +00001149 struct job *theJob;
Erik Andersen161220c2000-03-16 08:12:48 +00001150 int i;
Eric Andersen6efc48c2000-07-18 08:16:39 +00001151 int nextin, nextout;
Erik Andersen161220c2000-03-16 08:12:48 +00001152 int pipefds[2]; /* pipefd[0] is for reading */
1153 struct builtInCommand *x;
Eric Andersenb54833c2000-07-03 23:56:26 +00001154#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Erik Andersenbcd61772000-05-13 06:33:19 +00001155 const struct BB_applet *a = applets;
1156#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001157
Eric Andersen6efc48c2000-07-18 08:16:39 +00001158 nextin = 0, nextout = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001159 for (i = 0; i < newJob->numProgs; i++) {
1160 if ((i + 1) < newJob->numProgs) {
Erik Andersen161220c2000-03-16 08:12:48 +00001161 pipe(pipefds);
1162 nextout = pipefds[1];
1163 } else {
Eric Andersen6efc48c2000-07-18 08:16:39 +00001164 if (outPipe[1]!=-1) {
1165 nextout = outPipe[1];
1166 } else {
1167 nextout = 1;
1168 }
Erik Andersen161220c2000-03-16 08:12:48 +00001169 }
1170
Eric Andersen501c88b2000-07-28 15:14:45 +00001171#ifdef BB_FEATURE_SH_ENVIRONMENT
1172 if (showXtrace==TRUE) {
1173 int j;
1174 fprintf(stderr, "+ ");
1175 for (j = 0; newJob->progs[i].argv[j]; j++)
1176 fprintf(stderr, "%s ", newJob->progs[i].argv[j]);
1177 fprintf(stderr, "\n");
1178 }
1179#endif
1180
Eric Andersen34e19412000-07-10 18:47:24 +00001181 /* Check if the command matches any non-forking builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +00001182 for (x = bltins; x->cmd; x++) {
Eric Andersene92108a2000-07-26 00:53:56 +00001183 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001184 return(x->function(newJob, jobList));
Erik Andersen330fd2b2000-05-19 05:35:19 +00001185 }
1186 }
1187
Eric Andersenec10b9d2000-07-14 01:13:11 +00001188 if (!(newJob->progs[i].pid = fork())) {
Erik Andersen161220c2000-03-16 08:12:48 +00001189 signal(SIGTTOU, SIG_DFL);
1190
Eric Andersena1d187a2000-07-17 19:14:41 +00001191 if (outPipe[1]!=-1) {
1192 close(outPipe[0]);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001193 }
1194 if (nextin != 0) {
1195 dup2(nextin, 0);
1196 close(nextin);
1197 }
1198
1199 if (nextout != 1) {
Erik Andersen161220c2000-03-16 08:12:48 +00001200 dup2(nextout, 1);
Eric Andersena1d187a2000-07-17 19:14:41 +00001201 dup2(nextout, 2);
Erik Andersen161220c2000-03-16 08:12:48 +00001202 close(nextout);
Eric Andersen501c88b2000-07-28 15:14:45 +00001203 close(pipefds[0]);
Erik Andersen161220c2000-03-16 08:12:48 +00001204 }
1205
1206 /* explicit redirections override pipes */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001207 setupRedirections(newJob->progs + i);
Erik Andersen161220c2000-03-16 08:12:48 +00001208
Eric Andersen34e19412000-07-10 18:47:24 +00001209 /* Check if the command matches any of the other builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +00001210 for (x = bltins_forking; x->cmd; x++) {
Eric Andersene92108a2000-07-26 00:53:56 +00001211 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001212 applet_name=x->cmd;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001213 exit (x->function(newJob, jobList));
Erik Andersenbcd61772000-05-13 06:33:19 +00001214 }
1215 }
Eric Andersenb54833c2000-07-03 23:56:26 +00001216#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Eric Andersen34e19412000-07-10 18:47:24 +00001217 /* Check if the command matches any busybox internal commands here */
Erik Andersenbcd61772000-05-13 06:33:19 +00001218 while (a->name != 0) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001219 if (strcmp(get_last_path_component(newJob->progs[i].argv[0]), a->name) == 0) {
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001220 int argc_l;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001221 char** argv=newJob->progs[i].argv;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001222 for(argc_l=0;*argv!=NULL; argv++, argc_l++);
Eric Andersen501c88b2000-07-28 15:14:45 +00001223 applet_name=a->name;
Matt Kraai6085c722000-09-06 01:46:18 +00001224 optind = 1;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001225 exit((*(a->main)) (argc_l, newJob->progs[i].argv));
Erik Andersenbcd61772000-05-13 06:33:19 +00001226 }
1227 a++;
1228 }
1229#endif
1230
Eric Andersenec10b9d2000-07-14 01:13:11 +00001231 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001232 fatalError("%s: %s\n", newJob->progs[i].argv[0],
Erik Andersen161220c2000-03-16 08:12:48 +00001233 strerror(errno));
1234 }
Eric Andersen6efc48c2000-07-18 08:16:39 +00001235 if (outPipe[1]!=-1) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001236 close(outPipe[1]);
1237 }
Erik Andersen161220c2000-03-16 08:12:48 +00001238
1239 /* put our child in the process group whose leader is the
1240 first process in this pipe */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001241 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
Erik Andersen161220c2000-03-16 08:12:48 +00001242 if (nextin != 0)
1243 close(nextin);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001244 if (nextout != 1)
Erik Andersen161220c2000-03-16 08:12:48 +00001245 close(nextout);
1246
1247 /* If there isn't another process, nextin is garbage
1248 but it doesn't matter */
1249 nextin = pipefds[0];
1250 }
1251
Eric Andersenec10b9d2000-07-14 01:13:11 +00001252 newJob->pgrp = newJob->progs[0].pid;
Erik Andersen161220c2000-03-16 08:12:48 +00001253
Eric Andersenfad9c112000-07-25 18:06:52 +00001254 /* find the ID for the theJob to use */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001255 newJob->jobId = 1;
Eric Andersenfad9c112000-07-25 18:06:52 +00001256 for (theJob = jobList->head; theJob; theJob = theJob->next)
1257 if (theJob->jobId >= newJob->jobId)
1258 newJob->jobId = theJob->jobId + 1;
Erik Andersen161220c2000-03-16 08:12:48 +00001259
Eric Andersenfad9c112000-07-25 18:06:52 +00001260 /* add the theJob to the list of running jobs */
Erik Andersen161220c2000-03-16 08:12:48 +00001261 if (!jobList->head) {
Matt Kraaib8907522000-09-13 02:08:21 +00001262 theJob = jobList->head = xmalloc(sizeof(*theJob));
Erik Andersend75af992000-03-16 08:09:09 +00001263 } else {
Eric Andersenfad9c112000-07-25 18:06:52 +00001264 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
Matt Kraaib8907522000-09-13 02:08:21 +00001265 theJob->next = xmalloc(sizeof(*theJob));
Eric Andersenfad9c112000-07-25 18:06:52 +00001266 theJob = theJob->next;
Erik Andersend75af992000-03-16 08:09:09 +00001267 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001268
Eric Andersenfad9c112000-07-25 18:06:52 +00001269 *theJob = *newJob;
1270 theJob->next = NULL;
1271 theJob->runningProgs = theJob->numProgs;
1272 theJob->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001273
Erik Andersen161220c2000-03-16 08:12:48 +00001274 if (inBg) {
Eric Andersenfad9c112000-07-25 18:06:52 +00001275 /* we don't wait for background theJobs to return -- append it
1276 to the list of backgrounded theJobs and leave it alone */
1277 printf("[%d] %d\n", theJob->jobId,
Eric Andersenec10b9d2000-07-14 01:13:11 +00001278 newJob->progs[newJob->numProgs - 1].pid);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001279#ifdef BB_FEATURE_SH_ENVIRONMENT
1280 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1281#endif
Erik Andersen161220c2000-03-16 08:12:48 +00001282 } else {
Eric Andersenfad9c112000-07-25 18:06:52 +00001283 jobList->fg = theJob;
Erik Andersen3522eb12000-03-12 23:49:18 +00001284
Erik Andersen161220c2000-03-16 08:12:48 +00001285 /* move the new process group into the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001286 /* suppress messages when run from /linuxrc mag@sysgo.de */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001287 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
Erik Andersen161220c2000-03-16 08:12:48 +00001288 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001289 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001290
Erik Andersen161220c2000-03-16 08:12:48 +00001291 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001292}
1293
Erik Andersend75af992000-03-16 08:09:09 +00001294static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +00001295{
Erik Andersen161220c2000-03-16 08:12:48 +00001296 char *command;
1297 char *nextCommand = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001298 struct job newJob;
Eric Andersen1c314ad2000-06-28 16:56:25 +00001299 pid_t parent_pgrp;
Erik Andersen161220c2000-03-16 08:12:48 +00001300 int i;
Erik Andersen161220c2000-03-16 08:12:48 +00001301 int inBg;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001302 int status;
1303 newJob.jobContext = REGULAR_JOB_CONTEXT;
Erik Andersen3522eb12000-03-12 23:49:18 +00001304
Eric Andersen1c314ad2000-06-28 16:56:25 +00001305 /* save current owner of TTY so we can restore it on exit */
1306 parent_pgrp = tcgetpgrp(0);
1307
Matt Kraaib8907522000-09-13 02:08:21 +00001308 command = (char *) xcalloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +00001309
Erik Andersen161220c2000-03-16 08:12:48 +00001310 /* don't pay any attention to this signal; it just confuses
1311 things and isn't really meant for shells anyway */
1312 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +00001313
Erik Andersen161220c2000-03-16 08:12:48 +00001314 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +00001315 if (!jobList.fg) {
1316 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +00001317
Erik Andersend75af992000-03-16 08:09:09 +00001318 /* see if any background processes have exited */
1319 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +00001320
Erik Andersend75af992000-03-16 08:09:09 +00001321 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +00001322 if (getCommand(input, command))
1323 break;
1324 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +00001325 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001326
Eric Andersenec10b9d2000-07-14 01:13:11 +00001327 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +00001328 newJob.numProgs) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001329 int pipefds[2] = {-1,-1};
1330 runCommand(&newJob, &jobList, inBg, pipefds);
Eric Andersenfad9c112000-07-25 18:06:52 +00001331 }
1332 else {
Eric Andersena1d187a2000-07-17 19:14:41 +00001333 free(command);
Matt Kraaib8907522000-09-13 02:08:21 +00001334 command = (char *) xcalloc(BUFSIZ, sizeof(char));
Eric Andersen6efc48c2000-07-18 08:16:39 +00001335 nextCommand = NULL;
Erik Andersend75af992000-03-16 08:09:09 +00001336 }
1337 } else {
1338 /* a job is running in the foreground; wait for it */
1339 i = 0;
1340 while (!jobList.fg->progs[i].pid ||
Eric Andersenfad9c112000-07-25 18:06:52 +00001341 jobList.fg->progs[i].isStopped == 1) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +00001342
Erik Andersend75af992000-03-16 08:09:09 +00001343 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +00001344
Erik Andersend75af992000-03-16 08:09:09 +00001345 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +00001346 /* the child exited */
1347 jobList.fg->runningProgs--;
1348 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001349
Eric Andersen501c88b2000-07-28 15:14:45 +00001350#ifdef BB_FEATURE_SH_ENVIRONMENT
1351 lastReturnCode=WEXITSTATUS(status);
1352#endif
1353#if 0
1354 printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode);
1355#endif
Erik Andersen161220c2000-03-16 08:12:48 +00001356 if (!jobList.fg->runningProgs) {
1357 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +00001358
Erik Andersen161220c2000-03-16 08:12:48 +00001359 removeJob(&jobList, jobList.fg);
1360 jobList.fg = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001361 }
Erik Andersend75af992000-03-16 08:09:09 +00001362 } else {
Erik Andersen161220c2000-03-16 08:12:48 +00001363 /* the child was stopped */
1364 jobList.fg->stoppedProgs++;
1365 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +00001366
Erik Andersen161220c2000-03-16 08:12:48 +00001367 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1368 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1369 "Stopped", jobList.fg->text);
1370 jobList.fg = NULL;
1371 }
Erik Andersend75af992000-03-16 08:09:09 +00001372 }
1373
1374 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +00001375 /* move the shell to the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001376 /* suppress messages when run from /linuxrc mag@sysgo.de */
1377 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1378 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001379 }
1380 }
1381 }
Erik Andersen161220c2000-03-16 08:12:48 +00001382 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +00001383
Eric Andersen1c314ad2000-06-28 16:56:25 +00001384 /* return controlling TTY back to parent process group before exiting */
1385 if (tcsetpgrp(0, parent_pgrp))
Eric Andersenb54833c2000-07-03 23:56:26 +00001386 perror("tcsetpgrp");
1387
1388 /* return exit status if called with "-c" */
1389 if (input == NULL && WIFEXITED(status))
1390 return WEXITSTATUS(status);
1391
Erik Andersen161220c2000-03-16 08:12:48 +00001392 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001393}
1394
1395
Eric Andersenfad9c112000-07-25 18:06:52 +00001396#ifdef BB_FEATURE_CLEAN_UP
1397void free_memory(void)
1398{
1399 if (promptStr)
1400 free(promptStr);
1401 if (cwd)
1402 free(cwd);
1403 if (local_pending_command)
1404 free(local_pending_command);
1405
1406 if (jobList.fg && !jobList.fg->runningProgs) {
1407 removeJob(&jobList, jobList.fg);
1408 }
1409}
1410#endif
1411
Eric Andersen6efc48c2000-07-18 08:16:39 +00001412
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001413int shell_main(int argc_l, char **argv_l)
Erik Andersen3522eb12000-03-12 23:49:18 +00001414{
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001415 int opt, interactive=FALSE;
Erik Andersen161220c2000-03-16 08:12:48 +00001416 FILE *input = stdin;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001417 argc = argc_l;
1418 argv = argv_l;
Erik Andersen3522eb12000-03-12 23:49:18 +00001419
Eric Andersen501c88b2000-07-28 15:14:45 +00001420
1421 //if (argv[0] && argv[0][0] == '-') {
1422 // builtin_source("/etc/profile");
1423 //}
1424
Matt Kraai6085c722000-09-06 01:46:18 +00001425 while ((opt = getopt(argc_l, argv_l, "cx")) > 0) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001426 switch (opt) {
1427 case 'c':
1428 input = NULL;
Matt Kraai6085c722000-09-06 01:46:18 +00001429 if (local_pending_command != 0)
1430 fatalError("multiple -c arguments\n");
1431 local_pending_command = xstrdup(argv[optind]);
1432 optind++;
1433 argv = argv+optind;
Eric Andersen501c88b2000-07-28 15:14:45 +00001434 break;
Eric Andersen1428c4f2000-07-28 15:19:30 +00001435#ifdef BB_FEATURE_SH_ENVIRONMENT
Eric Andersen501c88b2000-07-28 15:14:45 +00001436 case 'x':
1437 showXtrace = TRUE;
1438 break;
Eric Andersen1428c4f2000-07-28 15:19:30 +00001439#endif
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001440 case 'i':
1441 interactive = TRUE;
1442 break;
Eric Andersen501c88b2000-07-28 15:14:45 +00001443 default:
1444 usage(shell_usage);
1445 }
1446 }
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001447 /* A shell is interactive if the `-i' flag was given, or if all of
1448 * the following conditions are met:
1449 * no -c command
1450 * no arguments remaining or the -s flag given
1451 * standard input is a terminal
1452 * standard output is a terminal
1453 * Refer to Posix.2, the description of the `sh' utility. */
1454 if (interactive==TRUE || ( argv[optind]==NULL && input==stdin && isatty(fileno(stdin)) && isatty(fileno(stdout)))) {
Eric Andersen851ce892000-08-21 22:34:23 +00001455 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
Eric Andersen501c88b2000-07-28 15:14:45 +00001456 /* Looks like they want an interactive shell */
1457 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1458 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001459 } else if (local_pending_command==NULL) {
Eric Andersen851ce892000-08-21 22:34:23 +00001460 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
Matt Kraaibbaef662000-09-27 02:43:35 +00001461 input = xfopen(argv[optind], "r");
Eric Andersen501c88b2000-07-28 15:14:45 +00001462 }
1463
Eric Andersen6efc48c2000-07-18 08:16:39 +00001464 /* initialize the cwd -- this is never freed...*/
1465 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1466 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen3522eb12000-03-12 23:49:18 +00001467
Eric Andersenfad9c112000-07-25 18:06:52 +00001468#ifdef BB_FEATURE_CLEAN_UP
1469 atexit(free_memory);
1470#endif
1471
Erik Andersenf0657d32000-04-12 17:49:52 +00001472#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenf0657d32000-04-12 17:49:52 +00001473 win_changed(0);
1474#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001475
Erik Andersen161220c2000-03-16 08:12:48 +00001476 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +00001477}