blob: 461f2c0920e3f92c38891e147a001a26e4140713 [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#include "cmdedit.h"
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"
Eric Andersen8c725e62000-11-30 00:27:06 +000053extern size_t NUM_APPLETS;
Erik Andersen3522eb12000-03-12 23:49:18 +000054
Erik Andersend75af992000-03-16 08:09:09 +000055
Eric Andersenb558e762000-11-30 22:43:16 +000056
57
Erik Andersend75af992000-03-16 08:09:09 +000058enum 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
Eric Andersenb558e762000-11-30 22:43:16 +0000170static char prompt[3];
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
Eric Andersenb558e762000-11-30 22:43:16 +0000183#ifdef DEBUG_SHELL
184static inline void debug_printf(const char *format, ...)
185{
186 va_list args;
187 va_start(args, format);
188 vfprintf(stderr, s, p);
189 va_end(args);
190}
191#else
192static inline void debug_printf(const char *format, ...) { }
193#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000194
Erik Andersenf0657d32000-04-12 17:49:52 +0000195#ifdef BB_FEATURE_SH_COMMAND_EDITING
Eric Andersenb558e762000-11-30 22:43:16 +0000196static inline void win_changed(int junk)
Erik Andersenf0657d32000-04-12 17:49:52 +0000197{
Eric Andersenfad04fd2000-07-14 06:49:52 +0000198 struct winsize win = { 0, 0, 0, 0 };
Erik Andersenf0657d32000-04-12 17:49:52 +0000199 ioctl(0, TIOCGWINSZ, &win);
200 if (win.ws_col > 0) {
201 cmdedit_setwidth( win.ws_col - 1);
202 }
203}
Eric Andersenb558e762000-11-30 22:43:16 +0000204#else
205static inline void win_changed(int junk) {}
Erik Andersenf0657d32000-04-12 17:49:52 +0000206#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000207
Erik Andersen3522eb12000-03-12 23:49:18 +0000208
Erik Andersend75af992000-03-16 08:09:09 +0000209/* built-in 'cd <path>' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000210static int builtin_cd(struct job *cmd, struct jobSet *junk)
Erik Andersend75af992000-03-16 08:09:09 +0000211{
Erik Andersen161220c2000-03-16 08:12:48 +0000212 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000213
Erik Andersen161220c2000-03-16 08:12:48 +0000214 if (!cmd->progs[0].argv[1] == 1)
215 newdir = getenv("HOME");
216 else
217 newdir = cmd->progs[0].argv[1];
218 if (chdir(newdir)) {
219 printf("cd: %s: %s\n", newdir, strerror(errno));
220 return FALSE;
221 }
Eric Andersen6efc48c2000-07-18 08:16:39 +0000222 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen161220c2000-03-16 08:12:48 +0000223
224 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000225}
226
227/* built-in 'env' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000228static int builtin_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000229{
Erik Andersen161220c2000-03-16 08:12:48 +0000230 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000231
Erik Andersen161220c2000-03-16 08:12:48 +0000232 for (e = environ; *e; e++) {
233 fprintf(stdout, "%s\n", *e);
234 }
235 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000236}
237
Eric Andersend2f56772000-09-21 02:48:07 +0000238/* built-in 'exec' handler */
239static int builtin_exec(struct job *cmd, struct jobSet *junk)
240{
241 if (cmd->progs[0].argv[1])
242 {
243 cmd->progs[0].argv++;
244 execvp(cmd->progs[0].argv[0], cmd->progs[0].argv);
245 fatalError("Exec to %s failed: %s\n", cmd->progs[0].argv[0],
246 strerror(errno));
247 }
248 return TRUE;
249}
250
Erik Andersen3522eb12000-03-12 23:49:18 +0000251/* built-in 'exit' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000252static int builtin_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000253{
Erik Andersen161220c2000-03-16 08:12:48 +0000254 if (!cmd->progs[0].argv[1] == 1)
255 exit TRUE;
256
Pavel Roskin5f84fd72000-09-15 00:46:51 +0000257 exit (atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000258}
259
260/* built-in 'fg' and 'bg' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000261static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000262{
Erik Andersen161220c2000-03-16 08:12:48 +0000263 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000264 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000265
Erik Andersen161220c2000-03-16 08:12:48 +0000266 if (!jobList->head) {
267 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
Matt Kraaid537a952000-07-14 01:51:25 +0000268 errorMsg("%s: exactly one argument is expected\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000269 cmd->progs[0].argv[0]);
270 return FALSE;
271 }
272 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
Matt Kraaid537a952000-07-14 01:51:25 +0000273 errorMsg("%s: bad argument '%s'\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000274 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
275 return FALSE;
276 for (job = jobList->head; job; job = job->next) {
277 if (job->jobId == jobNum) {
278 break;
279 }
280 }
281 }
282 } else {
283 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000284 }
Erik Andersen161220c2000-03-16 08:12:48 +0000285
286 if (!job) {
Matt Kraaid537a952000-07-14 01:51:25 +0000287 errorMsg("%s: unknown job %d\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000288 cmd->progs[0].argv[0], jobNum);
289 return FALSE;
Erik Andersend75af992000-03-16 08:09:09 +0000290 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000291
Erik Andersen161220c2000-03-16 08:12:48 +0000292 if (*cmd->progs[0].argv[0] == 'f') {
293 /* Make this job the foreground job */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000294 /* suppress messages when run from /linuxrc mag@sysgo.de */
295 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
296 perror("tcsetpgrp");
Erik Andersen161220c2000-03-16 08:12:48 +0000297 jobList->fg = job;
298 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000299
Erik Andersen161220c2000-03-16 08:12:48 +0000300 /* Restart the processes in the job */
301 for (i = 0; i < job->numProgs; i++)
302 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000303
Erik Andersen161220c2000-03-16 08:12:48 +0000304 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000305
Erik Andersen161220c2000-03-16 08:12:48 +0000306 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000307
Erik Andersen161220c2000-03-16 08:12:48 +0000308 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000309}
310
311/* built-in 'help' handler */
Eric Andersenfad04fd2000-07-14 06:49:52 +0000312static int builtin_help(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000313{
Erik Andersen161220c2000-03-16 08:12:48 +0000314 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000315
Erik Andersen161220c2000-03-16 08:12:48 +0000316 fprintf(stdout, "\nBuilt-in commands:\n");
317 fprintf(stdout, "-------------------\n");
318 for (x = bltins; x->cmd; x++) {
Eric Andersenfad9c112000-07-25 18:06:52 +0000319 if (x->descr==NULL)
320 continue;
Erik Andersen161220c2000-03-16 08:12:48 +0000321 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
322 }
Erik Andersen330fd2b2000-05-19 05:35:19 +0000323 for (x = bltins_forking; x->cmd; x++) {
Eric Andersenfad9c112000-07-25 18:06:52 +0000324 if (x->descr==NULL)
325 continue;
Erik Andersen330fd2b2000-05-19 05:35:19 +0000326 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
327 }
Erik Andersen161220c2000-03-16 08:12:48 +0000328 fprintf(stdout, "\n\n");
329 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000330}
331
332/* built-in 'jobs' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000333static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000334{
Erik Andersen161220c2000-03-16 08:12:48 +0000335 struct job *job;
336 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000337
Erik Andersen161220c2000-03-16 08:12:48 +0000338 for (job = jobList->head; job; job = job->next) {
339 if (job->runningProgs == job->stoppedProgs)
340 statusString = "Stopped";
341 else
342 statusString = "Running";
343
344 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
345 }
346 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000347}
348
349
350/* built-in 'pwd' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000351static int builtin_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000352{
Eric Andersen6efc48c2000-07-18 08:16:39 +0000353 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen161220c2000-03-16 08:12:48 +0000354 fprintf(stdout, "%s\n", cwd);
355 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000356}
357
Erik Andersen6273f652000-03-17 01:12:41 +0000358/* built-in 'export VAR=value' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000359static int builtin_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000360{
Erik Andersen161220c2000-03-16 08:12:48 +0000361 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000362
Erik Andersen161220c2000-03-16 08:12:48 +0000363 if (!cmd->progs[0].argv[1] == 1) {
Eric Andersen34e19412000-07-10 18:47:24 +0000364 return (builtin_env(cmd, junk));
Erik Andersen161220c2000-03-16 08:12:48 +0000365 }
366 res = putenv(cmd->progs[0].argv[1]);
367 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000368 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000369 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000370}
371
Eric Andersenb54833c2000-07-03 23:56:26 +0000372/* built-in 'read VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000373static int builtin_read(struct job *cmd, struct jobSet *junk)
Eric Andersenb54833c2000-07-03 23:56:26 +0000374{
375 int res = 0, len, newlen;
376 char *s;
377 char string[MAX_READ];
378
379 if (cmd->progs[0].argv[1]) {
380 /* argument (VAR) given: put "VAR=" into buffer */
381 strcpy(string, cmd->progs[0].argv[1]);
382 len = strlen(string);
383 string[len++] = '=';
384 string[len] = '\0';
385 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
386 newlen = strlen(string);
387 if(newlen > len)
388 string[--newlen] = '\0'; /* chomp trailing newline */
389 /*
390 ** string should now contain "VAR=<value>"
391 ** copy it (putenv() won't do that, so we must make sure
392 ** the string resides in a static buffer!)
393 */
394 res = -1;
395 if((s = strdup(string)))
396 res = putenv(s);
397 if (res)
398 fprintf(stdout, "read: %s\n", strerror(errno));
399 }
400 else
401 fgets(string, sizeof(string), stdin);
402
403 return (res);
404}
405
Eric Andersenfad9c112000-07-25 18:06:52 +0000406#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
407/* Built-in handler for 'if' commands */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000408static int builtin_if(struct job *cmd, struct jobSet *jobList)
Eric Andersenfad9c112000-07-25 18:06:52 +0000409{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000410 int status;
411 char* charptr1=cmd->text+3; /* skip over the leading 'if ' */
412
413 /* Now run the 'if' command */
414 status=strlen(charptr1);
415 local_pending_command = xmalloc(status+1);
416 strncpy(local_pending_command, charptr1, status);
Eric Andersen501c88b2000-07-28 15:14:45 +0000417 local_pending_command[status]='\0';
Eric Andersenb558e762000-11-30 22:43:16 +0000418 debug_printf(stderr, "'if' now testing '%s'\n", local_pending_command);
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000419 status = busy_loop(NULL); /* Frees local_pending_command */
Eric Andersenb558e762000-11-30 22:43:16 +0000420 debug_printf(stderr, "if test returned ");
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000421 if (status == 0) {
Eric Andersenb558e762000-11-30 22:43:16 +0000422 debug_printf(stderr, "TRUE\n");
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000423 cmd->jobContext |= IF_TRUE_CONTEXT;
424 } else {
Eric Andersenb558e762000-11-30 22:43:16 +0000425 debug_printf(stderr, "FALSE\n");
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000426 cmd->jobContext |= IF_FALSE_CONTEXT;
427 }
428
429 return status;
Eric Andersenfad9c112000-07-25 18:06:52 +0000430}
431
432/* Built-in handler for 'then' (part of the 'if' command) */
433static int builtin_then(struct job *cmd, struct jobSet *junk)
434{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000435 int status;
436 char* charptr1=cmd->text+5; /* skip over the leading 'then ' */
437
438 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
439 errorMsg("unexpected token `then'\n");
Eric Andersenfad9c112000-07-25 18:06:52 +0000440 return FALSE;
441 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000442 /* If the if result was FALSE, skip the 'then' stuff */
Eric Andersen501c88b2000-07-28 15:14:45 +0000443 if (cmd->jobContext & IF_FALSE_CONTEXT) {
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000444 return TRUE;
445 }
446
Eric Andersenfad9c112000-07-25 18:06:52 +0000447 cmd->jobContext |= THEN_EXP_CONTEXT;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000448 //printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
449
450 /* Now run the 'then' command */
451 status=strlen(charptr1);
452 local_pending_command = xmalloc(status+1);
453 strncpy(local_pending_command, charptr1, status);
Eric Andersen501c88b2000-07-28 15:14:45 +0000454 local_pending_command[status]='\0';
Eric Andersenb558e762000-11-30 22:43:16 +0000455 debug_printf(stderr, "'then' now running '%s'\n", charptr1);
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000456 return( busy_loop(NULL));
Eric Andersenfad9c112000-07-25 18:06:52 +0000457}
458
459/* Built-in handler for 'else' (part of the 'if' command) */
460static int builtin_else(struct job *cmd, struct jobSet *junk)
461{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000462 int status;
463 char* charptr1=cmd->text+5; /* skip over the leading 'else ' */
464
465 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
466 errorMsg("unexpected token `else'\n");
467 return FALSE;
468 }
469 /* If the if result was TRUE, skip the 'else' stuff */
Eric Andersen501c88b2000-07-28 15:14:45 +0000470 if (cmd->jobContext & IF_TRUE_CONTEXT) {
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000471 return TRUE;
472 }
473
Eric Andersenfad9c112000-07-25 18:06:52 +0000474 cmd->jobContext |= ELSE_EXP_CONTEXT;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000475 //printf("Hit an else -- jobContext=%d\n", cmd->jobContext);
476
477 /* Now run the 'else' command */
478 status=strlen(charptr1);
479 local_pending_command = xmalloc(status+1);
480 strncpy(local_pending_command, charptr1, status);
Eric Andersen501c88b2000-07-28 15:14:45 +0000481 local_pending_command[status]='\0';
Eric Andersenb558e762000-11-30 22:43:16 +0000482 debug_printf(stderr, "'else' now running '%s'\n", charptr1);
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000483 return( busy_loop(NULL));
Eric Andersenfad9c112000-07-25 18:06:52 +0000484}
485
486/* Built-in handler for 'fi' (part of the 'if' command) */
487static int builtin_fi(struct job *cmd, struct jobSet *junk)
488{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000489 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
490 errorMsg("unexpected token `fi'\n");
491 return FALSE;
492 }
493 /* Clear out the if and then context bits */
494 cmd->jobContext &= ~(IF_TRUE_CONTEXT|IF_FALSE_CONTEXT|THEN_EXP_CONTEXT|ELSE_EXP_CONTEXT);
Eric Andersenb558e762000-11-30 22:43:16 +0000495 debug_printf(stderr, "Hit an fi -- jobContext=%d\n", cmd->jobContext);
Eric Andersenfad9c112000-07-25 18:06:52 +0000496 return TRUE;
497}
498#endif
499
Erik Andersen3522eb12000-03-12 23:49:18 +0000500/* Built-in '.' handler (read-in and execute commands from file) */
Eric Andersen34e19412000-07-10 18:47:24 +0000501static int builtin_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000502{
Erik Andersen161220c2000-03-16 08:12:48 +0000503 FILE *input;
504 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000505
Erik Andersen161220c2000-03-16 08:12:48 +0000506 if (!cmd->progs[0].argv[1] == 1)
507 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000508
Erik Andersen161220c2000-03-16 08:12:48 +0000509 input = fopen(cmd->progs[0].argv[1], "r");
510 if (!input) {
511 fprintf(stdout, "Couldn't open file '%s'\n",
512 cmd->progs[0].argv[1]);
513 return FALSE;
514 }
Erik Andersend75af992000-03-16 08:09:09 +0000515
Erik Andersen161220c2000-03-16 08:12:48 +0000516 /* Now run the file */
517 status = busy_loop(input);
Matt Kraaidd450a02000-09-13 03:43:36 +0000518 fclose(input);
Erik Andersen161220c2000-03-16 08:12:48 +0000519 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000520}
521
522/* built-in 'unset VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000523static int builtin_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000524{
Erik Andersen161220c2000-03-16 08:12:48 +0000525 if (!cmd->progs[0].argv[1] == 1) {
526 fprintf(stdout, "unset: parameter required.\n");
527 return FALSE;
528 }
529 unsetenv(cmd->progs[0].argv[1]);
530 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000531}
532
533/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000534static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000535{
Erik Andersen161220c2000-03-16 08:12:48 +0000536 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000537
Erik Andersen161220c2000-03-16 08:12:48 +0000538 for (i = 0; i < cmd->numProgs; i++) {
539 free(cmd->progs[i].argv);
540 if (cmd->progs[i].redirections)
541 free(cmd->progs[i].redirections);
542 if (cmd->progs[i].freeGlob)
543 globfree(&cmd->progs[i].globResult);
544 }
545 free(cmd->progs);
546 if (cmd->text)
547 free(cmd->text);
548 free(cmd->cmdBuf);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000549 memset(cmd, 0, sizeof(struct job));
Erik Andersen3522eb12000-03-12 23:49:18 +0000550}
551
552/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000553static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000554{
Erik Andersen161220c2000-03-16 08:12:48 +0000555 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000556
Erik Andersen161220c2000-03-16 08:12:48 +0000557 freeJob(job);
558 if (job == jobList->head) {
559 jobList->head = job->next;
560 } else {
561 prevJob = jobList->head;
562 while (prevJob->next != job)
563 prevJob = prevJob->next;
564 prevJob->next = job->next;
565 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000566
Erik Andersen161220c2000-03-16 08:12:48 +0000567 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000568}
569
570/* Checks to see if any background processes have exited -- if they
571 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000572static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000573{
Erik Andersen161220c2000-03-16 08:12:48 +0000574 struct job *job;
575 pid_t childpid;
576 int status;
577 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000578
Erik Andersen161220c2000-03-16 08:12:48 +0000579 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
580 for (job = jobList->head; job; job = job->next) {
581 progNum = 0;
582 while (progNum < job->numProgs &&
583 job->progs[progNum].pid != childpid) progNum++;
584 if (progNum < job->numProgs)
585 break;
586 }
587
Eric Andersena1d187a2000-07-17 19:14:41 +0000588 /* This happens on backticked commands */
589 if(job==NULL)
590 return;
591
Erik Andersen161220c2000-03-16 08:12:48 +0000592 if (WIFEXITED(status) || WIFSIGNALED(status)) {
593 /* child exited */
594 job->runningProgs--;
595 job->progs[progNum].pid = 0;
596
597 if (!job->runningProgs) {
598 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
599 removeJob(jobList, job);
600 }
601 } else {
602 /* child stopped */
603 job->stoppedProgs++;
604 job->progs[progNum].isStopped = 1;
605
606 if (job->stoppedProgs == job->numProgs) {
607 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
608 job->text);
609 }
610 }
Erik Andersend75af992000-03-16 08:09:09 +0000611 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000612
Erik Andersen161220c2000-03-16 08:12:48 +0000613 if (childpid == -1 && errno != ECHILD)
614 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000615}
616
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000617static int setupRedirections(struct childProgram *prog)
618{
619 int i;
620 int openfd;
621 int mode = O_RDONLY;
622 struct redirectionSpecifier *redir = prog->redirections;
623
624 for (i = 0; i < prog->numRedirections; i++, redir++) {
625 switch (redir->type) {
626 case REDIRECT_INPUT:
627 mode = O_RDONLY;
628 break;
629 case REDIRECT_OVERWRITE:
Eric Andersen46f0beb2000-11-14 21:59:22 +0000630 mode = O_WRONLY | O_CREAT | O_TRUNC;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000631 break;
632 case REDIRECT_APPEND:
Eric Andersen46f0beb2000-11-14 21:59:22 +0000633 mode = O_WRONLY | O_CREAT | O_APPEND;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000634 break;
635 }
636
637 openfd = open(redir->filename, mode, 0666);
638 if (openfd < 0) {
639 /* this could get lost if stderr has been redirected, but
640 bash and ash both lose it as well (though zsh doesn't!) */
641 errorMsg("error opening %s: %s\n", redir->filename,
642 strerror(errno));
643 return 1;
644 }
645
646 if (openfd != redir->fd) {
647 dup2(openfd, redir->fd);
648 close(openfd);
649 }
650 }
651
652 return 0;
653}
654
655
Erik Andersend75af992000-03-16 08:09:09 +0000656static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000657{
Eric Andersenb558e762000-11-30 22:43:16 +0000658 char *user,buf[255],*s;
659
Eric Andersen1c314ad2000-06-28 16:56:25 +0000660 if (source == NULL) {
661 if (local_pending_command) {
662 /* a command specified (-c option): return it & mark it done */
663 strcpy(command, local_pending_command);
664 free(local_pending_command);
665 local_pending_command = NULL;
666 return 0;
667 }
668 return 1;
669 }
670
Eric Andersenb558e762000-11-30 22:43:16 +0000671 /* get User Name and setup prompt */
672 strcpy(prompt,( geteuid() != 0 ) ? "$ ":"# ");
673 user=xcalloc(sizeof(int), 9);
674 my_getpwuid(user, geteuid());
675
676 /* get HostName */
677 gethostname(buf, 255);
678 s = strchr(buf, '.');
679 if (s)
680 *s = 0;
681
Erik Andersen161220c2000-03-16 08:12:48 +0000682 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000683#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000684 int len;
Eric Andersen501c88b2000-07-28 15:14:45 +0000685
686 /*
687 ** enable command line editing only while a command line
688 ** is actually being read; otherwise, we'll end up bequeathing
689 ** atexit() handlers and other unwanted stuff to our
690 ** child processes (rob@sysgo.de)
691 */
692 cmdedit_init();
693 signal(SIGWINCH, win_changed);
Eric Andersenb558e762000-11-30 22:43:16 +0000694 len=fprintf(stdout, "[%s@%s %s]%s", user, buf,
695 get_last_path_component(cwd), prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000696 fflush(stdout);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000697 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
Eric Andersenb558e762000-11-30 22:43:16 +0000698 sprintf(promptStr, "[%s@%s %s]%s", user, buf,
699 get_last_path_component(cwd), prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000700 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000701 free( promptStr);
Eric Andersen501c88b2000-07-28 15:14:45 +0000702 cmdedit_terminate();
703 signal(SIGWINCH, SIG_DFL);
Erik Andersen161220c2000-03-16 08:12:48 +0000704 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000705#else
Eric Andersenb558e762000-11-30 22:43:16 +0000706 i=strlen(cwd);
707 i--;
708 if (i>1){
709 while ((i>0) && (*(cwd+i)!='/') ) i--;
710 if (*(cwd+i)=='/') i++;
711 }
712
713 fprintf(stdout, "[%s@%s %s]%s",user, buf, (cwd+i), prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000714 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000715#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000716 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000717
Eric Andersenb558e762000-11-30 22:43:16 +0000718 /* don't leak memory */
719 free(user);
720
Erik Andersen161220c2000-03-16 08:12:48 +0000721 if (!fgets(command, BUFSIZ - 2, source)) {
722 if (source == stdin)
723 printf("\n");
724 return 1;
725 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000726
Erik Andersen161220c2000-03-16 08:12:48 +0000727 /* remove trailing newline */
728 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000729
Erik Andersen161220c2000-03-16 08:12:48 +0000730 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000731}
732
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000733#ifdef BB_FEATURE_SH_ENVIRONMENT
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000734static char* itoa(register int i)
735{
Eric Andersenb558e762000-11-30 22:43:16 +0000736 static char a[7]; /* Max 7 ints */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000737 register char *b = a + sizeof(a) - 1;
738 int sign = (i < 0);
739
740 if (sign)
741 i = -i;
742 *b = 0;
743 do
744 {
745 *--b = '0' + (i % 10);
746 i /= 10;
747 }
748 while (i);
749 if (sign)
750 *--b = '-';
751 return b;
752}
753#endif
754
Erik Andersend75af992000-03-16 08:09:09 +0000755static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000756 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000757{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000758 int argc_l = *argcPtr;
Erik Andersen161220c2000-03-16 08:12:48 +0000759 int argcAlloced = *argcAllocedPtr;
760 int rc;
761 int flags;
762 int i;
Eric Andersenb54833c2000-07-03 23:56:26 +0000763 char *src, *dst, *var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000764
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000765 if (argc_l > 1) { /* cmd->globResult is already initialized */
Erik Andersen161220c2000-03-16 08:12:48 +0000766 flags = GLOB_APPEND;
767 i = prog->globResult.gl_pathc;
768 } else {
769 prog->freeGlob = 1;
770 flags = 0;
771 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000772 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000773 /* do shell variable substitution */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000774 if(*prog->argv[argc_l - 1] == '$') {
775 if ((var = getenv(prog->argv[argc_l - 1] + 1))) {
776 prog->argv[argc_l - 1] = var;
777 }
778#ifdef BB_FEATURE_SH_ENVIRONMENT
779 else {
780 switch(*(prog->argv[argc_l - 1] + 1)) {
781 case '?':
782 prog->argv[argc_l - 1] = itoa(lastReturnCode);
783 break;
784 case '$':
785 prog->argv[argc_l - 1] = itoa(getpid());
786 break;
787 case '#':
788 prog->argv[argc_l - 1] = itoa(argc-1);
789 break;
790 case '!':
791 if (lastBgPid==-1)
792 *(prog->argv[argc_l - 1])='\0';
793 else
794 prog->argv[argc_l - 1] = itoa(lastBgPid);
795 break;
796 case '0':case '1':case '2':case '3':case '4':
797 case '5':case '6':case '7':case '8':case '9':
798 {
799 int index=*(prog->argv[argc_l - 1] + 1)-48;
800 if (index >= argc) {
801 *(prog->argv[argc_l - 1])='\0';
802 } else {
803 prog->argv[argc_l - 1] = argv[index];
804 }
805 }
806 break;
807 }
808 }
809#endif
810 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000811
Eric Andersen46f0beb2000-11-14 21:59:22 +0000812 if (strpbrk(prog->argv[argc_l - 1],"*[]?")!= NULL){
813 rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
814 if (rc == GLOB_NOSPACE) {
815 errorMsg("out of space during glob operation\n");
816 return;
817 } else if (rc == GLOB_NOMATCH ||
Erik Andersen161220c2000-03-16 08:12:48 +0000818 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000819 strcmp(prog->argv[argc_l - 1],
Eric Andersene92108a2000-07-26 00:53:56 +0000820 prog->globResult.gl_pathv[i]) == 0)) {
Eric Andersen46f0beb2000-11-14 21:59:22 +0000821 /* we need to remove whatever \ quoting is still present */
822 src = dst = prog->argv[argc_l - 1];
823 while (*src) {
Eric Andersen8c2d3f42000-11-30 00:03:57 +0000824 if (*src == '\\') {
825 src++;
826 *dst++ = process_escape_sequence(&src);
827 } else {
Eric Andersen46f0beb2000-11-14 21:59:22 +0000828 *dst++ = *src;
Eric Andersen8c2d3f42000-11-30 00:03:57 +0000829 src++;
830 }
Eric Andersen46f0beb2000-11-14 21:59:22 +0000831 }
832 *dst = '\0';
833 } else if (!rc) {
834 argcAlloced += (prog->globResult.gl_pathc - i);
835 prog->argv = xrealloc(prog->argv, argcAlloced * sizeof(*prog->argv));
836 memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
837 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
838 argc_l += (prog->globResult.gl_pathc - i - 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000839 }
Eric Andersen46f0beb2000-11-14 21:59:22 +0000840 }else{
841 src = dst = prog->argv[argc_l - 1];
842 while (*src) {
Eric Andersen8c2d3f42000-11-30 00:03:57 +0000843 if (*src == '\\') {
844 src++;
845 *dst++ = process_escape_sequence(&src);
846 } else {
Eric Andersen46f0beb2000-11-14 21:59:22 +0000847 *dst++ = *src;
Eric Andersen8c2d3f42000-11-30 00:03:57 +0000848 src++;
849 }
Eric Andersen46f0beb2000-11-14 21:59:22 +0000850 }
851 *dst = '\0';
Eric Andersen8c2d3f42000-11-30 00:03:57 +0000852
Eric Andersen46f0beb2000-11-14 21:59:22 +0000853 prog->globResult.gl_pathc=0;
854 if (flags==0)
855 prog->globResult.gl_pathv=NULL;
Erik Andersen161220c2000-03-16 08:12:48 +0000856 }
Erik Andersen161220c2000-03-16 08:12:48 +0000857 *argcAllocedPtr = argcAlloced;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000858 *argcPtr = argc_l;
Erik Andersen3522eb12000-03-12 23:49:18 +0000859}
860
861/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
862 line). If a valid command is found, commandPtr is set to point to
863 the beginning of the next command (if the original command had more
864 then one job associated with it) or NULL if no more commands are
865 present. */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000866static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000867{
Erik Andersen161220c2000-03-16 08:12:48 +0000868 char *command;
869 char *returnCommand = NULL;
870 char *src, *buf, *chptr;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000871 int argc_l = 0;
Erik Andersen161220c2000-03-16 08:12:48 +0000872 int done = 0;
873 int argvAlloced;
874 int i;
875 char quote = '\0';
876 int count;
877 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000878
Erik Andersen161220c2000-03-16 08:12:48 +0000879 /* skip leading white space */
880 while (**commandPtr && isspace(**commandPtr))
881 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000882
Erik Andersen161220c2000-03-16 08:12:48 +0000883 /* this handles empty lines or leading '#' characters */
884 if (!**commandPtr || (**commandPtr == '#')) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000885 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000886 return 0;
887 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000888
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000889 *inBg = 0;
Erik Andersen161220c2000-03-16 08:12:48 +0000890 job->numProgs = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000891 job->progs = xmalloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000892
Erik Andersen161220c2000-03-16 08:12:48 +0000893 /* We set the argv elements to point inside of this string. The
Eric Andersenb54833c2000-07-03 23:56:26 +0000894 memory is freed by freeJob(). Allocate twice the original
895 length in case we need to quote every single character.
Erik Andersen3522eb12000-03-12 23:49:18 +0000896
Erik Andersen161220c2000-03-16 08:12:48 +0000897 Getting clean memory relieves us of the task of NULL
898 terminating things and makes the rest of this look a bit
899 cleaner (though it is, admittedly, a tad less efficient) */
Matt Kraaib8907522000-09-13 02:08:21 +0000900 job->cmdBuf = command = xcalloc(2*strlen(*commandPtr) + 1, sizeof(char));
Erik Andersen161220c2000-03-16 08:12:48 +0000901 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000902
Erik Andersen161220c2000-03-16 08:12:48 +0000903 prog = job->progs;
904 prog->numRedirections = 0;
905 prog->redirections = NULL;
906 prog->freeGlob = 0;
907 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000908
Erik Andersen161220c2000-03-16 08:12:48 +0000909 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000910 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000911 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000912
Erik Andersen161220c2000-03-16 08:12:48 +0000913 buf = command;
914 src = *commandPtr;
915 while (*src && !done) {
916 if (quote == *src) {
917 quote = '\0';
918 } else if (quote) {
919 if (*src == '\\') {
920 src++;
921 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000922 errorMsg("character expected after \\\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000923 freeJob(job);
924 return 1;
925 }
926
927 /* in shell, "\'" should yield \' */
928 if (*src != quote)
929 *buf++ = '\\';
930 } else if (*src == '*' || *src == '?' || *src == '[' ||
931 *src == ']') *buf++ = '\\';
932 *buf++ = *src;
933 } else if (isspace(*src)) {
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000934 if (*prog->argv[argc_l]) {
935 buf++, argc_l++;
Erik Andersen161220c2000-03-16 08:12:48 +0000936 /* +1 here leaves room for the NULL which ends argv */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000937 if ((argc_l + 1) == argvAlloced) {
Erik Andersen161220c2000-03-16 08:12:48 +0000938 argvAlloced += 5;
Matt Kraaib8907522000-09-13 02:08:21 +0000939 prog->argv = xrealloc(prog->argv,
940 sizeof(*prog->argv) *
941 argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000942 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000943 globLastArgument(prog, &argc_l, &argvAlloced);
944 prog->argv[argc_l] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000945 }
946 } else
947 switch (*src) {
948 case '"':
949 case '\'':
950 quote = *src;
951 break;
952
953 case '#': /* comment */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000954 if (*(src-1)== '$')
955 *buf++ = *src;
956 else
957 done = 1;
Erik Andersen161220c2000-03-16 08:12:48 +0000958 break;
959
960 case '>': /* redirections */
961 case '<':
962 i = prog->numRedirections++;
Matt Kraaib8907522000-09-13 02:08:21 +0000963 prog->redirections = xrealloc(prog->redirections,
964 sizeof(*prog->redirections) *
965 (i + 1));
Erik Andersen161220c2000-03-16 08:12:48 +0000966
967 prog->redirections[i].fd = -1;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000968 if (buf != prog->argv[argc_l]) {
Erik Andersen161220c2000-03-16 08:12:48 +0000969 /* the stuff before this character may be the file number
970 being redirected */
971 prog->redirections[i].fd =
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000972 strtol(prog->argv[argc_l], &chptr, 10);
Erik Andersen161220c2000-03-16 08:12:48 +0000973
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000974 if (*chptr && *prog->argv[argc_l]) {
975 buf++, argc_l++;
976 globLastArgument(prog, &argc_l, &argvAlloced);
977 prog->argv[argc_l] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000978 }
979 }
980
981 if (prog->redirections[i].fd == -1) {
982 if (*src == '>')
983 prog->redirections[i].fd = 1;
984 else
985 prog->redirections[i].fd = 0;
986 }
987
988 if (*src++ == '>') {
989 if (*src == '>')
990 prog->redirections[i].type =
991 REDIRECT_APPEND, src++;
992 else
993 prog->redirections[i].type = REDIRECT_OVERWRITE;
994 } else {
995 prog->redirections[i].type = REDIRECT_INPUT;
996 }
997
998 /* This isn't POSIX sh compliant. Oh well. */
999 chptr = src;
1000 while (isspace(*chptr))
1001 chptr++;
1002
1003 if (!*chptr) {
Matt Kraaid537a952000-07-14 01:51:25 +00001004 errorMsg("file name expected after %c\n", *src);
Erik Andersen161220c2000-03-16 08:12:48 +00001005 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +00001006 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +00001007 return 1;
1008 }
1009
1010 prog->redirections[i].filename = buf;
1011 while (*chptr && !isspace(*chptr))
1012 *buf++ = *chptr++;
1013
1014 src = chptr - 1; /* we src++ later */
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001015 prog->argv[argc_l] = ++buf;
Erik Andersen161220c2000-03-16 08:12:48 +00001016 break;
1017
1018 case '|': /* pipe */
1019 /* finish this command */
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001020 if (*prog->argv[argc_l])
1021 argc_l++;
1022 if (!argc_l) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001023 errorMsg("empty command in pipe\n");
Erik Andersen161220c2000-03-16 08:12:48 +00001024 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +00001025 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +00001026 return 1;
1027 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001028 prog->argv[argc_l] = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001029
1030 /* and start the next */
1031 job->numProgs++;
Matt Kraaib8907522000-09-13 02:08:21 +00001032 job->progs = xrealloc(job->progs,
1033 sizeof(*job->progs) * job->numProgs);
Erik Andersen161220c2000-03-16 08:12:48 +00001034 prog = job->progs + (job->numProgs - 1);
1035 prog->numRedirections = 0;
1036 prog->redirections = NULL;
1037 prog->freeGlob = 0;
Eric Andersen501c88b2000-07-28 15:14:45 +00001038 prog->isStopped = 0;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001039 argc_l = 0;
Erik Andersen161220c2000-03-16 08:12:48 +00001040
1041 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001042 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +00001043 prog->argv[0] = ++buf;
1044
1045 src++;
1046 while (*src && isspace(*src))
1047 src++;
1048
1049 if (!*src) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001050 errorMsg("empty command in pipe\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +00001051 freeJob(job);
1052 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +00001053 return 1;
1054 }
1055 src--; /* we'll ++ it at the end of the loop */
1056
1057 break;
1058
1059 case '&': /* background */
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001060 *inBg = 1;
Erik Andersen161220c2000-03-16 08:12:48 +00001061 case ';': /* multiple commands */
1062 done = 1;
1063 returnCommand = *commandPtr + (src - *commandPtr) + 1;
1064 break;
1065
Eric Andersena1d187a2000-07-17 19:14:41 +00001066#ifdef BB_FEATURE_SH_BACKTICKS
Eric Andersenec10b9d2000-07-14 01:13:11 +00001067 case '`':
1068 /* Exec a backtick-ed command */
1069 {
Eric Andersena1d187a2000-07-17 19:14:41 +00001070 char* charptr1=NULL, *charptr2;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001071 char* ptr=NULL;
Eric Andersena1d187a2000-07-17 19:14:41 +00001072 struct job *newJob;
1073 struct jobSet njobList = { NULL, NULL };
1074 int pipefd[2];
1075 int size;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001076
1077 ptr=strchr(++src, '`');
1078 if (ptr==NULL) {
1079 fprintf(stderr, "Unmatched '`' in command\n");
1080 freeJob(job);
1081 return 1;
1082 }
1083
Eric Andersena1d187a2000-07-17 19:14:41 +00001084 /* Make some space to hold just the backticked command */
Eric Andersen6efc48c2000-07-18 08:16:39 +00001085 charptr1 = charptr2 = xmalloc(1+ptr-src);
Matt Kraai0b2da462000-09-19 06:46:44 +00001086 memcpy(charptr1, src, ptr-src);
1087 charptr1[ptr-src] = '\0';
Eric Andersena1d187a2000-07-17 19:14:41 +00001088 newJob = xmalloc(sizeof(struct job));
1089 /* Now parse and run the backticked command */
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001090 if (!parseCommand(&charptr1, newJob, &njobList, inBg)
Eric Andersena1d187a2000-07-17 19:14:41 +00001091 && newJob->numProgs) {
1092 pipe(pipefd);
1093 runCommand(newJob, &njobList, 0, pipefd);
Eric Andersenec10b9d2000-07-14 01:13:11 +00001094 }
Eric Andersena1d187a2000-07-17 19:14:41 +00001095 checkJobs(jobList);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001096 freeJob(newJob);
1097 free(charptr2);
1098
1099 /* Make a copy of any stuff left over in the command
1100 * line after the second backtick */
1101 charptr2 = xmalloc(strlen(ptr)+1);
1102 memcpy(charptr2, ptr+1, strlen(ptr));
1103
Eric Andersenec10b9d2000-07-14 01:13:11 +00001104
Eric Andersena1d187a2000-07-17 19:14:41 +00001105 /* Copy the output from the backtick-ed command into the
1106 * command line, making extra room as needed */
1107 --src;
1108 charptr1 = xmalloc(BUFSIZ);
1109 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
1110 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1111 if (newSize > BUFSIZ) {
Matt Kraaib8907522000-09-13 02:08:21 +00001112 *commandPtr=xrealloc(*commandPtr, src - *commandPtr +
Eric Andersena1d187a2000-07-17 19:14:41 +00001113 size + 1 + strlen(charptr2));
1114 }
1115 memcpy(src, charptr1, size);
1116 src+=size;
1117 }
1118 free(charptr1);
1119 close(pipefd[0]);
1120 if (*(src-1)=='\n')
1121 --src;
1122
1123 /* Now paste into the *commandPtr all the stuff
1124 * leftover after the second backtick */
Matt Kraaicbbe4d62000-09-14 00:26:50 +00001125 memcpy(src, charptr2, strlen(charptr2)+1);
Eric Andersena1d187a2000-07-17 19:14:41 +00001126 free(charptr2);
1127
Eric Andersena1d187a2000-07-17 19:14:41 +00001128 /* Now recursively call parseCommand to deal with the new
1129 * and improved version of the command line with the backtick
1130 * results expanded in place... */
Eric Andersen6efc48c2000-07-18 08:16:39 +00001131 freeJob(job);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001132 return(parseCommand(commandPtr, job, jobList, inBg));
Eric Andersenec10b9d2000-07-14 01:13:11 +00001133 }
1134 break;
Eric Andersena1d187a2000-07-17 19:14:41 +00001135#endif // BB_FEATURE_SH_BACKTICKS
Matt Kraai131241f2000-09-14 00:43:20 +00001136
1137 case '\\':
1138 src++;
1139 if (!*src) {
1140 errorMsg("character expected after \\\n");
1141 freeJob(job);
1142 return 1;
1143 }
1144 if (*src == '*' || *src == '[' || *src == ']'
1145 || *src == '?') *buf++ = '\\';
1146 /* fallthrough */
Erik Andersen161220c2000-03-16 08:12:48 +00001147 default:
1148 *buf++ = *src;
1149 }
1150
Erik Andersend75af992000-03-16 08:09:09 +00001151 src++;
Erik Andersen161220c2000-03-16 08:12:48 +00001152 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001153
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001154 if (*prog->argv[argc_l]) {
1155 argc_l++;
1156 globLastArgument(prog, &argc_l, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +00001157 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001158 if (!argc_l) {
Erik Andersen161220c2000-03-16 08:12:48 +00001159 freeJob(job);
1160 return 0;
1161 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001162 prog->argv[argc_l] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +00001163
Erik Andersen161220c2000-03-16 08:12:48 +00001164 if (!returnCommand) {
Eric Andersenec10b9d2000-07-14 01:13:11 +00001165 job->text = xmalloc(strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +00001166 strcpy(job->text, *commandPtr);
1167 } else {
1168 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +00001169 count = returnCommand - *commandPtr;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001170 job->text = xmalloc(count + 1);
Erik Andersen161220c2000-03-16 08:12:48 +00001171 strncpy(job->text, *commandPtr, count);
1172 job->text[count] = '\0';
1173 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001174
Erik Andersen161220c2000-03-16 08:12:48 +00001175 *commandPtr = returnCommand;
Eric Andersen46f0beb2000-11-14 21:59:22 +00001176
Erik Andersend75af992000-03-16 08:09:09 +00001177 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001178}
1179
Eric Andersena1d187a2000-07-17 19:14:41 +00001180static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
Erik Andersen3522eb12000-03-12 23:49:18 +00001181{
Eric Andersenfad9c112000-07-25 18:06:52 +00001182 struct job *theJob;
Erik Andersen161220c2000-03-16 08:12:48 +00001183 int i;
Eric Andersen6efc48c2000-07-18 08:16:39 +00001184 int nextin, nextout;
Erik Andersen161220c2000-03-16 08:12:48 +00001185 int pipefds[2]; /* pipefd[0] is for reading */
1186 struct builtInCommand *x;
Eric Andersenb54833c2000-07-03 23:56:26 +00001187#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Eric Andersena683ee82000-11-17 18:51:45 +00001188 struct BB_applet search_applet, *applet;
Erik Andersenbcd61772000-05-13 06:33:19 +00001189#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001190
Eric Andersen6efc48c2000-07-18 08:16:39 +00001191 nextin = 0, nextout = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001192 for (i = 0; i < newJob->numProgs; i++) {
1193 if ((i + 1) < newJob->numProgs) {
Erik Andersen161220c2000-03-16 08:12:48 +00001194 pipe(pipefds);
1195 nextout = pipefds[1];
1196 } else {
Eric Andersen6efc48c2000-07-18 08:16:39 +00001197 if (outPipe[1]!=-1) {
1198 nextout = outPipe[1];
1199 } else {
1200 nextout = 1;
1201 }
Erik Andersen161220c2000-03-16 08:12:48 +00001202 }
1203
Eric Andersen501c88b2000-07-28 15:14:45 +00001204#ifdef BB_FEATURE_SH_ENVIRONMENT
1205 if (showXtrace==TRUE) {
1206 int j;
1207 fprintf(stderr, "+ ");
1208 for (j = 0; newJob->progs[i].argv[j]; j++)
1209 fprintf(stderr, "%s ", newJob->progs[i].argv[j]);
1210 fprintf(stderr, "\n");
1211 }
1212#endif
1213
Eric Andersen34e19412000-07-10 18:47:24 +00001214 /* Check if the command matches any non-forking builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +00001215 for (x = bltins; x->cmd; x++) {
Eric Andersene92108a2000-07-26 00:53:56 +00001216 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001217 return(x->function(newJob, jobList));
Erik Andersen330fd2b2000-05-19 05:35:19 +00001218 }
1219 }
1220
Eric Andersenec10b9d2000-07-14 01:13:11 +00001221 if (!(newJob->progs[i].pid = fork())) {
Erik Andersen161220c2000-03-16 08:12:48 +00001222 signal(SIGTTOU, SIG_DFL);
1223
Eric Andersena1d187a2000-07-17 19:14:41 +00001224 if (outPipe[1]!=-1) {
1225 close(outPipe[0]);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001226 }
1227 if (nextin != 0) {
1228 dup2(nextin, 0);
1229 close(nextin);
1230 }
1231
1232 if (nextout != 1) {
Erik Andersen161220c2000-03-16 08:12:48 +00001233 dup2(nextout, 1);
Eric Andersena1d187a2000-07-17 19:14:41 +00001234 dup2(nextout, 2);
Erik Andersen161220c2000-03-16 08:12:48 +00001235 close(nextout);
Eric Andersen501c88b2000-07-28 15:14:45 +00001236 close(pipefds[0]);
Erik Andersen161220c2000-03-16 08:12:48 +00001237 }
1238
1239 /* explicit redirections override pipes */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001240 setupRedirections(newJob->progs + i);
Erik Andersen161220c2000-03-16 08:12:48 +00001241
Eric Andersen34e19412000-07-10 18:47:24 +00001242 /* Check if the command matches any of the other builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +00001243 for (x = bltins_forking; x->cmd; x++) {
Eric Andersene92108a2000-07-26 00:53:56 +00001244 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001245 applet_name=x->cmd;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001246 exit (x->function(newJob, jobList));
Erik Andersenbcd61772000-05-13 06:33:19 +00001247 }
1248 }
Eric Andersenb54833c2000-07-03 23:56:26 +00001249#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Eric Andersene5aef922000-11-17 18:25:26 +00001250 /* Check if the command matches any busybox internal
1251 * commands ("applets") here. Following discussions from
1252 * November 2000 on busybox@opensource.lineo.com, don't use
1253 * get_last_path_component(). This way explicit (with
1254 * slashes) filenames will never be interpreted as an
1255 * applet, just like with builtins. This way the user can
1256 * override an applet with an explicit filename reference.
1257 * The only downside to this change is that an explicit
1258 * /bin/foo invocation fill fork and exec /bin/foo, even if
1259 * /bin/foo is a symlink to busybox.
1260 */
1261 search_applet.name = newJob->progs[i].argv[0];
1262
Eric Andersen50b31132000-11-17 18:07:30 +00001263#ifdef BB_FEATURE_SH_BUILTINS_ALWAYS_WIN
Eric Andersene5aef922000-11-17 18:25:26 +00001264 /* If you enable BB_FEATURE_SH_BUILTINS_ALWAYS_WIN, then
1265 * if you run /bin/cat, it will use BusyBox cat even if
1266 * /bin/cat exists on the filesystem and is _not_ busybox.
1267 * Some systems want this, others do not. Choose wisely. :-)
1268 */
1269 search_applet.name = get_last_path_component(search_applet.name);
Eric Andersen50b31132000-11-17 18:07:30 +00001270#endif
Eric Andersene5aef922000-11-17 18:25:26 +00001271
1272 /* Do a binary search to find the applet entry given the name. */
1273 applet = bsearch(&search_applet, applets, NUM_APPLETS,
1274 sizeof(struct BB_applet), applet_name_compare);
1275 if (applet != NULL) {
1276 int argc_l;
1277 char** argv=newJob->progs[i].argv;
1278 for(argc_l=0;*argv!=NULL; argv++, argc_l++);
1279 applet_name=applet->name;
1280 optind = 1;
1281 exit((*(applet->main)) (argc_l, newJob->progs[i].argv));
Erik Andersenbcd61772000-05-13 06:33:19 +00001282 }
1283#endif
1284
Eric Andersenec10b9d2000-07-14 01:13:11 +00001285 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001286 fatalError("%s: %s\n", newJob->progs[i].argv[0],
Eric Andersene5aef922000-11-17 18:25:26 +00001287 strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +00001288 }
Eric Andersen6efc48c2000-07-18 08:16:39 +00001289 if (outPipe[1]!=-1) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001290 close(outPipe[1]);
1291 }
Erik Andersen161220c2000-03-16 08:12:48 +00001292
1293 /* put our child in the process group whose leader is the
1294 first process in this pipe */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001295 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
Erik Andersen161220c2000-03-16 08:12:48 +00001296 if (nextin != 0)
1297 close(nextin);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001298 if (nextout != 1)
Erik Andersen161220c2000-03-16 08:12:48 +00001299 close(nextout);
1300
1301 /* If there isn't another process, nextin is garbage
1302 but it doesn't matter */
1303 nextin = pipefds[0];
1304 }
1305
Eric Andersenec10b9d2000-07-14 01:13:11 +00001306 newJob->pgrp = newJob->progs[0].pid;
Erik Andersen161220c2000-03-16 08:12:48 +00001307
Eric Andersenfad9c112000-07-25 18:06:52 +00001308 /* find the ID for the theJob to use */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001309 newJob->jobId = 1;
Eric Andersenfad9c112000-07-25 18:06:52 +00001310 for (theJob = jobList->head; theJob; theJob = theJob->next)
1311 if (theJob->jobId >= newJob->jobId)
1312 newJob->jobId = theJob->jobId + 1;
Erik Andersen161220c2000-03-16 08:12:48 +00001313
Eric Andersenfad9c112000-07-25 18:06:52 +00001314 /* add the theJob to the list of running jobs */
Erik Andersen161220c2000-03-16 08:12:48 +00001315 if (!jobList->head) {
Matt Kraaib8907522000-09-13 02:08:21 +00001316 theJob = jobList->head = xmalloc(sizeof(*theJob));
Erik Andersend75af992000-03-16 08:09:09 +00001317 } else {
Eric Andersenfad9c112000-07-25 18:06:52 +00001318 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
Matt Kraaib8907522000-09-13 02:08:21 +00001319 theJob->next = xmalloc(sizeof(*theJob));
Eric Andersenfad9c112000-07-25 18:06:52 +00001320 theJob = theJob->next;
Erik Andersend75af992000-03-16 08:09:09 +00001321 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001322
Eric Andersenfad9c112000-07-25 18:06:52 +00001323 *theJob = *newJob;
1324 theJob->next = NULL;
1325 theJob->runningProgs = theJob->numProgs;
1326 theJob->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001327
Erik Andersen161220c2000-03-16 08:12:48 +00001328 if (inBg) {
Eric Andersenfad9c112000-07-25 18:06:52 +00001329 /* we don't wait for background theJobs to return -- append it
1330 to the list of backgrounded theJobs and leave it alone */
1331 printf("[%d] %d\n", theJob->jobId,
Eric Andersenec10b9d2000-07-14 01:13:11 +00001332 newJob->progs[newJob->numProgs - 1].pid);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001333#ifdef BB_FEATURE_SH_ENVIRONMENT
1334 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1335#endif
Erik Andersen161220c2000-03-16 08:12:48 +00001336 } else {
Eric Andersenfad9c112000-07-25 18:06:52 +00001337 jobList->fg = theJob;
Erik Andersen3522eb12000-03-12 23:49:18 +00001338
Erik Andersen161220c2000-03-16 08:12:48 +00001339 /* move the new process group into the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001340 /* suppress messages when run from /linuxrc mag@sysgo.de */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001341 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
Erik Andersen161220c2000-03-16 08:12:48 +00001342 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001343 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001344
Erik Andersen161220c2000-03-16 08:12:48 +00001345 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001346}
1347
Erik Andersend75af992000-03-16 08:09:09 +00001348static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +00001349{
Erik Andersen161220c2000-03-16 08:12:48 +00001350 char *command;
1351 char *nextCommand = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001352 struct job newJob;
Eric Andersen1c314ad2000-06-28 16:56:25 +00001353 pid_t parent_pgrp;
Erik Andersen161220c2000-03-16 08:12:48 +00001354 int i;
Erik Andersen161220c2000-03-16 08:12:48 +00001355 int inBg;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001356 int status;
1357 newJob.jobContext = REGULAR_JOB_CONTEXT;
Erik Andersen3522eb12000-03-12 23:49:18 +00001358
Eric Andersen1c314ad2000-06-28 16:56:25 +00001359 /* save current owner of TTY so we can restore it on exit */
1360 parent_pgrp = tcgetpgrp(0);
1361
Matt Kraaib8907522000-09-13 02:08:21 +00001362 command = (char *) xcalloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +00001363
Erik Andersen161220c2000-03-16 08:12:48 +00001364 /* don't pay any attention to this signal; it just confuses
1365 things and isn't really meant for shells anyway */
1366 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +00001367
Erik Andersen161220c2000-03-16 08:12:48 +00001368 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +00001369 if (!jobList.fg) {
1370 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +00001371
Erik Andersend75af992000-03-16 08:09:09 +00001372 /* see if any background processes have exited */
1373 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +00001374
Erik Andersend75af992000-03-16 08:09:09 +00001375 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +00001376 if (getCommand(input, command))
1377 break;
1378 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +00001379 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001380
Eric Andersenec10b9d2000-07-14 01:13:11 +00001381 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +00001382 newJob.numProgs) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001383 int pipefds[2] = {-1,-1};
1384 runCommand(&newJob, &jobList, inBg, pipefds);
Eric Andersenfad9c112000-07-25 18:06:52 +00001385 }
1386 else {
Eric Andersena1d187a2000-07-17 19:14:41 +00001387 free(command);
Matt Kraaib8907522000-09-13 02:08:21 +00001388 command = (char *) xcalloc(BUFSIZ, sizeof(char));
Eric Andersen6efc48c2000-07-18 08:16:39 +00001389 nextCommand = NULL;
Erik Andersend75af992000-03-16 08:09:09 +00001390 }
1391 } else {
1392 /* a job is running in the foreground; wait for it */
1393 i = 0;
1394 while (!jobList.fg->progs[i].pid ||
Eric Andersenfad9c112000-07-25 18:06:52 +00001395 jobList.fg->progs[i].isStopped == 1) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +00001396
Erik Andersend75af992000-03-16 08:09:09 +00001397 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +00001398
Erik Andersend75af992000-03-16 08:09:09 +00001399 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +00001400 /* the child exited */
1401 jobList.fg->runningProgs--;
1402 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001403
Eric Andersen501c88b2000-07-28 15:14:45 +00001404#ifdef BB_FEATURE_SH_ENVIRONMENT
1405 lastReturnCode=WEXITSTATUS(status);
1406#endif
Eric Andersenb558e762000-11-30 22:43:16 +00001407 debug_printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode);
Erik Andersen161220c2000-03-16 08:12:48 +00001408 if (!jobList.fg->runningProgs) {
1409 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +00001410
Erik Andersen161220c2000-03-16 08:12:48 +00001411 removeJob(&jobList, jobList.fg);
1412 jobList.fg = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001413 }
Erik Andersend75af992000-03-16 08:09:09 +00001414 } else {
Erik Andersen161220c2000-03-16 08:12:48 +00001415 /* the child was stopped */
1416 jobList.fg->stoppedProgs++;
1417 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +00001418
Erik Andersen161220c2000-03-16 08:12:48 +00001419 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1420 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1421 "Stopped", jobList.fg->text);
1422 jobList.fg = NULL;
1423 }
Erik Andersend75af992000-03-16 08:09:09 +00001424 }
1425
1426 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +00001427 /* move the shell to the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001428 /* suppress messages when run from /linuxrc mag@sysgo.de */
1429 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1430 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001431 }
1432 }
1433 }
Erik Andersen161220c2000-03-16 08:12:48 +00001434 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +00001435
Eric Andersen1c314ad2000-06-28 16:56:25 +00001436 /* return controlling TTY back to parent process group before exiting */
1437 if (tcsetpgrp(0, parent_pgrp))
Eric Andersenb54833c2000-07-03 23:56:26 +00001438 perror("tcsetpgrp");
1439
1440 /* return exit status if called with "-c" */
1441 if (input == NULL && WIFEXITED(status))
1442 return WEXITSTATUS(status);
1443
Erik Andersen161220c2000-03-16 08:12:48 +00001444 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001445}
1446
1447
Eric Andersenfad9c112000-07-25 18:06:52 +00001448#ifdef BB_FEATURE_CLEAN_UP
1449void free_memory(void)
1450{
1451 if (promptStr)
1452 free(promptStr);
1453 if (cwd)
1454 free(cwd);
1455 if (local_pending_command)
1456 free(local_pending_command);
1457
1458 if (jobList.fg && !jobList.fg->runningProgs) {
1459 removeJob(&jobList, jobList.fg);
1460 }
1461}
1462#endif
1463
Eric Andersen6efc48c2000-07-18 08:16:39 +00001464
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001465int shell_main(int argc_l, char **argv_l)
Erik Andersen3522eb12000-03-12 23:49:18 +00001466{
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001467 int opt, interactive=FALSE;
Erik Andersen161220c2000-03-16 08:12:48 +00001468 FILE *input = stdin;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001469 argc = argc_l;
1470 argv = argv_l;
Erik Andersen3522eb12000-03-12 23:49:18 +00001471
Eric Andersen501c88b2000-07-28 15:14:45 +00001472
Eric Andersenb558e762000-11-30 22:43:16 +00001473 if (argv[0] && argv[0][0] == '-') {
1474 FILE *input;
1475 input = fopen("/etc/profile", "r");
1476 if (!input) {
1477 fprintf(stdout, "Couldn't open file '/etc/profile'\n");
1478 } else {
1479 /* Now run the file */
1480 busy_loop(input);
1481 fclose(input);
1482 }
1483 }
Eric Andersen501c88b2000-07-28 15:14:45 +00001484
Matt Kraai6085c722000-09-06 01:46:18 +00001485 while ((opt = getopt(argc_l, argv_l, "cx")) > 0) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001486 switch (opt) {
1487 case 'c':
1488 input = NULL;
Matt Kraai6085c722000-09-06 01:46:18 +00001489 if (local_pending_command != 0)
1490 fatalError("multiple -c arguments\n");
1491 local_pending_command = xstrdup(argv[optind]);
1492 optind++;
1493 argv = argv+optind;
Eric Andersen501c88b2000-07-28 15:14:45 +00001494 break;
Eric Andersen1428c4f2000-07-28 15:19:30 +00001495#ifdef BB_FEATURE_SH_ENVIRONMENT
Eric Andersen501c88b2000-07-28 15:14:45 +00001496 case 'x':
1497 showXtrace = TRUE;
1498 break;
Eric Andersen1428c4f2000-07-28 15:19:30 +00001499#endif
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001500 case 'i':
1501 interactive = TRUE;
1502 break;
Eric Andersen501c88b2000-07-28 15:14:45 +00001503 default:
1504 usage(shell_usage);
1505 }
1506 }
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001507 /* A shell is interactive if the `-i' flag was given, or if all of
1508 * the following conditions are met:
1509 * no -c command
1510 * no arguments remaining or the -s flag given
1511 * standard input is a terminal
1512 * standard output is a terminal
1513 * Refer to Posix.2, the description of the `sh' utility. */
1514 if (interactive==TRUE || ( argv[optind]==NULL && input==stdin && isatty(fileno(stdin)) && isatty(fileno(stdout)))) {
Eric Andersen851ce892000-08-21 22:34:23 +00001515 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
Eric Andersen501c88b2000-07-28 15:14:45 +00001516 /* Looks like they want an interactive shell */
1517 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1518 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001519 } else if (local_pending_command==NULL) {
Eric Andersen851ce892000-08-21 22:34:23 +00001520 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
Matt Kraaibbaef662000-09-27 02:43:35 +00001521 input = xfopen(argv[optind], "r");
Eric Andersen501c88b2000-07-28 15:14:45 +00001522 }
1523
Eric Andersen6efc48c2000-07-18 08:16:39 +00001524 /* initialize the cwd -- this is never freed...*/
1525 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1526 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen3522eb12000-03-12 23:49:18 +00001527
Eric Andersenfad9c112000-07-25 18:06:52 +00001528#ifdef BB_FEATURE_CLEAN_UP
1529 atexit(free_memory);
1530#endif
1531
Erik Andersenf0657d32000-04-12 17:49:52 +00001532 win_changed(0);
Erik Andersen161220c2000-03-16 08:12:48 +00001533 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +00001534}