summaryrefslogtreecommitdiff
path: root/sh.c
diff options
context:
space:
mode:
authorErik Andersen <andersen@codepoet.org>2000-03-16 08:12:48 +0000
committerErik Andersen <andersen@codepoet.org>2000-03-16 08:12:48 +0000
commit161220c4985b8c05a57f09b2693a6cad74d2e81d (patch)
tree55395efd08b325dd3fe7a4e27d988967bffbbe70 /sh.c
parent13456d1fcd0122d8464c3c3e1c356d86a56e6c08 (diff)
downloadbusybox-w32-161220c4985b8c05a57f09b2693a6cad74d2e81d.tar.gz
busybox-w32-161220c4985b8c05a57f09b2693a6cad74d2e81d.tar.bz2
busybox-w32-161220c4985b8c05a57f09b2693a6cad74d2e81d.zip
Fix fg bug
-Erik
Diffstat (limited to 'sh.c')
-rw-r--r--sh.c1346
1 files changed, 676 insertions, 670 deletions
diff --git a/sh.c b/sh.c
index 498f43779..e143cfe74 100644
--- a/sh.c
+++ b/sh.c
@@ -47,46 +47,47 @@
47 47
48 48
49enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE, 49enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
50 REDIRECT_APPEND }; 50 REDIRECT_APPEND
51};
51 52
52struct jobSet { 53struct jobSet {
53 struct job *head; /* head of list of running jobs */ 54 struct job *head; /* head of list of running jobs */
54 struct job *fg; /* current foreground job */ 55 struct job *fg; /* current foreground job */
55}; 56};
56 57
57struct redirectionSpecifier { 58struct redirectionSpecifier {
58 enum redirectionType type; /* type of redirection */ 59 enum redirectionType type; /* type of redirection */
59 int fd; /* file descriptor being redirected */ 60 int fd; /* file descriptor being redirected */
60 char *filename; /* file to redirect fd to */ 61 char *filename; /* file to redirect fd to */
61}; 62};
62 63
63struct childProgram { 64struct childProgram {
64 pid_t pid; /* 0 if exited */ 65 pid_t pid; /* 0 if exited */
65 char **argv; /* program name and arguments */ 66 char **argv; /* program name and arguments */
66 int numRedirections; /* elements in redirection array */ 67 int numRedirections; /* elements in redirection array */
67 struct redirectionSpecifier *redirections; /* I/O redirections */ 68 struct redirectionSpecifier *redirections; /* I/O redirections */
68 glob_t globResult; /* result of parameter globbing */ 69 glob_t globResult; /* result of parameter globbing */
69 int freeGlob; /* should we globfree(&globResult)? */ 70 int freeGlob; /* should we globfree(&globResult)? */
70 int isStopped; /* is the program currently running? */ 71 int isStopped; /* is the program currently running? */
71}; 72};
72 73
73struct job { 74struct job {
74 int jobId; /* job number */ 75 int jobId; /* job number */
75 int numProgs; /* total number of programs in job */ 76 int numProgs; /* total number of programs in job */
76 int runningProgs; /* number of programs running */ 77 int runningProgs; /* number of programs running */
77 char *text; /* name of job */ 78 char *text; /* name of job */
78 char *cmdBuf; /* buffer various argv's point into */ 79 char *cmdBuf; /* buffer various argv's point into */
79 pid_t pgrp; /* process group ID for the job */ 80 pid_t pgrp; /* process group ID for the job */
80 struct childProgram *progs; /* array of programs in job */ 81 struct childProgram *progs; /* array of programs in job */
81 struct job *next; /* to track background commands */ 82 struct job *next; /* to track background commands */
82 int stoppedProgs; /* number of programs alive, but stopped */ 83 int stoppedProgs; /* number of programs alive, but stopped */
83}; 84};
84 85
85struct builtInCommand { 86struct builtInCommand {
86 char *cmd; /* name */ 87 char *cmd; /* name */
87 char *descr; /* description */ 88 char *descr; /* description */
88 char *usage; /* usage */ 89 char *usage; /* usage */
89 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */ 90 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
90}; 91};
91 92
92/* Some function prototypes */ 93/* Some function prototypes */
@@ -111,24 +112,26 @@ static int busy_loop(FILE * input);
111 112
112/* Table of built-in functions */ 113/* Table of built-in functions */
113static struct builtInCommand bltins[] = { 114static struct builtInCommand bltins[] = {
114 {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg}, 115 {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg},
115 {"cd", "Change working directory", "cd [dir]", shell_cd}, 116 {"cd", "Change working directory", "cd [dir]", shell_cd},
116 //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo}, 117 //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo},
117 {"env", "Print all environment variables", "env", shell_env}, 118 {"env", "Print all environment variables", "env", shell_env},
118 {"exit", "Exit from shell()", "exit", shell_exit}, 119 {"exit", "Exit from shell()", "exit", shell_exit},
119 {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg}, 120 {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
120 {"jobs", "Lists the active jobs", "jobs", shell_jobs}, 121 {"jobs", "Lists the active jobs", "jobs", shell_jobs},
121 {"pwd", "Print current directory", "pwd", shell_pwd}, 122 {"pwd", "Print current directory", "pwd", shell_pwd},
122 {"set", "Set environment variable", "set [VAR=value]", shell_set}, 123 {"set", "Set environment variable", "set [VAR=value]", shell_set},
123 {"unset", "Unset environment variable", "unset VAR", shell_unset}, 124 {"unset", "Unset environment variable", "unset VAR", shell_unset},
124 {".", "Source-in and run commands in a file", ". filename", 125
125 shell_source}, 126 {".", "Source-in and run commands in a file", ". filename",
126 {"help", "List shell built-in commands", "help", shell_help}, 127 shell_source},
127 {NULL, NULL, NULL, NULL} 128 {"help", "List shell built-in commands", "help", shell_help},
129 {NULL, NULL, NULL, NULL}
128}; 130};
129 131
130static const char shell_usage[] = 132static const char shell_usage[] =
131 "sh [FILE]...\n\n" "The BusyBox command interpreter (shell).\n\n"; 133
134 "sh [FILE]...\n\n" "The BusyBox command interpreter (shell).\n\n";
132 135
133 136
134static char cwd[1024]; 137static char cwd[1024];
@@ -139,323 +142,326 @@ static char *prompt = "# ";
139/* built-in 'cd <path>' handler */ 142/* built-in 'cd <path>' handler */
140static int shell_cd(struct job *cmd, struct jobSet *junk) 143static int shell_cd(struct job *cmd, struct jobSet *junk)
141{ 144{
142 char *newdir; 145 char *newdir;
143 if (!cmd->progs[0].argv[1] == 1) 146
144 newdir = getenv("HOME"); 147 if (!cmd->progs[0].argv[1] == 1)
145 else 148 newdir = getenv("HOME");
146 newdir = cmd->progs[0].argv[1]; 149 else
147 if (chdir(newdir)) { 150 newdir = cmd->progs[0].argv[1];
148 printf("cd: %s: %s\n", newdir, strerror(errno)); 151 if (chdir(newdir)) {
149 return FALSE; 152 printf("cd: %s: %s\n", newdir, strerror(errno));
150 } 153 return FALSE;
151 getcwd(cwd, sizeof(cwd)); 154 }
152 155 getcwd(cwd, sizeof(cwd));
153 return TRUE; 156
157 return TRUE;
154} 158}
155 159
156/* built-in 'env' handler */ 160/* built-in 'env' handler */
157static int shell_env(struct job *dummy, struct jobSet *junk) 161static int shell_env(struct job *dummy, struct jobSet *junk)
158{ 162{
159 char **e; 163 char **e;
160 164
161 for (e = environ; *e; e++) { 165 for (e = environ; *e; e++) {
162 fprintf(stdout, "%s\n", *e); 166 fprintf(stdout, "%s\n", *e);
163 } 167 }
164 return (0); 168 return (0);
165} 169}
166 170
167/* built-in 'exit' handler */ 171/* built-in 'exit' handler */
168static int shell_exit(struct job *cmd, struct jobSet *junk) 172static int shell_exit(struct job *cmd, struct jobSet *junk)
169{ 173{
170 if (!cmd->progs[0].argv[1] == 1) 174 if (!cmd->progs[0].argv[1] == 1)
171 exit TRUE; 175 exit TRUE;
172 else 176
173 exit(atoi(cmd->progs[0].argv[1])); 177 else
178 exit(atoi(cmd->progs[0].argv[1]));
174} 179}
175 180
176/* built-in 'fg' and 'bg' handler */ 181/* built-in 'fg' and 'bg' handler */
177static int shell_fg_bg(struct job *cmd, struct jobSet *jobList) 182static int shell_fg_bg(struct job *cmd, struct jobSet *jobList)
178{ 183{
179 int i, jobNum; 184 int i, jobNum;
180 struct job *job; 185 struct job *job;
181 186
182 if (!jobList->head) { 187 if (!jobList->head) {
183 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) { 188 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
184 fprintf(stderr, "%s: exactly one argument is expected\n", 189 fprintf(stderr, "%s: exactly one argument is expected\n",
185 cmd->progs[0].argv[0]); 190 cmd->progs[0].argv[0]);
186 return FALSE; 191 return FALSE;
187 } 192 }
188 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) { 193 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
189 fprintf(stderr, "%s: bad argument '%s'\n", 194 fprintf(stderr, "%s: bad argument '%s'\n",
190 cmd->progs[0].argv[0], cmd->progs[0].argv[1]); 195 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
191 return FALSE; 196 return FALSE;
197 for (job = jobList->head; job; job = job->next) {
198 if (job->jobId == jobNum) {
199 break;
200 }
201 }
202 }
203 } else {
204 job = jobList->head;
192 } 205 }
193 } else {
194 job = jobList->head;
195 }
196
197 for (job = jobList->head; job; job = job->next)
198 if (job->jobId == jobNum)
199 break;
200
201 if (!job) {
202 fprintf(stderr, "%s: unknown job %d\n",
203 cmd->progs[0].argv[0], jobNum);
204 return FALSE;
205 }
206 206
207 if (*cmd->progs[0].argv[0] == 'f') { 207 if (!job) {
208 /* Make this job the foreground job */ 208 fprintf(stderr, "%s: unknown job %d\n",
209 cmd->progs[0].argv[0], jobNum);
210 return FALSE;
211 }
209 212
210 if (tcsetpgrp(0, job->pgrp)) 213 if (*cmd->progs[0].argv[0] == 'f') {
211 perror("tcsetpgrp"); 214 /* Make this job the foreground job */
212 jobList->fg = job; 215 if (tcsetpgrp(0, job->pgrp))
213 } 216 perror("tcsetpgrp");
217 jobList->fg = job;
218 }
214 219
215 /* Restart the processes in the job */ 220 /* Restart the processes in the job */
216 for (i = 0; i < job->numProgs; i++) 221 for (i = 0; i < job->numProgs; i++)
217 job->progs[i].isStopped = 0; 222 job->progs[i].isStopped = 0;
218 223
219 kill(-job->pgrp, SIGCONT); 224 kill(-job->pgrp, SIGCONT);
220 225
221 job->stoppedProgs = 0; 226 job->stoppedProgs = 0;
222 227
223 return TRUE; 228 return TRUE;
224} 229}
225 230
226/* built-in 'help' handler */ 231/* built-in 'help' handler */
227static int shell_help(struct job *cmd, struct jobSet *junk) 232static int shell_help(struct job *cmd, struct jobSet *junk)
228{ 233{
229 struct builtInCommand *x; 234 struct builtInCommand *x;
230 235
231 fprintf(stdout, "\nBuilt-in commands:\n"); 236 fprintf(stdout, "\nBuilt-in commands:\n");
232 fprintf(stdout, "-------------------\n"); 237 fprintf(stdout, "-------------------\n");
233 for (x = bltins; x->cmd; x++) { 238 for (x = bltins; x->cmd; x++) {
234 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr); 239 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
235 } 240 }
236 fprintf(stdout, "\n\n"); 241 fprintf(stdout, "\n\n");
237 return TRUE; 242 return TRUE;
238} 243}
239 244
240/* built-in 'jobs' handler */ 245/* built-in 'jobs' handler */
241static int shell_jobs(struct job *dummy, struct jobSet *jobList) 246static int shell_jobs(struct job *dummy, struct jobSet *jobList)
242{ 247{
243 struct job *job; 248 struct job *job;
244 char *statusString; 249 char *statusString;
245 for (job = jobList->head; job; job = job->next) { 250
246 if (job->runningProgs == job->stoppedProgs) 251 for (job = jobList->head; job; job = job->next) {
247 statusString = "Stopped"; 252 if (job->runningProgs == job->stoppedProgs)
248 else 253 statusString = "Stopped";
249 statusString = "Running"; 254 else
255 statusString = "Running";
250 256
251 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text); 257 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
252 } 258 }
253 return TRUE; 259 return TRUE;
254} 260}
255 261
256 262
257/* built-in 'pwd' handler */ 263/* built-in 'pwd' handler */
258static int shell_pwd(struct job *dummy, struct jobSet *junk) 264static int shell_pwd(struct job *dummy, struct jobSet *junk)
259{ 265{
260 getcwd(cwd, sizeof(cwd)); 266 getcwd(cwd, sizeof(cwd));
261 fprintf(stdout, "%s\n", cwd); 267 fprintf(stdout, "%s\n", cwd);
262 return TRUE; 268 return TRUE;
263} 269}
264 270
265/* built-in 'set VAR=value' handler */ 271/* built-in 'set VAR=value' handler */
266static int shell_set(struct job *cmd, struct jobSet *junk) 272static int shell_set(struct job *cmd, struct jobSet *junk)
267{ 273{
268 int res; 274 int res;
269 275
270 if (!cmd->progs[0].argv[1] == 1) { 276 if (!cmd->progs[0].argv[1] == 1) {
271 return (shell_env(cmd, junk)); 277 return (shell_env(cmd, junk));
272 } 278 }
273 res = putenv(cmd->progs[0].argv[1]); 279 res = putenv(cmd->progs[0].argv[1]);
274 if (res) 280 if (res)
275 fprintf(stdout, "set: %s\n", strerror(errno)); 281 fprintf(stdout, "set: %s\n", strerror(errno));
276 return (res); 282 return (res);
277} 283}
278 284
279/* Built-in '.' handler (read-in and execute commands from file) */ 285/* Built-in '.' handler (read-in and execute commands from file) */
280static int shell_source(struct job *cmd, struct jobSet *junk) 286static int shell_source(struct job *cmd, struct jobSet *junk)
281{ 287{
282 FILE *input; 288 FILE *input;
283 int status; 289 int status;
284 290
285 if (!cmd->progs[0].argv[1] == 1) 291 if (!cmd->progs[0].argv[1] == 1)
286 return FALSE; 292 return FALSE;
287 293
288 input = fopen(cmd->progs[0].argv[1], "r"); 294 input = fopen(cmd->progs[0].argv[1], "r");
289 if (!input) { 295 if (!input) {
290 fprintf(stdout, "Couldn't open file '%s'\n", 296 fprintf(stdout, "Couldn't open file '%s'\n",
291 cmd->progs[0].argv[1]); 297 cmd->progs[0].argv[1]);
292 return FALSE; 298 return FALSE;
293 } 299 }
294 300
295 /* Now run the file */ 301 /* Now run the file */
296 status = busy_loop(input); 302 status = busy_loop(input);
297 return (status); 303 return (status);
298} 304}
299 305
300/* built-in 'unset VAR' handler */ 306/* built-in 'unset VAR' handler */
301static int shell_unset(struct job *cmd, struct jobSet *junk) 307static int shell_unset(struct job *cmd, struct jobSet *junk)
302{ 308{
303 if (!cmd->progs[0].argv[1] == 1) { 309 if (!cmd->progs[0].argv[1] == 1) {
304 fprintf(stdout, "unset: parameter required.\n"); 310 fprintf(stdout, "unset: parameter required.\n");
305 return FALSE; 311 return FALSE;
306 } 312 }
307 unsetenv(cmd->progs[0].argv[1]); 313 unsetenv(cmd->progs[0].argv[1]);
308 return TRUE; 314 return TRUE;
309} 315}
310 316
311/* free up all memory from a job */ 317/* free up all memory from a job */
312static void freeJob(struct job *cmd) 318static void freeJob(struct job *cmd)
313{ 319{
314 int i; 320 int i;
315 321
316 for (i = 0; i < cmd->numProgs; i++) { 322 for (i = 0; i < cmd->numProgs; i++) {
317 free(cmd->progs[i].argv); 323 free(cmd->progs[i].argv);
318 if (cmd->progs[i].redirections) 324 if (cmd->progs[i].redirections)
319 free(cmd->progs[i].redirections); 325 free(cmd->progs[i].redirections);
320 if (cmd->progs[i].freeGlob) 326 if (cmd->progs[i].freeGlob)
321 globfree(&cmd->progs[i].globResult); 327 globfree(&cmd->progs[i].globResult);
322 } 328 }
323 free(cmd->progs); 329 free(cmd->progs);
324 if (cmd->text) 330 if (cmd->text)
325 free(cmd->text); 331 free(cmd->text);
326 free(cmd->cmdBuf); 332 free(cmd->cmdBuf);
327} 333}
328 334
329/* remove a job from the jobList */ 335/* remove a job from the jobList */
330static void removeJob(struct jobSet *jobList, struct job *job) 336static void removeJob(struct jobSet *jobList, struct job *job)
331{ 337{
332 struct job *prevJob; 338 struct job *prevJob;
333 339
334 freeJob(job); 340 freeJob(job);
335 if (job == jobList->head) { 341 if (job == jobList->head) {
336 jobList->head = job->next; 342 jobList->head = job->next;
337 } else { 343 } else {
338 prevJob = jobList->head; 344 prevJob = jobList->head;
339 while (prevJob->next != job) 345 while (prevJob->next != job)
340 prevJob = prevJob->next; 346 prevJob = prevJob->next;
341 prevJob->next = job->next; 347 prevJob->next = job->next;
342 } 348 }
343 349
344 free(job); 350 free(job);
345} 351}
346 352
347/* Checks to see if any background processes have exited -- if they 353/* Checks to see if any background processes have exited -- if they
348 have, figure out why and see if a job has completed */ 354 have, figure out why and see if a job has completed */
349static void checkJobs(struct jobSet *jobList) 355static void checkJobs(struct jobSet *jobList)
350{ 356{
351 struct job *job; 357 struct job *job;
352 pid_t childpid; 358 pid_t childpid;
353 int status; 359 int status;
354 int progNum = 0; 360 int progNum = 0;
361
362 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
363 for (job = jobList->head; job; job = job->next) {
364 progNum = 0;
365 while (progNum < job->numProgs &&
366 job->progs[progNum].pid != childpid) progNum++;
367 if (progNum < job->numProgs)
368 break;
369 }
355 370
356 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { 371 if (WIFEXITED(status) || WIFSIGNALED(status)) {
357 for (job = jobList->head; job; job = job->next) { 372 /* child exited */
358 progNum = 0; 373 job->runningProgs--;
359 while (progNum < job->numProgs && 374 job->progs[progNum].pid = 0;
360 job->progs[progNum].pid != childpid) progNum++;
361 if (progNum < job->numProgs)
362 break;
363 }
364 375
365 if (WIFEXITED(status) || WIFSIGNALED(status)) { 376 if (!job->runningProgs) {
366 /* child exited */ 377 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
367 job->runningProgs--; 378 removeJob(jobList, job);
368 job->progs[progNum].pid = 0; 379 }
380 } else {
381 /* child stopped */
382 job->stoppedProgs++;
383 job->progs[progNum].isStopped = 1;
369 384
370 if (!job->runningProgs) { 385 if (job->stoppedProgs == job->numProgs) {
371 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text); 386 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
372 removeJob(jobList, job); 387 job->text);
373 } 388 }
374 } else { 389 }
375 /* child stopped */
376 job->stoppedProgs++;
377 job->progs[progNum].isStopped = 1;
378
379 if (job->stoppedProgs == job->numProgs) {
380 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
381 job->text);
382 }
383 } 390 }
384 }
385 391
386 if (childpid == -1 && errno != ECHILD) 392 if (childpid == -1 && errno != ECHILD)
387 perror("waitpid"); 393 perror("waitpid");
388} 394}
389 395
390static int getCommand(FILE * source, char *command) 396static int getCommand(FILE * source, char *command)
391{ 397{
392 if (source == stdin) { 398 if (source == stdin) {
393 fprintf(stdout, "BBSHELL %s %s", cwd, prompt); 399 fprintf(stdout, "BBSHELL %s %s", cwd, prompt);
394 fflush(stdout); 400 fflush(stdout);
395#ifdef BB_FEATURE_SH_COMMAND_EDITING 401#ifdef BB_FEATURE_SH_COMMAND_EDITING
396 cmdedit_read_input(fileno(stdin), fileno(stdout), command); 402 cmdedit_read_input(fileno(stdin), fileno(stdout), command);
397 return 0; 403 return 0;
398#endif 404#endif
399 } 405 }
400 406
401 if (!fgets(command, BUFSIZ - 2, source)) { 407 if (!fgets(command, BUFSIZ - 2, source)) {
402 if (source == stdin) 408 if (source == stdin)
403 printf("\n"); 409 printf("\n");
404 return 1; 410 return 1;
405 } 411 }
406 412
407 /* remove trailing newline */ 413 /* remove trailing newline */
408 command[strlen(command) - 1] = '\0'; 414 command[strlen(command) - 1] = '\0';
409 415
410 return 0; 416 return 0;
411} 417}
412 418
413static void globLastArgument(struct childProgram *prog, int *argcPtr, 419static void globLastArgument(struct childProgram *prog, int *argcPtr,
414 int *argcAllocedPtr) 420 int *argcAllocedPtr)
415{ 421{
416 int argc = *argcPtr; 422 int argc = *argcPtr;
417 int argcAlloced = *argcAllocedPtr; 423 int argcAlloced = *argcAllocedPtr;
418 int rc; 424 int rc;
419 int flags; 425 int flags;
420 int i; 426 int i;
421 char *src, *dst; 427 char *src, *dst;
422 428
423 if (argc > 1) { /* cmd->globResult is already initialized */ 429 if (argc > 1) { /* cmd->globResult is already initialized */
424 flags = GLOB_APPEND; 430 flags = GLOB_APPEND;
425 i = prog->globResult.gl_pathc; 431 i = prog->globResult.gl_pathc;
426 } else { 432 } else {
427 prog->freeGlob = 1; 433 prog->freeGlob = 1;
428 flags = 0; 434 flags = 0;
429 i = 0; 435 i = 0;
430 } 436 }
431 437
432 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult); 438 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
433 if (rc == GLOB_NOSPACE) { 439 if (rc == GLOB_NOSPACE) {
434 fprintf(stderr, "out of space during glob operation\n"); 440 fprintf(stderr, "out of space during glob operation\n");
435 return; 441 return;
436 } else if (rc == GLOB_NOMATCH || 442 } else if (rc == GLOB_NOMATCH ||
437 (!rc && (prog->globResult.gl_pathc - i) == 1 && 443 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
438 !strcmp(prog->argv[argc - 1], 444 !strcmp(prog->argv[argc - 1],
439 prog->globResult.gl_pathv[i]))) { 445 prog->globResult.gl_pathv[i]))) {
440 /* we need to remove whatever \ quoting is still present */ 446 /* we need to remove whatever \ quoting is still present */
441 src = dst = prog->argv[argc - 1]; 447 src = dst = prog->argv[argc - 1];
442 while (*src) { 448 while (*src) {
443 if (*src != '\\') 449 if (*src != '\\')
444 *dst++ = *src; 450 *dst++ = *src;
445 src++; 451 src++;
452 }
453 *dst = '\0';
454 } else if (!rc) {
455 argcAlloced += (prog->globResult.gl_pathc - i);
456 prog->argv =
457 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
458 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
459 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
460 argc += (prog->globResult.gl_pathc - i - 1);
446 } 461 }
447 *dst = '\0'; 462
448 } else if (!rc) { 463 *argcAllocedPtr = argcAlloced;
449 argcAlloced += (prog->globResult.gl_pathc - i); 464 *argcPtr = argc;
450 prog->argv =
451 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
452 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
453 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
454 argc += (prog->globResult.gl_pathc - i - 1);
455 }
456
457 *argcAllocedPtr = argcAlloced;
458 *argcPtr = argc;
459} 465}
460 466
461/* Return cmd->numProgs as 0 if no command is present (e.g. an empty 467/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
@@ -465,396 +471,396 @@ static void globLastArgument(struct childProgram *prog, int *argcPtr,
465 present. */ 471 present. */
466static int parseCommand(char **commandPtr, struct job *job, int *isBg) 472static int parseCommand(char **commandPtr, struct job *job, int *isBg)
467{ 473{
468 char *command; 474 char *command;
469 char *returnCommand = NULL; 475 char *returnCommand = NULL;
470 char *src, *buf, *chptr; 476 char *src, *buf, *chptr;
471 int argc = 0; 477 int argc = 0;
472 int done = 0; 478 int done = 0;
473 int argvAlloced; 479 int argvAlloced;
474 int i; 480 int i;
475 char quote = '\0'; 481 char quote = '\0';
476 int count; 482 int count;
477 struct childProgram *prog; 483 struct childProgram *prog;
478 484
479 /* skip leading white space */ 485 /* skip leading white space */
480 while (**commandPtr && isspace(**commandPtr)) 486 while (**commandPtr && isspace(**commandPtr))
481 (*commandPtr)++; 487 (*commandPtr)++;
482 488
483 /* this handles empty lines or leading '#' characters */ 489 /* this handles empty lines or leading '#' characters */
484 if (!**commandPtr || (**commandPtr == '#')) { 490 if (!**commandPtr || (**commandPtr == '#')) {
485 job->numProgs = 0; 491 job->numProgs = 0;
486 *commandPtr = NULL; 492 *commandPtr = NULL;
487 return 0; 493 return 0;
488 } 494 }
489
490 *isBg = 0;
491 job->numProgs = 1;
492 job->progs = malloc(sizeof(*job->progs));
493
494 /* We set the argv elements to point inside of this string. The
495 memory is freed by freeJob().
496
497 Getting clean memory relieves us of the task of NULL
498 terminating things and makes the rest of this look a bit
499 cleaner (though it is, admittedly, a tad less efficient) */
500 job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1);
501 job->text = NULL;
502
503 prog = job->progs;
504 prog->numRedirections = 0;
505 prog->redirections = NULL;
506 prog->freeGlob = 0;
507 prog->isStopped = 0;
508
509 argvAlloced = 5;
510 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
511 prog->argv[0] = job->cmdBuf;
512
513 buf = command;
514 src = *commandPtr;
515 while (*src && !done) {
516 if (quote == *src) {
517 quote = '\0';
518 } else if (quote) {
519 if (*src == '\\') {
520 src++;
521 if (!*src) {
522 fprintf(stderr, "character expected after \\\n");
523 freeJob(job);
524 return 1;
525 }
526 495
527 /* in shell, "\'" should yield \' */ 496 *isBg = 0;
528 if (*src != quote) 497 job->numProgs = 1;
529 *buf++ = '\\'; 498 job->progs = malloc(sizeof(*job->progs));
530 } else if (*src == '*' || *src == '?' || *src == '[' || 499
531 *src == ']') *buf++ = '\\'; 500 /* We set the argv elements to point inside of this string. The
532 *buf++ = *src; 501 memory is freed by freeJob().
533 } else if (isspace(*src)) { 502
534 if (*prog->argv[argc]) { 503 Getting clean memory relieves us of the task of NULL
535 buf++, argc++; 504 terminating things and makes the rest of this look a bit
536 /* +1 here leaves room for the NULL which ends argv */ 505 cleaner (though it is, admittedly, a tad less efficient) */
537 if ((argc + 1) == argvAlloced) { 506 job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1);
538 argvAlloced += 5; 507 job->text = NULL;
539 prog->argv = realloc(prog->argv, 508
540 sizeof(*prog->argv) * 509 prog = job->progs;
541 argvAlloced); 510 prog->numRedirections = 0;
542 } 511 prog->redirections = NULL;
543 prog->argv[argc] = buf; 512 prog->freeGlob = 0;
513 prog->isStopped = 0;
514
515 argvAlloced = 5;
516 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
517 prog->argv[0] = job->cmdBuf;
518
519 buf = command;
520 src = *commandPtr;
521 while (*src && !done) {
522 if (quote == *src) {
523 quote = '\0';
524 } else if (quote) {
525 if (*src == '\\') {
526 src++;
527 if (!*src) {
528 fprintf(stderr, "character expected after \\\n");
529 freeJob(job);
530 return 1;
531 }
532
533 /* in shell, "\'" should yield \' */
534 if (*src != quote)
535 *buf++ = '\\';
536 } else if (*src == '*' || *src == '?' || *src == '[' ||
537 *src == ']') *buf++ = '\\';
538 *buf++ = *src;
539 } else if (isspace(*src)) {
540 if (*prog->argv[argc]) {
541 buf++, argc++;
542 /* +1 here leaves room for the NULL which ends argv */
543 if ((argc + 1) == argvAlloced) {
544 argvAlloced += 5;
545 prog->argv = realloc(prog->argv,
546 sizeof(*prog->argv) *
547 argvAlloced);
548 }
549 prog->argv[argc] = buf;
550
551 globLastArgument(prog, &argc, &argvAlloced);
552 }
553 } else
554 switch (*src) {
555 case '"':
556 case '\'':
557 quote = *src;
558 break;
559
560 case '#': /* comment */
561 done = 1;
562 break;
563
564 case '>': /* redirections */
565 case '<':
566 i = prog->numRedirections++;
567 prog->redirections = realloc(prog->redirections,
568 sizeof(*prog->redirections) *
569 (i + 1));
570
571 prog->redirections[i].fd = -1;
572 if (buf != prog->argv[argc]) {
573 /* the stuff before this character may be the file number
574 being redirected */
575 prog->redirections[i].fd =
576 strtol(prog->argv[argc], &chptr, 10);
577
578 if (*chptr && *prog->argv[argc]) {
579 buf++, argc++;
580 globLastArgument(prog, &argc, &argvAlloced);
581 }
582 }
583
584 if (prog->redirections[i].fd == -1) {
585 if (*src == '>')
586 prog->redirections[i].fd = 1;
587 else
588 prog->redirections[i].fd = 0;
589 }
590
591 if (*src++ == '>') {
592 if (*src == '>')
593 prog->redirections[i].type =
594 REDIRECT_APPEND, src++;
595 else
596 prog->redirections[i].type = REDIRECT_OVERWRITE;
597 } else {
598 prog->redirections[i].type = REDIRECT_INPUT;
599 }
600
601 /* This isn't POSIX sh compliant. Oh well. */
602 chptr = src;
603 while (isspace(*chptr))
604 chptr++;
605
606 if (!*chptr) {
607 fprintf(stderr, "file name expected after %c\n", *src);
608 freeJob(job);
609 return 1;
610 }
611
612 prog->redirections[i].filename = buf;
613 while (*chptr && !isspace(*chptr))
614 *buf++ = *chptr++;
615
616 src = chptr - 1; /* we src++ later */
617 prog->argv[argc] = ++buf;
618 break;
619
620 case '|': /* pipe */
621 /* finish this command */
622 if (*prog->argv[argc])
623 argc++;
624 if (!argc) {
625 fprintf(stderr, "empty command in pipe\n");
626 freeJob(job);
627 return 1;
628 }
629 prog->argv[argc] = NULL;
630
631 /* and start the next */
632 job->numProgs++;
633 job->progs = realloc(job->progs,
634 sizeof(*job->progs) * job->numProgs);
635 prog = job->progs + (job->numProgs - 1);
636 prog->numRedirections = 0;
637 prog->redirections = NULL;
638 prog->freeGlob = 0;
639 argc = 0;
640
641 argvAlloced = 5;
642 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
643 prog->argv[0] = ++buf;
644
645 src++;
646 while (*src && isspace(*src))
647 src++;
648
649 if (!*src) {
650 fprintf(stderr, "empty command in pipe\n");
651 return 1;
652 }
653 src--; /* we'll ++ it at the end of the loop */
654
655 break;
656
657 case '&': /* background */
658 *isBg = 1;
659 case ';': /* multiple commands */
660 done = 1;
661 returnCommand = *commandPtr + (src - *commandPtr) + 1;
662 break;
544 663
664 case '\\':
665 src++;
666 if (!*src) {
667 freeJob(job);
668 fprintf(stderr, "character expected after \\\n");
669 return 1;
670 }
671 if (*src == '*' || *src == '[' || *src == ']'
672 || *src == '?') *buf++ = '\\';
673 /* fallthrough */
674 default:
675 *buf++ = *src;
676 }
677
678 src++;
679 }
680
681 if (*prog->argv[argc]) {
682 argc++;
545 globLastArgument(prog, &argc, &argvAlloced); 683 globLastArgument(prog, &argc, &argvAlloced);
546 } 684 }
547 } else 685 if (!argc) {
548 switch (*src) { 686 freeJob(job);
549 case '"': 687 return 0;
550 case '\'': 688 }
551 quote = *src; 689 prog->argv[argc] = NULL;
552 break;
553
554 case '#': /* comment */
555 done = 1;
556 break;
557
558 case '>': /* redirections */
559 case '<':
560 i = prog->numRedirections++;
561 prog->redirections = realloc(prog->redirections,
562 sizeof(*prog->redirections) *
563 (i + 1));
564
565 prog->redirections[i].fd = -1;
566 if (buf != prog->argv[argc]) {
567 /* the stuff before this character may be the file number
568 being redirected */
569 prog->redirections[i].fd =
570 strtol(prog->argv[argc], &chptr, 10);
571
572 if (*chptr && *prog->argv[argc]) {
573 buf++, argc++;
574 globLastArgument(prog, &argc, &argvAlloced);
575 }
576 }
577 690
578 if (prog->redirections[i].fd == -1) { 691 if (!returnCommand) {
579 if (*src == '>') 692 job->text = malloc(strlen(*commandPtr) + 1);
580 prog->redirections[i].fd = 1; 693 strcpy(job->text, *commandPtr);
581 else 694 } else {
582 prog->redirections[i].fd = 0; 695 /* This leaves any trailing spaces, which is a bit sloppy */
583 }
584 696
585 if (*src++ == '>') { 697 count = returnCommand - *commandPtr;
586 if (*src == '>') 698 job->text = malloc(count + 1);
587 prog->redirections[i].type = 699 strncpy(job->text, *commandPtr, count);
588 REDIRECT_APPEND, src++; 700 job->text[count] = '\0';
589 else 701 }
590 prog->redirections[i].type = REDIRECT_OVERWRITE; 702
591 } else { 703 *commandPtr = returnCommand;
592 prog->redirections[i].type = REDIRECT_INPUT;
593 }
594 704
595 /* This isn't POSIX sh compliant. Oh well. */ 705 return 0;
596 chptr = src; 706}
597 while (isspace(*chptr))
598 chptr++;
599 707
600 if (!*chptr) { 708static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
601 fprintf(stderr, "file name expected after %c\n", *src); 709{
602 freeJob(job); 710 struct job *job;
603 return 1; 711 int i;
712 int nextin, nextout;
713 int pipefds[2]; /* pipefd[0] is for reading */
714 struct builtInCommand *x;
715
716 /* handle built-ins here -- we don't fork() so we can't background
717 these very easily */
718 for (x = bltins; x->cmd; x++) {
719 if (!strcmp(newJob.progs[0].argv[0], x->cmd)) {
720 return (x->function(&newJob, jobList));
604 } 721 }
722 }
605 723
606 prog->redirections[i].filename = buf; 724 nextin = 0, nextout = 1;
607 while (*chptr && !isspace(*chptr)) 725 for (i = 0; i < newJob.numProgs; i++) {
608 *buf++ = *chptr++; 726 if ((i + 1) < newJob.numProgs) {
609 727 pipe(pipefds);
610 src = chptr - 1; /* we src++ later */ 728 nextout = pipefds[1];
611 prog->argv[argc] = ++buf; 729 } else {
612 break; 730 nextout = 1;
613
614 case '|': /* pipe */
615 /* finish this command */
616 if (*prog->argv[argc])
617 argc++;
618 if (!argc) {
619 fprintf(stderr, "empty command in pipe\n");
620 freeJob(job);
621 return 1;
622 } 731 }
623 prog->argv[argc] = NULL;
624
625 /* and start the next */
626 job->numProgs++;
627 job->progs = realloc(job->progs,
628 sizeof(*job->progs) * job->numProgs);
629 prog = job->progs + (job->numProgs - 1);
630 prog->numRedirections = 0;
631 prog->redirections = NULL;
632 prog->freeGlob = 0;
633 argc = 0;
634
635 argvAlloced = 5;
636 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
637 prog->argv[0] = ++buf;
638 732
639 src++; 733 if (!(newJob.progs[i].pid = fork())) {
640 while (*src && isspace(*src)) 734 signal(SIGTTOU, SIG_DFL);
641 src++;
642 735
643 if (!*src) { 736 if (nextin != 0) {
644 fprintf(stderr, "empty command in pipe\n"); 737 dup2(nextin, 0);
645 return 1; 738 close(nextin);
646 } 739 }
647 src--; /* we'll ++ it at the end of the loop */
648 740
649 break; 741 if (nextout != 1) {
742 dup2(nextout, 1);
743 close(nextout);
744 }
650 745
651 case '&': /* background */ 746 /* explicit redirections override pipes */
652 *isBg = 1; 747 setupRedirections(newJob.progs + i);
653 case ';': /* multiple commands */
654 done = 1;
655 returnCommand = *commandPtr + (src - *commandPtr) + 1;
656 break;
657 748
658 case '\\': 749 execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
659 src++; 750 fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
660 if (!*src) { 751 strerror(errno));
661 freeJob(job);
662 fprintf(stderr, "character expected after \\\n");
663 return 1;
664 } 752 }
665 if (*src == '*' || *src == '[' || *src == ']'
666 || *src == '?') *buf++ = '\\';
667 /* fallthrough */
668 default:
669 *buf++ = *src;
670 }
671
672 src++;
673 }
674
675 if (*prog->argv[argc]) {
676 argc++;
677 globLastArgument(prog, &argc, &argvAlloced);
678 }
679 if (!argc) {
680 freeJob(job);
681 return 0;
682 }
683 prog->argv[argc] = NULL;
684 753
685 if (!returnCommand) { 754 /* put our child in the process group whose leader is the
686 job->text = malloc(strlen(*commandPtr) + 1); 755 first process in this pipe */
687 strcpy(job->text, *commandPtr); 756 setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
688 } else {
689 /* This leaves any trailing spaces, which is a bit sloppy */
690 757
691 count = returnCommand - *commandPtr; 758 if (nextin != 0)
692 job->text = malloc(count + 1); 759 close(nextin);
693 strncpy(job->text, *commandPtr, count); 760 if (nextout != 1)
694 job->text[count] = '\0'; 761 close(nextout);
695 }
696 762
697 *commandPtr = returnCommand; 763 /* If there isn't another process, nextin is garbage
764 but it doesn't matter */
765 nextin = pipefds[0];
766 }
698 767
699 return 0; 768 newJob.pgrp = newJob.progs[0].pid;
700}
701 769
702static int runCommand(struct job newJob, struct jobSet *jobList, int inBg) 770 /* find the ID for the job to use */
703{ 771 newJob.jobId = 1;
704 struct job *job; 772 for (job = jobList->head; job; job = job->next)
705 int i; 773 if (job->jobId >= newJob.jobId)
706 int nextin, nextout; 774 newJob.jobId = job->jobId + 1;
707 int pipefds[2]; /* pipefd[0] is for reading */
708 struct builtInCommand *x;
709
710 /* handle built-ins here -- we don't fork() so we can't background
711 these very easily */
712 for (x = bltins; x->cmd; x++) {
713 if (!strcmp(newJob.progs[0].argv[0], x->cmd)) {
714 return (x->function(&newJob, jobList));
715 }
716 }
717 775
718 nextin = 0, nextout = 1; 776 /* add the job to the list of running jobs */
719 for (i = 0; i < newJob.numProgs; i++) { 777 if (!jobList->head) {
720 if ((i + 1) < newJob.numProgs) { 778 job = jobList->head = malloc(sizeof(*job));
721 pipe(pipefds);
722 nextout = pipefds[1];
723 } else { 779 } else {
724 nextout = 1; 780 for (job = jobList->head; job->next; job = job->next);
781 job->next = malloc(sizeof(*job));
782 job = job->next;
725 } 783 }
726 784
727 if (!(newJob.progs[i].pid = fork())) { 785 *job = newJob;
728 signal(SIGTTOU, SIG_DFL); 786 job->next = NULL;
787 job->runningProgs = job->numProgs;
788 job->stoppedProgs = 0;
729 789
730 if (nextin != 0) { 790 if (inBg) {
731 dup2(nextin, 0); 791 /* we don't wait for background jobs to return -- append it
732 close(nextin); 792 to the list of backgrounded jobs and leave it alone */
733 }
734 793
735 if (nextout != 1) { 794 printf("[%d] %d\n", job->jobId,
736 dup2(nextout, 1); 795 newJob.progs[newJob.numProgs - 1].pid);
737 close(nextout); 796 } else {
738 } 797 jobList->fg = job;
739 798
740 /* explicit redirections override pipes */ 799 /* move the new process group into the foreground */
741 setupRedirections(newJob.progs + i);
742 800
743 execvp(newJob.progs[i].argv[0], newJob.progs[i].argv); 801 if (tcsetpgrp(0, newJob.pgrp))
744 fatalError("sh: %s: %s\n", newJob.progs[i].argv[0], 802 perror("tcsetpgrp");
745 strerror(errno));
746 } 803 }
747 804
748 /* put our child in the process group whose leader is the 805 return 0;
749 first process in this pipe */
750 setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
751
752 if (nextin != 0)
753 close(nextin);
754 if (nextout != 1)
755 close(nextout);
756
757 /* If there isn't another process, nextin is garbage
758 but it doesn't matter */
759 nextin = pipefds[0];
760 }
761
762 newJob.pgrp = newJob.progs[0].pid;
763
764 /* find the ID for the job to use */
765 newJob.jobId = 1;
766 for (job = jobList->head; job; job = job->next)
767 if (job->jobId >= newJob.jobId)
768 newJob.jobId = job->jobId + 1;
769
770 /* add the job to the list of running jobs */
771 if (!jobList->head) {
772 job = jobList->head = malloc(sizeof(*job));
773 } else {
774 for (job = jobList->head; job->next; job = job->next);
775 job->next = malloc(sizeof(*job));
776 job = job->next;
777 }
778
779 *job = newJob;
780 job->next = NULL;
781 job->runningProgs = job->numProgs;
782 job->stoppedProgs = 0;
783
784 if (inBg) {
785 /* we don't wait for background jobs to return -- append it
786 to the list of backgrounded jobs and leave it alone */
787
788 printf("[%d] %d\n", job->jobId,
789 newJob.progs[newJob.numProgs - 1].pid);
790 } else {
791 jobList->fg = job;
792
793 /* move the new process group into the foreground */
794
795 if (tcsetpgrp(0, newJob.pgrp))
796 perror("tcsetpgrp");
797 }
798
799 return 0;
800} 806}
801 807
802static int setupRedirections(struct childProgram *prog) 808static int setupRedirections(struct childProgram *prog)
803{ 809{
804 int i; 810 int i;
805 int openfd; 811 int openfd;
806 int mode = O_RDONLY; 812 int mode = O_RDONLY;
807 struct redirectionSpecifier *redir = prog->redirections; 813 struct redirectionSpecifier *redir = prog->redirections;
808 814
809 for (i = 0; i < prog->numRedirections; i++, redir++) { 815 for (i = 0; i < prog->numRedirections; i++, redir++) {
810 switch (redir->type) { 816 switch (redir->type) {
811 case REDIRECT_INPUT: 817 case REDIRECT_INPUT:
812 mode = O_RDONLY; 818 mode = O_RDONLY;
813 break; 819 break;
814 case REDIRECT_OVERWRITE: 820 case REDIRECT_OVERWRITE:
815 mode = O_RDWR | O_CREAT | O_TRUNC; 821 mode = O_RDWR | O_CREAT | O_TRUNC;
816 break; 822 break;
817 case REDIRECT_APPEND: 823 case REDIRECT_APPEND:
818 mode = O_RDWR | O_CREAT | O_APPEND; 824 mode = O_RDWR | O_CREAT | O_APPEND;
819 break; 825 break;
820 } 826 }
821 827
822 openfd = open(redir->filename, mode, 0666); 828 openfd = open(redir->filename, mode, 0666);
823 if (openfd < 0) { 829 if (openfd < 0) {
824 /* this could get lost if stderr has been redirected, but 830 /* this could get lost if stderr has been redirected, but
825 bash and ash both lose it as well (though zsh doesn't!) */ 831 bash and ash both lose it as well (though zsh doesn't!) */
826 fprintf(stderr, "error opening %s: %s\n", redir->filename, 832 fprintf(stderr, "error opening %s: %s\n", redir->filename,
827 strerror(errno)); 833 strerror(errno));
828 return 1; 834 return 1;
829 } 835 }
830 836
831 if (openfd != redir->fd) { 837 if (openfd != redir->fd) {
832 dup2(openfd, redir->fd); 838 dup2(openfd, redir->fd);
833 close(openfd); 839 close(openfd);
840 }
834 } 841 }
835 }
836 842
837 return 0; 843 return 0;
838} 844}
839 845
840 846
841static int busy_loop(FILE * input) 847static int busy_loop(FILE * input)
842{ 848{
843 char *command; 849 char *command;
844 char *nextCommand = NULL; 850 char *nextCommand = NULL;
845 struct jobSet jobList = { NULL, NULL }; 851 struct jobSet jobList = { NULL, NULL };
846 struct job newJob; 852 struct job newJob;
847 int i; 853 int i;
848 int status; 854 int status;
849 int inBg; 855 int inBg;
850 856
851 command = (char*) calloc(BUFSIZ, sizeof(char)); 857 command = (char *) calloc(BUFSIZ, sizeof(char));
852 858
853 /* don't pay any attention to this signal; it just confuses 859 /* don't pay any attention to this signal; it just confuses
854 things and isn't really meant for shells anyway */ 860 things and isn't really meant for shells anyway */
855 signal(SIGTTOU, SIG_IGN); 861 signal(SIGTTOU, SIG_IGN);
856 862
857 while (1) { 863 while (1) {
858 if (!jobList.fg) { 864 if (!jobList.fg) {
859 /* no job is in the foreground */ 865 /* no job is in the foreground */
860 866
@@ -862,97 +868,97 @@ static int busy_loop(FILE * input)
862 checkJobs(&jobList); 868 checkJobs(&jobList);
863 869
864 if (!nextCommand) { 870 if (!nextCommand) {
865 if (getCommand(input, command)) 871 if (getCommand(input, command))
866 break; 872 break;
867 nextCommand = command; 873 nextCommand = command;
868 } 874 }
869 875
870 if (!parseCommand(&nextCommand, &newJob, &inBg) && 876 if (!parseCommand(&nextCommand, &newJob, &inBg) &&
871 newJob.numProgs) { 877 newJob.numProgs) {
872 runCommand(newJob, &jobList, inBg); 878 runCommand(newJob, &jobList, inBg);
873 } 879 }
874 } else { 880 } else {
875 /* a job is running in the foreground; wait for it */ 881 /* a job is running in the foreground; wait for it */
876 i = 0; 882 i = 0;
877 while (!jobList.fg->progs[i].pid || 883 while (!jobList.fg->progs[i].pid ||
878 jobList.fg->progs[i].isStopped) i++; 884 jobList.fg->progs[i].isStopped) i++;
879 885
880 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED); 886 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
881 887
882 if (WIFEXITED(status) || WIFSIGNALED(status)) { 888 if (WIFEXITED(status) || WIFSIGNALED(status)) {
883 /* the child exited */ 889 /* the child exited */
884 jobList.fg->runningProgs--; 890 jobList.fg->runningProgs--;
885 jobList.fg->progs[i].pid = 0; 891 jobList.fg->progs[i].pid = 0;
886 892
887 if (!jobList.fg->runningProgs) { 893 if (!jobList.fg->runningProgs) {
888 /* child exited */ 894 /* child exited */
889 895
890 removeJob(&jobList, jobList.fg); 896 removeJob(&jobList, jobList.fg);
891 jobList.fg = NULL; 897 jobList.fg = NULL;
892 898
893 /* move the shell to the foreground */ 899 /* move the shell to the foreground */
894 if (tcsetpgrp(0, getpid())) 900 if (tcsetpgrp(0, getpid()))
895 perror("tcsetpgrp"); 901 perror("tcsetpgrp");
896 } 902 }
897 } else { 903 } else {
898 /* the child was stopped */ 904 /* the child was stopped */
899 jobList.fg->stoppedProgs++; 905 jobList.fg->stoppedProgs++;
900 jobList.fg->progs[i].isStopped = 1; 906 jobList.fg->progs[i].isStopped = 1;
901 907
902 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) { 908 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
903 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId, 909 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
904 "Stopped", jobList.fg->text); 910 "Stopped", jobList.fg->text);
905 jobList.fg = NULL; 911 jobList.fg = NULL;
906 } 912 }
907 } 913 }
908 914
909 if (!jobList.fg) { 915 if (!jobList.fg) {
910 /* move the shell to the foreground */ 916 /* move the shell to the foreground */
911 if (tcsetpgrp(0, getpid())) 917 if (tcsetpgrp(0, getpid()))
912 perror("tcsetpgrp"); 918 perror("tcsetpgrp");
913 } 919 }
914 } 920 }
915 } 921 }
916 free( command); 922 free(command);
917 923
918 return 0; 924 return 0;
919} 925}
920 926
921 927
922int shell_main(int argc, char **argv) 928int shell_main(int argc, char **argv)
923{ 929{
924 FILE *input = stdin; 930 FILE *input = stdin;
925 931
926 if (argc > 2) { 932 if (argc > 2) {
927 usage(shell_usage); 933 usage(shell_usage);
928 } 934 }
929 /* initialize the cwd */ 935 /* initialize the cwd */
930 getcwd(cwd, sizeof(cwd)); 936 getcwd(cwd, sizeof(cwd));
931 937
932 938
933 //if (argv[0] && argv[0][0] == '-') { 939 //if (argv[0] && argv[0][0] == '-') {
934 // shell_source("/etc/profile"); 940 // shell_source("/etc/profile");
935 //} 941 //}
936 942
937 if (argc < 2) { 943 if (argc < 2) {
938 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, 944 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER,
939 BB_BT); 945 BB_BT);
940 fprintf(stdout, 946 fprintf(stdout,
941 "Enter 'help' for a list of built-in commands.\n\n"); 947 "Enter 'help' for a list of built-in commands.\n\n");
942 } else { 948 } else {
943 input = fopen(argv[1], "r"); 949 input = fopen(argv[1], "r");
944 if (!input) 950 if (!input)
945 fatalError("A: Couldn't open file '%s': %s\n", argv[1], 951 fatalError("A: Couldn't open file '%s': %s\n", argv[1],
946 strerror(errno)); 952 strerror(errno));
947// else 953// else
948// fatalError("Got it.\n"); 954// fatalError("Got it.\n");
949 //exit(shell_source(argv[1])); 955 //exit(shell_source(argv[1]));
950 956
951 /* Set terminal IO to canonical mode, and save old term settings. */ 957 /* Set terminal IO to canonical mode, and save old term settings. */
952#ifdef BB_FEATURE_SH_COMMAND_EDITING 958#ifdef BB_FEATURE_SH_COMMAND_EDITING
953 cmdedit_init(); 959 cmdedit_init();
954#endif 960#endif
955 } 961 }
956 962
957 return (busy_loop(input)); 963 return (busy_loop(input));
958} 964}