blob: 785e9cc3fa233e0346e591bd3989bb6c9a7d7c47 [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[] =
Eric Andersen1c314ad2000-06-28 16:56:25 +0000134 "sh [FILE]...\n"
135 " or: sh -c command [args]...\n"
Erik Andersen7ab9c7e2000-05-12 19:41:47 +0000136#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 = "# ";
Eric Andersen1c314ad2000-06-28 16:56:25 +0000143static char *local_pending_command = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000144
Erik Andersenf0657d32000-04-12 17:49:52 +0000145#ifdef BB_FEATURE_SH_COMMAND_EDITING
146void win_changed(int sig)
147{
148 struct winsize win = { 0, 0 };
149 ioctl(0, TIOCGWINSZ, &win);
150 if (win.ws_col > 0) {
151 cmdedit_setwidth( win.ws_col - 1);
152 }
153}
154#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000155
Erik Andersen3522eb12000-03-12 23:49:18 +0000156
Erik Andersend75af992000-03-16 08:09:09 +0000157/* built-in 'cd <path>' handler */
158static int shell_cd(struct job *cmd, struct jobSet *junk)
159{
Erik Andersen161220c2000-03-16 08:12:48 +0000160 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000161
Erik Andersen161220c2000-03-16 08:12:48 +0000162 if (!cmd->progs[0].argv[1] == 1)
163 newdir = getenv("HOME");
164 else
165 newdir = cmd->progs[0].argv[1];
166 if (chdir(newdir)) {
167 printf("cd: %s: %s\n", newdir, strerror(errno));
168 return FALSE;
169 }
170 getcwd(cwd, sizeof(cwd));
171
172 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000173}
174
175/* built-in 'env' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000176static int shell_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000177{
Erik Andersen161220c2000-03-16 08:12:48 +0000178 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000179
Erik Andersen161220c2000-03-16 08:12:48 +0000180 for (e = environ; *e; e++) {
181 fprintf(stdout, "%s\n", *e);
182 }
183 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000184}
185
186/* built-in 'exit' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000187static int shell_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000188{
Erik Andersen161220c2000-03-16 08:12:48 +0000189 if (!cmd->progs[0].argv[1] == 1)
190 exit TRUE;
191
Eric Andersenb6106152000-06-19 17:25:40 +0000192 return(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 */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000229 /* suppress messages when run from /linuxrc mag@sysgo.de */
230 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
231 perror("tcsetpgrp");
Erik Andersen161220c2000-03-16 08:12:48 +0000232 jobList->fg = job;
233 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000234
Erik Andersen161220c2000-03-16 08:12:48 +0000235 /* Restart the processes in the job */
236 for (i = 0; i < job->numProgs; i++)
237 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000238
Erik Andersen161220c2000-03-16 08:12:48 +0000239 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000240
Erik Andersen161220c2000-03-16 08:12:48 +0000241 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000242
Erik Andersen161220c2000-03-16 08:12:48 +0000243 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000244}
245
246/* built-in 'help' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000247static int shell_help(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000248{
Erik Andersen161220c2000-03-16 08:12:48 +0000249 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000250
Erik Andersen161220c2000-03-16 08:12:48 +0000251 fprintf(stdout, "\nBuilt-in commands:\n");
252 fprintf(stdout, "-------------------\n");
253 for (x = bltins; x->cmd; x++) {
254 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
255 }
Erik Andersen330fd2b2000-05-19 05:35:19 +0000256 for (x = bltins_forking; x->cmd; x++) {
257 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
258 }
Erik Andersen161220c2000-03-16 08:12:48 +0000259 fprintf(stdout, "\n\n");
260 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000261}
262
263/* built-in 'jobs' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000264static int shell_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000265{
Erik Andersen161220c2000-03-16 08:12:48 +0000266 struct job *job;
267 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000268
Erik Andersen161220c2000-03-16 08:12:48 +0000269 for (job = jobList->head; job; job = job->next) {
270 if (job->runningProgs == job->stoppedProgs)
271 statusString = "Stopped";
272 else
273 statusString = "Running";
274
275 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
276 }
277 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000278}
279
280
281/* built-in 'pwd' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000282static int shell_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000283{
Erik Andersen161220c2000-03-16 08:12:48 +0000284 getcwd(cwd, sizeof(cwd));
285 fprintf(stdout, "%s\n", cwd);
286 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000287}
288
Erik Andersen6273f652000-03-17 01:12:41 +0000289/* built-in 'export VAR=value' handler */
290static int shell_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000291{
Erik Andersen161220c2000-03-16 08:12:48 +0000292 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000293
Erik Andersen161220c2000-03-16 08:12:48 +0000294 if (!cmd->progs[0].argv[1] == 1) {
295 return (shell_env(cmd, junk));
296 }
297 res = putenv(cmd->progs[0].argv[1]);
298 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000299 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000300 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000301}
302
303/* Built-in '.' handler (read-in and execute commands from file) */
Erik Andersend75af992000-03-16 08:09:09 +0000304static int shell_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000305{
Erik Andersen161220c2000-03-16 08:12:48 +0000306 FILE *input;
307 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000308
Erik Andersen161220c2000-03-16 08:12:48 +0000309 if (!cmd->progs[0].argv[1] == 1)
310 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000311
Erik Andersen161220c2000-03-16 08:12:48 +0000312 input = fopen(cmd->progs[0].argv[1], "r");
313 if (!input) {
314 fprintf(stdout, "Couldn't open file '%s'\n",
315 cmd->progs[0].argv[1]);
316 return FALSE;
317 }
Erik Andersend75af992000-03-16 08:09:09 +0000318
Erik Andersen161220c2000-03-16 08:12:48 +0000319 /* Now run the file */
320 status = busy_loop(input);
321 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000322}
323
324/* built-in 'unset VAR' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000325static int shell_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000326{
Erik Andersen161220c2000-03-16 08:12:48 +0000327 if (!cmd->progs[0].argv[1] == 1) {
328 fprintf(stdout, "unset: parameter required.\n");
329 return FALSE;
330 }
331 unsetenv(cmd->progs[0].argv[1]);
332 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000333}
334
335/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000336static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000337{
Erik Andersen161220c2000-03-16 08:12:48 +0000338 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000339
Erik Andersen161220c2000-03-16 08:12:48 +0000340 for (i = 0; i < cmd->numProgs; i++) {
341 free(cmd->progs[i].argv);
342 if (cmd->progs[i].redirections)
343 free(cmd->progs[i].redirections);
344 if (cmd->progs[i].freeGlob)
345 globfree(&cmd->progs[i].globResult);
346 }
347 free(cmd->progs);
348 if (cmd->text)
349 free(cmd->text);
350 free(cmd->cmdBuf);
Erik Andersen3522eb12000-03-12 23:49:18 +0000351}
352
353/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000354static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000355{
Erik Andersen161220c2000-03-16 08:12:48 +0000356 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000357
Erik Andersen161220c2000-03-16 08:12:48 +0000358 freeJob(job);
359 if (job == jobList->head) {
360 jobList->head = job->next;
361 } else {
362 prevJob = jobList->head;
363 while (prevJob->next != job)
364 prevJob = prevJob->next;
365 prevJob->next = job->next;
366 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000367
Erik Andersen161220c2000-03-16 08:12:48 +0000368 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000369}
370
371/* Checks to see if any background processes have exited -- if they
372 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000373static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000374{
Erik Andersen161220c2000-03-16 08:12:48 +0000375 struct job *job;
376 pid_t childpid;
377 int status;
378 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000379
Erik Andersen161220c2000-03-16 08:12:48 +0000380 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
381 for (job = jobList->head; job; job = job->next) {
382 progNum = 0;
383 while (progNum < job->numProgs &&
384 job->progs[progNum].pid != childpid) progNum++;
385 if (progNum < job->numProgs)
386 break;
387 }
388
389 if (WIFEXITED(status) || WIFSIGNALED(status)) {
390 /* child exited */
391 job->runningProgs--;
392 job->progs[progNum].pid = 0;
393
394 if (!job->runningProgs) {
395 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
396 removeJob(jobList, job);
397 }
398 } else {
399 /* child stopped */
400 job->stoppedProgs++;
401 job->progs[progNum].isStopped = 1;
402
403 if (job->stoppedProgs == job->numProgs) {
404 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
405 job->text);
406 }
407 }
Erik Andersend75af992000-03-16 08:09:09 +0000408 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000409
Erik Andersen161220c2000-03-16 08:12:48 +0000410 if (childpid == -1 && errno != ECHILD)
411 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000412}
413
Erik Andersend75af992000-03-16 08:09:09 +0000414static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000415{
Eric Andersen1c314ad2000-06-28 16:56:25 +0000416 if (source == NULL) {
417 if (local_pending_command) {
418 /* a command specified (-c option): return it & mark it done */
419 strcpy(command, local_pending_command);
420 free(local_pending_command);
421 local_pending_command = NULL;
422 return 0;
423 }
424 return 1;
425 }
426
Erik Andersen161220c2000-03-16 08:12:48 +0000427 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000428#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000429 int len;
430 char *promptStr;
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000431 len=fprintf(stdout, "%s %s", cwd, prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000432 fflush(stdout);
433 promptStr=(char*)malloc(sizeof(char)*(len+1));
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000434 sprintf(promptStr, "%s %s", cwd, prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000435 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000436 free( promptStr);
Erik Andersen161220c2000-03-16 08:12:48 +0000437 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000438#else
439 fprintf(stdout, "%s %s", cwd, prompt);
440 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000441#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000442 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000443
Erik Andersen161220c2000-03-16 08:12:48 +0000444 if (!fgets(command, BUFSIZ - 2, source)) {
445 if (source == stdin)
446 printf("\n");
447 return 1;
448 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000449
Erik Andersen161220c2000-03-16 08:12:48 +0000450 /* remove trailing newline */
451 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000452
Erik Andersen161220c2000-03-16 08:12:48 +0000453 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000454}
455
Erik Andersend75af992000-03-16 08:09:09 +0000456static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000457 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000458{
Erik Andersen161220c2000-03-16 08:12:48 +0000459 int argc = *argcPtr;
460 int argcAlloced = *argcAllocedPtr;
461 int rc;
462 int flags;
463 int i;
464 char *src, *dst;
Erik Andersen3522eb12000-03-12 23:49:18 +0000465
Erik Andersen161220c2000-03-16 08:12:48 +0000466 if (argc > 1) { /* cmd->globResult is already initialized */
467 flags = GLOB_APPEND;
468 i = prog->globResult.gl_pathc;
469 } else {
470 prog->freeGlob = 1;
471 flags = 0;
472 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000473 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000474
Erik Andersen161220c2000-03-16 08:12:48 +0000475 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
476 if (rc == GLOB_NOSPACE) {
477 fprintf(stderr, "out of space during glob operation\n");
478 return;
479 } else if (rc == GLOB_NOMATCH ||
480 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
481 !strcmp(prog->argv[argc - 1],
482 prog->globResult.gl_pathv[i]))) {
483 /* we need to remove whatever \ quoting is still present */
484 src = dst = prog->argv[argc - 1];
485 while (*src) {
486 if (*src != '\\')
487 *dst++ = *src;
488 src++;
489 }
490 *dst = '\0';
491 } else if (!rc) {
492 argcAlloced += (prog->globResult.gl_pathc - i);
493 prog->argv =
494 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
495 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
496 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
497 argc += (prog->globResult.gl_pathc - i - 1);
498 }
499
500 *argcAllocedPtr = argcAlloced;
501 *argcPtr = argc;
Erik Andersen3522eb12000-03-12 23:49:18 +0000502}
503
504/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
505 line). If a valid command is found, commandPtr is set to point to
506 the beginning of the next command (if the original command had more
507 then one job associated with it) or NULL if no more commands are
508 present. */
Erik Andersend75af992000-03-16 08:09:09 +0000509static int parseCommand(char **commandPtr, struct job *job, int *isBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000510{
Erik Andersen161220c2000-03-16 08:12:48 +0000511 char *command;
512 char *returnCommand = NULL;
513 char *src, *buf, *chptr;
514 int argc = 0;
515 int done = 0;
516 int argvAlloced;
517 int i;
518 char quote = '\0';
519 int count;
520 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000521
Erik Andersen161220c2000-03-16 08:12:48 +0000522 /* skip leading white space */
523 while (**commandPtr && isspace(**commandPtr))
524 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000525
Erik Andersen161220c2000-03-16 08:12:48 +0000526 /* this handles empty lines or leading '#' characters */
527 if (!**commandPtr || (**commandPtr == '#')) {
528 job->numProgs = 0;
529 *commandPtr = NULL;
530 return 0;
531 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000532
Erik Andersen161220c2000-03-16 08:12:48 +0000533 *isBg = 0;
534 job->numProgs = 1;
535 job->progs = malloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000536
Erik Andersen161220c2000-03-16 08:12:48 +0000537 /* We set the argv elements to point inside of this string. The
538 memory is freed by freeJob().
Erik Andersen3522eb12000-03-12 23:49:18 +0000539
Erik Andersen161220c2000-03-16 08:12:48 +0000540 Getting clean memory relieves us of the task of NULL
541 terminating things and makes the rest of this look a bit
542 cleaner (though it is, admittedly, a tad less efficient) */
543 job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1);
544 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000545
Erik Andersen161220c2000-03-16 08:12:48 +0000546 prog = job->progs;
547 prog->numRedirections = 0;
548 prog->redirections = NULL;
549 prog->freeGlob = 0;
550 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000551
Erik Andersen161220c2000-03-16 08:12:48 +0000552 argvAlloced = 5;
553 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
554 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000555
Erik Andersen161220c2000-03-16 08:12:48 +0000556 buf = command;
557 src = *commandPtr;
558 while (*src && !done) {
559 if (quote == *src) {
560 quote = '\0';
561 } else if (quote) {
562 if (*src == '\\') {
563 src++;
564 if (!*src) {
565 fprintf(stderr, "character expected after \\\n");
566 freeJob(job);
567 return 1;
568 }
569
570 /* in shell, "\'" should yield \' */
571 if (*src != quote)
572 *buf++ = '\\';
573 } else if (*src == '*' || *src == '?' || *src == '[' ||
574 *src == ']') *buf++ = '\\';
575 *buf++ = *src;
576 } else if (isspace(*src)) {
577 if (*prog->argv[argc]) {
578 buf++, argc++;
579 /* +1 here leaves room for the NULL which ends argv */
580 if ((argc + 1) == argvAlloced) {
581 argvAlloced += 5;
582 prog->argv = realloc(prog->argv,
583 sizeof(*prog->argv) *
584 argvAlloced);
585 }
586 prog->argv[argc] = buf;
587
588 globLastArgument(prog, &argc, &argvAlloced);
589 }
590 } else
591 switch (*src) {
592 case '"':
593 case '\'':
594 quote = *src;
595 break;
596
597 case '#': /* comment */
598 done = 1;
599 break;
600
601 case '>': /* redirections */
602 case '<':
603 i = prog->numRedirections++;
604 prog->redirections = realloc(prog->redirections,
605 sizeof(*prog->redirections) *
606 (i + 1));
607
608 prog->redirections[i].fd = -1;
609 if (buf != prog->argv[argc]) {
610 /* the stuff before this character may be the file number
611 being redirected */
612 prog->redirections[i].fd =
613 strtol(prog->argv[argc], &chptr, 10);
614
615 if (*chptr && *prog->argv[argc]) {
616 buf++, argc++;
617 globLastArgument(prog, &argc, &argvAlloced);
618 }
619 }
620
621 if (prog->redirections[i].fd == -1) {
622 if (*src == '>')
623 prog->redirections[i].fd = 1;
624 else
625 prog->redirections[i].fd = 0;
626 }
627
628 if (*src++ == '>') {
629 if (*src == '>')
630 prog->redirections[i].type =
631 REDIRECT_APPEND, src++;
632 else
633 prog->redirections[i].type = REDIRECT_OVERWRITE;
634 } else {
635 prog->redirections[i].type = REDIRECT_INPUT;
636 }
637
638 /* This isn't POSIX sh compliant. Oh well. */
639 chptr = src;
640 while (isspace(*chptr))
641 chptr++;
642
643 if (!*chptr) {
644 fprintf(stderr, "file name expected after %c\n", *src);
645 freeJob(job);
646 return 1;
647 }
648
649 prog->redirections[i].filename = buf;
650 while (*chptr && !isspace(*chptr))
651 *buf++ = *chptr++;
652
653 src = chptr - 1; /* we src++ later */
654 prog->argv[argc] = ++buf;
655 break;
656
657 case '|': /* pipe */
658 /* finish this command */
659 if (*prog->argv[argc])
660 argc++;
661 if (!argc) {
662 fprintf(stderr, "empty command in pipe\n");
663 freeJob(job);
664 return 1;
665 }
666 prog->argv[argc] = NULL;
667
668 /* and start the next */
669 job->numProgs++;
670 job->progs = realloc(job->progs,
671 sizeof(*job->progs) * job->numProgs);
672 prog = job->progs + (job->numProgs - 1);
673 prog->numRedirections = 0;
674 prog->redirections = NULL;
675 prog->freeGlob = 0;
676 argc = 0;
677
678 argvAlloced = 5;
679 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
680 prog->argv[0] = ++buf;
681
682 src++;
683 while (*src && isspace(*src))
684 src++;
685
686 if (!*src) {
687 fprintf(stderr, "empty command in pipe\n");
688 return 1;
689 }
690 src--; /* we'll ++ it at the end of the loop */
691
692 break;
693
694 case '&': /* background */
695 *isBg = 1;
696 case ';': /* multiple commands */
697 done = 1;
698 returnCommand = *commandPtr + (src - *commandPtr) + 1;
699 break;
700
701 case '\\':
702 src++;
703 if (!*src) {
704 freeJob(job);
705 fprintf(stderr, "character expected after \\\n");
706 return 1;
707 }
708 if (*src == '*' || *src == '[' || *src == ']'
709 || *src == '?') *buf++ = '\\';
710 /* fallthrough */
711 default:
712 *buf++ = *src;
713 }
714
Erik Andersend75af992000-03-16 08:09:09 +0000715 src++;
Erik Andersen161220c2000-03-16 08:12:48 +0000716 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000717
Erik Andersen161220c2000-03-16 08:12:48 +0000718 if (*prog->argv[argc]) {
719 argc++;
Erik Andersend75af992000-03-16 08:09:09 +0000720 globLastArgument(prog, &argc, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000721 }
722 if (!argc) {
723 freeJob(job);
724 return 0;
725 }
726 prog->argv[argc] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000727
Erik Andersen161220c2000-03-16 08:12:48 +0000728 if (!returnCommand) {
729 job->text = malloc(strlen(*commandPtr) + 1);
730 strcpy(job->text, *commandPtr);
731 } else {
732 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +0000733 count = returnCommand - *commandPtr;
734 job->text = malloc(count + 1);
735 strncpy(job->text, *commandPtr, count);
736 job->text[count] = '\0';
737 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000738
Erik Andersen161220c2000-03-16 08:12:48 +0000739 *commandPtr = returnCommand;
Erik Andersen3522eb12000-03-12 23:49:18 +0000740
Erik Andersend75af992000-03-16 08:09:09 +0000741 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000742}
743
Erik Andersenbcd61772000-05-13 06:33:19 +0000744
Erik Andersend75af992000-03-16 08:09:09 +0000745static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000746{
Erik Andersen161220c2000-03-16 08:12:48 +0000747 struct job *job;
748 int i;
749 int nextin, nextout;
750 int pipefds[2]; /* pipefd[0] is for reading */
751 struct builtInCommand *x;
Erik Andersenbcd61772000-05-13 06:33:19 +0000752#ifdef BB_FEATURE_STANDALONE_SHELL
753 const struct BB_applet *a = applets;
754#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000755
Erik Andersen3522eb12000-03-12 23:49:18 +0000756
Erik Andersen161220c2000-03-16 08:12:48 +0000757 nextin = 0, nextout = 1;
758 for (i = 0; i < newJob.numProgs; i++) {
759 if ((i + 1) < newJob.numProgs) {
760 pipe(pipefds);
761 nextout = pipefds[1];
762 } else {
763 nextout = 1;
764 }
765
Erik Andersen330fd2b2000-05-19 05:35:19 +0000766 /* Match any built-ins here */
767 for (x = bltins; x->cmd; x++) {
768 if (!strcmp(newJob.progs[i].argv[0], x->cmd)) {
769 return (x->function(&newJob, jobList));
770 }
771 }
772
Erik Andersen161220c2000-03-16 08:12:48 +0000773 if (!(newJob.progs[i].pid = fork())) {
774 signal(SIGTTOU, SIG_DFL);
775
776 if (nextin != 0) {
777 dup2(nextin, 0);
778 close(nextin);
779 }
780
781 if (nextout != 1) {
782 dup2(nextout, 1);
783 close(nextout);
784 }
785
786 /* explicit redirections override pipes */
787 setupRedirections(newJob.progs + i);
788
Erik Andersenbcd61772000-05-13 06:33:19 +0000789 /* Match any built-ins here */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000790 for (x = bltins_forking; x->cmd; x++) {
Erik Andersenbcd61772000-05-13 06:33:19 +0000791 if (!strcmp(newJob.progs[i].argv[0], x->cmd)) {
792 exit (x->function(&newJob, jobList));
793 }
794 }
795#ifdef BB_FEATURE_STANDALONE_SHELL
796 /* Handle busybox internals here */
797 while (a->name != 0) {
798 if (strcmp(newJob.progs[i].argv[0], a->name) == 0) {
799 int argc;
800 char** argv=newJob.progs[i].argv;
Erik Andersenc3f5c9c2000-05-13 19:00:07 +0000801 for(argc=0;*argv!=NULL; argv++, argc++);
Erik Andersenbcd61772000-05-13 06:33:19 +0000802 exit((*(a->main)) (argc, newJob.progs[i].argv));
803 }
804 a++;
805 }
806#endif
807
Erik Andersen161220c2000-03-16 08:12:48 +0000808 execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
809 fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
810 strerror(errno));
811 }
812
813 /* put our child in the process group whose leader is the
814 first process in this pipe */
815 setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
816
817 if (nextin != 0)
818 close(nextin);
819 if (nextout != 1)
820 close(nextout);
821
822 /* If there isn't another process, nextin is garbage
823 but it doesn't matter */
824 nextin = pipefds[0];
825 }
826
827 newJob.pgrp = newJob.progs[0].pid;
828
829 /* find the ID for the job to use */
830 newJob.jobId = 1;
831 for (job = jobList->head; job; job = job->next)
832 if (job->jobId >= newJob.jobId)
833 newJob.jobId = job->jobId + 1;
834
835 /* add the job to the list of running jobs */
836 if (!jobList->head) {
837 job = jobList->head = malloc(sizeof(*job));
Erik Andersend75af992000-03-16 08:09:09 +0000838 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000839 for (job = jobList->head; job->next; job = job->next);
840 job->next = malloc(sizeof(*job));
841 job = job->next;
Erik Andersend75af992000-03-16 08:09:09 +0000842 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000843
Erik Andersen161220c2000-03-16 08:12:48 +0000844 *job = newJob;
845 job->next = NULL;
846 job->runningProgs = job->numProgs;
847 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000848
Erik Andersen161220c2000-03-16 08:12:48 +0000849 if (inBg) {
850 /* we don't wait for background jobs to return -- append it
851 to the list of backgrounded jobs and leave it alone */
Erik Andersen161220c2000-03-16 08:12:48 +0000852 printf("[%d] %d\n", job->jobId,
853 newJob.progs[newJob.numProgs - 1].pid);
854 } else {
855 jobList->fg = job;
Erik Andersen3522eb12000-03-12 23:49:18 +0000856
Erik Andersen161220c2000-03-16 08:12:48 +0000857 /* move the new process group into the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000858 /* suppress messages when run from /linuxrc mag@sysgo.de */
859 if (tcsetpgrp(0, newJob.pgrp) && errno != ENOTTY)
Erik Andersen161220c2000-03-16 08:12:48 +0000860 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000861 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000862
Erik Andersen161220c2000-03-16 08:12:48 +0000863 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000864}
865
Erik Andersend75af992000-03-16 08:09:09 +0000866static int setupRedirections(struct childProgram *prog)
Erik Andersen3522eb12000-03-12 23:49:18 +0000867{
Erik Andersen161220c2000-03-16 08:12:48 +0000868 int i;
869 int openfd;
870 int mode = O_RDONLY;
871 struct redirectionSpecifier *redir = prog->redirections;
Erik Andersen3522eb12000-03-12 23:49:18 +0000872
Erik Andersen161220c2000-03-16 08:12:48 +0000873 for (i = 0; i < prog->numRedirections; i++, redir++) {
874 switch (redir->type) {
875 case REDIRECT_INPUT:
876 mode = O_RDONLY;
877 break;
878 case REDIRECT_OVERWRITE:
879 mode = O_RDWR | O_CREAT | O_TRUNC;
880 break;
881 case REDIRECT_APPEND:
882 mode = O_RDWR | O_CREAT | O_APPEND;
883 break;
884 }
885
886 openfd = open(redir->filename, mode, 0666);
887 if (openfd < 0) {
888 /* this could get lost if stderr has been redirected, but
889 bash and ash both lose it as well (though zsh doesn't!) */
890 fprintf(stderr, "error opening %s: %s\n", redir->filename,
891 strerror(errno));
892 return 1;
893 }
894
895 if (openfd != redir->fd) {
896 dup2(openfd, redir->fd);
897 close(openfd);
898 }
Erik Andersend75af992000-03-16 08:09:09 +0000899 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000900
Erik Andersen161220c2000-03-16 08:12:48 +0000901 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000902}
903
904
Erik Andersend75af992000-03-16 08:09:09 +0000905static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +0000906{
Erik Andersen161220c2000-03-16 08:12:48 +0000907 char *command;
908 char *nextCommand = NULL;
909 struct jobSet jobList = { NULL, NULL };
910 struct job newJob;
Eric Andersen1c314ad2000-06-28 16:56:25 +0000911 pid_t parent_pgrp;
Erik Andersen161220c2000-03-16 08:12:48 +0000912 int i;
913 int status;
914 int inBg;
Erik Andersen3522eb12000-03-12 23:49:18 +0000915
Eric Andersen1c314ad2000-06-28 16:56:25 +0000916 /* save current owner of TTY so we can restore it on exit */
917 parent_pgrp = tcgetpgrp(0);
918
Erik Andersen161220c2000-03-16 08:12:48 +0000919 command = (char *) calloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +0000920
Erik Andersen161220c2000-03-16 08:12:48 +0000921 /* don't pay any attention to this signal; it just confuses
922 things and isn't really meant for shells anyway */
923 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +0000924
Erik Andersen161220c2000-03-16 08:12:48 +0000925 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +0000926 if (!jobList.fg) {
927 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +0000928
Erik Andersend75af992000-03-16 08:09:09 +0000929 /* see if any background processes have exited */
930 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +0000931
Erik Andersend75af992000-03-16 08:09:09 +0000932 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +0000933 if (getCommand(input, command))
934 break;
935 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +0000936 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000937
Erik Andersend75af992000-03-16 08:09:09 +0000938 if (!parseCommand(&nextCommand, &newJob, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +0000939 newJob.numProgs) {
940 runCommand(newJob, &jobList, inBg);
Erik Andersend75af992000-03-16 08:09:09 +0000941 }
942 } else {
943 /* a job is running in the foreground; wait for it */
944 i = 0;
945 while (!jobList.fg->progs[i].pid ||
Erik Andersen161220c2000-03-16 08:12:48 +0000946 jobList.fg->progs[i].isStopped) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000947
Erik Andersend75af992000-03-16 08:09:09 +0000948 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +0000949
Erik Andersend75af992000-03-16 08:09:09 +0000950 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +0000951 /* the child exited */
952 jobList.fg->runningProgs--;
953 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000954
Erik Andersen161220c2000-03-16 08:12:48 +0000955 if (!jobList.fg->runningProgs) {
956 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +0000957
Erik Andersen161220c2000-03-16 08:12:48 +0000958 removeJob(&jobList, jobList.fg);
959 jobList.fg = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +0000960 }
Erik Andersend75af992000-03-16 08:09:09 +0000961 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000962 /* the child was stopped */
963 jobList.fg->stoppedProgs++;
964 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +0000965
Erik Andersen161220c2000-03-16 08:12:48 +0000966 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
967 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
968 "Stopped", jobList.fg->text);
969 jobList.fg = NULL;
970 }
Erik Andersend75af992000-03-16 08:09:09 +0000971 }
972
973 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +0000974 /* move the shell to the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000975 /* suppress messages when run from /linuxrc mag@sysgo.de */
976 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
977 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000978 }
979 }
980 }
Erik Andersen161220c2000-03-16 08:12:48 +0000981 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +0000982
Eric Andersen1c314ad2000-06-28 16:56:25 +0000983 /* return controlling TTY back to parent process group before exiting */
984 if (tcsetpgrp(0, parent_pgrp))
985 perror("tcsetpgrp");
986
Erik Andersen161220c2000-03-16 08:12:48 +0000987 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000988}
989
990
Erik Andersend75af992000-03-16 08:09:09 +0000991int shell_main(int argc, char **argv)
Erik Andersen3522eb12000-03-12 23:49:18 +0000992{
Erik Andersen161220c2000-03-16 08:12:48 +0000993 FILE *input = stdin;
Erik Andersen3522eb12000-03-12 23:49:18 +0000994
Erik Andersen161220c2000-03-16 08:12:48 +0000995 /* initialize the cwd */
996 getcwd(cwd, sizeof(cwd));
Erik Andersen3522eb12000-03-12 23:49:18 +0000997
Erik Andersenf0657d32000-04-12 17:49:52 +0000998#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersen1d1d9502000-04-21 01:26:49 +0000999 cmdedit_init();
Erik Andersenf0657d32000-04-12 17:49:52 +00001000 signal(SIGWINCH, win_changed);
1001 win_changed(0);
1002#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001003
Erik Andersen161220c2000-03-16 08:12:48 +00001004 //if (argv[0] && argv[0][0] == '-') {
1005 // shell_source("/etc/profile");
1006 //}
Erik Andersen3522eb12000-03-12 23:49:18 +00001007
Erik Andersen161220c2000-03-16 08:12:48 +00001008 if (argc < 2) {
Erik Andersenf0657d32000-04-12 17:49:52 +00001009 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1010 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Erik Andersen161220c2000-03-16 08:12:48 +00001011 } else {
Eric Andersen1c314ad2000-06-28 16:56:25 +00001012 if (argv[1][0]=='-' && argv[1][1]=='c') {
1013 int i;
1014 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1015 if (local_pending_command == 0) {
1016 fatalError("sh: out of memory\n");
1017 }
1018 for(i=2; i<argc; i++)
1019 {
1020 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
1021 fatalError("sh: commands for -c option too long\n");
1022 }
1023 strcat(local_pending_command, argv[i]);
1024 if (i + 1 < argc)
1025 strcat(local_pending_command, " ");
1026 }
1027 input = NULL;
1028
Erik Andersene5b6c7d2000-04-17 16:16:10 +00001029 }
Eric Andersen1c314ad2000-06-28 16:56:25 +00001030 else if (argv[1][0]=='-') {
1031 usage(shell_usage);
1032 }
1033 else {
1034 input = fopen(argv[1], "r");
1035 if (!input) {
1036 fatalError("sh: Couldn't open file '%s': %s\n", argv[1],
1037 strerror(errno));
1038 }
Erik Andersenf0657d32000-04-12 17:49:52 +00001039 }
Erik Andersen161220c2000-03-16 08:12:48 +00001040 }
Erik Andersend75af992000-03-16 08:09:09 +00001041
Erik Andersen161220c2000-03-16 08:12:48 +00001042 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +00001043}