blob: 9fc215c9897c5965436c93de42de4ead2e89acb1 [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 Andersen1e7cea92000-12-06 23:47:38 +000028//
29//This works pretty well now, and is not on by default.
Eric Andersen06f64b22000-09-19 07:16:39 +000030#define BB_FEATURE_SH_ENVIRONMENT
Eric Andersen1e7cea92000-12-06 23:47:38 +000031//
32//Backtick support has some problems, use at your own risk!
33//#define BB_FEATURE_SH_BACKTICKS
34//
35//If, then, else, etc. support is really, really broken. Don't even
36//bother to mess with this yet, since you will not be happy with it.
37//#define BB_FEATURE_SH_IF_EXPRESSIONS
38//
39//For debugging/development on the shell only...
Eric Andersen501c88b2000-07-28 15:14:45 +000040//#define DEBUG_SHELL
Eric Andersena1d187a2000-07-17 19:14:41 +000041
42
Eric Andersen3570a342000-09-25 21:45:58 +000043#include "busybox.h"
Erik Andersen3522eb12000-03-12 23:49:18 +000044#include <stdio.h>
45#include <stdlib.h>
46#include <ctype.h>
47#include <errno.h>
48#include <fcntl.h>
49#include <glob.h>
50#include <signal.h>
51#include <string.h>
52#include <sys/ioctl.h>
53#include <sys/wait.h>
54#include <unistd.h>
Eric Andersen501c88b2000-07-28 15:14:45 +000055#include <getopt.h>
Erik Andersenf0657d32000-04-12 17:49:52 +000056#include "cmdedit.h"
Erik Andersen3522eb12000-03-12 23:49:18 +000057
Eric Andersen6efc48c2000-07-18 08:16:39 +000058#define MAX_LINE 256 /* size of input buffer for `read' builtin */
Eric Andersenb54833c2000-07-03 23:56:26 +000059#define MAX_READ 128 /* size of input buffer for `read' builtin */
Erik Andersen3522eb12000-03-12 23:49:18 +000060#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
Eric Andersen8c725e62000-11-30 00:27:06 +000061extern size_t NUM_APPLETS;
Erik Andersen3522eb12000-03-12 23:49:18 +000062
Erik Andersend75af992000-03-16 08:09:09 +000063
Eric Andersenb558e762000-11-30 22:43:16 +000064
65
Erik Andersend75af992000-03-16 08:09:09 +000066enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
Erik Andersen161220c2000-03-16 08:12:48 +000067 REDIRECT_APPEND
68};
Erik Andersen3522eb12000-03-12 23:49:18 +000069
Eric Andersene92108a2000-07-26 00:53:56 +000070static const unsigned int REGULAR_JOB_CONTEXT=0x1;
Eric Andersen6a99aaf2000-07-27 00:15:20 +000071static const unsigned int IF_TRUE_CONTEXT=0x2;
72static const unsigned int IF_FALSE_CONTEXT=0x4;
73static const unsigned int THEN_EXP_CONTEXT=0x8;
74static const unsigned int ELSE_EXP_CONTEXT=0x10;
Eric Andersene92108a2000-07-26 00:53:56 +000075
Eric Andersenfad9c112000-07-25 18:06:52 +000076
Erik Andersen3522eb12000-03-12 23:49:18 +000077struct jobSet {
Erik Andersen161220c2000-03-16 08:12:48 +000078 struct job *head; /* head of list of running jobs */
79 struct job *fg; /* current foreground job */
Erik Andersen3522eb12000-03-12 23:49:18 +000080};
81
82struct redirectionSpecifier {
Erik Andersen161220c2000-03-16 08:12:48 +000083 enum redirectionType type; /* type of redirection */
84 int fd; /* file descriptor being redirected */
85 char *filename; /* file to redirect fd to */
Erik Andersen3522eb12000-03-12 23:49:18 +000086};
87
88struct childProgram {
Erik Andersen161220c2000-03-16 08:12:48 +000089 pid_t pid; /* 0 if exited */
90 char **argv; /* program name and arguments */
91 int numRedirections; /* elements in redirection array */
92 struct redirectionSpecifier *redirections; /* I/O redirections */
93 glob_t globResult; /* result of parameter globbing */
94 int freeGlob; /* should we globfree(&globResult)? */
95 int isStopped; /* is the program currently running? */
Erik Andersen3522eb12000-03-12 23:49:18 +000096};
97
98struct job {
Erik Andersen161220c2000-03-16 08:12:48 +000099 int jobId; /* job number */
100 int numProgs; /* total number of programs in job */
101 int runningProgs; /* number of programs running */
102 char *text; /* name of job */
103 char *cmdBuf; /* buffer various argv's point into */
104 pid_t pgrp; /* process group ID for the job */
105 struct childProgram *progs; /* array of programs in job */
106 struct job *next; /* to track background commands */
107 int stoppedProgs; /* number of programs alive, but stopped */
Eric Andersenfad9c112000-07-25 18:06:52 +0000108 int jobContext; /* bitmask defining current context */
Erik Andersen3522eb12000-03-12 23:49:18 +0000109};
110
111struct builtInCommand {
Erik Andersen161220c2000-03-16 08:12:48 +0000112 char *cmd; /* name */
113 char *descr; /* description */
Erik Andersen161220c2000-03-16 08:12:48 +0000114 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
Erik Andersen3522eb12000-03-12 23:49:18 +0000115};
116
Eric Andersen34e19412000-07-10 18:47:24 +0000117/* function prototypes for builtins */
118static int builtin_cd(struct job *cmd, struct jobSet *junk);
119static int builtin_env(struct job *dummy, struct jobSet *junk);
Eric Andersend2f56772000-09-21 02:48:07 +0000120static int builtin_exec(struct job *cmd, struct jobSet *junk);
Eric Andersen34e19412000-07-10 18:47:24 +0000121static int builtin_exit(struct job *cmd, struct jobSet *junk);
122static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
123static int builtin_help(struct job *cmd, struct jobSet *junk);
124static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
125static int builtin_pwd(struct job *dummy, struct jobSet *junk);
126static int builtin_export(struct job *cmd, struct jobSet *junk);
127static int builtin_source(struct job *cmd, struct jobSet *jobList);
128static int builtin_unset(struct job *cmd, struct jobSet *junk);
129static int builtin_read(struct job *cmd, struct jobSet *junk);
Eric Andersenfad9c112000-07-25 18:06:52 +0000130#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
131static int builtin_if(struct job *cmd, struct jobSet *junk);
132static int builtin_then(struct job *cmd, struct jobSet *junk);
133static int builtin_else(struct job *cmd, struct jobSet *junk);
134static int builtin_fi(struct job *cmd, struct jobSet *junk);
135#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000136
Eric Andersen34e19412000-07-10 18:47:24 +0000137
138/* function prototypes for shell stuff */
Erik Andersend75af992000-03-16 08:09:09 +0000139static void checkJobs(struct jobSet *jobList);
140static int getCommand(FILE * source, char *command);
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000141static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg);
Eric Andersena1d187a2000-07-17 19:14:41 +0000142static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
Erik Andersen3522eb12000-03-12 23:49:18 +0000143static int busy_loop(FILE * input);
144
Erik Andersend75af992000-03-16 08:09:09 +0000145
Mark Whitley37653aa2000-07-12 23:36:17 +0000146/* Table of built-in functions (these are non-forking builtins, meaning they
147 * can change global variables in the parent shell process but they will not
148 * work with pipes and redirects; 'unset foo | whatever' will not work) */
Erik Andersen3522eb12000-03-12 23:49:18 +0000149static struct builtInCommand bltins[] = {
Eric Andersenfad9c112000-07-25 18:06:52 +0000150 {"bg", "Resume a job in the background", builtin_fg_bg},
151 {"cd", "Change working directory", builtin_cd},
Eric Andersend2f56772000-09-21 02:48:07 +0000152 {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec},
Eric Andersenfad9c112000-07-25 18:06:52 +0000153 {"exit", "Exit from shell()", builtin_exit},
154 {"fg", "Bring job into the foreground", builtin_fg_bg},
155 {"jobs", "Lists the active jobs", builtin_jobs},
156 {"export", "Set environment variable", builtin_export},
157 {"unset", "Unset environment variable", builtin_unset},
158 {"read", "Input environment variable", builtin_read},
Matt Kraaidd450a02000-09-13 03:43:36 +0000159 {".", "Source-in and run commands in a file", builtin_source},
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000160#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
161 {"if", NULL, builtin_if},
162 {"then", NULL, builtin_then},
163 {"else", NULL, builtin_else},
164 {"fi", NULL, builtin_fi},
165#endif
Eric Andersenfad9c112000-07-25 18:06:52 +0000166 {NULL, NULL, NULL}
Erik Andersen330fd2b2000-05-19 05:35:19 +0000167};
168
Mark Whitley37653aa2000-07-12 23:36:17 +0000169/* Table of forking built-in functions (things that fork cannot change global
170 * variables in the parent process, such as the current working directory) */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000171static struct builtInCommand bltins_forking[] = {
Eric Andersenfad9c112000-07-25 18:06:52 +0000172 {"env", "Print all environment variables", builtin_env},
173 {"pwd", "Print current directory", builtin_pwd},
Eric Andersenfad9c112000-07-25 18:06:52 +0000174 {"help", "List shell built-in commands", builtin_help},
175 {NULL, NULL, NULL}
Erik Andersen3522eb12000-03-12 23:49:18 +0000176};
177
Eric Andersenb558e762000-11-30 22:43:16 +0000178static char prompt[3];
Eric Andersen6efc48c2000-07-18 08:16:39 +0000179static char *cwd;
Eric Andersen1c314ad2000-06-28 16:56:25 +0000180static char *local_pending_command = NULL;
Eric Andersenfad9c112000-07-25 18:06:52 +0000181static char *promptStr = NULL;
182static struct jobSet jobList = { NULL, NULL };
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000183static int argc;
184static char **argv;
185#ifdef BB_FEATURE_SH_ENVIRONMENT
186static int lastBgPid=-1;
187static int lastReturnCode=-1;
Eric Andersen501c88b2000-07-28 15:14:45 +0000188static int showXtrace=FALSE;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000189#endif
Eric Andersen501c88b2000-07-28 15:14:45 +0000190
Eric Andersenb558e762000-11-30 22:43:16 +0000191#ifdef DEBUG_SHELL
192static inline void debug_printf(const char *format, ...)
193{
194 va_list args;
195 va_start(args, format);
196 vfprintf(stderr, s, p);
197 va_end(args);
198}
199#else
200static inline void debug_printf(const char *format, ...) { }
201#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000202
Erik Andersenf0657d32000-04-12 17:49:52 +0000203#ifdef BB_FEATURE_SH_COMMAND_EDITING
Eric Andersenb558e762000-11-30 22:43:16 +0000204static inline void win_changed(int junk)
Erik Andersenf0657d32000-04-12 17:49:52 +0000205{
Eric Andersenfad04fd2000-07-14 06:49:52 +0000206 struct winsize win = { 0, 0, 0, 0 };
Erik Andersenf0657d32000-04-12 17:49:52 +0000207 ioctl(0, TIOCGWINSZ, &win);
208 if (win.ws_col > 0) {
209 cmdedit_setwidth( win.ws_col - 1);
210 }
211}
Eric Andersenb558e762000-11-30 22:43:16 +0000212#else
213static inline void win_changed(int junk) {}
Erik Andersenf0657d32000-04-12 17:49:52 +0000214#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000215
Erik Andersen3522eb12000-03-12 23:49:18 +0000216
Erik Andersend75af992000-03-16 08:09:09 +0000217/* built-in 'cd <path>' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000218static int builtin_cd(struct job *cmd, struct jobSet *junk)
Erik Andersend75af992000-03-16 08:09:09 +0000219{
Erik Andersen161220c2000-03-16 08:12:48 +0000220 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000221
Erik Andersen161220c2000-03-16 08:12:48 +0000222 if (!cmd->progs[0].argv[1] == 1)
223 newdir = getenv("HOME");
224 else
225 newdir = cmd->progs[0].argv[1];
226 if (chdir(newdir)) {
227 printf("cd: %s: %s\n", newdir, strerror(errno));
Matt Kraai3e856ce2000-12-01 02:55:13 +0000228 return EXIT_FAILURE;
Erik Andersen161220c2000-03-16 08:12:48 +0000229 }
Eric Andersen6efc48c2000-07-18 08:16:39 +0000230 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen161220c2000-03-16 08:12:48 +0000231
Matt Kraai3e856ce2000-12-01 02:55:13 +0000232 return EXIT_SUCCESS;
Erik Andersen3522eb12000-03-12 23:49:18 +0000233}
234
235/* built-in 'env' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000236static int builtin_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000237{
Erik Andersen161220c2000-03-16 08:12:48 +0000238 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000239
Erik Andersen161220c2000-03-16 08:12:48 +0000240 for (e = environ; *e; e++) {
241 fprintf(stdout, "%s\n", *e);
242 }
243 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000244}
245
Eric Andersend2f56772000-09-21 02:48:07 +0000246/* built-in 'exec' handler */
247static int builtin_exec(struct job *cmd, struct jobSet *junk)
248{
249 if (cmd->progs[0].argv[1])
250 {
251 cmd->progs[0].argv++;
252 execvp(cmd->progs[0].argv[0], cmd->progs[0].argv);
Mark Whitleyf57c9442000-12-07 19:56:48 +0000253 error_msg_and_die("Exec to %s failed: %s\n", cmd->progs[0].argv[0],
Eric Andersend2f56772000-09-21 02:48:07 +0000254 strerror(errno));
255 }
Matt Kraai3e856ce2000-12-01 02:55:13 +0000256 return EXIT_SUCCESS;
Eric Andersend2f56772000-09-21 02:48:07 +0000257}
258
Erik Andersen3522eb12000-03-12 23:49:18 +0000259/* built-in 'exit' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000260static int builtin_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000261{
Erik Andersen161220c2000-03-16 08:12:48 +0000262 if (!cmd->progs[0].argv[1] == 1)
Matt Kraai3e856ce2000-12-01 02:55:13 +0000263 exit(EXIT_SUCCESS);
Erik Andersen161220c2000-03-16 08:12:48 +0000264
Pavel Roskin5f84fd72000-09-15 00:46:51 +0000265 exit (atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000266}
267
268/* built-in 'fg' and 'bg' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000269static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000270{
Erik Andersen161220c2000-03-16 08:12:48 +0000271 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000272 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000273
Erik Andersen161220c2000-03-16 08:12:48 +0000274 if (!jobList->head) {
275 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
Mark Whitleyf57c9442000-12-07 19:56:48 +0000276 error_msg("%s: exactly one argument is expected\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000277 cmd->progs[0].argv[0]);
Matt Kraai3e856ce2000-12-01 02:55:13 +0000278 return EXIT_FAILURE;
Erik Andersen161220c2000-03-16 08:12:48 +0000279 }
280 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
Mark Whitleyf57c9442000-12-07 19:56:48 +0000281 error_msg("%s: bad argument '%s'\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000282 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
Matt Kraai3e856ce2000-12-01 02:55:13 +0000283 return EXIT_FAILURE;
Erik Andersen161220c2000-03-16 08:12:48 +0000284 for (job = jobList->head; job; job = job->next) {
285 if (job->jobId == jobNum) {
286 break;
287 }
288 }
289 }
290 } else {
291 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000292 }
Erik Andersen161220c2000-03-16 08:12:48 +0000293
294 if (!job) {
Mark Whitleyf57c9442000-12-07 19:56:48 +0000295 error_msg("%s: unknown job %d\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000296 cmd->progs[0].argv[0], jobNum);
Matt Kraai3e856ce2000-12-01 02:55:13 +0000297 return EXIT_FAILURE;
Erik Andersend75af992000-03-16 08:09:09 +0000298 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000299
Erik Andersen161220c2000-03-16 08:12:48 +0000300 if (*cmd->progs[0].argv[0] == 'f') {
301 /* Make this job the foreground job */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000302 /* suppress messages when run from /linuxrc mag@sysgo.de */
303 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
304 perror("tcsetpgrp");
Erik Andersen161220c2000-03-16 08:12:48 +0000305 jobList->fg = job;
306 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000307
Erik Andersen161220c2000-03-16 08:12:48 +0000308 /* Restart the processes in the job */
309 for (i = 0; i < job->numProgs; i++)
310 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000311
Erik Andersen161220c2000-03-16 08:12:48 +0000312 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000313
Erik Andersen161220c2000-03-16 08:12:48 +0000314 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000315
Matt Kraai3e856ce2000-12-01 02:55:13 +0000316 return EXIT_SUCCESS;
Erik Andersen3522eb12000-03-12 23:49:18 +0000317}
318
319/* built-in 'help' handler */
Eric Andersenfad04fd2000-07-14 06:49:52 +0000320static int builtin_help(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000321{
Erik Andersen161220c2000-03-16 08:12:48 +0000322 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000323
Erik Andersen161220c2000-03-16 08:12:48 +0000324 fprintf(stdout, "\nBuilt-in commands:\n");
325 fprintf(stdout, "-------------------\n");
326 for (x = bltins; x->cmd; x++) {
Eric Andersenfad9c112000-07-25 18:06:52 +0000327 if (x->descr==NULL)
328 continue;
Erik Andersen161220c2000-03-16 08:12:48 +0000329 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
330 }
Erik Andersen330fd2b2000-05-19 05:35:19 +0000331 for (x = bltins_forking; x->cmd; x++) {
Eric Andersenfad9c112000-07-25 18:06:52 +0000332 if (x->descr==NULL)
333 continue;
Erik Andersen330fd2b2000-05-19 05:35:19 +0000334 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
335 }
Erik Andersen161220c2000-03-16 08:12:48 +0000336 fprintf(stdout, "\n\n");
Matt Kraai3e856ce2000-12-01 02:55:13 +0000337 return EXIT_SUCCESS;
Erik Andersen3522eb12000-03-12 23:49:18 +0000338}
339
340/* built-in 'jobs' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000341static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000342{
Erik Andersen161220c2000-03-16 08:12:48 +0000343 struct job *job;
344 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000345
Erik Andersen161220c2000-03-16 08:12:48 +0000346 for (job = jobList->head; job; job = job->next) {
347 if (job->runningProgs == job->stoppedProgs)
348 statusString = "Stopped";
349 else
350 statusString = "Running";
351
352 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
353 }
Matt Kraai3e856ce2000-12-01 02:55:13 +0000354 return EXIT_SUCCESS;
Erik Andersen3522eb12000-03-12 23:49:18 +0000355}
356
357
358/* built-in 'pwd' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000359static int builtin_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000360{
Eric Andersen6efc48c2000-07-18 08:16:39 +0000361 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen161220c2000-03-16 08:12:48 +0000362 fprintf(stdout, "%s\n", cwd);
Matt Kraai3e856ce2000-12-01 02:55:13 +0000363 return EXIT_SUCCESS;
Erik Andersen3522eb12000-03-12 23:49:18 +0000364}
365
Erik Andersen6273f652000-03-17 01:12:41 +0000366/* built-in 'export VAR=value' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000367static int builtin_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000368{
Erik Andersen161220c2000-03-16 08:12:48 +0000369 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000370
Erik Andersen161220c2000-03-16 08:12:48 +0000371 if (!cmd->progs[0].argv[1] == 1) {
Eric Andersen34e19412000-07-10 18:47:24 +0000372 return (builtin_env(cmd, junk));
Erik Andersen161220c2000-03-16 08:12:48 +0000373 }
374 res = putenv(cmd->progs[0].argv[1]);
375 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000376 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000377 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000378}
379
Eric Andersenb54833c2000-07-03 23:56:26 +0000380/* built-in 'read VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000381static int builtin_read(struct job *cmd, struct jobSet *junk)
Eric Andersenb54833c2000-07-03 23:56:26 +0000382{
383 int res = 0, len, newlen;
384 char *s;
385 char string[MAX_READ];
386
387 if (cmd->progs[0].argv[1]) {
388 /* argument (VAR) given: put "VAR=" into buffer */
389 strcpy(string, cmd->progs[0].argv[1]);
390 len = strlen(string);
391 string[len++] = '=';
392 string[len] = '\0';
393 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
394 newlen = strlen(string);
395 if(newlen > len)
396 string[--newlen] = '\0'; /* chomp trailing newline */
397 /*
398 ** string should now contain "VAR=<value>"
399 ** copy it (putenv() won't do that, so we must make sure
400 ** the string resides in a static buffer!)
401 */
402 res = -1;
403 if((s = strdup(string)))
404 res = putenv(s);
405 if (res)
406 fprintf(stdout, "read: %s\n", strerror(errno));
407 }
408 else
409 fgets(string, sizeof(string), stdin);
410
411 return (res);
412}
413
Eric Andersenfad9c112000-07-25 18:06:52 +0000414#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
415/* Built-in handler for 'if' commands */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000416static int builtin_if(struct job *cmd, struct jobSet *jobList)
Eric Andersenfad9c112000-07-25 18:06:52 +0000417{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000418 int status;
419 char* charptr1=cmd->text+3; /* skip over the leading 'if ' */
420
421 /* Now run the 'if' command */
422 status=strlen(charptr1);
423 local_pending_command = xmalloc(status+1);
424 strncpy(local_pending_command, charptr1, status);
Eric Andersen501c88b2000-07-28 15:14:45 +0000425 local_pending_command[status]='\0';
Eric Andersenb558e762000-11-30 22:43:16 +0000426 debug_printf(stderr, "'if' now testing '%s'\n", local_pending_command);
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000427 status = busy_loop(NULL); /* Frees local_pending_command */
Eric Andersenb558e762000-11-30 22:43:16 +0000428 debug_printf(stderr, "if test returned ");
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000429 if (status == 0) {
Eric Andersenb558e762000-11-30 22:43:16 +0000430 debug_printf(stderr, "TRUE\n");
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000431 cmd->jobContext |= IF_TRUE_CONTEXT;
432 } else {
Eric Andersenb558e762000-11-30 22:43:16 +0000433 debug_printf(stderr, "FALSE\n");
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000434 cmd->jobContext |= IF_FALSE_CONTEXT;
435 }
436
437 return status;
Eric Andersenfad9c112000-07-25 18:06:52 +0000438}
439
440/* Built-in handler for 'then' (part of the 'if' command) */
441static int builtin_then(struct job *cmd, struct jobSet *junk)
442{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000443 int status;
444 char* charptr1=cmd->text+5; /* skip over the leading 'then ' */
445
446 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
Mark Whitleyf57c9442000-12-07 19:56:48 +0000447 error_msg("unexpected token `then'\n");
Matt Kraai3e856ce2000-12-01 02:55:13 +0000448 return EXIT_FAILURE;
Eric Andersenfad9c112000-07-25 18:06:52 +0000449 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000450 /* If the if result was FALSE, skip the 'then' stuff */
Eric Andersen501c88b2000-07-28 15:14:45 +0000451 if (cmd->jobContext & IF_FALSE_CONTEXT) {
Matt Kraai3e856ce2000-12-01 02:55:13 +0000452 return EXIT_SUCCESS;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000453 }
454
Eric Andersenfad9c112000-07-25 18:06:52 +0000455 cmd->jobContext |= THEN_EXP_CONTEXT;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000456 //printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
457
458 /* Now run the 'then' command */
459 status=strlen(charptr1);
460 local_pending_command = xmalloc(status+1);
461 strncpy(local_pending_command, charptr1, status);
Eric Andersen501c88b2000-07-28 15:14:45 +0000462 local_pending_command[status]='\0';
Eric Andersenb558e762000-11-30 22:43:16 +0000463 debug_printf(stderr, "'then' now running '%s'\n", charptr1);
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000464 return( busy_loop(NULL));
Eric Andersenfad9c112000-07-25 18:06:52 +0000465}
466
467/* Built-in handler for 'else' (part of the 'if' command) */
468static int builtin_else(struct job *cmd, struct jobSet *junk)
469{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000470 int status;
471 char* charptr1=cmd->text+5; /* skip over the leading 'else ' */
472
473 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
Mark Whitleyf57c9442000-12-07 19:56:48 +0000474 error_msg("unexpected token `else'\n");
Matt Kraai3e856ce2000-12-01 02:55:13 +0000475 return EXIT_FAILURE;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000476 }
477 /* If the if result was TRUE, skip the 'else' stuff */
Eric Andersen501c88b2000-07-28 15:14:45 +0000478 if (cmd->jobContext & IF_TRUE_CONTEXT) {
Matt Kraai3e856ce2000-12-01 02:55:13 +0000479 return EXIT_SUCCESS;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000480 }
481
Eric Andersenfad9c112000-07-25 18:06:52 +0000482 cmd->jobContext |= ELSE_EXP_CONTEXT;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000483 //printf("Hit an else -- jobContext=%d\n", cmd->jobContext);
484
485 /* Now run the 'else' command */
486 status=strlen(charptr1);
487 local_pending_command = xmalloc(status+1);
488 strncpy(local_pending_command, charptr1, status);
Eric Andersen501c88b2000-07-28 15:14:45 +0000489 local_pending_command[status]='\0';
Eric Andersenb558e762000-11-30 22:43:16 +0000490 debug_printf(stderr, "'else' now running '%s'\n", charptr1);
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000491 return( busy_loop(NULL));
Eric Andersenfad9c112000-07-25 18:06:52 +0000492}
493
494/* Built-in handler for 'fi' (part of the 'if' command) */
495static int builtin_fi(struct job *cmd, struct jobSet *junk)
496{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000497 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
Mark Whitleyf57c9442000-12-07 19:56:48 +0000498 error_msg("unexpected token `fi'\n");
Matt Kraai3e856ce2000-12-01 02:55:13 +0000499 return EXIT_FAILURE;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000500 }
501 /* Clear out the if and then context bits */
502 cmd->jobContext &= ~(IF_TRUE_CONTEXT|IF_FALSE_CONTEXT|THEN_EXP_CONTEXT|ELSE_EXP_CONTEXT);
Eric Andersenb558e762000-11-30 22:43:16 +0000503 debug_printf(stderr, "Hit an fi -- jobContext=%d\n", cmd->jobContext);
Matt Kraai3e856ce2000-12-01 02:55:13 +0000504 return EXIT_SUCCESS;
Eric Andersenfad9c112000-07-25 18:06:52 +0000505}
506#endif
507
Erik Andersen3522eb12000-03-12 23:49:18 +0000508/* Built-in '.' handler (read-in and execute commands from file) */
Eric Andersen34e19412000-07-10 18:47:24 +0000509static int builtin_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000510{
Erik Andersen161220c2000-03-16 08:12:48 +0000511 FILE *input;
512 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000513
Erik Andersen161220c2000-03-16 08:12:48 +0000514 if (!cmd->progs[0].argv[1] == 1)
Matt Kraai3e856ce2000-12-01 02:55:13 +0000515 return EXIT_FAILURE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000516
Erik Andersen161220c2000-03-16 08:12:48 +0000517 input = fopen(cmd->progs[0].argv[1], "r");
518 if (!input) {
519 fprintf(stdout, "Couldn't open file '%s'\n",
520 cmd->progs[0].argv[1]);
Matt Kraai3e856ce2000-12-01 02:55:13 +0000521 return EXIT_FAILURE;
Erik Andersen161220c2000-03-16 08:12:48 +0000522 }
Erik Andersend75af992000-03-16 08:09:09 +0000523
Erik Andersen161220c2000-03-16 08:12:48 +0000524 /* Now run the file */
525 status = busy_loop(input);
Matt Kraaidd450a02000-09-13 03:43:36 +0000526 fclose(input);
Erik Andersen161220c2000-03-16 08:12:48 +0000527 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000528}
529
530/* built-in 'unset VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000531static int builtin_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000532{
Erik Andersen161220c2000-03-16 08:12:48 +0000533 if (!cmd->progs[0].argv[1] == 1) {
534 fprintf(stdout, "unset: parameter required.\n");
Matt Kraai3e856ce2000-12-01 02:55:13 +0000535 return EXIT_FAILURE;
Erik Andersen161220c2000-03-16 08:12:48 +0000536 }
537 unsetenv(cmd->progs[0].argv[1]);
Matt Kraai3e856ce2000-12-01 02:55:13 +0000538 return EXIT_SUCCESS;
Erik Andersen3522eb12000-03-12 23:49:18 +0000539}
540
541/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000542static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000543{
Erik Andersen161220c2000-03-16 08:12:48 +0000544 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000545
Erik Andersen161220c2000-03-16 08:12:48 +0000546 for (i = 0; i < cmd->numProgs; i++) {
547 free(cmd->progs[i].argv);
548 if (cmd->progs[i].redirections)
549 free(cmd->progs[i].redirections);
550 if (cmd->progs[i].freeGlob)
551 globfree(&cmd->progs[i].globResult);
552 }
553 free(cmd->progs);
554 if (cmd->text)
555 free(cmd->text);
556 free(cmd->cmdBuf);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000557 memset(cmd, 0, sizeof(struct job));
Erik Andersen3522eb12000-03-12 23:49:18 +0000558}
559
560/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000561static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000562{
Erik Andersen161220c2000-03-16 08:12:48 +0000563 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000564
Erik Andersen161220c2000-03-16 08:12:48 +0000565 freeJob(job);
566 if (job == jobList->head) {
567 jobList->head = job->next;
568 } else {
569 prevJob = jobList->head;
570 while (prevJob->next != job)
571 prevJob = prevJob->next;
572 prevJob->next = job->next;
573 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000574
Erik Andersen161220c2000-03-16 08:12:48 +0000575 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000576}
577
578/* Checks to see if any background processes have exited -- if they
579 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000580static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000581{
Erik Andersen161220c2000-03-16 08:12:48 +0000582 struct job *job;
583 pid_t childpid;
584 int status;
585 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000586
Erik Andersen161220c2000-03-16 08:12:48 +0000587 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
588 for (job = jobList->head; job; job = job->next) {
589 progNum = 0;
590 while (progNum < job->numProgs &&
591 job->progs[progNum].pid != childpid) progNum++;
592 if (progNum < job->numProgs)
593 break;
594 }
595
Eric Andersena1d187a2000-07-17 19:14:41 +0000596 /* This happens on backticked commands */
597 if(job==NULL)
598 return;
599
Erik Andersen161220c2000-03-16 08:12:48 +0000600 if (WIFEXITED(status) || WIFSIGNALED(status)) {
601 /* child exited */
602 job->runningProgs--;
603 job->progs[progNum].pid = 0;
604
605 if (!job->runningProgs) {
606 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
607 removeJob(jobList, job);
608 }
609 } else {
610 /* child stopped */
611 job->stoppedProgs++;
612 job->progs[progNum].isStopped = 1;
613
614 if (job->stoppedProgs == job->numProgs) {
615 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
616 job->text);
617 }
618 }
Erik Andersend75af992000-03-16 08:09:09 +0000619 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000620
Erik Andersen161220c2000-03-16 08:12:48 +0000621 if (childpid == -1 && errno != ECHILD)
622 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000623}
624
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000625static int setupRedirections(struct childProgram *prog)
626{
627 int i;
628 int openfd;
629 int mode = O_RDONLY;
630 struct redirectionSpecifier *redir = prog->redirections;
631
632 for (i = 0; i < prog->numRedirections; i++, redir++) {
633 switch (redir->type) {
634 case REDIRECT_INPUT:
635 mode = O_RDONLY;
636 break;
637 case REDIRECT_OVERWRITE:
Eric Andersen46f0beb2000-11-14 21:59:22 +0000638 mode = O_WRONLY | O_CREAT | O_TRUNC;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000639 break;
640 case REDIRECT_APPEND:
Eric Andersen46f0beb2000-11-14 21:59:22 +0000641 mode = O_WRONLY | O_CREAT | O_APPEND;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000642 break;
643 }
644
645 openfd = open(redir->filename, mode, 0666);
646 if (openfd < 0) {
647 /* this could get lost if stderr has been redirected, but
648 bash and ash both lose it as well (though zsh doesn't!) */
Mark Whitleyf57c9442000-12-07 19:56:48 +0000649 error_msg("error opening %s: %s\n", redir->filename,
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000650 strerror(errno));
651 return 1;
652 }
653
654 if (openfd != redir->fd) {
655 dup2(openfd, redir->fd);
656 close(openfd);
657 }
658 }
659
660 return 0;
661}
662
663
Erik Andersend75af992000-03-16 08:09:09 +0000664static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000665{
Eric Andersenb558e762000-11-30 22:43:16 +0000666 char *user,buf[255],*s;
667
Eric Andersen1c314ad2000-06-28 16:56:25 +0000668 if (source == NULL) {
669 if (local_pending_command) {
670 /* a command specified (-c option): return it & mark it done */
671 strcpy(command, local_pending_command);
672 free(local_pending_command);
673 local_pending_command = NULL;
674 return 0;
675 }
676 return 1;
677 }
678
Eric Andersenb558e762000-11-30 22:43:16 +0000679 /* get User Name and setup prompt */
680 strcpy(prompt,( geteuid() != 0 ) ? "$ ":"# ");
681 user=xcalloc(sizeof(int), 9);
682 my_getpwuid(user, geteuid());
683
684 /* get HostName */
685 gethostname(buf, 255);
686 s = strchr(buf, '.');
687 if (s)
688 *s = 0;
689
Erik Andersen161220c2000-03-16 08:12:48 +0000690 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000691#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000692 int len;
Eric Andersen501c88b2000-07-28 15:14:45 +0000693
694 /*
695 ** enable command line editing only while a command line
696 ** is actually being read; otherwise, we'll end up bequeathing
697 ** atexit() handlers and other unwanted stuff to our
698 ** child processes (rob@sysgo.de)
699 */
700 cmdedit_init();
701 signal(SIGWINCH, win_changed);
Eric Andersenb558e762000-11-30 22:43:16 +0000702 len=fprintf(stdout, "[%s@%s %s]%s", user, buf,
703 get_last_path_component(cwd), prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000704 fflush(stdout);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000705 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
Eric Andersenb558e762000-11-30 22:43:16 +0000706 sprintf(promptStr, "[%s@%s %s]%s", user, buf,
707 get_last_path_component(cwd), prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000708 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000709 free( promptStr);
Eric Andersen501c88b2000-07-28 15:14:45 +0000710 cmdedit_terminate();
711 signal(SIGWINCH, SIG_DFL);
Erik Andersen161220c2000-03-16 08:12:48 +0000712 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000713#else
Eric Andersenb558e762000-11-30 22:43:16 +0000714 i=strlen(cwd);
715 i--;
716 if (i>1){
717 while ((i>0) && (*(cwd+i)!='/') ) i--;
718 if (*(cwd+i)=='/') i++;
719 }
720
721 fprintf(stdout, "[%s@%s %s]%s",user, buf, (cwd+i), prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000722 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000723#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000724 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000725
Eric Andersenb558e762000-11-30 22:43:16 +0000726 /* don't leak memory */
727 free(user);
728
Erik Andersen161220c2000-03-16 08:12:48 +0000729 if (!fgets(command, BUFSIZ - 2, source)) {
730 if (source == stdin)
731 printf("\n");
732 return 1;
733 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000734
Erik Andersen161220c2000-03-16 08:12:48 +0000735 /* remove trailing newline */
736 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000737
Erik Andersen161220c2000-03-16 08:12:48 +0000738 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000739}
740
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000741#ifdef BB_FEATURE_SH_ENVIRONMENT
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000742static char* itoa(register int i)
743{
Eric Andersenb558e762000-11-30 22:43:16 +0000744 static char a[7]; /* Max 7 ints */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000745 register char *b = a + sizeof(a) - 1;
746 int sign = (i < 0);
747
748 if (sign)
749 i = -i;
750 *b = 0;
751 do
752 {
753 *--b = '0' + (i % 10);
754 i /= 10;
755 }
756 while (i);
757 if (sign)
758 *--b = '-';
759 return b;
760}
761#endif
762
Erik Andersend75af992000-03-16 08:09:09 +0000763static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000764 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000765{
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000766 int argc_l = *argcPtr;
Erik Andersen161220c2000-03-16 08:12:48 +0000767 int argcAlloced = *argcAllocedPtr;
768 int rc;
769 int flags;
770 int i;
Eric Andersenb54833c2000-07-03 23:56:26 +0000771 char *src, *dst, *var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000772
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000773 if (argc_l > 1) { /* cmd->globResult is already initialized */
Erik Andersen161220c2000-03-16 08:12:48 +0000774 flags = GLOB_APPEND;
775 i = prog->globResult.gl_pathc;
776 } else {
777 prog->freeGlob = 1;
778 flags = 0;
779 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000780 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000781 /* do shell variable substitution */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000782 if(*prog->argv[argc_l - 1] == '$') {
783 if ((var = getenv(prog->argv[argc_l - 1] + 1))) {
784 prog->argv[argc_l - 1] = var;
785 }
786#ifdef BB_FEATURE_SH_ENVIRONMENT
787 else {
788 switch(*(prog->argv[argc_l - 1] + 1)) {
789 case '?':
790 prog->argv[argc_l - 1] = itoa(lastReturnCode);
791 break;
792 case '$':
793 prog->argv[argc_l - 1] = itoa(getpid());
794 break;
795 case '#':
796 prog->argv[argc_l - 1] = itoa(argc-1);
797 break;
798 case '!':
799 if (lastBgPid==-1)
800 *(prog->argv[argc_l - 1])='\0';
801 else
802 prog->argv[argc_l - 1] = itoa(lastBgPid);
803 break;
804 case '0':case '1':case '2':case '3':case '4':
805 case '5':case '6':case '7':case '8':case '9':
806 {
807 int index=*(prog->argv[argc_l - 1] + 1)-48;
808 if (index >= argc) {
809 *(prog->argv[argc_l - 1])='\0';
810 } else {
811 prog->argv[argc_l - 1] = argv[index];
812 }
813 }
814 break;
815 }
816 }
817#endif
818 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000819
Eric Andersen46f0beb2000-11-14 21:59:22 +0000820 if (strpbrk(prog->argv[argc_l - 1],"*[]?")!= NULL){
821 rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
822 if (rc == GLOB_NOSPACE) {
Mark Whitleyf57c9442000-12-07 19:56:48 +0000823 error_msg("out of space during glob operation\n");
Eric Andersen46f0beb2000-11-14 21:59:22 +0000824 return;
825 } else if (rc == GLOB_NOMATCH ||
Erik Andersen161220c2000-03-16 08:12:48 +0000826 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000827 strcmp(prog->argv[argc_l - 1],
Eric Andersene92108a2000-07-26 00:53:56 +0000828 prog->globResult.gl_pathv[i]) == 0)) {
Eric Andersen46f0beb2000-11-14 21:59:22 +0000829 /* we need to remove whatever \ quoting is still present */
830 src = dst = prog->argv[argc_l - 1];
831 while (*src) {
Eric Andersen8c2d3f42000-11-30 00:03:57 +0000832 if (*src == '\\') {
833 src++;
834 *dst++ = process_escape_sequence(&src);
835 } else {
Eric Andersen46f0beb2000-11-14 21:59:22 +0000836 *dst++ = *src;
Eric Andersen8c2d3f42000-11-30 00:03:57 +0000837 src++;
838 }
Eric Andersen46f0beb2000-11-14 21:59:22 +0000839 }
840 *dst = '\0';
841 } else if (!rc) {
842 argcAlloced += (prog->globResult.gl_pathc - i);
843 prog->argv = xrealloc(prog->argv, argcAlloced * sizeof(*prog->argv));
844 memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
845 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
846 argc_l += (prog->globResult.gl_pathc - i - 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000847 }
Eric Andersen46f0beb2000-11-14 21:59:22 +0000848 }else{
849 src = dst = prog->argv[argc_l - 1];
850 while (*src) {
Eric Andersen8c2d3f42000-11-30 00:03:57 +0000851 if (*src == '\\') {
852 src++;
853 *dst++ = process_escape_sequence(&src);
854 } else {
Eric Andersen46f0beb2000-11-14 21:59:22 +0000855 *dst++ = *src;
Eric Andersen8c2d3f42000-11-30 00:03:57 +0000856 src++;
857 }
Eric Andersen46f0beb2000-11-14 21:59:22 +0000858 }
859 *dst = '\0';
Eric Andersen8c2d3f42000-11-30 00:03:57 +0000860
Eric Andersen46f0beb2000-11-14 21:59:22 +0000861 prog->globResult.gl_pathc=0;
862 if (flags==0)
863 prog->globResult.gl_pathv=NULL;
Erik Andersen161220c2000-03-16 08:12:48 +0000864 }
Erik Andersen161220c2000-03-16 08:12:48 +0000865 *argcAllocedPtr = argcAlloced;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000866 *argcPtr = argc_l;
Erik Andersen3522eb12000-03-12 23:49:18 +0000867}
868
869/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
870 line). If a valid command is found, commandPtr is set to point to
871 the beginning of the next command (if the original command had more
872 then one job associated with it) or NULL if no more commands are
873 present. */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000874static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000875{
Erik Andersen161220c2000-03-16 08:12:48 +0000876 char *command;
877 char *returnCommand = NULL;
878 char *src, *buf, *chptr;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000879 int argc_l = 0;
Erik Andersen161220c2000-03-16 08:12:48 +0000880 int done = 0;
881 int argvAlloced;
882 int i;
883 char quote = '\0';
884 int count;
885 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000886
Erik Andersen161220c2000-03-16 08:12:48 +0000887 /* skip leading white space */
888 while (**commandPtr && isspace(**commandPtr))
889 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000890
Erik Andersen161220c2000-03-16 08:12:48 +0000891 /* this handles empty lines or leading '#' characters */
892 if (!**commandPtr || (**commandPtr == '#')) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000893 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000894 return 0;
895 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000896
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000897 *inBg = 0;
Erik Andersen161220c2000-03-16 08:12:48 +0000898 job->numProgs = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000899 job->progs = xmalloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000900
Erik Andersen161220c2000-03-16 08:12:48 +0000901 /* We set the argv elements to point inside of this string. The
Eric Andersenb54833c2000-07-03 23:56:26 +0000902 memory is freed by freeJob(). Allocate twice the original
903 length in case we need to quote every single character.
Erik Andersen3522eb12000-03-12 23:49:18 +0000904
Erik Andersen161220c2000-03-16 08:12:48 +0000905 Getting clean memory relieves us of the task of NULL
906 terminating things and makes the rest of this look a bit
907 cleaner (though it is, admittedly, a tad less efficient) */
Matt Kraaib8907522000-09-13 02:08:21 +0000908 job->cmdBuf = command = xcalloc(2*strlen(*commandPtr) + 1, sizeof(char));
Erik Andersen161220c2000-03-16 08:12:48 +0000909 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000910
Erik Andersen161220c2000-03-16 08:12:48 +0000911 prog = job->progs;
912 prog->numRedirections = 0;
913 prog->redirections = NULL;
914 prog->freeGlob = 0;
915 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000916
Erik Andersen161220c2000-03-16 08:12:48 +0000917 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000918 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000919 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000920
Erik Andersen161220c2000-03-16 08:12:48 +0000921 buf = command;
922 src = *commandPtr;
923 while (*src && !done) {
924 if (quote == *src) {
925 quote = '\0';
926 } else if (quote) {
927 if (*src == '\\') {
928 src++;
929 if (!*src) {
Mark Whitleyf57c9442000-12-07 19:56:48 +0000930 error_msg("character expected after \\\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000931 freeJob(job);
932 return 1;
933 }
934
935 /* in shell, "\'" should yield \' */
Eric Andersenb2356f62000-12-11 19:14:40 +0000936 if (*src != quote) {
Erik Andersen161220c2000-03-16 08:12:48 +0000937 *buf++ = '\\';
Eric Andersenb2356f62000-12-11 19:14:40 +0000938 *buf++ = '\\';
939 }
Erik Andersen161220c2000-03-16 08:12:48 +0000940 } else if (*src == '*' || *src == '?' || *src == '[' ||
941 *src == ']') *buf++ = '\\';
942 *buf++ = *src;
943 } else if (isspace(*src)) {
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000944 if (*prog->argv[argc_l]) {
945 buf++, argc_l++;
Erik Andersen161220c2000-03-16 08:12:48 +0000946 /* +1 here leaves room for the NULL which ends argv */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000947 if ((argc_l + 1) == argvAlloced) {
Erik Andersen161220c2000-03-16 08:12:48 +0000948 argvAlloced += 5;
Matt Kraaib8907522000-09-13 02:08:21 +0000949 prog->argv = xrealloc(prog->argv,
950 sizeof(*prog->argv) *
951 argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000952 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000953 globLastArgument(prog, &argc_l, &argvAlloced);
954 prog->argv[argc_l] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000955 }
956 } else
957 switch (*src) {
958 case '"':
959 case '\'':
960 quote = *src;
961 break;
962
963 case '#': /* comment */
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000964 if (*(src-1)== '$')
965 *buf++ = *src;
966 else
967 done = 1;
Erik Andersen161220c2000-03-16 08:12:48 +0000968 break;
969
970 case '>': /* redirections */
971 case '<':
972 i = prog->numRedirections++;
Matt Kraaib8907522000-09-13 02:08:21 +0000973 prog->redirections = xrealloc(prog->redirections,
974 sizeof(*prog->redirections) *
975 (i + 1));
Erik Andersen161220c2000-03-16 08:12:48 +0000976
977 prog->redirections[i].fd = -1;
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000978 if (buf != prog->argv[argc_l]) {
Erik Andersen161220c2000-03-16 08:12:48 +0000979 /* the stuff before this character may be the file number
980 being redirected */
981 prog->redirections[i].fd =
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000982 strtol(prog->argv[argc_l], &chptr, 10);
Erik Andersen161220c2000-03-16 08:12:48 +0000983
Eric Andersen6a99aaf2000-07-27 00:15:20 +0000984 if (*chptr && *prog->argv[argc_l]) {
985 buf++, argc_l++;
986 globLastArgument(prog, &argc_l, &argvAlloced);
987 prog->argv[argc_l] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000988 }
989 }
990
991 if (prog->redirections[i].fd == -1) {
992 if (*src == '>')
993 prog->redirections[i].fd = 1;
994 else
995 prog->redirections[i].fd = 0;
996 }
997
998 if (*src++ == '>') {
999 if (*src == '>')
1000 prog->redirections[i].type =
1001 REDIRECT_APPEND, src++;
1002 else
1003 prog->redirections[i].type = REDIRECT_OVERWRITE;
1004 } else {
1005 prog->redirections[i].type = REDIRECT_INPUT;
1006 }
1007
1008 /* This isn't POSIX sh compliant. Oh well. */
1009 chptr = src;
1010 while (isspace(*chptr))
1011 chptr++;
1012
1013 if (!*chptr) {
Mark Whitleyf57c9442000-12-07 19:56:48 +00001014 error_msg("file name expected after %c\n", *src);
Erik Andersen161220c2000-03-16 08:12:48 +00001015 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +00001016 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +00001017 return 1;
1018 }
1019
1020 prog->redirections[i].filename = buf;
1021 while (*chptr && !isspace(*chptr))
1022 *buf++ = *chptr++;
1023
1024 src = chptr - 1; /* we src++ later */
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001025 prog->argv[argc_l] = ++buf;
Erik Andersen161220c2000-03-16 08:12:48 +00001026 break;
1027
1028 case '|': /* pipe */
1029 /* finish this command */
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001030 if (*prog->argv[argc_l])
1031 argc_l++;
1032 if (!argc_l) {
Mark Whitleyf57c9442000-12-07 19:56:48 +00001033 error_msg("empty command in pipe\n");
Erik Andersen161220c2000-03-16 08:12:48 +00001034 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +00001035 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +00001036 return 1;
1037 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001038 prog->argv[argc_l] = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001039
1040 /* and start the next */
1041 job->numProgs++;
Matt Kraaib8907522000-09-13 02:08:21 +00001042 job->progs = xrealloc(job->progs,
1043 sizeof(*job->progs) * job->numProgs);
Erik Andersen161220c2000-03-16 08:12:48 +00001044 prog = job->progs + (job->numProgs - 1);
1045 prog->numRedirections = 0;
1046 prog->redirections = NULL;
1047 prog->freeGlob = 0;
Eric Andersen501c88b2000-07-28 15:14:45 +00001048 prog->isStopped = 0;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001049 argc_l = 0;
Erik Andersen161220c2000-03-16 08:12:48 +00001050
1051 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001052 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +00001053 prog->argv[0] = ++buf;
1054
1055 src++;
1056 while (*src && isspace(*src))
1057 src++;
1058
1059 if (!*src) {
Mark Whitleyf57c9442000-12-07 19:56:48 +00001060 error_msg("empty command in pipe\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +00001061 freeJob(job);
1062 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +00001063 return 1;
1064 }
1065 src--; /* we'll ++ it at the end of the loop */
1066
1067 break;
1068
1069 case '&': /* background */
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001070 *inBg = 1;
Erik Andersen161220c2000-03-16 08:12:48 +00001071 case ';': /* multiple commands */
1072 done = 1;
1073 returnCommand = *commandPtr + (src - *commandPtr) + 1;
1074 break;
1075
Eric Andersena1d187a2000-07-17 19:14:41 +00001076#ifdef BB_FEATURE_SH_BACKTICKS
Eric Andersenec10b9d2000-07-14 01:13:11 +00001077 case '`':
1078 /* Exec a backtick-ed command */
1079 {
Eric Andersena1d187a2000-07-17 19:14:41 +00001080 char* charptr1=NULL, *charptr2;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001081 char* ptr=NULL;
Eric Andersena1d187a2000-07-17 19:14:41 +00001082 struct job *newJob;
1083 struct jobSet njobList = { NULL, NULL };
1084 int pipefd[2];
1085 int size;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001086
1087 ptr=strchr(++src, '`');
1088 if (ptr==NULL) {
1089 fprintf(stderr, "Unmatched '`' in command\n");
1090 freeJob(job);
1091 return 1;
1092 }
1093
Eric Andersena1d187a2000-07-17 19:14:41 +00001094 /* Make some space to hold just the backticked command */
Eric Andersen6efc48c2000-07-18 08:16:39 +00001095 charptr1 = charptr2 = xmalloc(1+ptr-src);
Matt Kraai0b2da462000-09-19 06:46:44 +00001096 memcpy(charptr1, src, ptr-src);
1097 charptr1[ptr-src] = '\0';
Eric Andersena1d187a2000-07-17 19:14:41 +00001098 newJob = xmalloc(sizeof(struct job));
1099 /* Now parse and run the backticked command */
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001100 if (!parseCommand(&charptr1, newJob, &njobList, inBg)
Eric Andersena1d187a2000-07-17 19:14:41 +00001101 && newJob->numProgs) {
1102 pipe(pipefd);
1103 runCommand(newJob, &njobList, 0, pipefd);
Eric Andersenec10b9d2000-07-14 01:13:11 +00001104 }
Eric Andersena1d187a2000-07-17 19:14:41 +00001105 checkJobs(jobList);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001106 freeJob(newJob);
1107 free(charptr2);
1108
1109 /* Make a copy of any stuff left over in the command
1110 * line after the second backtick */
1111 charptr2 = xmalloc(strlen(ptr)+1);
1112 memcpy(charptr2, ptr+1, strlen(ptr));
1113
Eric Andersenec10b9d2000-07-14 01:13:11 +00001114
Eric Andersena1d187a2000-07-17 19:14:41 +00001115 /* Copy the output from the backtick-ed command into the
1116 * command line, making extra room as needed */
1117 --src;
1118 charptr1 = xmalloc(BUFSIZ);
Mark Whitleyf57c9442000-12-07 19:56:48 +00001119 while ( (size=full_read(pipefd[0], charptr1, BUFSIZ-1)) >0) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001120 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1121 if (newSize > BUFSIZ) {
Matt Kraaib8907522000-09-13 02:08:21 +00001122 *commandPtr=xrealloc(*commandPtr, src - *commandPtr +
Eric Andersena1d187a2000-07-17 19:14:41 +00001123 size + 1 + strlen(charptr2));
1124 }
1125 memcpy(src, charptr1, size);
1126 src+=size;
1127 }
1128 free(charptr1);
1129 close(pipefd[0]);
1130 if (*(src-1)=='\n')
1131 --src;
1132
1133 /* Now paste into the *commandPtr all the stuff
1134 * leftover after the second backtick */
Matt Kraaicbbe4d62000-09-14 00:26:50 +00001135 memcpy(src, charptr2, strlen(charptr2)+1);
Eric Andersena1d187a2000-07-17 19:14:41 +00001136 free(charptr2);
1137
Eric Andersena1d187a2000-07-17 19:14:41 +00001138 /* Now recursively call parseCommand to deal with the new
1139 * and improved version of the command line with the backtick
1140 * results expanded in place... */
Eric Andersen6efc48c2000-07-18 08:16:39 +00001141 freeJob(job);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001142 return(parseCommand(commandPtr, job, jobList, inBg));
Eric Andersenec10b9d2000-07-14 01:13:11 +00001143 }
1144 break;
Eric Andersena1d187a2000-07-17 19:14:41 +00001145#endif // BB_FEATURE_SH_BACKTICKS
Matt Kraai131241f2000-09-14 00:43:20 +00001146
1147 case '\\':
1148 src++;
1149 if (!*src) {
Mark Whitleyf57c9442000-12-07 19:56:48 +00001150 error_msg("character expected after \\\n");
Matt Kraai131241f2000-09-14 00:43:20 +00001151 freeJob(job);
1152 return 1;
1153 }
1154 if (*src == '*' || *src == '[' || *src == ']'
1155 || *src == '?') *buf++ = '\\';
1156 /* fallthrough */
Erik Andersen161220c2000-03-16 08:12:48 +00001157 default:
1158 *buf++ = *src;
1159 }
1160
Erik Andersend75af992000-03-16 08:09:09 +00001161 src++;
Erik Andersen161220c2000-03-16 08:12:48 +00001162 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001163
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001164 if (*prog->argv[argc_l]) {
1165 argc_l++;
1166 globLastArgument(prog, &argc_l, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +00001167 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001168 if (!argc_l) {
Erik Andersen161220c2000-03-16 08:12:48 +00001169 freeJob(job);
1170 return 0;
1171 }
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001172 prog->argv[argc_l] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +00001173
Erik Andersen161220c2000-03-16 08:12:48 +00001174 if (!returnCommand) {
Eric Andersenec10b9d2000-07-14 01:13:11 +00001175 job->text = xmalloc(strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +00001176 strcpy(job->text, *commandPtr);
1177 } else {
1178 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +00001179 count = returnCommand - *commandPtr;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001180 job->text = xmalloc(count + 1);
Erik Andersen161220c2000-03-16 08:12:48 +00001181 strncpy(job->text, *commandPtr, count);
1182 job->text[count] = '\0';
1183 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001184
Erik Andersen161220c2000-03-16 08:12:48 +00001185 *commandPtr = returnCommand;
Eric Andersen46f0beb2000-11-14 21:59:22 +00001186
Erik Andersend75af992000-03-16 08:09:09 +00001187 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001188}
1189
Eric Andersena1d187a2000-07-17 19:14:41 +00001190static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
Erik Andersen3522eb12000-03-12 23:49:18 +00001191{
Eric Andersenfad9c112000-07-25 18:06:52 +00001192 struct job *theJob;
Erik Andersen161220c2000-03-16 08:12:48 +00001193 int i;
Eric Andersen6efc48c2000-07-18 08:16:39 +00001194 int nextin, nextout;
Erik Andersen161220c2000-03-16 08:12:48 +00001195 int pipefds[2]; /* pipefd[0] is for reading */
1196 struct builtInCommand *x;
Eric Andersenb54833c2000-07-03 23:56:26 +00001197#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Eric Andersena683ee82000-11-17 18:51:45 +00001198 struct BB_applet search_applet, *applet;
Erik Andersenbcd61772000-05-13 06:33:19 +00001199#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001200
Eric Andersen6efc48c2000-07-18 08:16:39 +00001201 nextin = 0, nextout = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001202 for (i = 0; i < newJob->numProgs; i++) {
1203 if ((i + 1) < newJob->numProgs) {
Erik Andersen161220c2000-03-16 08:12:48 +00001204 pipe(pipefds);
1205 nextout = pipefds[1];
1206 } else {
Eric Andersen6efc48c2000-07-18 08:16:39 +00001207 if (outPipe[1]!=-1) {
1208 nextout = outPipe[1];
1209 } else {
1210 nextout = 1;
1211 }
Erik Andersen161220c2000-03-16 08:12:48 +00001212 }
1213
Eric Andersen501c88b2000-07-28 15:14:45 +00001214#ifdef BB_FEATURE_SH_ENVIRONMENT
1215 if (showXtrace==TRUE) {
1216 int j;
1217 fprintf(stderr, "+ ");
1218 for (j = 0; newJob->progs[i].argv[j]; j++)
1219 fprintf(stderr, "%s ", newJob->progs[i].argv[j]);
1220 fprintf(stderr, "\n");
1221 }
1222#endif
1223
Eric Andersen34e19412000-07-10 18:47:24 +00001224 /* Check if the command matches any non-forking builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +00001225 for (x = bltins; x->cmd; x++) {
Eric Andersene92108a2000-07-26 00:53:56 +00001226 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001227 return(x->function(newJob, jobList));
Erik Andersen330fd2b2000-05-19 05:35:19 +00001228 }
1229 }
1230
Eric Andersenec10b9d2000-07-14 01:13:11 +00001231 if (!(newJob->progs[i].pid = fork())) {
Erik Andersen161220c2000-03-16 08:12:48 +00001232 signal(SIGTTOU, SIG_DFL);
1233
Eric Andersena1d187a2000-07-17 19:14:41 +00001234 if (outPipe[1]!=-1) {
1235 close(outPipe[0]);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001236 }
1237 if (nextin != 0) {
1238 dup2(nextin, 0);
1239 close(nextin);
1240 }
1241
1242 if (nextout != 1) {
Erik Andersen161220c2000-03-16 08:12:48 +00001243 dup2(nextout, 1);
Eric Andersena1d187a2000-07-17 19:14:41 +00001244 dup2(nextout, 2);
Erik Andersen161220c2000-03-16 08:12:48 +00001245 close(nextout);
Eric Andersen501c88b2000-07-28 15:14:45 +00001246 close(pipefds[0]);
Erik Andersen161220c2000-03-16 08:12:48 +00001247 }
1248
1249 /* explicit redirections override pipes */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001250 setupRedirections(newJob->progs + i);
Erik Andersen161220c2000-03-16 08:12:48 +00001251
Eric Andersen34e19412000-07-10 18:47:24 +00001252 /* Check if the command matches any of the other builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +00001253 for (x = bltins_forking; x->cmd; x++) {
Eric Andersene92108a2000-07-26 00:53:56 +00001254 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001255 applet_name=x->cmd;
Eric Andersenec10b9d2000-07-14 01:13:11 +00001256 exit (x->function(newJob, jobList));
Erik Andersenbcd61772000-05-13 06:33:19 +00001257 }
1258 }
Eric Andersenb54833c2000-07-03 23:56:26 +00001259#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Eric Andersene5aef922000-11-17 18:25:26 +00001260 /* Check if the command matches any busybox internal
1261 * commands ("applets") here. Following discussions from
1262 * November 2000 on busybox@opensource.lineo.com, don't use
1263 * get_last_path_component(). This way explicit (with
1264 * slashes) filenames will never be interpreted as an
1265 * applet, just like with builtins. This way the user can
1266 * override an applet with an explicit filename reference.
1267 * The only downside to this change is that an explicit
1268 * /bin/foo invocation fill fork and exec /bin/foo, even if
1269 * /bin/foo is a symlink to busybox.
1270 */
1271 search_applet.name = newJob->progs[i].argv[0];
1272
Eric Andersen1d3523b2000-12-06 19:07:39 +00001273#ifdef BB_FEATURE_SH_APPLETS_ALWAYS_WIN
1274 /* If you enable BB_FEATURE_SH_APPLETS_ALWAYS_WIN, then
Eric Andersene5aef922000-11-17 18:25:26 +00001275 * if you run /bin/cat, it will use BusyBox cat even if
1276 * /bin/cat exists on the filesystem and is _not_ busybox.
1277 * Some systems want this, others do not. Choose wisely. :-)
1278 */
1279 search_applet.name = get_last_path_component(search_applet.name);
Eric Andersen50b31132000-11-17 18:07:30 +00001280#endif
Eric Andersene5aef922000-11-17 18:25:26 +00001281
1282 /* Do a binary search to find the applet entry given the name. */
1283 applet = bsearch(&search_applet, applets, NUM_APPLETS,
1284 sizeof(struct BB_applet), applet_name_compare);
1285 if (applet != NULL) {
1286 int argc_l;
1287 char** argv=newJob->progs[i].argv;
1288 for(argc_l=0;*argv!=NULL; argv++, argc_l++);
1289 applet_name=applet->name;
1290 optind = 1;
1291 exit((*(applet->main)) (argc_l, newJob->progs[i].argv));
Erik Andersenbcd61772000-05-13 06:33:19 +00001292 }
1293#endif
1294
Eric Andersenec10b9d2000-07-14 01:13:11 +00001295 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
Mark Whitleyf57c9442000-12-07 19:56:48 +00001296 error_msg_and_die("%s: %s\n", newJob->progs[i].argv[0],
Eric Andersene5aef922000-11-17 18:25:26 +00001297 strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +00001298 }
Eric Andersen6efc48c2000-07-18 08:16:39 +00001299 if (outPipe[1]!=-1) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001300 close(outPipe[1]);
1301 }
Erik Andersen161220c2000-03-16 08:12:48 +00001302
1303 /* put our child in the process group whose leader is the
1304 first process in this pipe */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001305 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
Erik Andersen161220c2000-03-16 08:12:48 +00001306 if (nextin != 0)
1307 close(nextin);
Eric Andersen6efc48c2000-07-18 08:16:39 +00001308 if (nextout != 1)
Erik Andersen161220c2000-03-16 08:12:48 +00001309 close(nextout);
1310
1311 /* If there isn't another process, nextin is garbage
1312 but it doesn't matter */
1313 nextin = pipefds[0];
1314 }
1315
Eric Andersenec10b9d2000-07-14 01:13:11 +00001316 newJob->pgrp = newJob->progs[0].pid;
Erik Andersen161220c2000-03-16 08:12:48 +00001317
Eric Andersenfad9c112000-07-25 18:06:52 +00001318 /* find the ID for the theJob to use */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001319 newJob->jobId = 1;
Eric Andersenfad9c112000-07-25 18:06:52 +00001320 for (theJob = jobList->head; theJob; theJob = theJob->next)
1321 if (theJob->jobId >= newJob->jobId)
1322 newJob->jobId = theJob->jobId + 1;
Erik Andersen161220c2000-03-16 08:12:48 +00001323
Eric Andersenfad9c112000-07-25 18:06:52 +00001324 /* add the theJob to the list of running jobs */
Erik Andersen161220c2000-03-16 08:12:48 +00001325 if (!jobList->head) {
Matt Kraaib8907522000-09-13 02:08:21 +00001326 theJob = jobList->head = xmalloc(sizeof(*theJob));
Erik Andersend75af992000-03-16 08:09:09 +00001327 } else {
Eric Andersenfad9c112000-07-25 18:06:52 +00001328 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
Matt Kraaib8907522000-09-13 02:08:21 +00001329 theJob->next = xmalloc(sizeof(*theJob));
Eric Andersenfad9c112000-07-25 18:06:52 +00001330 theJob = theJob->next;
Erik Andersend75af992000-03-16 08:09:09 +00001331 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001332
Eric Andersenfad9c112000-07-25 18:06:52 +00001333 *theJob = *newJob;
1334 theJob->next = NULL;
1335 theJob->runningProgs = theJob->numProgs;
1336 theJob->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001337
Erik Andersen161220c2000-03-16 08:12:48 +00001338 if (inBg) {
Eric Andersenfad9c112000-07-25 18:06:52 +00001339 /* we don't wait for background theJobs to return -- append it
1340 to the list of backgrounded theJobs and leave it alone */
1341 printf("[%d] %d\n", theJob->jobId,
Eric Andersenec10b9d2000-07-14 01:13:11 +00001342 newJob->progs[newJob->numProgs - 1].pid);
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001343#ifdef BB_FEATURE_SH_ENVIRONMENT
1344 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1345#endif
Erik Andersen161220c2000-03-16 08:12:48 +00001346 } else {
Eric Andersenfad9c112000-07-25 18:06:52 +00001347 jobList->fg = theJob;
Erik Andersen3522eb12000-03-12 23:49:18 +00001348
Erik Andersen161220c2000-03-16 08:12:48 +00001349 /* move the new process group into the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001350 /* suppress messages when run from /linuxrc mag@sysgo.de */
Eric Andersenec10b9d2000-07-14 01:13:11 +00001351 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
Erik Andersen161220c2000-03-16 08:12:48 +00001352 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001353 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001354
Erik Andersen161220c2000-03-16 08:12:48 +00001355 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001356}
1357
Erik Andersend75af992000-03-16 08:09:09 +00001358static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +00001359{
Erik Andersen161220c2000-03-16 08:12:48 +00001360 char *command;
1361 char *nextCommand = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001362 struct job newJob;
Eric Andersen1c314ad2000-06-28 16:56:25 +00001363 pid_t parent_pgrp;
Erik Andersen161220c2000-03-16 08:12:48 +00001364 int i;
Erik Andersen161220c2000-03-16 08:12:48 +00001365 int inBg;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001366 int status;
1367 newJob.jobContext = REGULAR_JOB_CONTEXT;
Erik Andersen3522eb12000-03-12 23:49:18 +00001368
Eric Andersen1c314ad2000-06-28 16:56:25 +00001369 /* save current owner of TTY so we can restore it on exit */
1370 parent_pgrp = tcgetpgrp(0);
1371
Matt Kraaib8907522000-09-13 02:08:21 +00001372 command = (char *) xcalloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +00001373
Erik Andersen161220c2000-03-16 08:12:48 +00001374 /* don't pay any attention to this signal; it just confuses
1375 things and isn't really meant for shells anyway */
1376 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +00001377
Erik Andersen161220c2000-03-16 08:12:48 +00001378 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +00001379 if (!jobList.fg) {
1380 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +00001381
Erik Andersend75af992000-03-16 08:09:09 +00001382 /* see if any background processes have exited */
1383 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +00001384
Erik Andersend75af992000-03-16 08:09:09 +00001385 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +00001386 if (getCommand(input, command))
1387 break;
1388 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +00001389 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001390
Eric Andersenec10b9d2000-07-14 01:13:11 +00001391 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +00001392 newJob.numProgs) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001393 int pipefds[2] = {-1,-1};
1394 runCommand(&newJob, &jobList, inBg, pipefds);
Eric Andersenfad9c112000-07-25 18:06:52 +00001395 }
1396 else {
Eric Andersena1d187a2000-07-17 19:14:41 +00001397 free(command);
Matt Kraaib8907522000-09-13 02:08:21 +00001398 command = (char *) xcalloc(BUFSIZ, sizeof(char));
Eric Andersen6efc48c2000-07-18 08:16:39 +00001399 nextCommand = NULL;
Erik Andersend75af992000-03-16 08:09:09 +00001400 }
1401 } else {
1402 /* a job is running in the foreground; wait for it */
1403 i = 0;
1404 while (!jobList.fg->progs[i].pid ||
Eric Andersenfad9c112000-07-25 18:06:52 +00001405 jobList.fg->progs[i].isStopped == 1) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +00001406
Erik Andersend75af992000-03-16 08:09:09 +00001407 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +00001408
Erik Andersend75af992000-03-16 08:09:09 +00001409 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +00001410 /* the child exited */
1411 jobList.fg->runningProgs--;
1412 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001413
Eric Andersen501c88b2000-07-28 15:14:45 +00001414#ifdef BB_FEATURE_SH_ENVIRONMENT
1415 lastReturnCode=WEXITSTATUS(status);
1416#endif
Eric Andersenb558e762000-11-30 22:43:16 +00001417 debug_printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode);
Erik Andersen161220c2000-03-16 08:12:48 +00001418 if (!jobList.fg->runningProgs) {
1419 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +00001420
Erik Andersen161220c2000-03-16 08:12:48 +00001421 removeJob(&jobList, jobList.fg);
1422 jobList.fg = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001423 }
Erik Andersend75af992000-03-16 08:09:09 +00001424 } else {
Erik Andersen161220c2000-03-16 08:12:48 +00001425 /* the child was stopped */
1426 jobList.fg->stoppedProgs++;
1427 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +00001428
Erik Andersen161220c2000-03-16 08:12:48 +00001429 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1430 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1431 "Stopped", jobList.fg->text);
1432 jobList.fg = NULL;
1433 }
Erik Andersend75af992000-03-16 08:09:09 +00001434 }
1435
1436 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +00001437 /* move the shell to the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001438 /* suppress messages when run from /linuxrc mag@sysgo.de */
1439 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1440 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001441 }
1442 }
1443 }
Erik Andersen161220c2000-03-16 08:12:48 +00001444 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +00001445
Eric Andersen1c314ad2000-06-28 16:56:25 +00001446 /* return controlling TTY back to parent process group before exiting */
1447 if (tcsetpgrp(0, parent_pgrp))
Eric Andersenb54833c2000-07-03 23:56:26 +00001448 perror("tcsetpgrp");
1449
1450 /* return exit status if called with "-c" */
1451 if (input == NULL && WIFEXITED(status))
1452 return WEXITSTATUS(status);
1453
Erik Andersen161220c2000-03-16 08:12:48 +00001454 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001455}
1456
1457
Eric Andersenfad9c112000-07-25 18:06:52 +00001458#ifdef BB_FEATURE_CLEAN_UP
1459void free_memory(void)
1460{
1461 if (promptStr)
1462 free(promptStr);
1463 if (cwd)
1464 free(cwd);
1465 if (local_pending_command)
1466 free(local_pending_command);
1467
1468 if (jobList.fg && !jobList.fg->runningProgs) {
1469 removeJob(&jobList, jobList.fg);
1470 }
1471}
1472#endif
1473
Eric Andersen6efc48c2000-07-18 08:16:39 +00001474
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001475int shell_main(int argc_l, char **argv_l)
Erik Andersen3522eb12000-03-12 23:49:18 +00001476{
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001477 int opt, interactive=FALSE;
Erik Andersen161220c2000-03-16 08:12:48 +00001478 FILE *input = stdin;
Eric Andersen6a99aaf2000-07-27 00:15:20 +00001479 argc = argc_l;
1480 argv = argv_l;
Erik Andersen3522eb12000-03-12 23:49:18 +00001481
Eric Andersen501c88b2000-07-28 15:14:45 +00001482
Eric Andersenb558e762000-11-30 22:43:16 +00001483 if (argv[0] && argv[0][0] == '-') {
1484 FILE *input;
1485 input = fopen("/etc/profile", "r");
1486 if (!input) {
1487 fprintf(stdout, "Couldn't open file '/etc/profile'\n");
1488 } else {
1489 /* Now run the file */
1490 busy_loop(input);
1491 fclose(input);
1492 }
1493 }
Eric Andersen501c88b2000-07-28 15:14:45 +00001494
Eric Andersenf21aa842000-12-08 20:50:30 +00001495 while ((opt = getopt(argc_l, argv_l, "cxi")) > 0) {
Eric Andersen501c88b2000-07-28 15:14:45 +00001496 switch (opt) {
1497 case 'c':
1498 input = NULL;
Matt Kraai6085c722000-09-06 01:46:18 +00001499 if (local_pending_command != 0)
Mark Whitleyf57c9442000-12-07 19:56:48 +00001500 error_msg_and_die("multiple -c arguments\n");
Matt Kraai6085c722000-09-06 01:46:18 +00001501 local_pending_command = xstrdup(argv[optind]);
1502 optind++;
1503 argv = argv+optind;
Eric Andersen501c88b2000-07-28 15:14:45 +00001504 break;
Eric Andersen1428c4f2000-07-28 15:19:30 +00001505#ifdef BB_FEATURE_SH_ENVIRONMENT
Eric Andersen501c88b2000-07-28 15:14:45 +00001506 case 'x':
1507 showXtrace = TRUE;
1508 break;
Eric Andersen1428c4f2000-07-28 15:19:30 +00001509#endif
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001510 case 'i':
1511 interactive = TRUE;
1512 break;
Eric Andersen501c88b2000-07-28 15:14:45 +00001513 default:
1514 usage(shell_usage);
1515 }
1516 }
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001517 /* A shell is interactive if the `-i' flag was given, or if all of
1518 * the following conditions are met:
1519 * no -c command
1520 * no arguments remaining or the -s flag given
1521 * standard input is a terminal
1522 * standard output is a terminal
1523 * Refer to Posix.2, the description of the `sh' utility. */
1524 if (interactive==TRUE || ( argv[optind]==NULL && input==stdin && isatty(fileno(stdin)) && isatty(fileno(stdout)))) {
Eric Andersen851ce892000-08-21 22:34:23 +00001525 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
Eric Andersen501c88b2000-07-28 15:14:45 +00001526 /* Looks like they want an interactive shell */
1527 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1528 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Eric Andersen6a4c33c2000-07-28 17:08:36 +00001529 } else if (local_pending_command==NULL) {
Eric Andersen851ce892000-08-21 22:34:23 +00001530 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
Matt Kraaibbaef662000-09-27 02:43:35 +00001531 input = xfopen(argv[optind], "r");
Eric Andersen501c88b2000-07-28 15:14:45 +00001532 }
1533
Eric Andersen6efc48c2000-07-18 08:16:39 +00001534 /* initialize the cwd -- this is never freed...*/
1535 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1536 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen3522eb12000-03-12 23:49:18 +00001537
Eric Andersenfad9c112000-07-25 18:06:52 +00001538#ifdef BB_FEATURE_CLEAN_UP
1539 atexit(free_memory);
1540#endif
1541
Erik Andersenf0657d32000-04-12 17:49:52 +00001542 win_changed(0);
Erik Andersen161220c2000-03-16 08:12:48 +00001543 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +00001544}