blob: eba60b3b79a7a1247b51fed04098750bf5d83c53 [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"
Eric Andersen8c725e62000-11-30 00:27:06 +000055extern size_t NUM_APPLETS;
Erik Andersen3522eb12000-03-12 23:49:18 +000056
Erik Andersend75af992000-03-16 08:09:09 +000057
58enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
Erik Andersen161220c2000-03-16 08:12:48 +000059 REDIRECT_APPEND
60};
Erik Andersen3522eb12000-03-12 23:49:18 +000061
Eric Andersene92108a2000-07-26 00:53:56 +000062static const unsigned int REGULAR_JOB_CONTEXT=0x1;
Eric Andersen6a99aaf2000-07-27 00:15:20 +000063static const unsigned int IF_TRUE_CONTEXT=0x2;
64static const unsigned int IF_FALSE_CONTEXT=0x4;
65static const unsigned int THEN_EXP_CONTEXT=0x8;
66static const unsigned int ELSE_EXP_CONTEXT=0x10;
Eric Andersene92108a2000-07-26 00:53:56 +000067
Eric Andersenfad9c112000-07-25 18:06:52 +000068
Erik Andersen3522eb12000-03-12 23:49:18 +000069struct jobSet {
Erik Andersen161220c2000-03-16 08:12:48 +000070 struct job *head; /* head of list of running jobs */
71 struct job *fg; /* current foreground job */
Erik Andersen3522eb12000-03-12 23:49:18 +000072};
73
74struct redirectionSpecifier {
Erik Andersen161220c2000-03-16 08:12:48 +000075 enum redirectionType type; /* type of redirection */
76 int fd; /* file descriptor being redirected */
77 char *filename; /* file to redirect fd to */
Erik Andersen3522eb12000-03-12 23:49:18 +000078};
79
80struct childProgram {
Erik Andersen161220c2000-03-16 08:12:48 +000081 pid_t pid; /* 0 if exited */
82 char **argv; /* program name and arguments */
83 int numRedirections; /* elements in redirection array */
84 struct redirectionSpecifier *redirections; /* I/O redirections */
85 glob_t globResult; /* result of parameter globbing */
86 int freeGlob; /* should we globfree(&globResult)? */
87 int isStopped; /* is the program currently running? */
Erik Andersen3522eb12000-03-12 23:49:18 +000088};
89
90struct job {
Erik Andersen161220c2000-03-16 08:12:48 +000091 int jobId; /* job number */
92 int numProgs; /* total number of programs in job */
93 int runningProgs; /* number of programs running */
94 char *text; /* name of job */
95 char *cmdBuf; /* buffer various argv's point into */
96 pid_t pgrp; /* process group ID for the job */
97 struct childProgram *progs; /* array of programs in job */
98 struct job *next; /* to track background commands */
99 int stoppedProgs; /* number of programs alive, but stopped */
Eric Andersenfad9c112000-07-25 18:06:52 +0000100 int jobContext; /* bitmask defining current context */
Erik Andersen3522eb12000-03-12 23:49:18 +0000101};
102
103struct builtInCommand {
Erik Andersen161220c2000-03-16 08:12:48 +0000104 char *cmd; /* name */
105 char *descr; /* description */
Erik Andersen161220c2000-03-16 08:12:48 +0000106 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
Erik Andersen3522eb12000-03-12 23:49:18 +0000107};
108
Eric Andersen34e19412000-07-10 18:47:24 +0000109/* function prototypes for builtins */
110static int builtin_cd(struct job *cmd, struct jobSet *junk);
111static int builtin_env(struct job *dummy, struct jobSet *junk);
Eric Andersend2f56772000-09-21 02:48:07 +0000112static int builtin_exec(struct job *cmd, struct jobSet *junk);
Eric Andersen34e19412000-07-10 18:47:24 +0000113static int builtin_exit(struct job *cmd, struct jobSet *junk);
114static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
115static int builtin_help(struct job *cmd, struct jobSet *junk);
116static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
117static int builtin_pwd(struct job *dummy, struct jobSet *junk);
118static int builtin_export(struct job *cmd, struct jobSet *junk);
119static int builtin_source(struct job *cmd, struct jobSet *jobList);
120static int builtin_unset(struct job *cmd, struct jobSet *junk);
121static int builtin_read(struct job *cmd, struct jobSet *junk);
Eric Andersenfad9c112000-07-25 18:06:52 +0000122#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
123static int builtin_if(struct job *cmd, struct jobSet *junk);
124static int builtin_then(struct job *cmd, struct jobSet *junk);
125static int builtin_else(struct job *cmd, struct jobSet *junk);
126static int builtin_fi(struct job *cmd, struct jobSet *junk);
127#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000128
Eric Andersen34e19412000-07-10 18:47:24 +0000129
130/* function prototypes for shell stuff */
Erik Andersend75af992000-03-16 08:09:09 +0000131static void checkJobs(struct jobSet *jobList);
132static int getCommand(FILE * source, char *command);
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000133static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg);
Eric Andersena1d187a2000-07-17 19:14:41 +0000134static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
Erik Andersen3522eb12000-03-12 23:49:18 +0000135static int busy_loop(FILE * input);
136
Erik Andersend75af992000-03-16 08:09:09 +0000137
Mark Whitley37653aa2000-07-12 23:36:17 +0000138/* Table of built-in functions (these are non-forking builtins, meaning they
139 * can change global variables in the parent shell process but they will not
140 * work with pipes and redirects; 'unset foo | whatever' will not work) */
Erik Andersen3522eb12000-03-12 23:49:18 +0000141static struct builtInCommand bltins[] = {
Eric Andersenfad9c112000-07-25 18:06:52 +0000142 {"bg", "Resume a job in the background", builtin_fg_bg},
143 {"cd", "Change working directory", builtin_cd},
Eric Andersend2f56772000-09-21 02:48:07 +0000144 {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec},
Eric Andersenfad9c112000-07-25 18:06:52 +0000145 {"exit", "Exit from shell()", builtin_exit},
146 {"fg", "Bring job into the foreground", builtin_fg_bg},
147 {"jobs", "Lists the active jobs", builtin_jobs},
148 {"export", "Set environment variable", builtin_export},
149 {"unset", "Unset environment variable", builtin_unset},
150 {"read", "Input environment variable", builtin_read},
Matt Kraaidd450a02000-09-13 03:43:36 +0000151 {".", "Source-in and run commands in a file", builtin_source},
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000152#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
153 {"if", NULL, builtin_if},
154 {"then", NULL, builtin_then},
155 {"else", NULL, builtin_else},
156 {"fi", NULL, builtin_fi},
157#endif
Eric Andersenfad9c112000-07-25 18:06:52 +0000158 {NULL, NULL, NULL}
Erik Andersen330fd2b2000-05-19 05:35:19 +0000159};
160
Mark Whitley37653aa2000-07-12 23:36:17 +0000161/* Table of forking built-in functions (things that fork cannot change global
162 * variables in the parent process, such as the current working directory) */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000163static struct builtInCommand bltins_forking[] = {
Eric Andersenfad9c112000-07-25 18:06:52 +0000164 {"env", "Print all environment variables", builtin_env},
165 {"pwd", "Print current directory", builtin_pwd},
Eric Andersenfad9c112000-07-25 18:06:52 +0000166 {"help", "List shell built-in commands", builtin_help},
167 {NULL, NULL, NULL}
Erik Andersen3522eb12000-03-12 23:49:18 +0000168};
169
Erik Andersen3522eb12000-03-12 23:49:18 +0000170static char *prompt = "# ";
Eric Andersen6efc48c2000-07-18 08:16:39 +0000171static char *cwd;
Eric Andersen1c314ad2000-06-28 16:56:25 +0000172static char *local_pending_command = NULL;
Eric Andersenfad9c112000-07-25 18:06:52 +0000173static char *promptStr = NULL;
174static struct jobSet jobList = { NULL, NULL };
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000175static int argc;
176static char **argv;
177#ifdef BB_FEATURE_SH_ENVIRONMENT
178static int lastBgPid=-1;
179static int lastReturnCode=-1;
Eric Andersen501c88b2000-07-28 15:14:45 +0000180static int showXtrace=FALSE;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000181#endif
Eric Andersen501c88b2000-07-28 15:14:45 +0000182
Erik Andersen3522eb12000-03-12 23:49:18 +0000183
Erik Andersenf0657d32000-04-12 17:49:52 +0000184#ifdef BB_FEATURE_SH_COMMAND_EDITING
Eric Andersenfad04fd2000-07-14 06:49:52 +0000185void win_changed(int junk)
Erik Andersenf0657d32000-04-12 17:49:52 +0000186{
Eric Andersenfad04fd2000-07-14 06:49:52 +0000187 struct winsize win = { 0, 0, 0, 0 };
Erik Andersenf0657d32000-04-12 17:49:52 +0000188 ioctl(0, TIOCGWINSZ, &win);
189 if (win.ws_col > 0) {
190 cmdedit_setwidth( win.ws_col - 1);
191 }
192}
193#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000194
Erik Andersen3522eb12000-03-12 23:49:18 +0000195
Erik Andersend75af992000-03-16 08:09:09 +0000196/* built-in 'cd <path>' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000197static int builtin_cd(struct job *cmd, struct jobSet *junk)
Erik Andersend75af992000-03-16 08:09:09 +0000198{
Erik Andersen161220c2000-03-16 08:12:48 +0000199 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000200
Erik Andersen161220c2000-03-16 08:12:48 +0000201 if (!cmd->progs[0].argv[1] == 1)
202 newdir = getenv("HOME");
203 else
204 newdir = cmd->progs[0].argv[1];
205 if (chdir(newdir)) {
206 printf("cd: %s: %s\n", newdir, strerror(errno));
207 return FALSE;
208 }
Eric Andersen6efc48c2000-07-18 08:16:39 +0000209 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen161220c2000-03-16 08:12:48 +0000210
211 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000212}
213
214/* built-in 'env' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000215static int builtin_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000216{
Erik Andersen161220c2000-03-16 08:12:48 +0000217 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000218
Erik Andersen161220c2000-03-16 08:12:48 +0000219 for (e = environ; *e; e++) {
220 fprintf(stdout, "%s\n", *e);
221 }
222 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000223}
224
Eric Andersend2f56772000-09-21 02:48:07 +0000225/* built-in 'exec' handler */
226static int builtin_exec(struct job *cmd, struct jobSet *junk)
227{
228 if (cmd->progs[0].argv[1])
229 {
230 cmd->progs[0].argv++;
231 execvp(cmd->progs[0].argv[0], cmd->progs[0].argv);
232 fatalError("Exec to %s failed: %s\n", cmd->progs[0].argv[0],
233 strerror(errno));
234 }
235 return TRUE;
236}
237
Erik Andersen3522eb12000-03-12 23:49:18 +0000238/* built-in 'exit' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000239static int builtin_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000240{
Erik Andersen161220c2000-03-16 08:12:48 +0000241 if (!cmd->progs[0].argv[1] == 1)
242 exit TRUE;
243
Pavel Roskin5f84fd72000-09-15 00:46:51 +0000244 exit (atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000245}
246
247/* built-in 'fg' and 'bg' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000248static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000249{
Erik Andersen161220c2000-03-16 08:12:48 +0000250 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000251 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000252
Erik Andersen161220c2000-03-16 08:12:48 +0000253 if (!jobList->head) {
254 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
Matt Kraaid537a952000-07-14 01:51:25 +0000255 errorMsg("%s: exactly one argument is expected\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000256 cmd->progs[0].argv[0]);
257 return FALSE;
258 }
259 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
Matt Kraaid537a952000-07-14 01:51:25 +0000260 errorMsg("%s: bad argument '%s'\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000261 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
262 return FALSE;
263 for (job = jobList->head; job; job = job->next) {
264 if (job->jobId == jobNum) {
265 break;
266 }
267 }
268 }
269 } else {
270 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000271 }
Erik Andersen161220c2000-03-16 08:12:48 +0000272
273 if (!job) {
Matt Kraaid537a952000-07-14 01:51:25 +0000274 errorMsg("%s: unknown job %d\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000275 cmd->progs[0].argv[0], jobNum);
276 return FALSE;
Erik Andersend75af992000-03-16 08:09:09 +0000277 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000278
Erik Andersen161220c2000-03-16 08:12:48 +0000279 if (*cmd->progs[0].argv[0] == 'f') {
280 /* Make this job the foreground job */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000281 /* suppress messages when run from /linuxrc mag@sysgo.de */
282 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
283 perror("tcsetpgrp");
Erik Andersen161220c2000-03-16 08:12:48 +0000284 jobList->fg = job;
285 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000286
Erik Andersen161220c2000-03-16 08:12:48 +0000287 /* Restart the processes in the job */
288 for (i = 0; i < job->numProgs; i++)
289 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000290
Erik Andersen161220c2000-03-16 08:12:48 +0000291 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000292
Erik Andersen161220c2000-03-16 08:12:48 +0000293 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000294
Erik Andersen161220c2000-03-16 08:12:48 +0000295 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000296}
297
298/* built-in 'help' handler */
Eric Andersenfad04fd2000-07-14 06:49:52 +0000299static int builtin_help(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000300{
Erik Andersen161220c2000-03-16 08:12:48 +0000301 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000302
Erik Andersen161220c2000-03-16 08:12:48 +0000303 fprintf(stdout, "\nBuilt-in commands:\n");
304 fprintf(stdout, "-------------------\n");
305 for (x = bltins; x->cmd; x++) {
Eric Andersenfad9c112000-07-25 18:06:52 +0000306 if (x->descr==NULL)
307 continue;
Erik Andersen161220c2000-03-16 08:12:48 +0000308 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
309 }
Erik Andersen330fd2b2000-05-19 05:35:19 +0000310 for (x = bltins_forking; x->cmd; x++) {
Eric Andersenfad9c112000-07-25 18:06:52 +0000311 if (x->descr==NULL)
312 continue;
Erik Andersen330fd2b2000-05-19 05:35:19 +0000313 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
314 }
Erik Andersen161220c2000-03-16 08:12:48 +0000315 fprintf(stdout, "\n\n");
316 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000317}
318
319/* built-in 'jobs' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000320static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000321{
Erik Andersen161220c2000-03-16 08:12:48 +0000322 struct job *job;
323 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000324
Erik Andersen161220c2000-03-16 08:12:48 +0000325 for (job = jobList->head; job; job = job->next) {
326 if (job->runningProgs == job->stoppedProgs)
327 statusString = "Stopped";
328 else
329 statusString = "Running";
330
331 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
332 }
333 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000334}
335
336
337/* built-in 'pwd' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000338static int builtin_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000339{
Eric Andersen6efc48c2000-07-18 08:16:39 +0000340 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen161220c2000-03-16 08:12:48 +0000341 fprintf(stdout, "%s\n", cwd);
342 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000343}
344
Erik Andersen6273f652000-03-17 01:12:41 +0000345/* built-in 'export VAR=value' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000346static int builtin_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000347{
Erik Andersen161220c2000-03-16 08:12:48 +0000348 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000349
Erik Andersen161220c2000-03-16 08:12:48 +0000350 if (!cmd->progs[0].argv[1] == 1) {
Eric Andersen34e19412000-07-10 18:47:24 +0000351 return (builtin_env(cmd, junk));
Erik Andersen161220c2000-03-16 08:12:48 +0000352 }
353 res = putenv(cmd->progs[0].argv[1]);
354 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000355 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000356 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000357}
358
Eric Andersenb54833c2000-07-03 23:56:26 +0000359/* built-in 'read VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000360static int builtin_read(struct job *cmd, struct jobSet *junk)
Eric Andersenb54833c2000-07-03 23:56:26 +0000361{
362 int res = 0, len, newlen;
363 char *s;
364 char string[MAX_READ];
365
366 if (cmd->progs[0].argv[1]) {
367 /* argument (VAR) given: put "VAR=" into buffer */
368 strcpy(string, cmd->progs[0].argv[1]);
369 len = strlen(string);
370 string[len++] = '=';
371 string[len] = '\0';
372 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
373 newlen = strlen(string);
374 if(newlen > len)
375 string[--newlen] = '\0'; /* chomp trailing newline */
376 /*
377 ** string should now contain "VAR=<value>"
378 ** copy it (putenv() won't do that, so we must make sure
379 ** the string resides in a static buffer!)
380 */
381 res = -1;
382 if((s = strdup(string)))
383 res = putenv(s);
384 if (res)
385 fprintf(stdout, "read: %s\n", strerror(errno));
386 }
387 else
388 fgets(string, sizeof(string), stdin);
389
390 return (res);
391}
392
Eric Andersenfad9c112000-07-25 18:06:52 +0000393#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
394/* Built-in handler for 'if' commands */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000395static int builtin_if(struct job *cmd, struct jobSet *jobList)
Eric Andersenfad9c112000-07-25 18:06:52 +0000396{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000397 int status;
398 char* charptr1=cmd->text+3; /* skip over the leading 'if ' */
399
400 /* Now run the 'if' command */
401 status=strlen(charptr1);
402 local_pending_command = xmalloc(status+1);
403 strncpy(local_pending_command, charptr1, status);
Eric Andersen501c88b2000-07-28 15:14:45 +0000404 local_pending_command[status]='\0';
405#ifdef DEBUG_SHELL
406 fprintf(stderr, "'if' now testing '%s'\n", local_pending_command);
407#endif
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000408 status = busy_loop(NULL); /* Frees local_pending_command */
Eric Andersen501c88b2000-07-28 15:14:45 +0000409#ifdef DEBUG_SHELL
410 fprintf(stderr, "if test returned ");
411#endif
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000412 if (status == 0) {
Eric Andersen501c88b2000-07-28 15:14:45 +0000413#ifdef DEBUG_SHELL
414 fprintf(stderr, "TRUE\n");
415#endif
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000416 cmd->jobContext |= IF_TRUE_CONTEXT;
417 } else {
Eric Andersen501c88b2000-07-28 15:14:45 +0000418#ifdef DEBUG_SHELL
419 fprintf(stderr, "FALSE\n");
420#endif
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000421 cmd->jobContext |= IF_FALSE_CONTEXT;
422 }
423
424 return status;
Eric Andersenfad9c112000-07-25 18:06:52 +0000425}
426
427/* Built-in handler for 'then' (part of the 'if' command) */
428static int builtin_then(struct job *cmd, struct jobSet *junk)
429{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000430 int status;
431 char* charptr1=cmd->text+5; /* skip over the leading 'then ' */
432
433 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
434 errorMsg("unexpected token `then'\n");
Eric Andersenfad9c112000-07-25 18:06:52 +0000435 return FALSE;
436 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000437 /* If the if result was FALSE, skip the 'then' stuff */
Eric Andersen501c88b2000-07-28 15:14:45 +0000438 if (cmd->jobContext & IF_FALSE_CONTEXT) {
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000439 return TRUE;
440 }
441
Eric Andersenfad9c112000-07-25 18:06:52 +0000442 cmd->jobContext |= THEN_EXP_CONTEXT;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000443 //printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
444
445 /* Now run the 'then' command */
446 status=strlen(charptr1);
447 local_pending_command = xmalloc(status+1);
448 strncpy(local_pending_command, charptr1, status);
Eric Andersen501c88b2000-07-28 15:14:45 +0000449 local_pending_command[status]='\0';
450#ifdef DEBUG_SHELL
451 fprintf(stderr, "'then' now running '%s'\n", charptr1);
452#endif
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000453 return( busy_loop(NULL));
Eric Andersenfad9c112000-07-25 18:06:52 +0000454}
455
456/* Built-in handler for 'else' (part of the 'if' command) */
457static int builtin_else(struct job *cmd, struct jobSet *junk)
458{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000459 int status;
460 char* charptr1=cmd->text+5; /* skip over the leading 'else ' */
461
462 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
463 errorMsg("unexpected token `else'\n");
464 return FALSE;
465 }
466 /* If the if result was TRUE, skip the 'else' stuff */
Eric Andersen501c88b2000-07-28 15:14:45 +0000467 if (cmd->jobContext & IF_TRUE_CONTEXT) {
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000468 return TRUE;
469 }
470
Eric Andersenfad9c112000-07-25 18:06:52 +0000471 cmd->jobContext |= ELSE_EXP_CONTEXT;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000472 //printf("Hit an else -- jobContext=%d\n", cmd->jobContext);
473
474 /* Now run the 'else' command */
475 status=strlen(charptr1);
476 local_pending_command = xmalloc(status+1);
477 strncpy(local_pending_command, charptr1, status);
Eric Andersen501c88b2000-07-28 15:14:45 +0000478 local_pending_command[status]='\0';
479#ifdef DEBUG_SHELL
480 fprintf(stderr, "'else' now running '%s'\n", charptr1);
481#endif
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000482 return( busy_loop(NULL));
Eric Andersenfad9c112000-07-25 18:06:52 +0000483}
484
485/* Built-in handler for 'fi' (part of the 'if' command) */
486static int builtin_fi(struct job *cmd, struct jobSet *junk)
487{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000488 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
489 errorMsg("unexpected token `fi'\n");
490 return FALSE;
491 }
492 /* Clear out the if and then context bits */
493 cmd->jobContext &= ~(IF_TRUE_CONTEXT|IF_FALSE_CONTEXT|THEN_EXP_CONTEXT|ELSE_EXP_CONTEXT);
Eric Andersen501c88b2000-07-28 15:14:45 +0000494#ifdef DEBUG_SHELL
495 fprintf(stderr, "Hit an fi -- jobContext=%d\n", cmd->jobContext);
496#endif
Eric Andersenfad9c112000-07-25 18:06:52 +0000497 return TRUE;
498}
499#endif
500
Erik Andersen3522eb12000-03-12 23:49:18 +0000501/* Built-in '.' handler (read-in and execute commands from file) */
Eric Andersen34e19412000-07-10 18:47:24 +0000502static int builtin_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000503{
Erik Andersen161220c2000-03-16 08:12:48 +0000504 FILE *input;
505 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000506
Erik Andersen161220c2000-03-16 08:12:48 +0000507 if (!cmd->progs[0].argv[1] == 1)
508 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000509
Erik Andersen161220c2000-03-16 08:12:48 +0000510 input = fopen(cmd->progs[0].argv[1], "r");
511 if (!input) {
512 fprintf(stdout, "Couldn't open file '%s'\n",
513 cmd->progs[0].argv[1]);
514 return FALSE;
515 }
Erik Andersend75af992000-03-16 08:09:09 +0000516
Erik Andersen161220c2000-03-16 08:12:48 +0000517 /* Now run the file */
518 status = busy_loop(input);
Matt Kraaidd450a02000-09-13 03:43:36 +0000519 fclose(input);
Erik Andersen161220c2000-03-16 08:12:48 +0000520 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000521}
522
523/* built-in 'unset VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000524static int builtin_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000525{
Erik Andersen161220c2000-03-16 08:12:48 +0000526 if (!cmd->progs[0].argv[1] == 1) {
527 fprintf(stdout, "unset: parameter required.\n");
528 return FALSE;
529 }
530 unsetenv(cmd->progs[0].argv[1]);
531 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000532}
533
534/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000535static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000536{
Erik Andersen161220c2000-03-16 08:12:48 +0000537 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000538
Erik Andersen161220c2000-03-16 08:12:48 +0000539 for (i = 0; i < cmd->numProgs; i++) {
540 free(cmd->progs[i].argv);
541 if (cmd->progs[i].redirections)
542 free(cmd->progs[i].redirections);
543 if (cmd->progs[i].freeGlob)
544 globfree(&cmd->progs[i].globResult);
545 }
546 free(cmd->progs);
547 if (cmd->text)
548 free(cmd->text);
549 free(cmd->cmdBuf);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000550 memset(cmd, 0, sizeof(struct job));
Erik Andersen3522eb12000-03-12 23:49:18 +0000551}
552
553/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000554static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000555{
Erik Andersen161220c2000-03-16 08:12:48 +0000556 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000557
Erik Andersen161220c2000-03-16 08:12:48 +0000558 freeJob(job);
559 if (job == jobList->head) {
560 jobList->head = job->next;
561 } else {
562 prevJob = jobList->head;
563 while (prevJob->next != job)
564 prevJob = prevJob->next;
565 prevJob->next = job->next;
566 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000567
Erik Andersen161220c2000-03-16 08:12:48 +0000568 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000569}
570
571/* Checks to see if any background processes have exited -- if they
572 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000573static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000574{
Erik Andersen161220c2000-03-16 08:12:48 +0000575 struct job *job;
576 pid_t childpid;
577 int status;
578 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000579
Erik Andersen161220c2000-03-16 08:12:48 +0000580 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
581 for (job = jobList->head; job; job = job->next) {
582 progNum = 0;
583 while (progNum < job->numProgs &&
584 job->progs[progNum].pid != childpid) progNum++;
585 if (progNum < job->numProgs)
586 break;
587 }
588
Eric Andersena1d187a2000-07-17 19:14:41 +0000589 /* This happens on backticked commands */
590 if(job==NULL)
591 return;
592
Erik Andersen161220c2000-03-16 08:12:48 +0000593 if (WIFEXITED(status) || WIFSIGNALED(status)) {
594 /* child exited */
595 job->runningProgs--;
596 job->progs[progNum].pid = 0;
597
598 if (!job->runningProgs) {
599 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
600 removeJob(jobList, job);
601 }
602 } else {
603 /* child stopped */
604 job->stoppedProgs++;
605 job->progs[progNum].isStopped = 1;
606
607 if (job->stoppedProgs == job->numProgs) {
608 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
609 job->text);
610 }
611 }
Erik Andersend75af992000-03-16 08:09:09 +0000612 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000613
Erik Andersen161220c2000-03-16 08:12:48 +0000614 if (childpid == -1 && errno != ECHILD)
615 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000616}
617
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000618static int setupRedirections(struct childProgram *prog)
619{
620 int i;
621 int openfd;
622 int mode = O_RDONLY;
623 struct redirectionSpecifier *redir = prog->redirections;
624
625 for (i = 0; i < prog->numRedirections; i++, redir++) {
626 switch (redir->type) {
627 case REDIRECT_INPUT:
628 mode = O_RDONLY;
629 break;
630 case REDIRECT_OVERWRITE:
Eric Andersen46f0beb2000-11-14 21:59:22 +0000631 mode = O_WRONLY | O_CREAT | O_TRUNC;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000632 break;
633 case REDIRECT_APPEND:
Eric Andersen46f0beb2000-11-14 21:59:22 +0000634 mode = O_WRONLY | O_CREAT | O_APPEND;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000635 break;
636 }
637
638 openfd = open(redir->filename, mode, 0666);
639 if (openfd < 0) {
640 /* this could get lost if stderr has been redirected, but
641 bash and ash both lose it as well (though zsh doesn't!) */
642 errorMsg("error opening %s: %s\n", redir->filename,
643 strerror(errno));
644 return 1;
645 }
646
647 if (openfd != redir->fd) {
648 dup2(openfd, redir->fd);
649 close(openfd);
650 }
651 }
652
653 return 0;
654}
655
656
Erik Andersend75af992000-03-16 08:09:09 +0000657static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000658{
Eric Andersen1c314ad2000-06-28 16:56:25 +0000659 if (source == NULL) {
660 if (local_pending_command) {
661 /* a command specified (-c option): return it & mark it done */
662 strcpy(command, local_pending_command);
663 free(local_pending_command);
664 local_pending_command = NULL;
665 return 0;
666 }
667 return 1;
668 }
669
Erik Andersen161220c2000-03-16 08:12:48 +0000670 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000671#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000672 int len;
Eric Andersen501c88b2000-07-28 15:14:45 +0000673
674 /*
675 ** enable command line editing only while a command line
676 ** is actually being read; otherwise, we'll end up bequeathing
677 ** atexit() handlers and other unwanted stuff to our
678 ** child processes (rob@sysgo.de)
679 */
680 cmdedit_init();
681 signal(SIGWINCH, win_changed);
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000682 len=fprintf(stdout, "%s %s", cwd, prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000683 fflush(stdout);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000684 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000685 sprintf(promptStr, "%s %s", cwd, prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000686 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000687 free( promptStr);
Eric Andersen501c88b2000-07-28 15:14:45 +0000688 cmdedit_terminate();
689 signal(SIGWINCH, SIG_DFL);
Erik Andersen161220c2000-03-16 08:12:48 +0000690 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000691#else
692 fprintf(stdout, "%s %s", cwd, prompt);
693 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000694#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000695 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000696
Erik Andersen161220c2000-03-16 08:12:48 +0000697 if (!fgets(command, BUFSIZ - 2, source)) {
698 if (source == stdin)
699 printf("\n");
700 return 1;
701 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000702
Erik Andersen161220c2000-03-16 08:12:48 +0000703 /* remove trailing newline */
704 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000705
Erik Andersen161220c2000-03-16 08:12:48 +0000706 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000707}
708
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000709#ifdef BB_FEATURE_SH_ENVIRONMENT
710#define __MAX_INT_CHARS 7
711static char* itoa(register int i)
712{
713 static char a[__MAX_INT_CHARS];
714 register char *b = a + sizeof(a) - 1;
715 int sign = (i < 0);
716
717 if (sign)
718 i = -i;
719 *b = 0;
720 do
721 {
722 *--b = '0' + (i % 10);
723 i /= 10;
724 }
725 while (i);
726 if (sign)
727 *--b = '-';
728 return b;
729}
730#endif
731
Erik Andersend75af992000-03-16 08:09:09 +0000732static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000733 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000734{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000735 int argc_l = *argcPtr;
Erik Andersen161220c2000-03-16 08:12:48 +0000736 int argcAlloced = *argcAllocedPtr;
737 int rc;
738 int flags;
739 int i;
Eric Andersenb54833c2000-07-03 23:56:26 +0000740 char *src, *dst, *var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000741
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000742 if (argc_l > 1) { /* cmd->globResult is already initialized */
Erik Andersen161220c2000-03-16 08:12:48 +0000743 flags = GLOB_APPEND;
744 i = prog->globResult.gl_pathc;
745 } else {
746 prog->freeGlob = 1;
747 flags = 0;
748 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000749 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000750 /* do shell variable substitution */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000751 if(*prog->argv[argc_l - 1] == '$') {
752 if ((var = getenv(prog->argv[argc_l - 1] + 1))) {
753 prog->argv[argc_l - 1] = var;
754 }
755#ifdef BB_FEATURE_SH_ENVIRONMENT
756 else {
757 switch(*(prog->argv[argc_l - 1] + 1)) {
758 case '?':
759 prog->argv[argc_l - 1] = itoa(lastReturnCode);
760 break;
761 case '$':
762 prog->argv[argc_l - 1] = itoa(getpid());
763 break;
764 case '#':
765 prog->argv[argc_l - 1] = itoa(argc-1);
766 break;
767 case '!':
768 if (lastBgPid==-1)
769 *(prog->argv[argc_l - 1])='\0';
770 else
771 prog->argv[argc_l - 1] = itoa(lastBgPid);
772 break;
773 case '0':case '1':case '2':case '3':case '4':
774 case '5':case '6':case '7':case '8':case '9':
775 {
776 int index=*(prog->argv[argc_l - 1] + 1)-48;
777 if (index >= argc) {
778 *(prog->argv[argc_l - 1])='\0';
779 } else {
780 prog->argv[argc_l - 1] = argv[index];
781 }
782 }
783 break;
784 }
785 }
786#endif
787 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000788
Eric Andersen46f0beb2000-11-14 21:59:22 +0000789 if (strpbrk(prog->argv[argc_l - 1],"*[]?")!= NULL){
790 rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
791 if (rc == GLOB_NOSPACE) {
792 errorMsg("out of space during glob operation\n");
793 return;
794 } else if (rc == GLOB_NOMATCH ||
Erik Andersen161220c2000-03-16 08:12:48 +0000795 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000796 strcmp(prog->argv[argc_l - 1],
Eric Andersene92108a2000-07-26 00:53:56 +0000797 prog->globResult.gl_pathv[i]) == 0)) {
Eric Andersen46f0beb2000-11-14 21:59:22 +0000798 /* we need to remove whatever \ quoting is still present */
799 src = dst = prog->argv[argc_l - 1];
800 while (*src) {
Eric Andersen8c2d3f42000-11-30 00:03:57 +0000801 if (*src == '\\') {
802 src++;
803 *dst++ = process_escape_sequence(&src);
804 } else {
Eric Andersen46f0beb2000-11-14 21:59:22 +0000805 *dst++ = *src;
Eric Andersen8c2d3f42000-11-30 00:03:57 +0000806 src++;
807 }
Eric Andersen46f0beb2000-11-14 21:59:22 +0000808 }
809 *dst = '\0';
810 } else if (!rc) {
811 argcAlloced += (prog->globResult.gl_pathc - i);
812 prog->argv = xrealloc(prog->argv, argcAlloced * sizeof(*prog->argv));
813 memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
814 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
815 argc_l += (prog->globResult.gl_pathc - i - 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000816 }
Eric Andersen46f0beb2000-11-14 21:59:22 +0000817 }else{
818 src = dst = prog->argv[argc_l - 1];
819 while (*src) {
Eric Andersen8c2d3f42000-11-30 00:03:57 +0000820 if (*src == '\\') {
821 src++;
822 *dst++ = process_escape_sequence(&src);
823 } else {
Eric Andersen46f0beb2000-11-14 21:59:22 +0000824 *dst++ = *src;
Eric Andersen8c2d3f42000-11-30 00:03:57 +0000825 src++;
826 }
Eric Andersen46f0beb2000-11-14 21:59:22 +0000827 }
828 *dst = '\0';
Eric Andersen8c2d3f42000-11-30 00:03:57 +0000829
Eric Andersen46f0beb2000-11-14 21:59:22 +0000830 prog->globResult.gl_pathc=0;
831 if (flags==0)
832 prog->globResult.gl_pathv=NULL;
Erik Andersen161220c2000-03-16 08:12:48 +0000833 }
Erik Andersen161220c2000-03-16 08:12:48 +0000834 *argcAllocedPtr = argcAlloced;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000835 *argcPtr = argc_l;
Erik Andersen3522eb12000-03-12 23:49:18 +0000836}
837
838/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
839 line). If a valid command is found, commandPtr is set to point to
840 the beginning of the next command (if the original command had more
841 then one job associated with it) or NULL if no more commands are
842 present. */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000843static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000844{
Erik Andersen161220c2000-03-16 08:12:48 +0000845 char *command;
846 char *returnCommand = NULL;
847 char *src, *buf, *chptr;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000848 int argc_l = 0;
Erik Andersen161220c2000-03-16 08:12:48 +0000849 int done = 0;
850 int argvAlloced;
851 int i;
852 char quote = '\0';
853 int count;
854 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000855
Erik Andersen161220c2000-03-16 08:12:48 +0000856 /* skip leading white space */
857 while (**commandPtr && isspace(**commandPtr))
858 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000859
Erik Andersen161220c2000-03-16 08:12:48 +0000860 /* this handles empty lines or leading '#' characters */
861 if (!**commandPtr || (**commandPtr == '#')) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000862 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000863 return 0;
864 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000865
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000866 *inBg = 0;
Erik Andersen161220c2000-03-16 08:12:48 +0000867 job->numProgs = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000868 job->progs = xmalloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000869
Erik Andersen161220c2000-03-16 08:12:48 +0000870 /* We set the argv elements to point inside of this string. The
Eric Andersenb54833c2000-07-03 23:56:26 +0000871 memory is freed by freeJob(). Allocate twice the original
872 length in case we need to quote every single character.
Erik Andersen3522eb12000-03-12 23:49:18 +0000873
Erik Andersen161220c2000-03-16 08:12:48 +0000874 Getting clean memory relieves us of the task of NULL
875 terminating things and makes the rest of this look a bit
876 cleaner (though it is, admittedly, a tad less efficient) */
Matt Kraaib8907522000-09-13 02:08:21 +0000877 job->cmdBuf = command = xcalloc(2*strlen(*commandPtr) + 1, sizeof(char));
Erik Andersen161220c2000-03-16 08:12:48 +0000878 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000879
Erik Andersen161220c2000-03-16 08:12:48 +0000880 prog = job->progs;
881 prog->numRedirections = 0;
882 prog->redirections = NULL;
883 prog->freeGlob = 0;
884 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000885
Erik Andersen161220c2000-03-16 08:12:48 +0000886 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000887 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000888 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000889
Erik Andersen161220c2000-03-16 08:12:48 +0000890 buf = command;
891 src = *commandPtr;
892 while (*src && !done) {
893 if (quote == *src) {
894 quote = '\0';
895 } else if (quote) {
896 if (*src == '\\') {
897 src++;
898 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000899 errorMsg("character expected after \\\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000900 freeJob(job);
901 return 1;
902 }
903
904 /* in shell, "\'" should yield \' */
905 if (*src != quote)
906 *buf++ = '\\';
907 } else if (*src == '*' || *src == '?' || *src == '[' ||
908 *src == ']') *buf++ = '\\';
909 *buf++ = *src;
910 } else if (isspace(*src)) {
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000911 if (*prog->argv[argc_l]) {
912 buf++, argc_l++;
Erik Andersen161220c2000-03-16 08:12:48 +0000913 /* +1 here leaves room for the NULL which ends argv */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000914 if ((argc_l + 1) == argvAlloced) {
Erik Andersen161220c2000-03-16 08:12:48 +0000915 argvAlloced += 5;
Matt Kraaib8907522000-09-13 02:08:21 +0000916 prog->argv = xrealloc(prog->argv,
917 sizeof(*prog->argv) *
918 argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000919 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000920 globLastArgument(prog, &argc_l, &argvAlloced);
921 prog->argv[argc_l] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000922 }
923 } else
924 switch (*src) {
925 case '"':
926 case '\'':
927 quote = *src;
928 break;
929
930 case '#': /* comment */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000931 if (*(src-1)== '$')
932 *buf++ = *src;
933 else
934 done = 1;
Erik Andersen161220c2000-03-16 08:12:48 +0000935 break;
936
937 case '>': /* redirections */
938 case '<':
939 i = prog->numRedirections++;
Matt Kraaib8907522000-09-13 02:08:21 +0000940 prog->redirections = xrealloc(prog->redirections,
941 sizeof(*prog->redirections) *
942 (i + 1));
Erik Andersen161220c2000-03-16 08:12:48 +0000943
944 prog->redirections[i].fd = -1;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000945 if (buf != prog->argv[argc_l]) {
Erik Andersen161220c2000-03-16 08:12:48 +0000946 /* the stuff before this character may be the file number
947 being redirected */
948 prog->redirections[i].fd =
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000949 strtol(prog->argv[argc_l], &chptr, 10);
Erik Andersen161220c2000-03-16 08:12:48 +0000950
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000951 if (*chptr && *prog->argv[argc_l]) {
952 buf++, argc_l++;
953 globLastArgument(prog, &argc_l, &argvAlloced);
954 prog->argv[argc_l] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000955 }
956 }
957
958 if (prog->redirections[i].fd == -1) {
959 if (*src == '>')
960 prog->redirections[i].fd = 1;
961 else
962 prog->redirections[i].fd = 0;
963 }
964
965 if (*src++ == '>') {
966 if (*src == '>')
967 prog->redirections[i].type =
968 REDIRECT_APPEND, src++;
969 else
970 prog->redirections[i].type = REDIRECT_OVERWRITE;
971 } else {
972 prog->redirections[i].type = REDIRECT_INPUT;
973 }
974
975 /* This isn't POSIX sh compliant. Oh well. */
976 chptr = src;
977 while (isspace(*chptr))
978 chptr++;
979
980 if (!*chptr) {
Matt Kraaid537a952000-07-14 01:51:25 +0000981 errorMsg("file name expected after %c\n", *src);
Erik Andersen161220c2000-03-16 08:12:48 +0000982 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000983 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000984 return 1;
985 }
986
987 prog->redirections[i].filename = buf;
988 while (*chptr && !isspace(*chptr))
989 *buf++ = *chptr++;
990
991 src = chptr - 1; /* we src++ later */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000992 prog->argv[argc_l] = ++buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000993 break;
994
995 case '|': /* pipe */
996 /* finish this command */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000997 if (*prog->argv[argc_l])
998 argc_l++;
999 if (!argc_l) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001000 errorMsg("empty command in pipe\n");
Erik Andersen161220c2000-03-16 08:12:48 +00001001 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +00001002 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +00001003 return 1;
1004 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001005 prog->argv[argc_l] = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001006
1007 /* and start the next */
1008 job->numProgs++;
Matt Kraaib8907522000-09-13 02:08:21 +00001009 job->progs = xrealloc(job->progs,
1010 sizeof(*job->progs) * job->numProgs);
Erik Andersen161220c2000-03-16 08:12:48 +00001011 prog = job->progs + (job->numProgs - 1);
1012 prog->numRedirections = 0;
1013 prog->redirections = NULL;
1014 prog->freeGlob = 0;
Eric Andersen501c88b2000-07-28 15:14:45 +00001015 prog->isStopped = 0;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001016 argc_l = 0;
Erik Andersen161220c2000-03-16 08:12:48 +00001017
1018 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001019 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +00001020 prog->argv[0] = ++buf;
1021
1022 src++;
1023 while (*src && isspace(*src))
1024 src++;
1025
1026 if (!*src) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001027 errorMsg("empty command in pipe\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +00001028 freeJob(job);
1029 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +00001030 return 1;
1031 }
1032 src--; /* we'll ++ it at the end of the loop */
1033
1034 break;
1035
1036 case '&': /* background */
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001037 *inBg = 1;
Erik Andersen161220c2000-03-16 08:12:48 +00001038 case ';': /* multiple commands */
1039 done = 1;
1040 returnCommand = *commandPtr + (src - *commandPtr) + 1;
1041 break;
1042
Eric Andersena1d187a2000-07-17 19:14:41 +00001043#ifdef BB_FEATURE_SH_BACKTICKS
Eric Andersenec10b9d2000-07-14 01:13:11 +00001044 case '`':
1045 /* Exec a backtick-ed command */
1046 {
Eric Andersena1d187a2000-07-17 19:14:41 +00001047 char* charptr1=NULL, *charptr2;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001048 char* ptr=NULL;
Eric Andersena1d187a2000-07-17 19:14:41 +00001049 struct job *newJob;
1050 struct jobSet njobList = { NULL, NULL };
1051 int pipefd[2];
1052 int size;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001053
1054 ptr=strchr(++src, '`');
1055 if (ptr==NULL) {
1056 fprintf(stderr, "Unmatched '`' in command\n");
1057 freeJob(job);
1058 return 1;
1059 }
1060
Eric Andersena1d187a2000-07-17 19:14:41 +00001061 /* Make some space to hold just the backticked command */
Eric Andersen6efc48c2000-07-18 08:16:39 +00001062 charptr1 = charptr2 = xmalloc(1+ptr-src);
Matt Kraai0b2da462000-09-19 06:46:44 +00001063 memcpy(charptr1, src, ptr-src);
1064 charptr1[ptr-src] = '\0';
Eric Andersena1d187a2000-07-17 19:14:41 +00001065 newJob = xmalloc(sizeof(struct job));
1066 /* Now parse and run the backticked command */
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001067 if (!parseCommand(&charptr1, newJob, &njobList, inBg)
Eric Andersena1d187a2000-07-17 19:14:41 +00001068 && newJob->numProgs) {
1069 pipe(pipefd);
1070 runCommand(newJob, &njobList, 0, pipefd);
Eric Andersenec10b9d2000-07-14 01:13:11 +00001071 }
Eric Andersena1d187a2000-07-17 19:14:41 +00001072 checkJobs(jobList);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001073 freeJob(newJob);
1074 free(charptr2);
1075
1076 /* Make a copy of any stuff left over in the command
1077 * line after the second backtick */
1078 charptr2 = xmalloc(strlen(ptr)+1);
1079 memcpy(charptr2, ptr+1, strlen(ptr));
1080
Eric Andersenec10b9d2000-07-14 01:13:11 +00001081
Eric Andersena1d187a2000-07-17 19:14:41 +00001082 /* Copy the output from the backtick-ed command into the
1083 * command line, making extra room as needed */
1084 --src;
1085 charptr1 = xmalloc(BUFSIZ);
1086 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
1087 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1088 if (newSize > BUFSIZ) {
Matt Kraaib8907522000-09-13 02:08:21 +00001089 *commandPtr=xrealloc(*commandPtr, src - *commandPtr +
Eric Andersena1d187a2000-07-17 19:14:41 +00001090 size + 1 + strlen(charptr2));
1091 }
1092 memcpy(src, charptr1, size);
1093 src+=size;
1094 }
1095 free(charptr1);
1096 close(pipefd[0]);
1097 if (*(src-1)=='\n')
1098 --src;
1099
1100 /* Now paste into the *commandPtr all the stuff
1101 * leftover after the second backtick */
Matt Kraaicbbe4d62000-09-14 00:26:50 +00001102 memcpy(src, charptr2, strlen(charptr2)+1);
Eric Andersena1d187a2000-07-17 19:14:41 +00001103 free(charptr2);
1104
Eric Andersena1d187a2000-07-17 19:14:41 +00001105 /* Now recursively call parseCommand to deal with the new
1106 * and improved version of the command line with the backtick
1107 * results expanded in place... */
Eric Andersen6efc48c2000-07-18 08:16:39 +00001108 freeJob(job);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001109 return(parseCommand(commandPtr, job, jobList, inBg));
Eric Andersenec10b9d2000-07-14 01:13:11 +00001110 }
1111 break;
Eric Andersena1d187a2000-07-17 19:14:41 +00001112#endif // BB_FEATURE_SH_BACKTICKS
Matt Kraai131241f2000-09-14 00:43:20 +00001113
1114 case '\\':
1115 src++;
1116 if (!*src) {
1117 errorMsg("character expected after \\\n");
1118 freeJob(job);
1119 return 1;
1120 }
1121 if (*src == '*' || *src == '[' || *src == ']'
1122 || *src == '?') *buf++ = '\\';
1123 /* fallthrough */
Erik Andersen161220c2000-03-16 08:12:48 +00001124 default:
1125 *buf++ = *src;
1126 }
1127
Erik Andersend75af992000-03-16 08:09:09 +00001128 src++;
Erik Andersen161220c2000-03-16 08:12:48 +00001129 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001130
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001131 if (*prog->argv[argc_l]) {
1132 argc_l++;
1133 globLastArgument(prog, &argc_l, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +00001134 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001135 if (!argc_l) {
Erik Andersen161220c2000-03-16 08:12:48 +00001136 freeJob(job);
1137 return 0;
1138 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001139 prog->argv[argc_l] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +00001140
Erik Andersen161220c2000-03-16 08:12:48 +00001141 if (!returnCommand) {
Eric Andersenec10b9d2000-07-14 01:13:11 +00001142 job->text = xmalloc(strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +00001143 strcpy(job->text, *commandPtr);
1144 } else {
1145 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +00001146 count = returnCommand - *commandPtr;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001147 job->text = xmalloc(count + 1);
Erik Andersen161220c2000-03-16 08:12:48 +00001148 strncpy(job->text, *commandPtr, count);
1149 job->text[count] = '\0';
1150 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001151
Erik Andersen161220c2000-03-16 08:12:48 +00001152 *commandPtr = returnCommand;
Eric Andersen46f0beb2000-11-14 21:59:22 +00001153
Erik Andersend75af992000-03-16 08:09:09 +00001154 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001155}
1156
Eric Andersena1d187a2000-07-17 19:14:41 +00001157static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
Erik Andersen3522eb12000-03-12 23:49:18 +00001158{
Eric Andersenfad9c112000-07-25 18:06:52 +00001159 struct job *theJob;
Erik Andersen161220c2000-03-16 08:12:48 +00001160 int i;
Eric Andersen6efc48c2000-07-18 08:16:39 +00001161 int nextin, nextout;
Erik Andersen161220c2000-03-16 08:12:48 +00001162 int pipefds[2]; /* pipefd[0] is for reading */
1163 struct builtInCommand *x;
Eric Andersenb54833c2000-07-03 23:56:26 +00001164#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Eric Andersena683ee82000-11-17 18:51:45 +00001165 struct BB_applet search_applet, *applet;
Erik Andersenbcd61772000-05-13 06:33:19 +00001166#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001167
Eric Andersen6efc48c2000-07-18 08:16:39 +00001168 nextin = 0, nextout = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001169 for (i = 0; i < newJob->numProgs; i++) {
1170 if ((i + 1) < newJob->numProgs) {
Erik Andersen161220c2000-03-16 08:12:48 +00001171 pipe(pipefds);
1172 nextout = pipefds[1];
1173 } else {
Eric Andersen6efc48c2000-07-18 08:16:39 +00001174 if (outPipe[1]!=-1) {
1175 nextout = outPipe[1];
1176 } else {
1177 nextout = 1;
1178 }
Erik Andersen161220c2000-03-16 08:12:48 +00001179 }
1180
Eric Andersen501c88b2000-07-28 15:14:45 +00001181#ifdef BB_FEATURE_SH_ENVIRONMENT
1182 if (showXtrace==TRUE) {
1183 int j;
1184 fprintf(stderr, "+ ");
1185 for (j = 0; newJob->progs[i].argv[j]; j++)
1186 fprintf(stderr, "%s ", newJob->progs[i].argv[j]);
1187 fprintf(stderr, "\n");
1188 }
1189#endif
1190
Eric Andersen34e19412000-07-10 18:47:24 +00001191 /* Check if the command matches any non-forking builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +00001192 for (x = bltins; x->cmd; x++) {
Eric Andersene92108a2000-07-26 00:53:56 +00001193 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001194 return(x->function(newJob, jobList));
Erik Andersen330fd2b2000-05-19 05:35:19 +00001195 }
1196 }
1197
Eric Andersenec10b9d2000-07-14 01:13:11 +00001198 if (!(newJob->progs[i].pid = fork())) {
Erik Andersen161220c2000-03-16 08:12:48 +00001199 signal(SIGTTOU, SIG_DFL);
1200
Eric Andersena1d187a2000-07-17 19:14:41 +00001201 if (outPipe[1]!=-1) {
1202 close(outPipe[0]);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001203 }
1204 if (nextin != 0) {
1205 dup2(nextin, 0);
1206 close(nextin);
1207 }
1208
1209 if (nextout != 1) {
Erik Andersen161220c2000-03-16 08:12:48 +00001210 dup2(nextout, 1);
Eric Andersena1d187a2000-07-17 19:14:41 +00001211 dup2(nextout, 2);
Erik Andersen161220c2000-03-16 08:12:48 +00001212 close(nextout);
Eric Andersen501c88b2000-07-28 15:14:45 +00001213 close(pipefds[0]);
Erik Andersen161220c2000-03-16 08:12:48 +00001214 }
1215
1216 /* explicit redirections override pipes */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001217 setupRedirections(newJob->progs + i);
Erik Andersen161220c2000-03-16 08:12:48 +00001218
Eric Andersen34e19412000-07-10 18:47:24 +00001219 /* Check if the command matches any of the other builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +00001220 for (x = bltins_forking; x->cmd; x++) {
Eric Andersene92108a2000-07-26 00:53:56 +00001221 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001222 applet_name=x->cmd;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001223 exit (x->function(newJob, jobList));
Erik Andersenbcd61772000-05-13 06:33:19 +00001224 }
1225 }
Eric Andersenb54833c2000-07-03 23:56:26 +00001226#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Eric Andersene5aef922000-11-17 18:25:26 +00001227 /* Check if the command matches any busybox internal
1228 * commands ("applets") here. Following discussions from
1229 * November 2000 on busybox@opensource.lineo.com, don't use
1230 * get_last_path_component(). This way explicit (with
1231 * slashes) filenames will never be interpreted as an
1232 * applet, just like with builtins. This way the user can
1233 * override an applet with an explicit filename reference.
1234 * The only downside to this change is that an explicit
1235 * /bin/foo invocation fill fork and exec /bin/foo, even if
1236 * /bin/foo is a symlink to busybox.
1237 */
1238 search_applet.name = newJob->progs[i].argv[0];
1239
Eric Andersen50b31132000-11-17 18:07:30 +00001240#ifdef BB_FEATURE_SH_BUILTINS_ALWAYS_WIN
Eric Andersene5aef922000-11-17 18:25:26 +00001241 /* If you enable BB_FEATURE_SH_BUILTINS_ALWAYS_WIN, then
1242 * if you run /bin/cat, it will use BusyBox cat even if
1243 * /bin/cat exists on the filesystem and is _not_ busybox.
1244 * Some systems want this, others do not. Choose wisely. :-)
1245 */
1246 search_applet.name = get_last_path_component(search_applet.name);
Eric Andersen50b31132000-11-17 18:07:30 +00001247#endif
Eric Andersene5aef922000-11-17 18:25:26 +00001248
1249 /* Do a binary search to find the applet entry given the name. */
1250 applet = bsearch(&search_applet, applets, NUM_APPLETS,
1251 sizeof(struct BB_applet), applet_name_compare);
1252 if (applet != NULL) {
1253 int argc_l;
1254 char** argv=newJob->progs[i].argv;
1255 for(argc_l=0;*argv!=NULL; argv++, argc_l++);
1256 applet_name=applet->name;
1257 optind = 1;
1258 exit((*(applet->main)) (argc_l, newJob->progs[i].argv));
Erik Andersenbcd61772000-05-13 06:33:19 +00001259 }
1260#endif
1261
Eric Andersenec10b9d2000-07-14 01:13:11 +00001262 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001263 fatalError("%s: %s\n", newJob->progs[i].argv[0],
Eric Andersene5aef922000-11-17 18:25:26 +00001264 strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +00001265 }
Eric Andersen6efc48c2000-07-18 08:16:39 +00001266 if (outPipe[1]!=-1) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001267 close(outPipe[1]);
1268 }
Erik Andersen161220c2000-03-16 08:12:48 +00001269
1270 /* put our child in the process group whose leader is the
1271 first process in this pipe */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001272 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
Erik Andersen161220c2000-03-16 08:12:48 +00001273 if (nextin != 0)
1274 close(nextin);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001275 if (nextout != 1)
Erik Andersen161220c2000-03-16 08:12:48 +00001276 close(nextout);
1277
1278 /* If there isn't another process, nextin is garbage
1279 but it doesn't matter */
1280 nextin = pipefds[0];
1281 }
1282
Eric Andersenec10b9d2000-07-14 01:13:11 +00001283 newJob->pgrp = newJob->progs[0].pid;
Erik Andersen161220c2000-03-16 08:12:48 +00001284
Eric Andersenfad9c112000-07-25 18:06:52 +00001285 /* find the ID for the theJob to use */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001286 newJob->jobId = 1;
Eric Andersenfad9c112000-07-25 18:06:52 +00001287 for (theJob = jobList->head; theJob; theJob = theJob->next)
1288 if (theJob->jobId >= newJob->jobId)
1289 newJob->jobId = theJob->jobId + 1;
Erik Andersen161220c2000-03-16 08:12:48 +00001290
Eric Andersenfad9c112000-07-25 18:06:52 +00001291 /* add the theJob to the list of running jobs */
Erik Andersen161220c2000-03-16 08:12:48 +00001292 if (!jobList->head) {
Matt Kraaib8907522000-09-13 02:08:21 +00001293 theJob = jobList->head = xmalloc(sizeof(*theJob));
Erik Andersend75af992000-03-16 08:09:09 +00001294 } else {
Eric Andersenfad9c112000-07-25 18:06:52 +00001295 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
Matt Kraaib8907522000-09-13 02:08:21 +00001296 theJob->next = xmalloc(sizeof(*theJob));
Eric Andersenfad9c112000-07-25 18:06:52 +00001297 theJob = theJob->next;
Erik Andersend75af992000-03-16 08:09:09 +00001298 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001299
Eric Andersenfad9c112000-07-25 18:06:52 +00001300 *theJob = *newJob;
1301 theJob->next = NULL;
1302 theJob->runningProgs = theJob->numProgs;
1303 theJob->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001304
Erik Andersen161220c2000-03-16 08:12:48 +00001305 if (inBg) {
Eric Andersenfad9c112000-07-25 18:06:52 +00001306 /* we don't wait for background theJobs to return -- append it
1307 to the list of backgrounded theJobs and leave it alone */
1308 printf("[%d] %d\n", theJob->jobId,
Eric Andersenec10b9d2000-07-14 01:13:11 +00001309 newJob->progs[newJob->numProgs - 1].pid);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001310#ifdef BB_FEATURE_SH_ENVIRONMENT
1311 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1312#endif
Erik Andersen161220c2000-03-16 08:12:48 +00001313 } else {
Eric Andersenfad9c112000-07-25 18:06:52 +00001314 jobList->fg = theJob;
Erik Andersen3522eb12000-03-12 23:49:18 +00001315
Erik Andersen161220c2000-03-16 08:12:48 +00001316 /* move the new process group into the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001317 /* suppress messages when run from /linuxrc mag@sysgo.de */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001318 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
Erik Andersen161220c2000-03-16 08:12:48 +00001319 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001320 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001321
Erik Andersen161220c2000-03-16 08:12:48 +00001322 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001323}
1324
Erik Andersend75af992000-03-16 08:09:09 +00001325static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +00001326{
Erik Andersen161220c2000-03-16 08:12:48 +00001327 char *command;
1328 char *nextCommand = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001329 struct job newJob;
Eric Andersen1c314ad2000-06-28 16:56:25 +00001330 pid_t parent_pgrp;
Erik Andersen161220c2000-03-16 08:12:48 +00001331 int i;
Erik Andersen161220c2000-03-16 08:12:48 +00001332 int inBg;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001333 int status;
1334 newJob.jobContext = REGULAR_JOB_CONTEXT;
Erik Andersen3522eb12000-03-12 23:49:18 +00001335
Eric Andersen1c314ad2000-06-28 16:56:25 +00001336 /* save current owner of TTY so we can restore it on exit */
1337 parent_pgrp = tcgetpgrp(0);
1338
Matt Kraaib8907522000-09-13 02:08:21 +00001339 command = (char *) xcalloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +00001340
Erik Andersen161220c2000-03-16 08:12:48 +00001341 /* don't pay any attention to this signal; it just confuses
1342 things and isn't really meant for shells anyway */
1343 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +00001344
Erik Andersen161220c2000-03-16 08:12:48 +00001345 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +00001346 if (!jobList.fg) {
1347 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +00001348
Erik Andersend75af992000-03-16 08:09:09 +00001349 /* see if any background processes have exited */
1350 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +00001351
Erik Andersend75af992000-03-16 08:09:09 +00001352 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +00001353 if (getCommand(input, command))
1354 break;
1355 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +00001356 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001357
Eric Andersenec10b9d2000-07-14 01:13:11 +00001358 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +00001359 newJob.numProgs) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001360 int pipefds[2] = {-1,-1};
1361 runCommand(&newJob, &jobList, inBg, pipefds);
Eric Andersenfad9c112000-07-25 18:06:52 +00001362 }
1363 else {
Eric Andersena1d187a2000-07-17 19:14:41 +00001364 free(command);
Matt Kraaib8907522000-09-13 02:08:21 +00001365 command = (char *) xcalloc(BUFSIZ, sizeof(char));
Eric Andersen6efc48c2000-07-18 08:16:39 +00001366 nextCommand = NULL;
Erik Andersend75af992000-03-16 08:09:09 +00001367 }
1368 } else {
1369 /* a job is running in the foreground; wait for it */
1370 i = 0;
1371 while (!jobList.fg->progs[i].pid ||
Eric Andersenfad9c112000-07-25 18:06:52 +00001372 jobList.fg->progs[i].isStopped == 1) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +00001373
Erik Andersend75af992000-03-16 08:09:09 +00001374 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +00001375
Erik Andersend75af992000-03-16 08:09:09 +00001376 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +00001377 /* the child exited */
1378 jobList.fg->runningProgs--;
1379 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001380
Eric Andersen501c88b2000-07-28 15:14:45 +00001381#ifdef BB_FEATURE_SH_ENVIRONMENT
1382 lastReturnCode=WEXITSTATUS(status);
1383#endif
1384#if 0
1385 printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode);
1386#endif
Erik Andersen161220c2000-03-16 08:12:48 +00001387 if (!jobList.fg->runningProgs) {
1388 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +00001389
Erik Andersen161220c2000-03-16 08:12:48 +00001390 removeJob(&jobList, jobList.fg);
1391 jobList.fg = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001392 }
Erik Andersend75af992000-03-16 08:09:09 +00001393 } else {
Erik Andersen161220c2000-03-16 08:12:48 +00001394 /* the child was stopped */
1395 jobList.fg->stoppedProgs++;
1396 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +00001397
Erik Andersen161220c2000-03-16 08:12:48 +00001398 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1399 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1400 "Stopped", jobList.fg->text);
1401 jobList.fg = NULL;
1402 }
Erik Andersend75af992000-03-16 08:09:09 +00001403 }
1404
1405 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +00001406 /* move the shell to the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001407 /* suppress messages when run from /linuxrc mag@sysgo.de */
1408 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1409 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001410 }
1411 }
1412 }
Erik Andersen161220c2000-03-16 08:12:48 +00001413 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +00001414
Eric Andersen1c314ad2000-06-28 16:56:25 +00001415 /* return controlling TTY back to parent process group before exiting */
1416 if (tcsetpgrp(0, parent_pgrp))
Eric Andersenb54833c2000-07-03 23:56:26 +00001417 perror("tcsetpgrp");
1418
1419 /* return exit status if called with "-c" */
1420 if (input == NULL && WIFEXITED(status))
1421 return WEXITSTATUS(status);
1422
Erik Andersen161220c2000-03-16 08:12:48 +00001423 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001424}
1425
1426
Eric Andersenfad9c112000-07-25 18:06:52 +00001427#ifdef BB_FEATURE_CLEAN_UP
1428void free_memory(void)
1429{
1430 if (promptStr)
1431 free(promptStr);
1432 if (cwd)
1433 free(cwd);
1434 if (local_pending_command)
1435 free(local_pending_command);
1436
1437 if (jobList.fg && !jobList.fg->runningProgs) {
1438 removeJob(&jobList, jobList.fg);
1439 }
1440}
1441#endif
1442
Eric Andersen6efc48c2000-07-18 08:16:39 +00001443
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001444int shell_main(int argc_l, char **argv_l)
Erik Andersen3522eb12000-03-12 23:49:18 +00001445{
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001446 int opt, interactive=FALSE;
Erik Andersen161220c2000-03-16 08:12:48 +00001447 FILE *input = stdin;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001448 argc = argc_l;
1449 argv = argv_l;
Erik Andersen3522eb12000-03-12 23:49:18 +00001450
Eric Andersen501c88b2000-07-28 15:14:45 +00001451
1452 //if (argv[0] && argv[0][0] == '-') {
1453 // builtin_source("/etc/profile");
1454 //}
1455
Matt Kraai6085c722000-09-06 01:46:18 +00001456 while ((opt = getopt(argc_l, argv_l, "cx")) > 0) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001457 switch (opt) {
1458 case 'c':
1459 input = NULL;
Matt Kraai6085c722000-09-06 01:46:18 +00001460 if (local_pending_command != 0)
1461 fatalError("multiple -c arguments\n");
1462 local_pending_command = xstrdup(argv[optind]);
1463 optind++;
1464 argv = argv+optind;
Eric Andersen501c88b2000-07-28 15:14:45 +00001465 break;
Eric Andersen1428c4f2000-07-28 15:19:30 +00001466#ifdef BB_FEATURE_SH_ENVIRONMENT
Eric Andersen501c88b2000-07-28 15:14:45 +00001467 case 'x':
1468 showXtrace = TRUE;
1469 break;
Eric Andersen1428c4f2000-07-28 15:19:30 +00001470#endif
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001471 case 'i':
1472 interactive = TRUE;
1473 break;
Eric Andersen501c88b2000-07-28 15:14:45 +00001474 default:
1475 usage(shell_usage);
1476 }
1477 }
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001478 /* A shell is interactive if the `-i' flag was given, or if all of
1479 * the following conditions are met:
1480 * no -c command
1481 * no arguments remaining or the -s flag given
1482 * standard input is a terminal
1483 * standard output is a terminal
1484 * Refer to Posix.2, the description of the `sh' utility. */
1485 if (interactive==TRUE || ( argv[optind]==NULL && input==stdin && isatty(fileno(stdin)) && isatty(fileno(stdout)))) {
Eric Andersen851ce892000-08-21 22:34:23 +00001486 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
Eric Andersen501c88b2000-07-28 15:14:45 +00001487 /* Looks like they want an interactive shell */
1488 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1489 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001490 } else if (local_pending_command==NULL) {
Eric Andersen851ce892000-08-21 22:34:23 +00001491 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
Matt Kraaibbaef662000-09-27 02:43:35 +00001492 input = xfopen(argv[optind], "r");
Eric Andersen501c88b2000-07-28 15:14:45 +00001493 }
1494
Eric Andersen6efc48c2000-07-18 08:16:39 +00001495 /* initialize the cwd -- this is never freed...*/
1496 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1497 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen3522eb12000-03-12 23:49:18 +00001498
Eric Andersenfad9c112000-07-25 18:06:52 +00001499#ifdef BB_FEATURE_CLEAN_UP
1500 atexit(free_memory);
1501#endif
1502
Erik Andersenf0657d32000-04-12 17:49:52 +00001503#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenf0657d32000-04-12 17:49:52 +00001504 win_changed(0);
1505#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001506
Erik Andersen161220c2000-03-16 08:12:48 +00001507 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +00001508}