blob: 8ea5e305befcbca4b28f9bc2f0ab9144b40e28cc [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
191 else
192 exit(atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000193}
194
195/* built-in 'fg' and 'bg' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000196static int shell_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000197{
Erik Andersen161220c2000-03-16 08:12:48 +0000198 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000199 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000200
Erik Andersen161220c2000-03-16 08:12:48 +0000201 if (!jobList->head) {
202 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
203 fprintf(stderr, "%s: exactly one argument is expected\n",
204 cmd->progs[0].argv[0]);
205 return FALSE;
206 }
207 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
208 fprintf(stderr, "%s: bad argument '%s'\n",
209 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
210 return FALSE;
211 for (job = jobList->head; job; job = job->next) {
212 if (job->jobId == jobNum) {
213 break;
214 }
215 }
216 }
217 } else {
218 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000219 }
Erik Andersen161220c2000-03-16 08:12:48 +0000220
221 if (!job) {
222 fprintf(stderr, "%s: unknown job %d\n",
223 cmd->progs[0].argv[0], jobNum);
224 return FALSE;
Erik Andersend75af992000-03-16 08:09:09 +0000225 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000226
Erik Andersen161220c2000-03-16 08:12:48 +0000227 if (*cmd->progs[0].argv[0] == 'f') {
228 /* Make this job the foreground job */
229 if (tcsetpgrp(0, job->pgrp))
230 perror("tcsetpgrp");
231 jobList->fg = job;
232 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000233
Erik Andersen161220c2000-03-16 08:12:48 +0000234 /* Restart the processes in the job */
235 for (i = 0; i < job->numProgs; i++)
236 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000237
Erik Andersen161220c2000-03-16 08:12:48 +0000238 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000239
Erik Andersen161220c2000-03-16 08:12:48 +0000240 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000241
Erik Andersen161220c2000-03-16 08:12:48 +0000242 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000243}
244
245/* built-in 'help' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000246static int shell_help(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000247{
Erik Andersen161220c2000-03-16 08:12:48 +0000248 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000249
Erik Andersen161220c2000-03-16 08:12:48 +0000250 fprintf(stdout, "\nBuilt-in commands:\n");
251 fprintf(stdout, "-------------------\n");
252 for (x = bltins; x->cmd; x++) {
253 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
254 }
Erik Andersen330fd2b2000-05-19 05:35:19 +0000255 for (x = bltins_forking; x->cmd; x++) {
256 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
257 }
Erik Andersen161220c2000-03-16 08:12:48 +0000258 fprintf(stdout, "\n\n");
259 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000260}
261
262/* built-in 'jobs' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000263static int shell_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000264{
Erik Andersen161220c2000-03-16 08:12:48 +0000265 struct job *job;
266 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000267
Erik Andersen161220c2000-03-16 08:12:48 +0000268 for (job = jobList->head; job; job = job->next) {
269 if (job->runningProgs == job->stoppedProgs)
270 statusString = "Stopped";
271 else
272 statusString = "Running";
273
274 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
275 }
276 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000277}
278
279
280/* built-in 'pwd' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000281static int shell_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000282{
Erik Andersen161220c2000-03-16 08:12:48 +0000283 getcwd(cwd, sizeof(cwd));
284 fprintf(stdout, "%s\n", cwd);
285 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000286}
287
Erik Andersen6273f652000-03-17 01:12:41 +0000288/* built-in 'export VAR=value' handler */
289static int shell_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000290{
Erik Andersen161220c2000-03-16 08:12:48 +0000291 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000292
Erik Andersen161220c2000-03-16 08:12:48 +0000293 if (!cmd->progs[0].argv[1] == 1) {
294 return (shell_env(cmd, junk));
295 }
296 res = putenv(cmd->progs[0].argv[1]);
297 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000298 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000299 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000300}
301
302/* Built-in '.' handler (read-in and execute commands from file) */
Erik Andersend75af992000-03-16 08:09:09 +0000303static int shell_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000304{
Erik Andersen161220c2000-03-16 08:12:48 +0000305 FILE *input;
306 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000307
Erik Andersen161220c2000-03-16 08:12:48 +0000308 if (!cmd->progs[0].argv[1] == 1)
309 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000310
Erik Andersen161220c2000-03-16 08:12:48 +0000311 input = fopen(cmd->progs[0].argv[1], "r");
312 if (!input) {
313 fprintf(stdout, "Couldn't open file '%s'\n",
314 cmd->progs[0].argv[1]);
315 return FALSE;
316 }
Erik Andersend75af992000-03-16 08:09:09 +0000317
Erik Andersen161220c2000-03-16 08:12:48 +0000318 /* Now run the file */
319 status = busy_loop(input);
320 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000321}
322
323/* built-in 'unset VAR' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000324static int shell_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000325{
Erik Andersen161220c2000-03-16 08:12:48 +0000326 if (!cmd->progs[0].argv[1] == 1) {
327 fprintf(stdout, "unset: parameter required.\n");
328 return FALSE;
329 }
330 unsetenv(cmd->progs[0].argv[1]);
331 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000332}
333
334/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000335static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000336{
Erik Andersen161220c2000-03-16 08:12:48 +0000337 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000338
Erik Andersen161220c2000-03-16 08:12:48 +0000339 for (i = 0; i < cmd->numProgs; i++) {
340 free(cmd->progs[i].argv);
341 if (cmd->progs[i].redirections)
342 free(cmd->progs[i].redirections);
343 if (cmd->progs[i].freeGlob)
344 globfree(&cmd->progs[i].globResult);
345 }
346 free(cmd->progs);
347 if (cmd->text)
348 free(cmd->text);
349 free(cmd->cmdBuf);
Erik Andersen3522eb12000-03-12 23:49:18 +0000350}
351
352/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000353static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000354{
Erik Andersen161220c2000-03-16 08:12:48 +0000355 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000356
Erik Andersen161220c2000-03-16 08:12:48 +0000357 freeJob(job);
358 if (job == jobList->head) {
359 jobList->head = job->next;
360 } else {
361 prevJob = jobList->head;
362 while (prevJob->next != job)
363 prevJob = prevJob->next;
364 prevJob->next = job->next;
365 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000366
Erik Andersen161220c2000-03-16 08:12:48 +0000367 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000368}
369
370/* Checks to see if any background processes have exited -- if they
371 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000372static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000373{
Erik Andersen161220c2000-03-16 08:12:48 +0000374 struct job *job;
375 pid_t childpid;
376 int status;
377 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000378
Erik Andersen161220c2000-03-16 08:12:48 +0000379 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
380 for (job = jobList->head; job; job = job->next) {
381 progNum = 0;
382 while (progNum < job->numProgs &&
383 job->progs[progNum].pid != childpid) progNum++;
384 if (progNum < job->numProgs)
385 break;
386 }
387
388 if (WIFEXITED(status) || WIFSIGNALED(status)) {
389 /* child exited */
390 job->runningProgs--;
391 job->progs[progNum].pid = 0;
392
393 if (!job->runningProgs) {
394 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
395 removeJob(jobList, job);
396 }
397 } else {
398 /* child stopped */
399 job->stoppedProgs++;
400 job->progs[progNum].isStopped = 1;
401
402 if (job->stoppedProgs == job->numProgs) {
403 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
404 job->text);
405 }
406 }
Erik Andersend75af992000-03-16 08:09:09 +0000407 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000408
Erik Andersen161220c2000-03-16 08:12:48 +0000409 if (childpid == -1 && errno != ECHILD)
410 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000411}
412
Erik Andersend75af992000-03-16 08:09:09 +0000413static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000414{
Erik Andersen161220c2000-03-16 08:12:48 +0000415 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000416#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000417 int len;
418 char *promptStr;
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000419 len=fprintf(stdout, "%s %s", cwd, prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000420 fflush(stdout);
421 promptStr=(char*)malloc(sizeof(char)*(len+1));
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000422 sprintf(promptStr, "%s %s", cwd, prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000423 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000424 free( promptStr);
Erik Andersen161220c2000-03-16 08:12:48 +0000425 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000426#else
427 fprintf(stdout, "%s %s", cwd, prompt);
428 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000429#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000430 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000431
Erik Andersen161220c2000-03-16 08:12:48 +0000432 if (!fgets(command, BUFSIZ - 2, source)) {
433 if (source == stdin)
434 printf("\n");
435 return 1;
436 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000437
Erik Andersen161220c2000-03-16 08:12:48 +0000438 /* remove trailing newline */
439 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000440
Erik Andersen161220c2000-03-16 08:12:48 +0000441 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000442}
443
Erik Andersend75af992000-03-16 08:09:09 +0000444static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000445 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000446{
Erik Andersen161220c2000-03-16 08:12:48 +0000447 int argc = *argcPtr;
448 int argcAlloced = *argcAllocedPtr;
449 int rc;
450 int flags;
451 int i;
452 char *src, *dst;
Erik Andersen3522eb12000-03-12 23:49:18 +0000453
Erik Andersen161220c2000-03-16 08:12:48 +0000454 if (argc > 1) { /* cmd->globResult is already initialized */
455 flags = GLOB_APPEND;
456 i = prog->globResult.gl_pathc;
457 } else {
458 prog->freeGlob = 1;
459 flags = 0;
460 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000461 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000462
Erik Andersen161220c2000-03-16 08:12:48 +0000463 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
464 if (rc == GLOB_NOSPACE) {
465 fprintf(stderr, "out of space during glob operation\n");
466 return;
467 } else if (rc == GLOB_NOMATCH ||
468 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
469 !strcmp(prog->argv[argc - 1],
470 prog->globResult.gl_pathv[i]))) {
471 /* we need to remove whatever \ quoting is still present */
472 src = dst = prog->argv[argc - 1];
473 while (*src) {
474 if (*src != '\\')
475 *dst++ = *src;
476 src++;
477 }
478 *dst = '\0';
479 } else if (!rc) {
480 argcAlloced += (prog->globResult.gl_pathc - i);
481 prog->argv =
482 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
483 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
484 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
485 argc += (prog->globResult.gl_pathc - i - 1);
486 }
487
488 *argcAllocedPtr = argcAlloced;
489 *argcPtr = argc;
Erik Andersen3522eb12000-03-12 23:49:18 +0000490}
491
492/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
493 line). If a valid command is found, commandPtr is set to point to
494 the beginning of the next command (if the original command had more
495 then one job associated with it) or NULL if no more commands are
496 present. */
Erik Andersend75af992000-03-16 08:09:09 +0000497static int parseCommand(char **commandPtr, struct job *job, int *isBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000498{
Erik Andersen161220c2000-03-16 08:12:48 +0000499 char *command;
500 char *returnCommand = NULL;
501 char *src, *buf, *chptr;
502 int argc = 0;
503 int done = 0;
504 int argvAlloced;
505 int i;
506 char quote = '\0';
507 int count;
508 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000509
Erik Andersen161220c2000-03-16 08:12:48 +0000510 /* skip leading white space */
511 while (**commandPtr && isspace(**commandPtr))
512 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000513
Erik Andersen161220c2000-03-16 08:12:48 +0000514 /* this handles empty lines or leading '#' characters */
515 if (!**commandPtr || (**commandPtr == '#')) {
516 job->numProgs = 0;
517 *commandPtr = NULL;
518 return 0;
519 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000520
Erik Andersen161220c2000-03-16 08:12:48 +0000521 *isBg = 0;
522 job->numProgs = 1;
523 job->progs = malloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000524
Erik Andersen161220c2000-03-16 08:12:48 +0000525 /* We set the argv elements to point inside of this string. The
526 memory is freed by freeJob().
Erik Andersen3522eb12000-03-12 23:49:18 +0000527
Erik Andersen161220c2000-03-16 08:12:48 +0000528 Getting clean memory relieves us of the task of NULL
529 terminating things and makes the rest of this look a bit
530 cleaner (though it is, admittedly, a tad less efficient) */
531 job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1);
532 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000533
Erik Andersen161220c2000-03-16 08:12:48 +0000534 prog = job->progs;
535 prog->numRedirections = 0;
536 prog->redirections = NULL;
537 prog->freeGlob = 0;
538 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000539
Erik Andersen161220c2000-03-16 08:12:48 +0000540 argvAlloced = 5;
541 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
542 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000543
Erik Andersen161220c2000-03-16 08:12:48 +0000544 buf = command;
545 src = *commandPtr;
546 while (*src && !done) {
547 if (quote == *src) {
548 quote = '\0';
549 } else if (quote) {
550 if (*src == '\\') {
551 src++;
552 if (!*src) {
553 fprintf(stderr, "character expected after \\\n");
554 freeJob(job);
555 return 1;
556 }
557
558 /* in shell, "\'" should yield \' */
559 if (*src != quote)
560 *buf++ = '\\';
561 } else if (*src == '*' || *src == '?' || *src == '[' ||
562 *src == ']') *buf++ = '\\';
563 *buf++ = *src;
564 } else if (isspace(*src)) {
565 if (*prog->argv[argc]) {
566 buf++, argc++;
567 /* +1 here leaves room for the NULL which ends argv */
568 if ((argc + 1) == argvAlloced) {
569 argvAlloced += 5;
570 prog->argv = realloc(prog->argv,
571 sizeof(*prog->argv) *
572 argvAlloced);
573 }
574 prog->argv[argc] = buf;
575
576 globLastArgument(prog, &argc, &argvAlloced);
577 }
578 } else
579 switch (*src) {
580 case '"':
581 case '\'':
582 quote = *src;
583 break;
584
585 case '#': /* comment */
586 done = 1;
587 break;
588
589 case '>': /* redirections */
590 case '<':
591 i = prog->numRedirections++;
592 prog->redirections = realloc(prog->redirections,
593 sizeof(*prog->redirections) *
594 (i + 1));
595
596 prog->redirections[i].fd = -1;
597 if (buf != prog->argv[argc]) {
598 /* the stuff before this character may be the file number
599 being redirected */
600 prog->redirections[i].fd =
601 strtol(prog->argv[argc], &chptr, 10);
602
603 if (*chptr && *prog->argv[argc]) {
604 buf++, argc++;
605 globLastArgument(prog, &argc, &argvAlloced);
606 }
607 }
608
609 if (prog->redirections[i].fd == -1) {
610 if (*src == '>')
611 prog->redirections[i].fd = 1;
612 else
613 prog->redirections[i].fd = 0;
614 }
615
616 if (*src++ == '>') {
617 if (*src == '>')
618 prog->redirections[i].type =
619 REDIRECT_APPEND, src++;
620 else
621 prog->redirections[i].type = REDIRECT_OVERWRITE;
622 } else {
623 prog->redirections[i].type = REDIRECT_INPUT;
624 }
625
626 /* This isn't POSIX sh compliant. Oh well. */
627 chptr = src;
628 while (isspace(*chptr))
629 chptr++;
630
631 if (!*chptr) {
632 fprintf(stderr, "file name expected after %c\n", *src);
633 freeJob(job);
634 return 1;
635 }
636
637 prog->redirections[i].filename = buf;
638 while (*chptr && !isspace(*chptr))
639 *buf++ = *chptr++;
640
641 src = chptr - 1; /* we src++ later */
642 prog->argv[argc] = ++buf;
643 break;
644
645 case '|': /* pipe */
646 /* finish this command */
647 if (*prog->argv[argc])
648 argc++;
649 if (!argc) {
650 fprintf(stderr, "empty command in pipe\n");
651 freeJob(job);
652 return 1;
653 }
654 prog->argv[argc] = NULL;
655
656 /* and start the next */
657 job->numProgs++;
658 job->progs = realloc(job->progs,
659 sizeof(*job->progs) * job->numProgs);
660 prog = job->progs + (job->numProgs - 1);
661 prog->numRedirections = 0;
662 prog->redirections = NULL;
663 prog->freeGlob = 0;
664 argc = 0;
665
666 argvAlloced = 5;
667 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
668 prog->argv[0] = ++buf;
669
670 src++;
671 while (*src && isspace(*src))
672 src++;
673
674 if (!*src) {
675 fprintf(stderr, "empty command in pipe\n");
676 return 1;
677 }
678 src--; /* we'll ++ it at the end of the loop */
679
680 break;
681
682 case '&': /* background */
683 *isBg = 1;
684 case ';': /* multiple commands */
685 done = 1;
686 returnCommand = *commandPtr + (src - *commandPtr) + 1;
687 break;
688
689 case '\\':
690 src++;
691 if (!*src) {
692 freeJob(job);
693 fprintf(stderr, "character expected after \\\n");
694 return 1;
695 }
696 if (*src == '*' || *src == '[' || *src == ']'
697 || *src == '?') *buf++ = '\\';
698 /* fallthrough */
699 default:
700 *buf++ = *src;
701 }
702
Erik Andersend75af992000-03-16 08:09:09 +0000703 src++;
Erik Andersen161220c2000-03-16 08:12:48 +0000704 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000705
Erik Andersen161220c2000-03-16 08:12:48 +0000706 if (*prog->argv[argc]) {
707 argc++;
Erik Andersend75af992000-03-16 08:09:09 +0000708 globLastArgument(prog, &argc, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000709 }
710 if (!argc) {
711 freeJob(job);
712 return 0;
713 }
714 prog->argv[argc] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000715
Erik Andersen161220c2000-03-16 08:12:48 +0000716 if (!returnCommand) {
717 job->text = malloc(strlen(*commandPtr) + 1);
718 strcpy(job->text, *commandPtr);
719 } else {
720 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +0000721 count = returnCommand - *commandPtr;
722 job->text = malloc(count + 1);
723 strncpy(job->text, *commandPtr, count);
724 job->text[count] = '\0';
725 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000726
Erik Andersen161220c2000-03-16 08:12:48 +0000727 *commandPtr = returnCommand;
Erik Andersen3522eb12000-03-12 23:49:18 +0000728
Erik Andersend75af992000-03-16 08:09:09 +0000729 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000730}
731
Erik Andersenbcd61772000-05-13 06:33:19 +0000732
Erik Andersend75af992000-03-16 08:09:09 +0000733static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000734{
Erik Andersen161220c2000-03-16 08:12:48 +0000735 struct job *job;
736 int i;
737 int nextin, nextout;
738 int pipefds[2]; /* pipefd[0] is for reading */
739 struct builtInCommand *x;
Erik Andersenbcd61772000-05-13 06:33:19 +0000740#ifdef BB_FEATURE_STANDALONE_SHELL
741 const struct BB_applet *a = applets;
742#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000743
Erik Andersen3522eb12000-03-12 23:49:18 +0000744
Erik Andersen161220c2000-03-16 08:12:48 +0000745 nextin = 0, nextout = 1;
746 for (i = 0; i < newJob.numProgs; i++) {
747 if ((i + 1) < newJob.numProgs) {
748 pipe(pipefds);
749 nextout = pipefds[1];
750 } else {
751 nextout = 1;
752 }
753
Erik Andersen330fd2b2000-05-19 05:35:19 +0000754 /* Match any built-ins here */
755 for (x = bltins; x->cmd; x++) {
756 if (!strcmp(newJob.progs[i].argv[0], x->cmd)) {
757 return (x->function(&newJob, jobList));
758 }
759 }
760
Erik Andersen161220c2000-03-16 08:12:48 +0000761 if (!(newJob.progs[i].pid = fork())) {
762 signal(SIGTTOU, SIG_DFL);
763
764 if (nextin != 0) {
765 dup2(nextin, 0);
766 close(nextin);
767 }
768
769 if (nextout != 1) {
770 dup2(nextout, 1);
771 close(nextout);
772 }
773
774 /* explicit redirections override pipes */
775 setupRedirections(newJob.progs + i);
776
Erik Andersenbcd61772000-05-13 06:33:19 +0000777 /* Match any built-ins here */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000778 for (x = bltins_forking; x->cmd; x++) {
Erik Andersenbcd61772000-05-13 06:33:19 +0000779 if (!strcmp(newJob.progs[i].argv[0], x->cmd)) {
780 exit (x->function(&newJob, jobList));
781 }
782 }
783#ifdef BB_FEATURE_STANDALONE_SHELL
784 /* Handle busybox internals here */
785 while (a->name != 0) {
786 if (strcmp(newJob.progs[i].argv[0], a->name) == 0) {
787 int argc;
788 char** argv=newJob.progs[i].argv;
Erik Andersenc3f5c9c2000-05-13 19:00:07 +0000789 for(argc=0;*argv!=NULL; argv++, argc++);
Erik Andersenbcd61772000-05-13 06:33:19 +0000790 exit((*(a->main)) (argc, newJob.progs[i].argv));
791 }
792 a++;
793 }
794#endif
795
Erik Andersen161220c2000-03-16 08:12:48 +0000796 execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
797 fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
798 strerror(errno));
799 }
800
801 /* put our child in the process group whose leader is the
802 first process in this pipe */
803 setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
804
805 if (nextin != 0)
806 close(nextin);
807 if (nextout != 1)
808 close(nextout);
809
810 /* If there isn't another process, nextin is garbage
811 but it doesn't matter */
812 nextin = pipefds[0];
813 }
814
815 newJob.pgrp = newJob.progs[0].pid;
816
817 /* find the ID for the job to use */
818 newJob.jobId = 1;
819 for (job = jobList->head; job; job = job->next)
820 if (job->jobId >= newJob.jobId)
821 newJob.jobId = job->jobId + 1;
822
823 /* add the job to the list of running jobs */
824 if (!jobList->head) {
825 job = jobList->head = malloc(sizeof(*job));
Erik Andersend75af992000-03-16 08:09:09 +0000826 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000827 for (job = jobList->head; job->next; job = job->next);
828 job->next = malloc(sizeof(*job));
829 job = job->next;
Erik Andersend75af992000-03-16 08:09:09 +0000830 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000831
Erik Andersen161220c2000-03-16 08:12:48 +0000832 *job = newJob;
833 job->next = NULL;
834 job->runningProgs = job->numProgs;
835 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000836
Erik Andersen161220c2000-03-16 08:12:48 +0000837 if (inBg) {
838 /* we don't wait for background jobs to return -- append it
839 to the list of backgrounded jobs and leave it alone */
Erik Andersen161220c2000-03-16 08:12:48 +0000840 printf("[%d] %d\n", job->jobId,
841 newJob.progs[newJob.numProgs - 1].pid);
842 } else {
843 jobList->fg = job;
Erik Andersen3522eb12000-03-12 23:49:18 +0000844
Erik Andersen161220c2000-03-16 08:12:48 +0000845 /* move the new process group into the foreground */
Erik Andersen161220c2000-03-16 08:12:48 +0000846 if (tcsetpgrp(0, newJob.pgrp))
847 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000848 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000849
Erik Andersen161220c2000-03-16 08:12:48 +0000850 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000851}
852
Erik Andersend75af992000-03-16 08:09:09 +0000853static int setupRedirections(struct childProgram *prog)
Erik Andersen3522eb12000-03-12 23:49:18 +0000854{
Erik Andersen161220c2000-03-16 08:12:48 +0000855 int i;
856 int openfd;
857 int mode = O_RDONLY;
858 struct redirectionSpecifier *redir = prog->redirections;
Erik Andersen3522eb12000-03-12 23:49:18 +0000859
Erik Andersen161220c2000-03-16 08:12:48 +0000860 for (i = 0; i < prog->numRedirections; i++, redir++) {
861 switch (redir->type) {
862 case REDIRECT_INPUT:
863 mode = O_RDONLY;
864 break;
865 case REDIRECT_OVERWRITE:
866 mode = O_RDWR | O_CREAT | O_TRUNC;
867 break;
868 case REDIRECT_APPEND:
869 mode = O_RDWR | O_CREAT | O_APPEND;
870 break;
871 }
872
873 openfd = open(redir->filename, mode, 0666);
874 if (openfd < 0) {
875 /* this could get lost if stderr has been redirected, but
876 bash and ash both lose it as well (though zsh doesn't!) */
877 fprintf(stderr, "error opening %s: %s\n", redir->filename,
878 strerror(errno));
879 return 1;
880 }
881
882 if (openfd != redir->fd) {
883 dup2(openfd, redir->fd);
884 close(openfd);
885 }
Erik Andersend75af992000-03-16 08:09:09 +0000886 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000887
Erik Andersen161220c2000-03-16 08:12:48 +0000888 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000889}
890
891
Erik Andersend75af992000-03-16 08:09:09 +0000892static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +0000893{
Erik Andersen161220c2000-03-16 08:12:48 +0000894 char *command;
895 char *nextCommand = NULL;
896 struct jobSet jobList = { NULL, NULL };
897 struct job newJob;
898 int i;
899 int status;
900 int inBg;
Erik Andersen3522eb12000-03-12 23:49:18 +0000901
Erik Andersen161220c2000-03-16 08:12:48 +0000902 command = (char *) calloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +0000903
Erik Andersen161220c2000-03-16 08:12:48 +0000904 /* don't pay any attention to this signal; it just confuses
905 things and isn't really meant for shells anyway */
906 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +0000907
Erik Andersen161220c2000-03-16 08:12:48 +0000908 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +0000909 if (!jobList.fg) {
910 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +0000911
Erik Andersend75af992000-03-16 08:09:09 +0000912 /* see if any background processes have exited */
913 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +0000914
Erik Andersend75af992000-03-16 08:09:09 +0000915 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +0000916 if (getCommand(input, command))
917 break;
918 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +0000919 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000920
Erik Andersend75af992000-03-16 08:09:09 +0000921 if (!parseCommand(&nextCommand, &newJob, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +0000922 newJob.numProgs) {
923 runCommand(newJob, &jobList, inBg);
Erik Andersend75af992000-03-16 08:09:09 +0000924 }
925 } else {
926 /* a job is running in the foreground; wait for it */
927 i = 0;
928 while (!jobList.fg->progs[i].pid ||
Erik Andersen161220c2000-03-16 08:12:48 +0000929 jobList.fg->progs[i].isStopped) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000930
Erik Andersend75af992000-03-16 08:09:09 +0000931 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +0000932
Erik Andersend75af992000-03-16 08:09:09 +0000933 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +0000934 /* the child exited */
935 jobList.fg->runningProgs--;
936 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000937
Erik Andersen161220c2000-03-16 08:12:48 +0000938 if (!jobList.fg->runningProgs) {
939 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +0000940
Erik Andersen161220c2000-03-16 08:12:48 +0000941 removeJob(&jobList, jobList.fg);
942 jobList.fg = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000943
Erik Andersen161220c2000-03-16 08:12:48 +0000944 /* move the shell to the foreground */
945 if (tcsetpgrp(0, getpid()))
946 perror("tcsetpgrp");
947 }
Erik Andersend75af992000-03-16 08:09:09 +0000948 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000949 /* the child was stopped */
950 jobList.fg->stoppedProgs++;
951 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +0000952
Erik Andersen161220c2000-03-16 08:12:48 +0000953 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
954 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
955 "Stopped", jobList.fg->text);
956 jobList.fg = NULL;
957 }
Erik Andersend75af992000-03-16 08:09:09 +0000958 }
959
960 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +0000961 /* move the shell to the foreground */
962 if (tcsetpgrp(0, getpid()))
963 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000964 }
965 }
966 }
Erik Andersen161220c2000-03-16 08:12:48 +0000967 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +0000968
Erik Andersen161220c2000-03-16 08:12:48 +0000969 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000970}
971
972
Erik Andersend75af992000-03-16 08:09:09 +0000973int shell_main(int argc, char **argv)
Erik Andersen3522eb12000-03-12 23:49:18 +0000974{
Erik Andersen161220c2000-03-16 08:12:48 +0000975 FILE *input = stdin;
Erik Andersen3522eb12000-03-12 23:49:18 +0000976
Erik Andersen161220c2000-03-16 08:12:48 +0000977 if (argc > 2) {
978 usage(shell_usage);
979 }
980 /* initialize the cwd */
981 getcwd(cwd, sizeof(cwd));
Erik Andersen3522eb12000-03-12 23:49:18 +0000982
Erik Andersenf0657d32000-04-12 17:49:52 +0000983#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersen1d1d9502000-04-21 01:26:49 +0000984 cmdedit_init();
Erik Andersenf0657d32000-04-12 17:49:52 +0000985 signal(SIGWINCH, win_changed);
986 win_changed(0);
987#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000988
Erik Andersen161220c2000-03-16 08:12:48 +0000989 //if (argv[0] && argv[0][0] == '-') {
990 // shell_source("/etc/profile");
991 //}
Erik Andersen3522eb12000-03-12 23:49:18 +0000992
Erik Andersen161220c2000-03-16 08:12:48 +0000993 if (argc < 2) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000994 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
995 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000996 } else {
Erik Andersene5b6c7d2000-04-17 16:16:10 +0000997 if (*argv[1]=='-') {
998 usage("sh\n\nlash -- the BusyBox LAme SHell (command interpreter)\n");
999 }
Erik Andersen161220c2000-03-16 08:12:48 +00001000 input = fopen(argv[1], "r");
Erik Andersenf0657d32000-04-12 17:49:52 +00001001 if (!input) {
Erik Andersene5b6c7d2000-04-17 16:16:10 +00001002 fatalError("sh: Couldn't open file '%s': %s\n", argv[1],
Erik Andersen161220c2000-03-16 08:12:48 +00001003 strerror(errno));
Erik Andersenf0657d32000-04-12 17:49:52 +00001004 }
Erik Andersen161220c2000-03-16 08:12:48 +00001005 }
Erik Andersend75af992000-03-16 08:09:09 +00001006
Erik Andersen161220c2000-03-16 08:12:48 +00001007 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +00001008}