blob: 56d94258cdf6331ed3312958488b1473f7ec497f [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
28#include "internal.h"
29#include <stdio.h>
30#include <stdlib.h>
31#include <ctype.h>
32#include <errno.h>
33#include <fcntl.h>
34#include <glob.h>
35#include <signal.h>
36#include <string.h>
37#include <sys/ioctl.h>
38#include <sys/wait.h>
39#include <unistd.h>
Erik Andersenf0657d32000-04-12 17:49:52 +000040#ifdef BB_FEATURE_SH_COMMAND_EDITING
41#include "cmdedit.h"
42#endif
Erik Andersen3522eb12000-03-12 23:49:18 +000043
44
Erik Andersen3522eb12000-03-12 23:49:18 +000045#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
46
Erik Andersend75af992000-03-16 08:09:09 +000047
48enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
Erik Andersen161220c2000-03-16 08:12:48 +000049 REDIRECT_APPEND
50};
Erik Andersen3522eb12000-03-12 23:49:18 +000051
52struct jobSet {
Erik Andersen161220c2000-03-16 08:12:48 +000053 struct job *head; /* head of list of running jobs */
54 struct job *fg; /* current foreground job */
Erik Andersen3522eb12000-03-12 23:49:18 +000055};
56
57struct redirectionSpecifier {
Erik Andersen161220c2000-03-16 08:12:48 +000058 enum redirectionType type; /* type of redirection */
59 int fd; /* file descriptor being redirected */
60 char *filename; /* file to redirect fd to */
Erik Andersen3522eb12000-03-12 23:49:18 +000061};
62
63struct childProgram {
Erik Andersen161220c2000-03-16 08:12:48 +000064 pid_t pid; /* 0 if exited */
65 char **argv; /* program name and arguments */
66 int numRedirections; /* elements in redirection array */
67 struct redirectionSpecifier *redirections; /* I/O redirections */
68 glob_t globResult; /* result of parameter globbing */
69 int freeGlob; /* should we globfree(&globResult)? */
70 int isStopped; /* is the program currently running? */
Erik Andersen3522eb12000-03-12 23:49:18 +000071};
72
73struct job {
Erik Andersen161220c2000-03-16 08:12:48 +000074 int jobId; /* job number */
75 int numProgs; /* total number of programs in job */
76 int runningProgs; /* number of programs running */
77 char *text; /* name of job */
78 char *cmdBuf; /* buffer various argv's point into */
79 pid_t pgrp; /* process group ID for the job */
80 struct childProgram *progs; /* array of programs in job */
81 struct job *next; /* to track background commands */
82 int stoppedProgs; /* number of programs alive, but stopped */
Erik Andersen3522eb12000-03-12 23:49:18 +000083};
84
85struct builtInCommand {
Erik Andersen161220c2000-03-16 08:12:48 +000086 char *cmd; /* name */
87 char *descr; /* description */
88 char *usage; /* usage */
89 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
Erik Andersen3522eb12000-03-12 23:49:18 +000090};
91
92/* Some function prototypes */
Erik Andersend75af992000-03-16 08:09:09 +000093static int shell_cd(struct job *cmd, struct jobSet *junk);
94static int shell_env(struct job *dummy, struct jobSet *junk);
95static int shell_exit(struct job *cmd, struct jobSet *junk);
96static int shell_fg_bg(struct job *cmd, struct jobSet *jobList);
97static int shell_help(struct job *cmd, struct jobSet *junk);
98static int shell_jobs(struct job *dummy, struct jobSet *jobList);
99static int shell_pwd(struct job *dummy, struct jobSet *junk);
Erik Andersen6273f652000-03-17 01:12:41 +0000100static int shell_export(struct job *cmd, struct jobSet *junk);
Erik Andersend75af992000-03-16 08:09:09 +0000101static int shell_source(struct job *cmd, struct jobSet *jobList);
102static int shell_unset(struct job *cmd, struct jobSet *junk);
Erik Andersen3522eb12000-03-12 23:49:18 +0000103
Erik Andersend75af992000-03-16 08:09:09 +0000104static void checkJobs(struct jobSet *jobList);
105static int getCommand(FILE * source, char *command);
106static int parseCommand(char **commandPtr, struct job *job, int *isBg);
107static int setupRedirections(struct childProgram *prog);
108static int runCommand(struct job newJob, struct jobSet *jobList, int inBg);
Erik Andersen3522eb12000-03-12 23:49:18 +0000109static int busy_loop(FILE * input);
110
Erik Andersend75af992000-03-16 08:09:09 +0000111
Erik Andersen3522eb12000-03-12 23:49:18 +0000112/* Table of built-in functions */
113static struct builtInCommand bltins[] = {
Erik Andersen161220c2000-03-16 08:12:48 +0000114 {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg},
115 {"cd", "Change working directory", "cd [dir]", shell_cd},
Erik Andersen161220c2000-03-16 08:12:48 +0000116 {"exit", "Exit from shell()", "exit", shell_exit},
117 {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
118 {"jobs", "Lists the active jobs", "jobs", shell_jobs},
Erik Andersen6273f652000-03-17 01:12:41 +0000119 {"export", "Set environment variable", "export [VAR=value]", shell_export},
Erik Andersen161220c2000-03-16 08:12:48 +0000120 {"unset", "Unset environment variable", "unset VAR", shell_unset},
Erik Andersen330fd2b2000-05-19 05:35:19 +0000121 {NULL, NULL, NULL, NULL}
122};
123
124/* Table of built-in functions */
125static struct builtInCommand bltins_forking[] = {
126 {"env", "Print all environment variables", "env", shell_env},
127 {"pwd", "Print current directory", "pwd", shell_pwd},
Erik Andersenc7c634b2000-03-19 05:28:55 +0000128 {".", "Source-in and run commands in a file", ". filename", shell_source},
Erik Andersen161220c2000-03-16 08:12:48 +0000129 {"help", "List shell built-in commands", "help", shell_help},
130 {NULL, NULL, NULL, NULL}
Erik Andersen3522eb12000-03-12 23:49:18 +0000131};
132
133static const char shell_usage[] =
Erik Andersen161220c2000-03-16 08:12:48 +0000134
Erik Andersen7ab9c7e2000-05-12 19:41:47 +0000135 "sh [FILE]...\n"
136#ifndef BB_FEATURE_TRIVIAL_HELP
137 "\nlash: The BusyBox command interpreter (shell).\n\n"
138#endif
139 ;
Erik Andersen3522eb12000-03-12 23:49:18 +0000140
141static char cwd[1024];
142static char *prompt = "# ";
143
Erik Andersenf0657d32000-04-12 17:49:52 +0000144#ifdef BB_FEATURE_SH_COMMAND_EDITING
145void win_changed(int sig)
146{
147 struct winsize win = { 0, 0 };
148 ioctl(0, TIOCGWINSZ, &win);
149 if (win.ws_col > 0) {
150 cmdedit_setwidth( win.ws_col - 1);
151 }
152}
153#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000154
Erik Andersen3522eb12000-03-12 23:49:18 +0000155
Erik Andersend75af992000-03-16 08:09:09 +0000156/* built-in 'cd <path>' handler */
157static int shell_cd(struct job *cmd, struct jobSet *junk)
158{
Erik Andersen161220c2000-03-16 08:12:48 +0000159 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000160
Erik Andersen161220c2000-03-16 08:12:48 +0000161 if (!cmd->progs[0].argv[1] == 1)
162 newdir = getenv("HOME");
163 else
164 newdir = cmd->progs[0].argv[1];
165 if (chdir(newdir)) {
166 printf("cd: %s: %s\n", newdir, strerror(errno));
167 return FALSE;
168 }
169 getcwd(cwd, sizeof(cwd));
170
171 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000172}
173
174/* built-in 'env' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000175static int shell_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000176{
Erik Andersen161220c2000-03-16 08:12:48 +0000177 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000178
Erik Andersen161220c2000-03-16 08:12:48 +0000179 for (e = environ; *e; e++) {
180 fprintf(stdout, "%s\n", *e);
181 }
182 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000183}
184
185/* built-in 'exit' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000186static int shell_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000187{
Erik Andersen161220c2000-03-16 08:12:48 +0000188 if (!cmd->progs[0].argv[1] == 1)
189 exit TRUE;
190
Eric Andersenb6106152000-06-19 17:25:40 +0000191 return(atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000192}
193
194/* built-in 'fg' and 'bg' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000195static int shell_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000196{
Erik Andersen161220c2000-03-16 08:12:48 +0000197 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000198 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000199
Erik Andersen161220c2000-03-16 08:12:48 +0000200 if (!jobList->head) {
201 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
202 fprintf(stderr, "%s: exactly one argument is expected\n",
203 cmd->progs[0].argv[0]);
204 return FALSE;
205 }
206 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
207 fprintf(stderr, "%s: bad argument '%s'\n",
208 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
209 return FALSE;
210 for (job = jobList->head; job; job = job->next) {
211 if (job->jobId == jobNum) {
212 break;
213 }
214 }
215 }
216 } else {
217 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000218 }
Erik Andersen161220c2000-03-16 08:12:48 +0000219
220 if (!job) {
221 fprintf(stderr, "%s: unknown job %d\n",
222 cmd->progs[0].argv[0], jobNum);
223 return FALSE;
Erik Andersend75af992000-03-16 08:09:09 +0000224 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000225
Erik Andersen161220c2000-03-16 08:12:48 +0000226 if (*cmd->progs[0].argv[0] == 'f') {
227 /* Make this job the foreground job */
228 if (tcsetpgrp(0, job->pgrp))
229 perror("tcsetpgrp");
230 jobList->fg = job;
231 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000232
Erik Andersen161220c2000-03-16 08:12:48 +0000233 /* Restart the processes in the job */
234 for (i = 0; i < job->numProgs; i++)
235 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000236
Erik Andersen161220c2000-03-16 08:12:48 +0000237 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000238
Erik Andersen161220c2000-03-16 08:12:48 +0000239 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000240
Erik Andersen161220c2000-03-16 08:12:48 +0000241 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000242}
243
244/* built-in 'help' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000245static int shell_help(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000246{
Erik Andersen161220c2000-03-16 08:12:48 +0000247 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000248
Erik Andersen161220c2000-03-16 08:12:48 +0000249 fprintf(stdout, "\nBuilt-in commands:\n");
250 fprintf(stdout, "-------------------\n");
251 for (x = bltins; x->cmd; x++) {
252 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
253 }
Erik Andersen330fd2b2000-05-19 05:35:19 +0000254 for (x = bltins_forking; x->cmd; x++) {
255 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
256 }
Erik Andersen161220c2000-03-16 08:12:48 +0000257 fprintf(stdout, "\n\n");
258 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000259}
260
261/* built-in 'jobs' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000262static int shell_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000263{
Erik Andersen161220c2000-03-16 08:12:48 +0000264 struct job *job;
265 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000266
Erik Andersen161220c2000-03-16 08:12:48 +0000267 for (job = jobList->head; job; job = job->next) {
268 if (job->runningProgs == job->stoppedProgs)
269 statusString = "Stopped";
270 else
271 statusString = "Running";
272
273 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
274 }
275 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000276}
277
278
279/* built-in 'pwd' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000280static int shell_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000281{
Erik Andersen161220c2000-03-16 08:12:48 +0000282 getcwd(cwd, sizeof(cwd));
283 fprintf(stdout, "%s\n", cwd);
284 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000285}
286
Erik Andersen6273f652000-03-17 01:12:41 +0000287/* built-in 'export VAR=value' handler */
288static int shell_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000289{
Erik Andersen161220c2000-03-16 08:12:48 +0000290 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000291
Erik Andersen161220c2000-03-16 08:12:48 +0000292 if (!cmd->progs[0].argv[1] == 1) {
293 return (shell_env(cmd, junk));
294 }
295 res = putenv(cmd->progs[0].argv[1]);
296 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000297 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000298 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000299}
300
301/* Built-in '.' handler (read-in and execute commands from file) */
Erik Andersend75af992000-03-16 08:09:09 +0000302static int shell_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000303{
Erik Andersen161220c2000-03-16 08:12:48 +0000304 FILE *input;
305 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000306
Erik Andersen161220c2000-03-16 08:12:48 +0000307 if (!cmd->progs[0].argv[1] == 1)
308 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000309
Erik Andersen161220c2000-03-16 08:12:48 +0000310 input = fopen(cmd->progs[0].argv[1], "r");
311 if (!input) {
312 fprintf(stdout, "Couldn't open file '%s'\n",
313 cmd->progs[0].argv[1]);
314 return FALSE;
315 }
Erik Andersend75af992000-03-16 08:09:09 +0000316
Erik Andersen161220c2000-03-16 08:12:48 +0000317 /* Now run the file */
318 status = busy_loop(input);
319 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000320}
321
322/* built-in 'unset VAR' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000323static int shell_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000324{
Erik Andersen161220c2000-03-16 08:12:48 +0000325 if (!cmd->progs[0].argv[1] == 1) {
326 fprintf(stdout, "unset: parameter required.\n");
327 return FALSE;
328 }
329 unsetenv(cmd->progs[0].argv[1]);
330 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000331}
332
333/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000334static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000335{
Erik Andersen161220c2000-03-16 08:12:48 +0000336 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000337
Erik Andersen161220c2000-03-16 08:12:48 +0000338 for (i = 0; i < cmd->numProgs; i++) {
339 free(cmd->progs[i].argv);
340 if (cmd->progs[i].redirections)
341 free(cmd->progs[i].redirections);
342 if (cmd->progs[i].freeGlob)
343 globfree(&cmd->progs[i].globResult);
344 }
345 free(cmd->progs);
346 if (cmd->text)
347 free(cmd->text);
348 free(cmd->cmdBuf);
Erik Andersen3522eb12000-03-12 23:49:18 +0000349}
350
351/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000352static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000353{
Erik Andersen161220c2000-03-16 08:12:48 +0000354 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000355
Erik Andersen161220c2000-03-16 08:12:48 +0000356 freeJob(job);
357 if (job == jobList->head) {
358 jobList->head = job->next;
359 } else {
360 prevJob = jobList->head;
361 while (prevJob->next != job)
362 prevJob = prevJob->next;
363 prevJob->next = job->next;
364 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000365
Erik Andersen161220c2000-03-16 08:12:48 +0000366 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000367}
368
369/* Checks to see if any background processes have exited -- if they
370 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000371static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000372{
Erik Andersen161220c2000-03-16 08:12:48 +0000373 struct job *job;
374 pid_t childpid;
375 int status;
376 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000377
Erik Andersen161220c2000-03-16 08:12:48 +0000378 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
379 for (job = jobList->head; job; job = job->next) {
380 progNum = 0;
381 while (progNum < job->numProgs &&
382 job->progs[progNum].pid != childpid) progNum++;
383 if (progNum < job->numProgs)
384 break;
385 }
386
387 if (WIFEXITED(status) || WIFSIGNALED(status)) {
388 /* child exited */
389 job->runningProgs--;
390 job->progs[progNum].pid = 0;
391
392 if (!job->runningProgs) {
393 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
394 removeJob(jobList, job);
395 }
396 } else {
397 /* child stopped */
398 job->stoppedProgs++;
399 job->progs[progNum].isStopped = 1;
400
401 if (job->stoppedProgs == job->numProgs) {
402 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
403 job->text);
404 }
405 }
Erik Andersend75af992000-03-16 08:09:09 +0000406 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000407
Erik Andersen161220c2000-03-16 08:12:48 +0000408 if (childpid == -1 && errno != ECHILD)
409 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000410}
411
Erik Andersend75af992000-03-16 08:09:09 +0000412static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000413{
Erik Andersen161220c2000-03-16 08:12:48 +0000414 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000415#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000416 int len;
417 char *promptStr;
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000418 len=fprintf(stdout, "%s %s", cwd, prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000419 fflush(stdout);
420 promptStr=(char*)malloc(sizeof(char)*(len+1));
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000421 sprintf(promptStr, "%s %s", cwd, prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000422 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000423 free( promptStr);
Erik Andersen161220c2000-03-16 08:12:48 +0000424 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000425#else
426 fprintf(stdout, "%s %s", cwd, prompt);
427 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000428#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000429 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000430
Erik Andersen161220c2000-03-16 08:12:48 +0000431 if (!fgets(command, BUFSIZ - 2, source)) {
432 if (source == stdin)
433 printf("\n");
434 return 1;
435 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000436
Erik Andersen161220c2000-03-16 08:12:48 +0000437 /* remove trailing newline */
438 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000439
Erik Andersen161220c2000-03-16 08:12:48 +0000440 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000441}
442
Erik Andersend75af992000-03-16 08:09:09 +0000443static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000444 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000445{
Erik Andersen161220c2000-03-16 08:12:48 +0000446 int argc = *argcPtr;
447 int argcAlloced = *argcAllocedPtr;
448 int rc;
449 int flags;
450 int i;
451 char *src, *dst;
Erik Andersen3522eb12000-03-12 23:49:18 +0000452
Erik Andersen161220c2000-03-16 08:12:48 +0000453 if (argc > 1) { /* cmd->globResult is already initialized */
454 flags = GLOB_APPEND;
455 i = prog->globResult.gl_pathc;
456 } else {
457 prog->freeGlob = 1;
458 flags = 0;
459 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000460 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000461
Erik Andersen161220c2000-03-16 08:12:48 +0000462 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
463 if (rc == GLOB_NOSPACE) {
464 fprintf(stderr, "out of space during glob operation\n");
465 return;
466 } else if (rc == GLOB_NOMATCH ||
467 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
468 !strcmp(prog->argv[argc - 1],
469 prog->globResult.gl_pathv[i]))) {
470 /* we need to remove whatever \ quoting is still present */
471 src = dst = prog->argv[argc - 1];
472 while (*src) {
473 if (*src != '\\')
474 *dst++ = *src;
475 src++;
476 }
477 *dst = '\0';
478 } else if (!rc) {
479 argcAlloced += (prog->globResult.gl_pathc - i);
480 prog->argv =
481 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
482 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
483 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
484 argc += (prog->globResult.gl_pathc - i - 1);
485 }
486
487 *argcAllocedPtr = argcAlloced;
488 *argcPtr = argc;
Erik Andersen3522eb12000-03-12 23:49:18 +0000489}
490
491/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
492 line). If a valid command is found, commandPtr is set to point to
493 the beginning of the next command (if the original command had more
494 then one job associated with it) or NULL if no more commands are
495 present. */
Erik Andersend75af992000-03-16 08:09:09 +0000496static int parseCommand(char **commandPtr, struct job *job, int *isBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000497{
Erik Andersen161220c2000-03-16 08:12:48 +0000498 char *command;
499 char *returnCommand = NULL;
500 char *src, *buf, *chptr;
501 int argc = 0;
502 int done = 0;
503 int argvAlloced;
504 int i;
505 char quote = '\0';
506 int count;
507 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000508
Erik Andersen161220c2000-03-16 08:12:48 +0000509 /* skip leading white space */
510 while (**commandPtr && isspace(**commandPtr))
511 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000512
Erik Andersen161220c2000-03-16 08:12:48 +0000513 /* this handles empty lines or leading '#' characters */
514 if (!**commandPtr || (**commandPtr == '#')) {
515 job->numProgs = 0;
516 *commandPtr = NULL;
517 return 0;
518 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000519
Erik Andersen161220c2000-03-16 08:12:48 +0000520 *isBg = 0;
521 job->numProgs = 1;
522 job->progs = malloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000523
Erik Andersen161220c2000-03-16 08:12:48 +0000524 /* We set the argv elements to point inside of this string. The
525 memory is freed by freeJob().
Erik Andersen3522eb12000-03-12 23:49:18 +0000526
Erik Andersen161220c2000-03-16 08:12:48 +0000527 Getting clean memory relieves us of the task of NULL
528 terminating things and makes the rest of this look a bit
529 cleaner (though it is, admittedly, a tad less efficient) */
530 job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1);
531 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000532
Erik Andersen161220c2000-03-16 08:12:48 +0000533 prog = job->progs;
534 prog->numRedirections = 0;
535 prog->redirections = NULL;
536 prog->freeGlob = 0;
537 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000538
Erik Andersen161220c2000-03-16 08:12:48 +0000539 argvAlloced = 5;
540 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
541 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000542
Erik Andersen161220c2000-03-16 08:12:48 +0000543 buf = command;
544 src = *commandPtr;
545 while (*src && !done) {
546 if (quote == *src) {
547 quote = '\0';
548 } else if (quote) {
549 if (*src == '\\') {
550 src++;
551 if (!*src) {
552 fprintf(stderr, "character expected after \\\n");
553 freeJob(job);
554 return 1;
555 }
556
557 /* in shell, "\'" should yield \' */
558 if (*src != quote)
559 *buf++ = '\\';
560 } else if (*src == '*' || *src == '?' || *src == '[' ||
561 *src == ']') *buf++ = '\\';
562 *buf++ = *src;
563 } else if (isspace(*src)) {
564 if (*prog->argv[argc]) {
565 buf++, argc++;
566 /* +1 here leaves room for the NULL which ends argv */
567 if ((argc + 1) == argvAlloced) {
568 argvAlloced += 5;
569 prog->argv = realloc(prog->argv,
570 sizeof(*prog->argv) *
571 argvAlloced);
572 }
573 prog->argv[argc] = buf;
574
575 globLastArgument(prog, &argc, &argvAlloced);
576 }
577 } else
578 switch (*src) {
579 case '"':
580 case '\'':
581 quote = *src;
582 break;
583
584 case '#': /* comment */
585 done = 1;
586 break;
587
588 case '>': /* redirections */
589 case '<':
590 i = prog->numRedirections++;
591 prog->redirections = realloc(prog->redirections,
592 sizeof(*prog->redirections) *
593 (i + 1));
594
595 prog->redirections[i].fd = -1;
596 if (buf != prog->argv[argc]) {
597 /* the stuff before this character may be the file number
598 being redirected */
599 prog->redirections[i].fd =
600 strtol(prog->argv[argc], &chptr, 10);
601
602 if (*chptr && *prog->argv[argc]) {
603 buf++, argc++;
604 globLastArgument(prog, &argc, &argvAlloced);
605 }
606 }
607
608 if (prog->redirections[i].fd == -1) {
609 if (*src == '>')
610 prog->redirections[i].fd = 1;
611 else
612 prog->redirections[i].fd = 0;
613 }
614
615 if (*src++ == '>') {
616 if (*src == '>')
617 prog->redirections[i].type =
618 REDIRECT_APPEND, src++;
619 else
620 prog->redirections[i].type = REDIRECT_OVERWRITE;
621 } else {
622 prog->redirections[i].type = REDIRECT_INPUT;
623 }
624
625 /* This isn't POSIX sh compliant. Oh well. */
626 chptr = src;
627 while (isspace(*chptr))
628 chptr++;
629
630 if (!*chptr) {
631 fprintf(stderr, "file name expected after %c\n", *src);
632 freeJob(job);
633 return 1;
634 }
635
636 prog->redirections[i].filename = buf;
637 while (*chptr && !isspace(*chptr))
638 *buf++ = *chptr++;
639
640 src = chptr - 1; /* we src++ later */
641 prog->argv[argc] = ++buf;
642 break;
643
644 case '|': /* pipe */
645 /* finish this command */
646 if (*prog->argv[argc])
647 argc++;
648 if (!argc) {
649 fprintf(stderr, "empty command in pipe\n");
650 freeJob(job);
651 return 1;
652 }
653 prog->argv[argc] = NULL;
654
655 /* and start the next */
656 job->numProgs++;
657 job->progs = realloc(job->progs,
658 sizeof(*job->progs) * job->numProgs);
659 prog = job->progs + (job->numProgs - 1);
660 prog->numRedirections = 0;
661 prog->redirections = NULL;
662 prog->freeGlob = 0;
663 argc = 0;
664
665 argvAlloced = 5;
666 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
667 prog->argv[0] = ++buf;
668
669 src++;
670 while (*src && isspace(*src))
671 src++;
672
673 if (!*src) {
674 fprintf(stderr, "empty command in pipe\n");
675 return 1;
676 }
677 src--; /* we'll ++ it at the end of the loop */
678
679 break;
680
681 case '&': /* background */
682 *isBg = 1;
683 case ';': /* multiple commands */
684 done = 1;
685 returnCommand = *commandPtr + (src - *commandPtr) + 1;
686 break;
687
688 case '\\':
689 src++;
690 if (!*src) {
691 freeJob(job);
692 fprintf(stderr, "character expected after \\\n");
693 return 1;
694 }
695 if (*src == '*' || *src == '[' || *src == ']'
696 || *src == '?') *buf++ = '\\';
697 /* fallthrough */
698 default:
699 *buf++ = *src;
700 }
701
Erik Andersend75af992000-03-16 08:09:09 +0000702 src++;
Erik Andersen161220c2000-03-16 08:12:48 +0000703 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000704
Erik Andersen161220c2000-03-16 08:12:48 +0000705 if (*prog->argv[argc]) {
706 argc++;
Erik Andersend75af992000-03-16 08:09:09 +0000707 globLastArgument(prog, &argc, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000708 }
709 if (!argc) {
710 freeJob(job);
711 return 0;
712 }
713 prog->argv[argc] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000714
Erik Andersen161220c2000-03-16 08:12:48 +0000715 if (!returnCommand) {
716 job->text = malloc(strlen(*commandPtr) + 1);
717 strcpy(job->text, *commandPtr);
718 } else {
719 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +0000720 count = returnCommand - *commandPtr;
721 job->text = malloc(count + 1);
722 strncpy(job->text, *commandPtr, count);
723 job->text[count] = '\0';
724 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000725
Erik Andersen161220c2000-03-16 08:12:48 +0000726 *commandPtr = returnCommand;
Erik Andersen3522eb12000-03-12 23:49:18 +0000727
Erik Andersend75af992000-03-16 08:09:09 +0000728 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000729}
730
Erik Andersenbcd61772000-05-13 06:33:19 +0000731
Erik Andersend75af992000-03-16 08:09:09 +0000732static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000733{
Erik Andersen161220c2000-03-16 08:12:48 +0000734 struct job *job;
735 int i;
736 int nextin, nextout;
737 int pipefds[2]; /* pipefd[0] is for reading */
738 struct builtInCommand *x;
Erik Andersenbcd61772000-05-13 06:33:19 +0000739#ifdef BB_FEATURE_STANDALONE_SHELL
740 const struct BB_applet *a = applets;
741#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000742
Erik Andersen3522eb12000-03-12 23:49:18 +0000743
Erik Andersen161220c2000-03-16 08:12:48 +0000744 nextin = 0, nextout = 1;
745 for (i = 0; i < newJob.numProgs; i++) {
746 if ((i + 1) < newJob.numProgs) {
747 pipe(pipefds);
748 nextout = pipefds[1];
749 } else {
750 nextout = 1;
751 }
752
Erik Andersen330fd2b2000-05-19 05:35:19 +0000753 /* Match any built-ins here */
754 for (x = bltins; x->cmd; x++) {
755 if (!strcmp(newJob.progs[i].argv[0], x->cmd)) {
756 return (x->function(&newJob, jobList));
757 }
758 }
759
Erik Andersen161220c2000-03-16 08:12:48 +0000760 if (!(newJob.progs[i].pid = fork())) {
761 signal(SIGTTOU, SIG_DFL);
762
763 if (nextin != 0) {
764 dup2(nextin, 0);
765 close(nextin);
766 }
767
768 if (nextout != 1) {
769 dup2(nextout, 1);
770 close(nextout);
771 }
772
773 /* explicit redirections override pipes */
774 setupRedirections(newJob.progs + i);
775
Erik Andersenbcd61772000-05-13 06:33:19 +0000776 /* Match any built-ins here */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000777 for (x = bltins_forking; x->cmd; x++) {
Erik Andersenbcd61772000-05-13 06:33:19 +0000778 if (!strcmp(newJob.progs[i].argv[0], x->cmd)) {
779 exit (x->function(&newJob, jobList));
780 }
781 }
782#ifdef BB_FEATURE_STANDALONE_SHELL
783 /* Handle busybox internals here */
784 while (a->name != 0) {
785 if (strcmp(newJob.progs[i].argv[0], a->name) == 0) {
786 int argc;
787 char** argv=newJob.progs[i].argv;
Erik Andersenc3f5c9c2000-05-13 19:00:07 +0000788 for(argc=0;*argv!=NULL; argv++, argc++);
Erik Andersenbcd61772000-05-13 06:33:19 +0000789 exit((*(a->main)) (argc, newJob.progs[i].argv));
790 }
791 a++;
792 }
793#endif
794
Erik Andersen161220c2000-03-16 08:12:48 +0000795 execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
796 fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
797 strerror(errno));
798 }
799
800 /* put our child in the process group whose leader is the
801 first process in this pipe */
802 setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
803
804 if (nextin != 0)
805 close(nextin);
806 if (nextout != 1)
807 close(nextout);
808
809 /* If there isn't another process, nextin is garbage
810 but it doesn't matter */
811 nextin = pipefds[0];
812 }
813
814 newJob.pgrp = newJob.progs[0].pid;
815
816 /* find the ID for the job to use */
817 newJob.jobId = 1;
818 for (job = jobList->head; job; job = job->next)
819 if (job->jobId >= newJob.jobId)
820 newJob.jobId = job->jobId + 1;
821
822 /* add the job to the list of running jobs */
823 if (!jobList->head) {
824 job = jobList->head = malloc(sizeof(*job));
Erik Andersend75af992000-03-16 08:09:09 +0000825 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000826 for (job = jobList->head; job->next; job = job->next);
827 job->next = malloc(sizeof(*job));
828 job = job->next;
Erik Andersend75af992000-03-16 08:09:09 +0000829 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000830
Erik Andersen161220c2000-03-16 08:12:48 +0000831 *job = newJob;
832 job->next = NULL;
833 job->runningProgs = job->numProgs;
834 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000835
Erik Andersen161220c2000-03-16 08:12:48 +0000836 if (inBg) {
837 /* we don't wait for background jobs to return -- append it
838 to the list of backgrounded jobs and leave it alone */
Erik Andersen161220c2000-03-16 08:12:48 +0000839 printf("[%d] %d\n", job->jobId,
840 newJob.progs[newJob.numProgs - 1].pid);
841 } else {
842 jobList->fg = job;
Erik Andersen3522eb12000-03-12 23:49:18 +0000843
Erik Andersen161220c2000-03-16 08:12:48 +0000844 /* move the new process group into the foreground */
Erik Andersen161220c2000-03-16 08:12:48 +0000845 if (tcsetpgrp(0, newJob.pgrp))
846 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000847 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000848
Erik Andersen161220c2000-03-16 08:12:48 +0000849 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000850}
851
Erik Andersend75af992000-03-16 08:09:09 +0000852static int setupRedirections(struct childProgram *prog)
Erik Andersen3522eb12000-03-12 23:49:18 +0000853{
Erik Andersen161220c2000-03-16 08:12:48 +0000854 int i;
855 int openfd;
856 int mode = O_RDONLY;
857 struct redirectionSpecifier *redir = prog->redirections;
Erik Andersen3522eb12000-03-12 23:49:18 +0000858
Erik Andersen161220c2000-03-16 08:12:48 +0000859 for (i = 0; i < prog->numRedirections; i++, redir++) {
860 switch (redir->type) {
861 case REDIRECT_INPUT:
862 mode = O_RDONLY;
863 break;
864 case REDIRECT_OVERWRITE:
865 mode = O_RDWR | O_CREAT | O_TRUNC;
866 break;
867 case REDIRECT_APPEND:
868 mode = O_RDWR | O_CREAT | O_APPEND;
869 break;
870 }
871
872 openfd = open(redir->filename, mode, 0666);
873 if (openfd < 0) {
874 /* this could get lost if stderr has been redirected, but
875 bash and ash both lose it as well (though zsh doesn't!) */
876 fprintf(stderr, "error opening %s: %s\n", redir->filename,
877 strerror(errno));
878 return 1;
879 }
880
881 if (openfd != redir->fd) {
882 dup2(openfd, redir->fd);
883 close(openfd);
884 }
Erik Andersend75af992000-03-16 08:09:09 +0000885 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000886
Erik Andersen161220c2000-03-16 08:12:48 +0000887 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000888}
889
890
Erik Andersend75af992000-03-16 08:09:09 +0000891static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +0000892{
Erik Andersen161220c2000-03-16 08:12:48 +0000893 char *command;
894 char *nextCommand = NULL;
895 struct jobSet jobList = { NULL, NULL };
896 struct job newJob;
897 int i;
898 int status;
899 int inBg;
Erik Andersen3522eb12000-03-12 23:49:18 +0000900
Erik Andersen161220c2000-03-16 08:12:48 +0000901 command = (char *) calloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +0000902
Erik Andersen161220c2000-03-16 08:12:48 +0000903 /* don't pay any attention to this signal; it just confuses
904 things and isn't really meant for shells anyway */
905 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +0000906
Erik Andersen161220c2000-03-16 08:12:48 +0000907 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +0000908 if (!jobList.fg) {
909 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +0000910
Erik Andersend75af992000-03-16 08:09:09 +0000911 /* see if any background processes have exited */
912 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +0000913
Erik Andersend75af992000-03-16 08:09:09 +0000914 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +0000915 if (getCommand(input, command))
916 break;
917 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +0000918 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000919
Erik Andersend75af992000-03-16 08:09:09 +0000920 if (!parseCommand(&nextCommand, &newJob, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +0000921 newJob.numProgs) {
922 runCommand(newJob, &jobList, inBg);
Erik Andersend75af992000-03-16 08:09:09 +0000923 }
924 } else {
925 /* a job is running in the foreground; wait for it */
926 i = 0;
927 while (!jobList.fg->progs[i].pid ||
Erik Andersen161220c2000-03-16 08:12:48 +0000928 jobList.fg->progs[i].isStopped) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000929
Erik Andersend75af992000-03-16 08:09:09 +0000930 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +0000931
Erik Andersend75af992000-03-16 08:09:09 +0000932 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +0000933 /* the child exited */
934 jobList.fg->runningProgs--;
935 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000936
Erik Andersen161220c2000-03-16 08:12:48 +0000937 if (!jobList.fg->runningProgs) {
938 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +0000939
Erik Andersen161220c2000-03-16 08:12:48 +0000940 removeJob(&jobList, jobList.fg);
941 jobList.fg = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000942
Erik Andersen161220c2000-03-16 08:12:48 +0000943 /* move the shell to the foreground */
944 if (tcsetpgrp(0, getpid()))
945 perror("tcsetpgrp");
946 }
Erik Andersend75af992000-03-16 08:09:09 +0000947 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000948 /* the child was stopped */
949 jobList.fg->stoppedProgs++;
950 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +0000951
Erik Andersen161220c2000-03-16 08:12:48 +0000952 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
953 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
954 "Stopped", jobList.fg->text);
955 jobList.fg = NULL;
956 }
Erik Andersend75af992000-03-16 08:09:09 +0000957 }
958
959 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +0000960 /* move the shell to the foreground */
961 if (tcsetpgrp(0, getpid()))
962 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000963 }
964 }
965 }
Erik Andersen161220c2000-03-16 08:12:48 +0000966 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +0000967
Erik Andersen161220c2000-03-16 08:12:48 +0000968 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000969}
970
971
Erik Andersend75af992000-03-16 08:09:09 +0000972int shell_main(int argc, char **argv)
Erik Andersen3522eb12000-03-12 23:49:18 +0000973{
Erik Andersen161220c2000-03-16 08:12:48 +0000974 FILE *input = stdin;
Erik Andersen3522eb12000-03-12 23:49:18 +0000975
Erik Andersen161220c2000-03-16 08:12:48 +0000976 if (argc > 2) {
977 usage(shell_usage);
978 }
979 /* initialize the cwd */
980 getcwd(cwd, sizeof(cwd));
Erik Andersen3522eb12000-03-12 23:49:18 +0000981
Erik Andersenf0657d32000-04-12 17:49:52 +0000982#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersen1d1d9502000-04-21 01:26:49 +0000983 cmdedit_init();
Erik Andersenf0657d32000-04-12 17:49:52 +0000984 signal(SIGWINCH, win_changed);
985 win_changed(0);
986#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000987
Erik Andersen161220c2000-03-16 08:12:48 +0000988 //if (argv[0] && argv[0][0] == '-') {
989 // shell_source("/etc/profile");
990 //}
Erik Andersen3522eb12000-03-12 23:49:18 +0000991
Erik Andersen161220c2000-03-16 08:12:48 +0000992 if (argc < 2) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000993 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
994 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000995 } else {
Erik Andersene5b6c7d2000-04-17 16:16:10 +0000996 if (*argv[1]=='-') {
997 usage("sh\n\nlash -- the BusyBox LAme SHell (command interpreter)\n");
998 }
Erik Andersen161220c2000-03-16 08:12:48 +0000999 input = fopen(argv[1], "r");
Erik Andersenf0657d32000-04-12 17:49:52 +00001000 if (!input) {
Erik Andersene5b6c7d2000-04-17 16:16:10 +00001001 fatalError("sh: Couldn't open file '%s': %s\n", argv[1],
Erik Andersen161220c2000-03-16 08:12:48 +00001002 strerror(errno));
Erik Andersenf0657d32000-04-12 17:49:52 +00001003 }
Erik Andersen161220c2000-03-16 08:12:48 +00001004 }
Erik Andersend75af992000-03-16 08:09:09 +00001005
Erik Andersen161220c2000-03-16 08:12:48 +00001006 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +00001007}