blob: b2d1d43d31e72c67f0739a7a89ea8a5a155feaff [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
29#define BB_FEATURE_SH_BACKTICKS
Eric Andersenfad9c112000-07-25 18:06:52 +000030//#define BB_FEATURE_SH_IF_EXPRESSIONS
Eric Andersena1d187a2000-07-17 19:14:41 +000031
32
33
Erik Andersen3522eb12000-03-12 23:49:18 +000034#include "internal.h"
35#include <stdio.h>
36#include <stdlib.h>
37#include <ctype.h>
38#include <errno.h>
39#include <fcntl.h>
40#include <glob.h>
41#include <signal.h>
42#include <string.h>
43#include <sys/ioctl.h>
44#include <sys/wait.h>
45#include <unistd.h>
Erik Andersenf0657d32000-04-12 17:49:52 +000046#ifdef BB_FEATURE_SH_COMMAND_EDITING
47#include "cmdedit.h"
48#endif
Erik Andersen3522eb12000-03-12 23:49:18 +000049
Eric Andersen6efc48c2000-07-18 08:16:39 +000050#define MAX_LINE 256 /* size of input buffer for `read' builtin */
Eric Andersenb54833c2000-07-03 23:56:26 +000051#define MAX_READ 128 /* size of input buffer for `read' builtin */
Erik Andersen3522eb12000-03-12 23:49:18 +000052#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
53
Erik Andersend75af992000-03-16 08:09:09 +000054
55enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
Erik Andersen161220c2000-03-16 08:12:48 +000056 REDIRECT_APPEND
57};
Erik Andersen3522eb12000-03-12 23:49:18 +000058
Eric Andersenfad9c112000-07-25 18:06:52 +000059#define REGULAR_JOB_CONTEXT 0x1
60#define IF_EXP_CONTEXT 0x2
61#define THEN_EXP_CONTEXT 0x4
62#define ELSE_EXP_CONTEXT 0x8
63
64enum jobContext { REGULAR_APP, IF_CONTEXT, THEN_CONTEXT
65};
66
Erik Andersen3522eb12000-03-12 23:49:18 +000067struct jobSet {
Erik Andersen161220c2000-03-16 08:12:48 +000068 struct job *head; /* head of list of running jobs */
69 struct job *fg; /* current foreground job */
Erik Andersen3522eb12000-03-12 23:49:18 +000070};
71
72struct redirectionSpecifier {
Erik Andersen161220c2000-03-16 08:12:48 +000073 enum redirectionType type; /* type of redirection */
74 int fd; /* file descriptor being redirected */
75 char *filename; /* file to redirect fd to */
Erik Andersen3522eb12000-03-12 23:49:18 +000076};
77
78struct childProgram {
Erik Andersen161220c2000-03-16 08:12:48 +000079 pid_t pid; /* 0 if exited */
80 char **argv; /* program name and arguments */
81 int numRedirections; /* elements in redirection array */
82 struct redirectionSpecifier *redirections; /* I/O redirections */
83 glob_t globResult; /* result of parameter globbing */
84 int freeGlob; /* should we globfree(&globResult)? */
85 int isStopped; /* is the program currently running? */
Erik Andersen3522eb12000-03-12 23:49:18 +000086};
87
88struct job {
Erik Andersen161220c2000-03-16 08:12:48 +000089 int jobId; /* job number */
90 int numProgs; /* total number of programs in job */
91 int runningProgs; /* number of programs running */
92 char *text; /* name of job */
93 char *cmdBuf; /* buffer various argv's point into */
94 pid_t pgrp; /* process group ID for the job */
95 struct childProgram *progs; /* array of programs in job */
96 struct job *next; /* to track background commands */
97 int stoppedProgs; /* number of programs alive, but stopped */
Eric Andersenfad9c112000-07-25 18:06:52 +000098 int jobContext; /* bitmask defining current context */
Erik Andersen3522eb12000-03-12 23:49:18 +000099};
100
101struct builtInCommand {
Erik Andersen161220c2000-03-16 08:12:48 +0000102 char *cmd; /* name */
103 char *descr; /* description */
Erik Andersen161220c2000-03-16 08:12:48 +0000104 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
Erik Andersen3522eb12000-03-12 23:49:18 +0000105};
106
Eric Andersen34e19412000-07-10 18:47:24 +0000107/* function prototypes for builtins */
108static int builtin_cd(struct job *cmd, struct jobSet *junk);
109static int builtin_env(struct job *dummy, struct jobSet *junk);
110static int builtin_exit(struct job *cmd, struct jobSet *junk);
111static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
112static int builtin_help(struct job *cmd, struct jobSet *junk);
113static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
114static int builtin_pwd(struct job *dummy, struct jobSet *junk);
115static int builtin_export(struct job *cmd, struct jobSet *junk);
116static int builtin_source(struct job *cmd, struct jobSet *jobList);
117static int builtin_unset(struct job *cmd, struct jobSet *junk);
118static int builtin_read(struct job *cmd, struct jobSet *junk);
Eric Andersenfad9c112000-07-25 18:06:52 +0000119#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
120static int builtin_if(struct job *cmd, struct jobSet *junk);
121static int builtin_then(struct job *cmd, struct jobSet *junk);
122static int builtin_else(struct job *cmd, struct jobSet *junk);
123static int builtin_fi(struct job *cmd, struct jobSet *junk);
124#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000125
Eric Andersen34e19412000-07-10 18:47:24 +0000126
127/* function prototypes for shell stuff */
Erik Andersend75af992000-03-16 08:09:09 +0000128static void checkJobs(struct jobSet *jobList);
129static int getCommand(FILE * source, char *command);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000130static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg);
Erik Andersend75af992000-03-16 08:09:09 +0000131static int setupRedirections(struct childProgram *prog);
Eric Andersena1d187a2000-07-17 19:14:41 +0000132static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
Erik Andersen3522eb12000-03-12 23:49:18 +0000133static int busy_loop(FILE * input);
134
Erik Andersend75af992000-03-16 08:09:09 +0000135
Mark Whitley37653aa2000-07-12 23:36:17 +0000136/* Table of built-in functions (these are non-forking builtins, meaning they
137 * can change global variables in the parent shell process but they will not
138 * work with pipes and redirects; 'unset foo | whatever' will not work) */
Erik Andersen3522eb12000-03-12 23:49:18 +0000139static struct builtInCommand bltins[] = {
Eric Andersenfad9c112000-07-25 18:06:52 +0000140 {"bg", "Resume a job in the background", builtin_fg_bg},
141 {"cd", "Change working directory", builtin_cd},
142 {"exit", "Exit from shell()", builtin_exit},
143 {"fg", "Bring job into the foreground", builtin_fg_bg},
144 {"jobs", "Lists the active jobs", builtin_jobs},
145 {"export", "Set environment variable", builtin_export},
146 {"unset", "Unset environment variable", builtin_unset},
147 {"read", "Input environment variable", builtin_read},
148 {NULL, NULL, NULL}
Erik Andersen330fd2b2000-05-19 05:35:19 +0000149};
150
Mark Whitley37653aa2000-07-12 23:36:17 +0000151/* Table of forking built-in functions (things that fork cannot change global
152 * variables in the parent process, such as the current working directory) */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000153static struct builtInCommand bltins_forking[] = {
Eric Andersenfad9c112000-07-25 18:06:52 +0000154 {"env", "Print all environment variables", builtin_env},
155 {"pwd", "Print current directory", builtin_pwd},
156#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
157 {"if", NULL, builtin_if},
158 {"then", NULL, builtin_then},
159 {"else", NULL, builtin_else},
160 {"fi", NULL, builtin_fi},
161#endif
162 {".", "Source-in and run commands in a file", builtin_source},
163 {"help", "List shell built-in commands", builtin_help},
164 {NULL, NULL, NULL}
Erik Andersen3522eb12000-03-12 23:49:18 +0000165};
166
Erik Andersen3522eb12000-03-12 23:49:18 +0000167static char *prompt = "# ";
Eric Andersen6efc48c2000-07-18 08:16:39 +0000168static char *cwd;
Eric Andersen1c314ad2000-06-28 16:56:25 +0000169static char *local_pending_command = NULL;
Eric Andersenfad9c112000-07-25 18:06:52 +0000170static char *promptStr = NULL;
171static struct jobSet jobList = { NULL, NULL };
Erik Andersen3522eb12000-03-12 23:49:18 +0000172
Erik Andersenf0657d32000-04-12 17:49:52 +0000173#ifdef BB_FEATURE_SH_COMMAND_EDITING
Eric Andersenfad04fd2000-07-14 06:49:52 +0000174void win_changed(int junk)
Erik Andersenf0657d32000-04-12 17:49:52 +0000175{
Eric Andersenfad04fd2000-07-14 06:49:52 +0000176 struct winsize win = { 0, 0, 0, 0 };
Erik Andersenf0657d32000-04-12 17:49:52 +0000177 ioctl(0, TIOCGWINSZ, &win);
178 if (win.ws_col > 0) {
179 cmdedit_setwidth( win.ws_col - 1);
180 }
181}
182#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000183
Erik Andersen3522eb12000-03-12 23:49:18 +0000184
Erik Andersend75af992000-03-16 08:09:09 +0000185/* built-in 'cd <path>' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000186static int builtin_cd(struct job *cmd, struct jobSet *junk)
Erik Andersend75af992000-03-16 08:09:09 +0000187{
Erik Andersen161220c2000-03-16 08:12:48 +0000188 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000189
Erik Andersen161220c2000-03-16 08:12:48 +0000190 if (!cmd->progs[0].argv[1] == 1)
191 newdir = getenv("HOME");
192 else
193 newdir = cmd->progs[0].argv[1];
194 if (chdir(newdir)) {
195 printf("cd: %s: %s\n", newdir, strerror(errno));
196 return FALSE;
197 }
Eric Andersen6efc48c2000-07-18 08:16:39 +0000198 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen161220c2000-03-16 08:12:48 +0000199
200 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000201}
202
203/* built-in 'env' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000204static int builtin_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000205{
Erik Andersen161220c2000-03-16 08:12:48 +0000206 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000207
Erik Andersen161220c2000-03-16 08:12:48 +0000208 for (e = environ; *e; e++) {
209 fprintf(stdout, "%s\n", *e);
210 }
211 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000212}
213
214/* built-in 'exit' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000215static int builtin_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000216{
Erik Andersen161220c2000-03-16 08:12:48 +0000217 if (!cmd->progs[0].argv[1] == 1)
218 exit TRUE;
219
Eric Andersenb6106152000-06-19 17:25:40 +0000220 return(atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000221}
222
223/* built-in 'fg' and 'bg' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000224static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000225{
Erik Andersen161220c2000-03-16 08:12:48 +0000226 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000227 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000228
Erik Andersen161220c2000-03-16 08:12:48 +0000229 if (!jobList->head) {
230 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
Matt Kraaid537a952000-07-14 01:51:25 +0000231 errorMsg("%s: exactly one argument is expected\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000232 cmd->progs[0].argv[0]);
233 return FALSE;
234 }
235 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
Matt Kraaid537a952000-07-14 01:51:25 +0000236 errorMsg("%s: bad argument '%s'\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000237 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
238 return FALSE;
239 for (job = jobList->head; job; job = job->next) {
240 if (job->jobId == jobNum) {
241 break;
242 }
243 }
244 }
245 } else {
246 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000247 }
Erik Andersen161220c2000-03-16 08:12:48 +0000248
249 if (!job) {
Matt Kraaid537a952000-07-14 01:51:25 +0000250 errorMsg("%s: unknown job %d\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000251 cmd->progs[0].argv[0], jobNum);
252 return FALSE;
Erik Andersend75af992000-03-16 08:09:09 +0000253 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000254
Erik Andersen161220c2000-03-16 08:12:48 +0000255 if (*cmd->progs[0].argv[0] == 'f') {
256 /* Make this job the foreground job */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000257 /* suppress messages when run from /linuxrc mag@sysgo.de */
258 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
259 perror("tcsetpgrp");
Erik Andersen161220c2000-03-16 08:12:48 +0000260 jobList->fg = job;
261 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000262
Erik Andersen161220c2000-03-16 08:12:48 +0000263 /* Restart the processes in the job */
264 for (i = 0; i < job->numProgs; i++)
265 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000266
Erik Andersen161220c2000-03-16 08:12:48 +0000267 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000268
Erik Andersen161220c2000-03-16 08:12:48 +0000269 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000270
Erik Andersen161220c2000-03-16 08:12:48 +0000271 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000272}
273
274/* built-in 'help' handler */
Eric Andersenfad04fd2000-07-14 06:49:52 +0000275static int builtin_help(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000276{
Erik Andersen161220c2000-03-16 08:12:48 +0000277 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000278
Erik Andersen161220c2000-03-16 08:12:48 +0000279 fprintf(stdout, "\nBuilt-in commands:\n");
280 fprintf(stdout, "-------------------\n");
281 for (x = bltins; x->cmd; x++) {
Eric Andersenfad9c112000-07-25 18:06:52 +0000282 if (x->descr==NULL)
283 continue;
Erik Andersen161220c2000-03-16 08:12:48 +0000284 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
285 }
Erik Andersen330fd2b2000-05-19 05:35:19 +0000286 for (x = bltins_forking; x->cmd; x++) {
Eric Andersenfad9c112000-07-25 18:06:52 +0000287 if (x->descr==NULL)
288 continue;
Erik Andersen330fd2b2000-05-19 05:35:19 +0000289 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
290 }
Erik Andersen161220c2000-03-16 08:12:48 +0000291 fprintf(stdout, "\n\n");
292 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000293}
294
295/* built-in 'jobs' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000296static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000297{
Erik Andersen161220c2000-03-16 08:12:48 +0000298 struct job *job;
299 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000300
Erik Andersen161220c2000-03-16 08:12:48 +0000301 for (job = jobList->head; job; job = job->next) {
302 if (job->runningProgs == job->stoppedProgs)
303 statusString = "Stopped";
304 else
305 statusString = "Running";
306
307 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
308 }
309 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000310}
311
312
313/* built-in 'pwd' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000314static int builtin_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000315{
Eric Andersen6efc48c2000-07-18 08:16:39 +0000316 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen161220c2000-03-16 08:12:48 +0000317 fprintf(stdout, "%s\n", cwd);
318 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000319}
320
Erik Andersen6273f652000-03-17 01:12:41 +0000321/* built-in 'export VAR=value' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000322static int builtin_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000323{
Erik Andersen161220c2000-03-16 08:12:48 +0000324 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000325
Erik Andersen161220c2000-03-16 08:12:48 +0000326 if (!cmd->progs[0].argv[1] == 1) {
Eric Andersen34e19412000-07-10 18:47:24 +0000327 return (builtin_env(cmd, junk));
Erik Andersen161220c2000-03-16 08:12:48 +0000328 }
329 res = putenv(cmd->progs[0].argv[1]);
330 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000331 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000332 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000333}
334
Eric Andersenb54833c2000-07-03 23:56:26 +0000335/* built-in 'read VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000336static int builtin_read(struct job *cmd, struct jobSet *junk)
Eric Andersenb54833c2000-07-03 23:56:26 +0000337{
338 int res = 0, len, newlen;
339 char *s;
340 char string[MAX_READ];
341
342 if (cmd->progs[0].argv[1]) {
343 /* argument (VAR) given: put "VAR=" into buffer */
344 strcpy(string, cmd->progs[0].argv[1]);
345 len = strlen(string);
346 string[len++] = '=';
347 string[len] = '\0';
348 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
349 newlen = strlen(string);
350 if(newlen > len)
351 string[--newlen] = '\0'; /* chomp trailing newline */
352 /*
353 ** string should now contain "VAR=<value>"
354 ** copy it (putenv() won't do that, so we must make sure
355 ** the string resides in a static buffer!)
356 */
357 res = -1;
358 if((s = strdup(string)))
359 res = putenv(s);
360 if (res)
361 fprintf(stdout, "read: %s\n", strerror(errno));
362 }
363 else
364 fgets(string, sizeof(string), stdin);
365
366 return (res);
367}
368
Eric Andersenfad9c112000-07-25 18:06:52 +0000369#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
370/* Built-in handler for 'if' commands */
371static int builtin_if(struct job *cmd, struct jobSet *junk)
372{
373 cmd->jobContext |= IF_EXP_CONTEXT;
374 printf("Hit an if -- jobContext=%d\n", cmd->jobContext);
375 return TRUE;
376}
377
378/* Built-in handler for 'then' (part of the 'if' command) */
379static int builtin_then(struct job *cmd, struct jobSet *junk)
380{
381 if (cmd->jobContext & IF_EXP_CONTEXT) {
382 fprintf(stderr, "unexpected token `then'\n");
383 fflush(stderr);
384 return FALSE;
385 }
386 cmd->jobContext |= THEN_EXP_CONTEXT;
387 printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
388 return TRUE;
389}
390
391/* Built-in handler for 'else' (part of the 'if' command) */
392static int builtin_else(struct job *cmd, struct jobSet *junk)
393{
394 printf("Hit an else\n");
395 cmd->jobContext |= ELSE_EXP_CONTEXT;
396 return TRUE;
397}
398
399/* Built-in handler for 'fi' (part of the 'if' command) */
400static int builtin_fi(struct job *cmd, struct jobSet *junk)
401{
402 printf("Hit an fi\n");
403 return TRUE;
404}
405#endif
406
Erik Andersen3522eb12000-03-12 23:49:18 +0000407/* Built-in '.' handler (read-in and execute commands from file) */
Eric Andersen34e19412000-07-10 18:47:24 +0000408static int builtin_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000409{
Erik Andersen161220c2000-03-16 08:12:48 +0000410 FILE *input;
411 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000412
Erik Andersen161220c2000-03-16 08:12:48 +0000413 if (!cmd->progs[0].argv[1] == 1)
414 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000415
Erik Andersen161220c2000-03-16 08:12:48 +0000416 input = fopen(cmd->progs[0].argv[1], "r");
417 if (!input) {
418 fprintf(stdout, "Couldn't open file '%s'\n",
419 cmd->progs[0].argv[1]);
420 return FALSE;
421 }
Erik Andersend75af992000-03-16 08:09:09 +0000422
Erik Andersen161220c2000-03-16 08:12:48 +0000423 /* Now run the file */
424 status = busy_loop(input);
425 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000426}
427
428/* built-in 'unset VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000429static int builtin_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000430{
Erik Andersen161220c2000-03-16 08:12:48 +0000431 if (!cmd->progs[0].argv[1] == 1) {
432 fprintf(stdout, "unset: parameter required.\n");
433 return FALSE;
434 }
435 unsetenv(cmd->progs[0].argv[1]);
436 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000437}
438
439/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000440static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000441{
Erik Andersen161220c2000-03-16 08:12:48 +0000442 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000443
Erik Andersen161220c2000-03-16 08:12:48 +0000444 for (i = 0; i < cmd->numProgs; i++) {
445 free(cmd->progs[i].argv);
446 if (cmd->progs[i].redirections)
447 free(cmd->progs[i].redirections);
448 if (cmd->progs[i].freeGlob)
449 globfree(&cmd->progs[i].globResult);
450 }
451 free(cmd->progs);
452 if (cmd->text)
453 free(cmd->text);
454 free(cmd->cmdBuf);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000455 memset(cmd, 0, sizeof(struct job));
Erik Andersen3522eb12000-03-12 23:49:18 +0000456}
457
458/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000459static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000460{
Erik Andersen161220c2000-03-16 08:12:48 +0000461 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000462
Erik Andersen161220c2000-03-16 08:12:48 +0000463 freeJob(job);
464 if (job == jobList->head) {
465 jobList->head = job->next;
466 } else {
467 prevJob = jobList->head;
468 while (prevJob->next != job)
469 prevJob = prevJob->next;
470 prevJob->next = job->next;
471 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000472
Erik Andersen161220c2000-03-16 08:12:48 +0000473 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000474}
475
476/* Checks to see if any background processes have exited -- if they
477 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000478static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000479{
Erik Andersen161220c2000-03-16 08:12:48 +0000480 struct job *job;
481 pid_t childpid;
482 int status;
483 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000484
Erik Andersen161220c2000-03-16 08:12:48 +0000485 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
486 for (job = jobList->head; job; job = job->next) {
487 progNum = 0;
488 while (progNum < job->numProgs &&
489 job->progs[progNum].pid != childpid) progNum++;
490 if (progNum < job->numProgs)
491 break;
492 }
493
Eric Andersena1d187a2000-07-17 19:14:41 +0000494 /* This happens on backticked commands */
495 if(job==NULL)
496 return;
497
Erik Andersen161220c2000-03-16 08:12:48 +0000498 if (WIFEXITED(status) || WIFSIGNALED(status)) {
499 /* child exited */
500 job->runningProgs--;
501 job->progs[progNum].pid = 0;
502
503 if (!job->runningProgs) {
504 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
505 removeJob(jobList, job);
506 }
507 } else {
508 /* child stopped */
509 job->stoppedProgs++;
510 job->progs[progNum].isStopped = 1;
511
512 if (job->stoppedProgs == job->numProgs) {
513 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
514 job->text);
515 }
516 }
Erik Andersend75af992000-03-16 08:09:09 +0000517 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000518
Erik Andersen161220c2000-03-16 08:12:48 +0000519 if (childpid == -1 && errno != ECHILD)
520 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000521}
522
Erik Andersend75af992000-03-16 08:09:09 +0000523static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000524{
Eric Andersen1c314ad2000-06-28 16:56:25 +0000525 if (source == NULL) {
526 if (local_pending_command) {
527 /* a command specified (-c option): return it & mark it done */
528 strcpy(command, local_pending_command);
529 free(local_pending_command);
530 local_pending_command = NULL;
531 return 0;
532 }
533 return 1;
534 }
535
Erik Andersen161220c2000-03-16 08:12:48 +0000536 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000537#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000538 int len;
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000539 len=fprintf(stdout, "%s %s", cwd, prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000540 fflush(stdout);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000541 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000542 sprintf(promptStr, "%s %s", cwd, prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000543 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000544 free( promptStr);
Erik Andersen161220c2000-03-16 08:12:48 +0000545 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000546#else
547 fprintf(stdout, "%s %s", cwd, prompt);
548 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000549#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000550 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000551
Erik Andersen161220c2000-03-16 08:12:48 +0000552 if (!fgets(command, BUFSIZ - 2, source)) {
553 if (source == stdin)
554 printf("\n");
555 return 1;
556 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000557
Erik Andersen161220c2000-03-16 08:12:48 +0000558 /* remove trailing newline */
559 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000560
Erik Andersen161220c2000-03-16 08:12:48 +0000561 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000562}
563
Erik Andersend75af992000-03-16 08:09:09 +0000564static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000565 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000566{
Erik Andersen161220c2000-03-16 08:12:48 +0000567 int argc = *argcPtr;
568 int argcAlloced = *argcAllocedPtr;
569 int rc;
570 int flags;
571 int i;
Eric Andersenb54833c2000-07-03 23:56:26 +0000572 char *src, *dst, *var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000573
Erik Andersen161220c2000-03-16 08:12:48 +0000574 if (argc > 1) { /* cmd->globResult is already initialized */
575 flags = GLOB_APPEND;
576 i = prog->globResult.gl_pathc;
577 } else {
578 prog->freeGlob = 1;
579 flags = 0;
580 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000581 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000582 /* do shell variable substitution */
583 if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
584 prog->argv[argc - 1] = var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000585
Erik Andersen161220c2000-03-16 08:12:48 +0000586 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
587 if (rc == GLOB_NOSPACE) {
Matt Kraaid537a952000-07-14 01:51:25 +0000588 errorMsg("out of space during glob operation\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000589 return;
590 } else if (rc == GLOB_NOMATCH ||
591 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
592 !strcmp(prog->argv[argc - 1],
593 prog->globResult.gl_pathv[i]))) {
594 /* we need to remove whatever \ quoting is still present */
595 src = dst = prog->argv[argc - 1];
596 while (*src) {
597 if (*src != '\\')
598 *dst++ = *src;
599 src++;
600 }
601 *dst = '\0';
602 } else if (!rc) {
603 argcAlloced += (prog->globResult.gl_pathc - i);
604 prog->argv =
605 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
606 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
607 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
608 argc += (prog->globResult.gl_pathc - i - 1);
609 }
610
611 *argcAllocedPtr = argcAlloced;
612 *argcPtr = argc;
Erik Andersen3522eb12000-03-12 23:49:18 +0000613}
614
615/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
616 line). If a valid command is found, commandPtr is set to point to
617 the beginning of the next command (if the original command had more
618 then one job associated with it) or NULL if no more commands are
619 present. */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000620static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000621{
Erik Andersen161220c2000-03-16 08:12:48 +0000622 char *command;
623 char *returnCommand = NULL;
624 char *src, *buf, *chptr;
625 int argc = 0;
626 int done = 0;
627 int argvAlloced;
628 int i;
629 char quote = '\0';
630 int count;
631 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000632
Erik Andersen161220c2000-03-16 08:12:48 +0000633 /* skip leading white space */
634 while (**commandPtr && isspace(**commandPtr))
635 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000636
Erik Andersen161220c2000-03-16 08:12:48 +0000637 /* this handles empty lines or leading '#' characters */
638 if (!**commandPtr || (**commandPtr == '#')) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000639 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000640 return 0;
641 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000642
Erik Andersen161220c2000-03-16 08:12:48 +0000643 *isBg = 0;
644 job->numProgs = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000645 job->progs = xmalloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000646
Erik Andersen161220c2000-03-16 08:12:48 +0000647 /* We set the argv elements to point inside of this string. The
Eric Andersenb54833c2000-07-03 23:56:26 +0000648 memory is freed by freeJob(). Allocate twice the original
649 length in case we need to quote every single character.
Erik Andersen3522eb12000-03-12 23:49:18 +0000650
Erik Andersen161220c2000-03-16 08:12:48 +0000651 Getting clean memory relieves us of the task of NULL
652 terminating things and makes the rest of this look a bit
653 cleaner (though it is, admittedly, a tad less efficient) */
Eric Andersenfad9c112000-07-25 18:06:52 +0000654 job->cmdBuf = command = calloc(2*strlen(*commandPtr) + 1, sizeof(char));
Erik Andersen161220c2000-03-16 08:12:48 +0000655 job->text = NULL;
Eric Andersenfad9c112000-07-25 18:06:52 +0000656 job->jobContext = REGULAR_JOB_CONTEXT;
Erik Andersen3522eb12000-03-12 23:49:18 +0000657
Erik Andersen161220c2000-03-16 08:12:48 +0000658 prog = job->progs;
659 prog->numRedirections = 0;
660 prog->redirections = NULL;
661 prog->freeGlob = 0;
662 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000663
Erik Andersen161220c2000-03-16 08:12:48 +0000664 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000665 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000666 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000667
Erik Andersen161220c2000-03-16 08:12:48 +0000668 buf = command;
669 src = *commandPtr;
670 while (*src && !done) {
671 if (quote == *src) {
672 quote = '\0';
673 } else if (quote) {
674 if (*src == '\\') {
675 src++;
676 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000677 errorMsg("character expected after \\\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000678 freeJob(job);
679 return 1;
680 }
681
682 /* in shell, "\'" should yield \' */
683 if (*src != quote)
684 *buf++ = '\\';
685 } else if (*src == '*' || *src == '?' || *src == '[' ||
686 *src == ']') *buf++ = '\\';
687 *buf++ = *src;
688 } else if (isspace(*src)) {
689 if (*prog->argv[argc]) {
690 buf++, argc++;
691 /* +1 here leaves room for the NULL which ends argv */
692 if ((argc + 1) == argvAlloced) {
693 argvAlloced += 5;
694 prog->argv = realloc(prog->argv,
695 sizeof(*prog->argv) *
696 argvAlloced);
697 }
Erik Andersen161220c2000-03-16 08:12:48 +0000698 globLastArgument(prog, &argc, &argvAlloced);
Eric Andersenb54833c2000-07-03 23:56:26 +0000699 prog->argv[argc] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000700 }
701 } else
702 switch (*src) {
703 case '"':
704 case '\'':
705 quote = *src;
706 break;
707
708 case '#': /* comment */
709 done = 1;
710 break;
711
712 case '>': /* redirections */
713 case '<':
714 i = prog->numRedirections++;
715 prog->redirections = realloc(prog->redirections,
716 sizeof(*prog->redirections) *
717 (i + 1));
718
719 prog->redirections[i].fd = -1;
720 if (buf != prog->argv[argc]) {
721 /* the stuff before this character may be the file number
722 being redirected */
723 prog->redirections[i].fd =
724 strtol(prog->argv[argc], &chptr, 10);
725
726 if (*chptr && *prog->argv[argc]) {
727 buf++, argc++;
728 globLastArgument(prog, &argc, &argvAlloced);
Eric Andersenb54833c2000-07-03 23:56:26 +0000729 prog->argv[argc] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000730 }
731 }
732
733 if (prog->redirections[i].fd == -1) {
734 if (*src == '>')
735 prog->redirections[i].fd = 1;
736 else
737 prog->redirections[i].fd = 0;
738 }
739
740 if (*src++ == '>') {
741 if (*src == '>')
742 prog->redirections[i].type =
743 REDIRECT_APPEND, src++;
744 else
745 prog->redirections[i].type = REDIRECT_OVERWRITE;
746 } else {
747 prog->redirections[i].type = REDIRECT_INPUT;
748 }
749
750 /* This isn't POSIX sh compliant. Oh well. */
751 chptr = src;
752 while (isspace(*chptr))
753 chptr++;
754
755 if (!*chptr) {
Matt Kraaid537a952000-07-14 01:51:25 +0000756 errorMsg("file name expected after %c\n", *src);
Erik Andersen161220c2000-03-16 08:12:48 +0000757 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000758 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000759 return 1;
760 }
761
762 prog->redirections[i].filename = buf;
763 while (*chptr && !isspace(*chptr))
764 *buf++ = *chptr++;
765
766 src = chptr - 1; /* we src++ later */
767 prog->argv[argc] = ++buf;
768 break;
769
770 case '|': /* pipe */
771 /* finish this command */
772 if (*prog->argv[argc])
773 argc++;
774 if (!argc) {
Eric Andersena1d187a2000-07-17 19:14:41 +0000775 errorMsg("empty command in pipe\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000776 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000777 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000778 return 1;
779 }
780 prog->argv[argc] = NULL;
781
782 /* and start the next */
783 job->numProgs++;
784 job->progs = realloc(job->progs,
785 sizeof(*job->progs) * job->numProgs);
786 prog = job->progs + (job->numProgs - 1);
787 prog->numRedirections = 0;
788 prog->redirections = NULL;
789 prog->freeGlob = 0;
790 argc = 0;
791
792 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000793 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000794 prog->argv[0] = ++buf;
795
796 src++;
797 while (*src && isspace(*src))
798 src++;
799
800 if (!*src) {
Eric Andersena1d187a2000-07-17 19:14:41 +0000801 errorMsg("empty command in pipe\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +0000802 freeJob(job);
803 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000804 return 1;
805 }
806 src--; /* we'll ++ it at the end of the loop */
807
808 break;
809
810 case '&': /* background */
811 *isBg = 1;
812 case ';': /* multiple commands */
813 done = 1;
814 returnCommand = *commandPtr + (src - *commandPtr) + 1;
815 break;
816
817 case '\\':
818 src++;
819 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000820 errorMsg("character expected after \\\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +0000821 freeJob(job);
Erik Andersen161220c2000-03-16 08:12:48 +0000822 return 1;
823 }
824 if (*src == '*' || *src == '[' || *src == ']'
825 || *src == '?') *buf++ = '\\';
826 /* fallthrough */
Eric Andersena1d187a2000-07-17 19:14:41 +0000827#ifdef BB_FEATURE_SH_BACKTICKS
Eric Andersenec10b9d2000-07-14 01:13:11 +0000828 case '`':
829 /* Exec a backtick-ed command */
830 {
Eric Andersena1d187a2000-07-17 19:14:41 +0000831 char* charptr1=NULL, *charptr2;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000832 char* ptr=NULL;
Eric Andersena1d187a2000-07-17 19:14:41 +0000833 struct job *newJob;
834 struct jobSet njobList = { NULL, NULL };
835 int pipefd[2];
836 int size;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000837
838 ptr=strchr(++src, '`');
839 if (ptr==NULL) {
840 fprintf(stderr, "Unmatched '`' in command\n");
841 freeJob(job);
842 return 1;
843 }
844
Eric Andersena1d187a2000-07-17 19:14:41 +0000845 /* Make some space to hold just the backticked command */
Eric Andersen6efc48c2000-07-18 08:16:39 +0000846 charptr1 = charptr2 = xmalloc(1+ptr-src);
Eric Andersena1d187a2000-07-17 19:14:41 +0000847 snprintf(charptr1, 1+ptr-src, src);
848 newJob = xmalloc(sizeof(struct job));
849 /* Now parse and run the backticked command */
850 if (!parseCommand(&charptr1, newJob, &njobList, isBg)
851 && newJob->numProgs) {
852 pipe(pipefd);
853 runCommand(newJob, &njobList, 0, pipefd);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000854 }
Eric Andersena1d187a2000-07-17 19:14:41 +0000855 checkJobs(jobList);
Eric Andersen6efc48c2000-07-18 08:16:39 +0000856 freeJob(newJob);
857 free(charptr2);
858
859 /* Make a copy of any stuff left over in the command
860 * line after the second backtick */
861 charptr2 = xmalloc(strlen(ptr)+1);
862 memcpy(charptr2, ptr+1, strlen(ptr));
863
Eric Andersenec10b9d2000-07-14 01:13:11 +0000864
Eric Andersena1d187a2000-07-17 19:14:41 +0000865 /* Copy the output from the backtick-ed command into the
866 * command line, making extra room as needed */
867 --src;
868 charptr1 = xmalloc(BUFSIZ);
869 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
870 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
871 if (newSize > BUFSIZ) {
872 *commandPtr=realloc(*commandPtr, src - *commandPtr +
873 size + 1 + strlen(charptr2));
874 }
875 memcpy(src, charptr1, size);
876 src+=size;
877 }
878 free(charptr1);
879 close(pipefd[0]);
880 if (*(src-1)=='\n')
881 --src;
882
883 /* Now paste into the *commandPtr all the stuff
884 * leftover after the second backtick */
885 memcpy(src, charptr2, strlen(charptr2));
Eric Andersena1d187a2000-07-17 19:14:41 +0000886 free(charptr2);
887
Eric Andersena1d187a2000-07-17 19:14:41 +0000888 /* Now recursively call parseCommand to deal with the new
889 * and improved version of the command line with the backtick
890 * results expanded in place... */
Eric Andersen6efc48c2000-07-18 08:16:39 +0000891 freeJob(job);
Eric Andersena1d187a2000-07-17 19:14:41 +0000892 return(parseCommand(commandPtr, job, jobList, isBg));
Eric Andersenec10b9d2000-07-14 01:13:11 +0000893 }
894 break;
Eric Andersena1d187a2000-07-17 19:14:41 +0000895#endif // BB_FEATURE_SH_BACKTICKS
Erik Andersen161220c2000-03-16 08:12:48 +0000896 default:
897 *buf++ = *src;
898 }
899
Erik Andersend75af992000-03-16 08:09:09 +0000900 src++;
Erik Andersen161220c2000-03-16 08:12:48 +0000901 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000902
Erik Andersen161220c2000-03-16 08:12:48 +0000903 if (*prog->argv[argc]) {
904 argc++;
Erik Andersend75af992000-03-16 08:09:09 +0000905 globLastArgument(prog, &argc, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000906 }
907 if (!argc) {
908 freeJob(job);
909 return 0;
910 }
911 prog->argv[argc] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000912
Erik Andersen161220c2000-03-16 08:12:48 +0000913 if (!returnCommand) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000914 job->text = xmalloc(strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000915 strcpy(job->text, *commandPtr);
916 } else {
917 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +0000918 count = returnCommand - *commandPtr;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000919 job->text = xmalloc(count + 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000920 strncpy(job->text, *commandPtr, count);
921 job->text[count] = '\0';
922 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000923
Erik Andersen161220c2000-03-16 08:12:48 +0000924 *commandPtr = returnCommand;
Erik Andersen3522eb12000-03-12 23:49:18 +0000925
Erik Andersend75af992000-03-16 08:09:09 +0000926 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000927}
928
Eric Andersena1d187a2000-07-17 19:14:41 +0000929static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
Erik Andersen3522eb12000-03-12 23:49:18 +0000930{
Eric Andersenfad9c112000-07-25 18:06:52 +0000931 struct job *theJob;
Erik Andersen161220c2000-03-16 08:12:48 +0000932 int i;
Eric Andersen6efc48c2000-07-18 08:16:39 +0000933 int nextin, nextout;
Erik Andersen161220c2000-03-16 08:12:48 +0000934 int pipefds[2]; /* pipefd[0] is for reading */
935 struct builtInCommand *x;
Eric Andersenb54833c2000-07-03 23:56:26 +0000936#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Erik Andersenbcd61772000-05-13 06:33:19 +0000937 const struct BB_applet *a = applets;
938#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000939
Eric Andersen6efc48c2000-07-18 08:16:39 +0000940
941 nextin = 0, nextout = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000942 for (i = 0; i < newJob->numProgs; i++) {
943 if ((i + 1) < newJob->numProgs) {
Erik Andersen161220c2000-03-16 08:12:48 +0000944 pipe(pipefds);
945 nextout = pipefds[1];
946 } else {
Eric Andersen6efc48c2000-07-18 08:16:39 +0000947 if (outPipe[1]!=-1) {
948 nextout = outPipe[1];
949 } else {
950 nextout = 1;
951 }
Erik Andersen161220c2000-03-16 08:12:48 +0000952 }
953
Eric Andersen34e19412000-07-10 18:47:24 +0000954 /* Check if the command matches any non-forking builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000955 for (x = bltins; x->cmd; x++) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000956 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
957 return (x->function(newJob, jobList));
Erik Andersen330fd2b2000-05-19 05:35:19 +0000958 }
959 }
960
Eric Andersenec10b9d2000-07-14 01:13:11 +0000961 if (!(newJob->progs[i].pid = fork())) {
Erik Andersen161220c2000-03-16 08:12:48 +0000962 signal(SIGTTOU, SIG_DFL);
963
Eric Andersena1d187a2000-07-17 19:14:41 +0000964 if (outPipe[1]!=-1) {
965 close(outPipe[0]);
Eric Andersen6efc48c2000-07-18 08:16:39 +0000966 }
967 if (nextin != 0) {
968 dup2(nextin, 0);
969 close(nextin);
970 }
971
972 if (nextout != 1) {
Erik Andersen161220c2000-03-16 08:12:48 +0000973 dup2(nextout, 1);
Eric Andersena1d187a2000-07-17 19:14:41 +0000974 dup2(nextout, 2);
Erik Andersen161220c2000-03-16 08:12:48 +0000975 close(nextout);
976 }
977
978 /* explicit redirections override pipes */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000979 setupRedirections(newJob->progs + i);
Erik Andersen161220c2000-03-16 08:12:48 +0000980
Eric Andersen34e19412000-07-10 18:47:24 +0000981 /* Check if the command matches any of the other builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000982 for (x = bltins_forking; x->cmd; x++) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000983 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
984 exit (x->function(newJob, jobList));
Erik Andersenbcd61772000-05-13 06:33:19 +0000985 }
986 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000987#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Eric Andersen34e19412000-07-10 18:47:24 +0000988 /* Check if the command matches any busybox internal commands here */
Eric Andersen6efc48c2000-07-18 08:16:39 +0000989 /* TODO: Add matching when paths are appended (i.e. 'cat' currently
990 * works, but '/bin/cat' doesn't ) */
Erik Andersenbcd61772000-05-13 06:33:19 +0000991 while (a->name != 0) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000992 if (strcmp(newJob->progs[i].argv[0], a->name) == 0) {
Erik Andersenbcd61772000-05-13 06:33:19 +0000993 int argc;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000994 char** argv=newJob->progs[i].argv;
Erik Andersenc3f5c9c2000-05-13 19:00:07 +0000995 for(argc=0;*argv!=NULL; argv++, argc++);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000996 exit((*(a->main)) (argc, newJob->progs[i].argv));
Erik Andersenbcd61772000-05-13 06:33:19 +0000997 }
998 a++;
999 }
1000#endif
1001
Eric Andersenec10b9d2000-07-14 01:13:11 +00001002 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001003 fatalError("sh: %s: %s\n", newJob->progs[i].argv[0],
Erik Andersen161220c2000-03-16 08:12:48 +00001004 strerror(errno));
1005 }
Eric Andersen6efc48c2000-07-18 08:16:39 +00001006 if (outPipe[1]!=-1) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001007 close(outPipe[1]);
1008 }
Erik Andersen161220c2000-03-16 08:12:48 +00001009
1010 /* put our child in the process group whose leader is the
1011 first process in this pipe */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001012 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
Erik Andersen161220c2000-03-16 08:12:48 +00001013 if (nextin != 0)
1014 close(nextin);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001015 if (nextout != 1)
Erik Andersen161220c2000-03-16 08:12:48 +00001016 close(nextout);
1017
1018 /* If there isn't another process, nextin is garbage
1019 but it doesn't matter */
1020 nextin = pipefds[0];
1021 }
1022
Eric Andersenec10b9d2000-07-14 01:13:11 +00001023 newJob->pgrp = newJob->progs[0].pid;
Erik Andersen161220c2000-03-16 08:12:48 +00001024
Eric Andersenfad9c112000-07-25 18:06:52 +00001025 /* find the ID for the theJob to use */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001026 newJob->jobId = 1;
Eric Andersenfad9c112000-07-25 18:06:52 +00001027 for (theJob = jobList->head; theJob; theJob = theJob->next)
1028 if (theJob->jobId >= newJob->jobId)
1029 newJob->jobId = theJob->jobId + 1;
Erik Andersen161220c2000-03-16 08:12:48 +00001030
Eric Andersenfad9c112000-07-25 18:06:52 +00001031 /* add the theJob to the list of running jobs */
Erik Andersen161220c2000-03-16 08:12:48 +00001032 if (!jobList->head) {
Eric Andersenfad9c112000-07-25 18:06:52 +00001033 theJob = jobList->head = malloc(sizeof(*theJob));
Erik Andersend75af992000-03-16 08:09:09 +00001034 } else {
Eric Andersenfad9c112000-07-25 18:06:52 +00001035 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
1036 theJob->next = malloc(sizeof(*theJob));
1037 theJob = theJob->next;
Erik Andersend75af992000-03-16 08:09:09 +00001038 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001039
Eric Andersenfad9c112000-07-25 18:06:52 +00001040 *theJob = *newJob;
1041 theJob->next = NULL;
1042 theJob->runningProgs = theJob->numProgs;
1043 theJob->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001044
Erik Andersen161220c2000-03-16 08:12:48 +00001045 if (inBg) {
Eric Andersenfad9c112000-07-25 18:06:52 +00001046 /* we don't wait for background theJobs to return -- append it
1047 to the list of backgrounded theJobs and leave it alone */
1048 printf("[%d] %d\n", theJob->jobId,
Eric Andersenec10b9d2000-07-14 01:13:11 +00001049 newJob->progs[newJob->numProgs - 1].pid);
Erik Andersen161220c2000-03-16 08:12:48 +00001050 } else {
Eric Andersenfad9c112000-07-25 18:06:52 +00001051 jobList->fg = theJob;
Erik Andersen3522eb12000-03-12 23:49:18 +00001052
Erik Andersen161220c2000-03-16 08:12:48 +00001053 /* move the new process group into the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001054 /* suppress messages when run from /linuxrc mag@sysgo.de */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001055 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
Erik Andersen161220c2000-03-16 08:12:48 +00001056 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001057 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001058
Erik Andersen161220c2000-03-16 08:12:48 +00001059 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001060}
1061
Erik Andersend75af992000-03-16 08:09:09 +00001062static int setupRedirections(struct childProgram *prog)
Erik Andersen3522eb12000-03-12 23:49:18 +00001063{
Erik Andersen161220c2000-03-16 08:12:48 +00001064 int i;
1065 int openfd;
1066 int mode = O_RDONLY;
1067 struct redirectionSpecifier *redir = prog->redirections;
Erik Andersen3522eb12000-03-12 23:49:18 +00001068
Erik Andersen161220c2000-03-16 08:12:48 +00001069 for (i = 0; i < prog->numRedirections; i++, redir++) {
1070 switch (redir->type) {
1071 case REDIRECT_INPUT:
1072 mode = O_RDONLY;
1073 break;
1074 case REDIRECT_OVERWRITE:
1075 mode = O_RDWR | O_CREAT | O_TRUNC;
1076 break;
1077 case REDIRECT_APPEND:
1078 mode = O_RDWR | O_CREAT | O_APPEND;
1079 break;
1080 }
1081
1082 openfd = open(redir->filename, mode, 0666);
1083 if (openfd < 0) {
1084 /* this could get lost if stderr has been redirected, but
1085 bash and ash both lose it as well (though zsh doesn't!) */
Matt Kraaid537a952000-07-14 01:51:25 +00001086 errorMsg("error opening %s: %s\n", redir->filename,
Erik Andersen161220c2000-03-16 08:12:48 +00001087 strerror(errno));
1088 return 1;
1089 }
1090
1091 if (openfd != redir->fd) {
1092 dup2(openfd, redir->fd);
1093 close(openfd);
1094 }
Erik Andersend75af992000-03-16 08:09:09 +00001095 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001096
Erik Andersen161220c2000-03-16 08:12:48 +00001097 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001098}
1099
1100
Erik Andersend75af992000-03-16 08:09:09 +00001101static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +00001102{
Erik Andersen161220c2000-03-16 08:12:48 +00001103 char *command;
1104 char *nextCommand = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001105 struct job newJob;
Eric Andersen1c314ad2000-06-28 16:56:25 +00001106 pid_t parent_pgrp;
Erik Andersen161220c2000-03-16 08:12:48 +00001107 int i;
1108 int status;
1109 int inBg;
Erik Andersen3522eb12000-03-12 23:49:18 +00001110
Eric Andersen1c314ad2000-06-28 16:56:25 +00001111 /* save current owner of TTY so we can restore it on exit */
1112 parent_pgrp = tcgetpgrp(0);
1113
Erik Andersen161220c2000-03-16 08:12:48 +00001114 command = (char *) calloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +00001115
Erik Andersen161220c2000-03-16 08:12:48 +00001116 /* don't pay any attention to this signal; it just confuses
1117 things and isn't really meant for shells anyway */
1118 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +00001119
Erik Andersen161220c2000-03-16 08:12:48 +00001120 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +00001121 if (!jobList.fg) {
1122 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +00001123
Erik Andersend75af992000-03-16 08:09:09 +00001124 /* see if any background processes have exited */
1125 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +00001126
Erik Andersend75af992000-03-16 08:09:09 +00001127 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +00001128 if (getCommand(input, command))
1129 break;
1130 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +00001131 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001132
Eric Andersenec10b9d2000-07-14 01:13:11 +00001133 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +00001134 newJob.numProgs) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001135 int pipefds[2] = {-1,-1};
1136 runCommand(&newJob, &jobList, inBg, pipefds);
Eric Andersenfad9c112000-07-25 18:06:52 +00001137 }
1138 else {
Eric Andersena1d187a2000-07-17 19:14:41 +00001139 free(command);
1140 command = (char *) calloc(BUFSIZ, sizeof(char));
Eric Andersen6efc48c2000-07-18 08:16:39 +00001141 nextCommand = NULL;
Erik Andersend75af992000-03-16 08:09:09 +00001142 }
1143 } else {
1144 /* a job is running in the foreground; wait for it */
1145 i = 0;
1146 while (!jobList.fg->progs[i].pid ||
Eric Andersenfad9c112000-07-25 18:06:52 +00001147 jobList.fg->progs[i].isStopped == 1) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +00001148
Erik Andersend75af992000-03-16 08:09:09 +00001149 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +00001150
Erik Andersend75af992000-03-16 08:09:09 +00001151 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +00001152 /* the child exited */
1153 jobList.fg->runningProgs--;
1154 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001155
Erik Andersen161220c2000-03-16 08:12:48 +00001156 if (!jobList.fg->runningProgs) {
1157 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +00001158
Erik Andersen161220c2000-03-16 08:12:48 +00001159 removeJob(&jobList, jobList.fg);
1160 jobList.fg = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001161 }
Erik Andersend75af992000-03-16 08:09:09 +00001162 } else {
Erik Andersen161220c2000-03-16 08:12:48 +00001163 /* the child was stopped */
1164 jobList.fg->stoppedProgs++;
1165 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +00001166
Erik Andersen161220c2000-03-16 08:12:48 +00001167 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1168 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1169 "Stopped", jobList.fg->text);
1170 jobList.fg = NULL;
1171 }
Erik Andersend75af992000-03-16 08:09:09 +00001172 }
1173
1174 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +00001175 /* move the shell to the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001176 /* suppress messages when run from /linuxrc mag@sysgo.de */
1177 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1178 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001179 }
1180 }
1181 }
Erik Andersen161220c2000-03-16 08:12:48 +00001182 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +00001183
Eric Andersen1c314ad2000-06-28 16:56:25 +00001184 /* return controlling TTY back to parent process group before exiting */
1185 if (tcsetpgrp(0, parent_pgrp))
Eric Andersenb54833c2000-07-03 23:56:26 +00001186 perror("tcsetpgrp");
1187
1188 /* return exit status if called with "-c" */
1189 if (input == NULL && WIFEXITED(status))
1190 return WEXITSTATUS(status);
1191
Erik Andersen161220c2000-03-16 08:12:48 +00001192 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001193}
1194
1195
Eric Andersenfad9c112000-07-25 18:06:52 +00001196#ifdef BB_FEATURE_CLEAN_UP
1197void free_memory(void)
1198{
1199 if (promptStr)
1200 free(promptStr);
1201 if (cwd)
1202 free(cwd);
1203 if (local_pending_command)
1204 free(local_pending_command);
1205
1206 if (jobList.fg && !jobList.fg->runningProgs) {
1207 removeJob(&jobList, jobList.fg);
1208 }
1209}
1210#endif
1211
Eric Andersen6efc48c2000-07-18 08:16:39 +00001212
Erik Andersend75af992000-03-16 08:09:09 +00001213int shell_main(int argc, char **argv)
Erik Andersen3522eb12000-03-12 23:49:18 +00001214{
Erik Andersen161220c2000-03-16 08:12:48 +00001215 FILE *input = stdin;
Erik Andersen3522eb12000-03-12 23:49:18 +00001216
Eric Andersen6efc48c2000-07-18 08:16:39 +00001217 /* initialize the cwd -- this is never freed...*/
1218 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1219 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen3522eb12000-03-12 23:49:18 +00001220
Eric Andersenfad9c112000-07-25 18:06:52 +00001221#ifdef BB_FEATURE_CLEAN_UP
1222 atexit(free_memory);
1223#endif
1224
Erik Andersenf0657d32000-04-12 17:49:52 +00001225#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersen1d1d9502000-04-21 01:26:49 +00001226 cmdedit_init();
Erik Andersenf0657d32000-04-12 17:49:52 +00001227 signal(SIGWINCH, win_changed);
1228 win_changed(0);
1229#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001230
Erik Andersen161220c2000-03-16 08:12:48 +00001231 //if (argv[0] && argv[0][0] == '-') {
Eric Andersen34e19412000-07-10 18:47:24 +00001232 // builtin_source("/etc/profile");
Erik Andersen161220c2000-03-16 08:12:48 +00001233 //}
Erik Andersen3522eb12000-03-12 23:49:18 +00001234
Eric Andersen6efc48c2000-07-18 08:16:39 +00001235
Erik Andersen161220c2000-03-16 08:12:48 +00001236 if (argc < 2) {
Eric Andersen6efc48c2000-07-18 08:16:39 +00001237 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
Erik Andersenf0657d32000-04-12 17:49:52 +00001238 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Erik Andersen161220c2000-03-16 08:12:48 +00001239 } else {
Eric Andersen1c314ad2000-06-28 16:56:25 +00001240 if (argv[1][0]=='-' && argv[1][1]=='c') {
1241 int i;
1242 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1243 if (local_pending_command == 0) {
Matt Kraaibe84cd42000-07-12 17:02:35 +00001244 fatalError("out of memory\n");
Eric Andersen1c314ad2000-06-28 16:56:25 +00001245 }
1246 for(i=2; i<argc; i++)
1247 {
1248 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
Eric Andersenb54833c2000-07-03 23:56:26 +00001249 local_pending_command = realloc(local_pending_command,
1250 strlen(local_pending_command) + strlen(argv[i]));
1251 if (local_pending_command==NULL)
Matt Kraaibe84cd42000-07-12 17:02:35 +00001252 fatalError("commands for -c option too long\n");
Eric Andersen1c314ad2000-06-28 16:56:25 +00001253 }
1254 strcat(local_pending_command, argv[i]);
Eric Andersenb54833c2000-07-03 23:56:26 +00001255 if ( (i + 1) < argc)
Eric Andersen1c314ad2000-06-28 16:56:25 +00001256 strcat(local_pending_command, " ");
1257 }
1258 input = NULL;
1259
Erik Andersene5b6c7d2000-04-17 16:16:10 +00001260 }
Eric Andersen1c314ad2000-06-28 16:56:25 +00001261 else if (argv[1][0]=='-') {
1262 usage(shell_usage);
1263 }
1264 else {
1265 input = fopen(argv[1], "r");
1266 if (!input) {
Matt Kraaibe84cd42000-07-12 17:02:35 +00001267 fatalError("Couldn't open file '%s': %s\n", argv[1],
Eric Andersen1c314ad2000-06-28 16:56:25 +00001268 strerror(errno));
1269 }
Erik Andersenf0657d32000-04-12 17:49:52 +00001270 }
Erik Andersen161220c2000-03-16 08:12:48 +00001271 }
Erik Andersend75af992000-03-16 08:09:09 +00001272
Erik Andersen161220c2000-03-16 08:12:48 +00001273 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +00001274}