aboutsummaryrefslogtreecommitdiff
path: root/shell/lash_unused.c
diff options
context:
space:
mode:
Diffstat (limited to 'shell/lash_unused.c')
-rw-r--r--shell/lash_unused.c1576
1 files changed, 0 insertions, 1576 deletions
diff --git a/shell/lash_unused.c b/shell/lash_unused.c
deleted file mode 100644
index 513918962..000000000
--- a/shell/lash_unused.c
+++ /dev/null
@@ -1,1576 +0,0 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * lash -- the BusyBox Lame-Ass SHell
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * Based in part on ladsh.c by Michael K. Johnson and Erik W. Troan, which is
8 * under the following liberal license: "We have placed this source code in the
9 * public domain. Use it in any project, free or commercial."
10 *
11 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
12 */
13
14/* This shell's parsing engine is officially at a dead-end. Future
15 * work shell work should be done using hush, msh, or ash. This is
16 * still a very useful, small shell -- it just don't need any more
17 * features beyond what it already has...
18 */
19
20//For debugging/development on the shell only...
21//#define DEBUG_SHELL
22
23#include <getopt.h>
24#include <glob.h>
25
26#include "libbb.h"
27
28#define expand_t glob_t
29
30/* Always enable for the moment... */
31#define CONFIG_LASH_PIPE_N_REDIRECTS
32#define CONFIG_LASH_JOB_CONTROL
33#define ENABLE_LASH_PIPE_N_REDIRECTS 1
34#define ENABLE_LASH_JOB_CONTROL 1
35
36
37enum { MAX_READ = 128 }; /* size of input buffer for 'read' builtin */
38#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
39
40
41#if ENABLE_LASH_PIPE_N_REDIRECTS
42enum redir_type { REDIRECT_INPUT, REDIRECT_OVERWRITE,
43 REDIRECT_APPEND
44};
45#endif
46
47enum {
48 DEFAULT_CONTEXT = 0x1,
49 IF_TRUE_CONTEXT = 0x2,
50 IF_FALSE_CONTEXT = 0x4,
51 THEN_EXP_CONTEXT = 0x8,
52 ELSE_EXP_CONTEXT = 0x10
53};
54
55#define LASH_OPT_DONE (1)
56#define LASH_OPT_SAW_QUOTE (2)
57
58#if ENABLE_LASH_PIPE_N_REDIRECTS
59struct redir_struct {
60 enum redir_type type; /* type of redirection */
61 int fd; /* file descriptor being redirected */
62 char *filename; /* file to redirect fd to */
63};
64#endif
65
66struct child_prog {
67 pid_t pid; /* 0 if exited */
68 char **argv; /* program name and arguments */
69 int num_redirects; /* elements in redirection array */
70 int is_stopped; /* is the program currently running? */
71 struct job *family; /* pointer back to the child's parent job */
72#if ENABLE_LASH_PIPE_N_REDIRECTS
73 struct redir_struct *redirects; /* I/O redirects */
74#endif
75};
76
77struct jobset {
78 struct job *head; /* head of list of running jobs */
79 struct job *fg; /* current foreground job */
80};
81
82struct job {
83 int jobid; /* job number */
84 int num_progs; /* total number of programs in job */
85 int running_progs; /* number of programs running */
86 char *text; /* name of job */
87 char *cmdbuf; /* buffer various argv's point into */
88 pid_t pgrp; /* process group ID for the job */
89 struct child_prog *progs; /* array of programs in job */
90 struct job *next; /* to track background commands */
91 int stopped_progs; /* number of programs alive, but stopped */
92 unsigned int job_context; /* bitmask defining current context */
93 struct jobset *job_list;
94};
95
96struct built_in_command {
97 const char *cmd; /* name */
98 const char *descr; /* description */
99 int (*function) (struct child_prog *); /* function ptr */
100};
101
102/* function prototypes for builtins */
103static int builtin_cd(struct child_prog *cmd);
104static int builtin_exec(struct child_prog *cmd);
105static int builtin_exit(struct child_prog *cmd);
106static int builtin_fg_bg(struct child_prog *cmd);
107static int builtin_help(struct child_prog *cmd);
108static int builtin_jobs(struct child_prog *dummy);
109static int builtin_pwd(struct child_prog *dummy);
110static int builtin_export(struct child_prog *cmd);
111static int builtin_source(struct child_prog *cmd);
112static int builtin_unset(struct child_prog *cmd);
113static int builtin_read(struct child_prog *cmd);
114
115
116/* function prototypes for shell stuff */
117static void checkjobs(struct jobset *job_list);
118static void remove_job(struct jobset *j_list, struct job *job);
119static int get_command_bufsiz(FILE *source, char *command);
120static int parse_command(char **command_ptr, struct job *job, int *inbg);
121static int run_command(struct job *newjob, int inbg, int outpipe[2]);
122static int pseudo_exec(struct child_prog *cmd) NORETURN;
123static int busy_loop(FILE *input);
124
125
126/* Table of built-in functions (these are non-forking builtins, meaning they
127 * can change global variables in the parent shell process but they will not
128 * work with pipes and redirects; 'unset foo | whatever' will not work) */
129static const struct built_in_command bltins[] = {
130 {"bg" , "Resume a job in the background", builtin_fg_bg},
131 {"cd" , "Change working directory", builtin_cd},
132 {"exec" , "Exec command, replacing this shell with the exec'd process", builtin_exec},
133 {"exit" , "Exit from shell()", builtin_exit},
134 {"fg" , "Bring job into the foreground", builtin_fg_bg},
135 {"jobs" , "Lists the active jobs", builtin_jobs},
136 {"export", "Set environment variable", builtin_export},
137 {"unset" , "Unset environment variable", builtin_unset},
138 {"read" , "Input environment variable", builtin_read},
139 {"." , "Source-in and run commands in a file", builtin_source},
140 /* These were "forked applets", but distinction was nuked */
141 /* Original comment retained: */
142 /* Table of forking built-in functions (things that fork cannot change global
143 * variables in the parent process, such as the current working directory) */
144 {"pwd" , "Print current directory", builtin_pwd},
145 {"help" , "List shell built-in commands", builtin_help},
146 /* to do: add ulimit */
147};
148
149
150#define VEC_LAST(v) v[ARRAY_SIZE(v)-1]
151
152
153static int shell_context; /* Type prompt trigger (PS1 or PS2) */
154
155
156/* Globals that are static to this file */
157static char *cwd;
158static char *local_pending_command;
159static struct jobset job_list = { NULL, NULL };
160static int global_argc;
161static char **global_argv;
162static llist_t *close_me_list;
163static int last_return_code;
164static int last_bg_pid;
165static unsigned int last_jobid;
166static int shell_terminal;
167static const char *PS1;
168static const char *PS2 = "> ";
169
170
171#ifdef DEBUG_SHELL
172static inline void debug_printf(const char *format, ...)
173{
174 va_list args;
175 va_start(args, format);
176 vfprintf(stderr, format, args);
177 va_end(args);
178}
179#else
180static inline void debug_printf(const char UNUSED_PARAM *format, ...) { }
181#endif
182
183/*
184 Most builtins need access to the struct child_prog that has
185 their arguments, previously coded as cmd->progs[0]. That coding
186 can exhibit a bug, if the builtin is not the first command in
187 a pipeline: "echo foo | exec sort" will attempt to exec foo.
188
189builtin previous use notes
190------ ----------------- ---------
191cd cmd->progs[0]
192exec cmd->progs[0] squashed bug: didn't look for applets or forking builtins
193exit cmd->progs[0]
194fg_bg cmd->progs[0], job_list->head, job_list->fg
195help 0
196jobs job_list->head
197pwd 0
198export cmd->progs[0]
199source cmd->progs[0]
200unset cmd->progs[0]
201read cmd->progs[0]
202
203I added "struct job *family;" to struct child_prog,
204and switched API to builtin_foo(struct child_prog *child);
205So cmd->text becomes child->family->text
206 cmd->job_context becomes child->family->job_context
207 cmd->progs[0] becomes *child
208 job_list becomes child->family->job_list
209 */
210
211
212static void update_cwd(void)
213{
214 cwd = xrealloc_getcwd_or_warn(cwd);
215 if (!cwd)
216 cwd = xstrdup(bb_msg_unknown);
217}
218
219/* built-in 'cd <path>' handler */
220static int builtin_cd(struct child_prog *child)
221{
222 char *newdir;
223
224 if (child->argv[1] == NULL)
225 newdir = getenv("HOME");
226 else
227 newdir = child->argv[1];
228 if (chdir(newdir)) {
229 bb_perror_msg("cd: %s", newdir);
230 return EXIT_FAILURE;
231 }
232 update_cwd();
233 return EXIT_SUCCESS;
234}
235
236/* built-in 'exec' handler */
237static int builtin_exec(struct child_prog *child)
238{
239 if (child->argv[1] == NULL)
240 return EXIT_SUCCESS; /* Really? */
241 child->argv++;
242 while (close_me_list)
243 close((long)llist_pop(&close_me_list));
244 pseudo_exec(child);
245 /* never returns */
246}
247
248/* built-in 'exit' handler */
249static int builtin_exit(struct child_prog *child)
250{
251 if (child->argv[1] == NULL)
252 exit(EXIT_SUCCESS);
253
254 exit(atoi(child->argv[1]));
255}
256
257/* built-in 'fg' and 'bg' handler */
258static int builtin_fg_bg(struct child_prog *child)
259{
260 int i, jobnum;
261 struct job *job;
262
263 /* If they gave us no args, assume they want the last backgrounded task */
264 if (!child->argv[1]) {
265 for (job = child->family->job_list->head; job; job = job->next) {
266 if (job->jobid == last_jobid) {
267 goto found;
268 }
269 }
270 bb_error_msg("%s: no current job", child->argv[0]);
271 return EXIT_FAILURE;
272 }
273 if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) {
274 bb_error_msg(bb_msg_invalid_arg, child->argv[1], child->argv[0]);
275 return EXIT_FAILURE;
276 }
277 for (job = child->family->job_list->head; job; job = job->next) {
278 if (job->jobid == jobnum) {
279 goto found;
280 }
281 }
282 bb_error_msg("%s: %d: no such job", child->argv[0], jobnum);
283 return EXIT_FAILURE;
284 found:
285 if (*child->argv[0] == 'f') {
286 /* Put the job into the foreground. */
287 tcsetpgrp(shell_terminal, job->pgrp);
288
289 child->family->job_list->fg = job;
290 }
291
292 /* Restart the processes in the job */
293 for (i = 0; i < job->num_progs; i++)
294 job->progs[i].is_stopped = 0;
295
296 job->stopped_progs = 0;
297
298 i = kill(- job->pgrp, SIGCONT);
299 if (i < 0) {
300 if (errno == ESRCH) {
301 remove_job(&job_list, job);
302 } else {
303 bb_perror_msg("kill (SIGCONT)");
304 }
305 }
306
307 return EXIT_SUCCESS;
308}
309
310/* built-in 'help' handler */
311static int builtin_help(struct child_prog UNUSED_PARAM *dummy)
312{
313 const struct built_in_command *x;
314
315 printf(
316 "Built-in commands:\n"
317 "------------------\n");
318 for (x = bltins; x <= &VEC_LAST(bltins); x++) {
319 if (x->descr == NULL)
320 continue;
321 printf("%s\t%s\n", x->cmd, x->descr);
322 }
323 bb_putchar('\n');
324 return EXIT_SUCCESS;
325}
326
327/* built-in 'jobs' handler */
328static int builtin_jobs(struct child_prog *child)
329{
330 struct job *job;
331 const char *status_string;
332
333 for (job = child->family->job_list->head; job; job = job->next) {
334 if (job->running_progs == job->stopped_progs)
335 status_string = "Stopped";
336 else
337 status_string = "Running";
338
339 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->text);
340 }
341 return EXIT_SUCCESS;
342}
343
344
345/* built-in 'pwd' handler */
346static int builtin_pwd(struct child_prog UNUSED_PARAM *dummy)
347{
348 update_cwd();
349 puts(cwd);
350 return EXIT_SUCCESS;
351}
352
353/* built-in 'export VAR=value' handler */
354static int builtin_export(struct child_prog *child)
355{
356 int res;
357 char *v = child->argv[1];
358
359 if (v == NULL) {
360 char **e;
361 for (e = environ; *e; e++) {
362 puts(*e);
363 }
364 return 0;
365 }
366 res = putenv(v);
367 if (res)
368 bb_perror_msg("export");
369#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
370 if (strncmp(v, "PS1=", 4) == 0)
371 PS1 = getenv("PS1");
372#endif
373
374#if ENABLE_LOCALE_SUPPORT
375 // TODO: why getenv? "" would be just as good...
376 if (strncmp(v, "LC_ALL=", 7) == 0)
377 setlocale(LC_ALL, getenv("LC_ALL"));
378 if (strncmp(v, "LC_CTYPE=", 9) == 0)
379 setlocale(LC_CTYPE, getenv("LC_CTYPE"));
380#endif
381
382 return res;
383}
384
385/* built-in 'read VAR' handler */
386static int builtin_read(struct child_prog *child)
387{
388 int res = 0, len;
389 char *s;
390 char string[MAX_READ];
391
392 if (child->argv[1]) {
393 /* argument (VAR) given: put "VAR=" into buffer */
394 safe_strncpy(string, child->argv[1], MAX_READ-1);
395 len = strlen(string);
396 string[len++] = '=';
397 string[len] = '\0';
398 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
399 res = strlen(string);
400 if (res > len)
401 string[--res] = '\0'; /* chomp trailing newline */
402 /*
403 ** string should now contain "VAR=<value>"
404 ** copy it (putenv() won't do that, so we must make sure
405 ** the string resides in a static buffer!)
406 */
407 res = -1;
408 s = strdup(string);
409 if (s)
410 res = putenv(s);
411 if (res)
412 bb_perror_msg("read");
413 } else
414 fgets(string, sizeof(string), stdin);
415
416 return res;
417}
418
419/* Built-in '.' handler (read-in and execute commands from file) */
420static int builtin_source(struct child_prog *child)
421{
422 FILE *input;
423 int status;
424
425 input = fopen_or_warn(child->argv[1], "r");
426 if (!input) {
427 return EXIT_FAILURE;
428 }
429
430 llist_add_to(&close_me_list, (void *)(long)fileno(input));
431 /* Now run the file */
432 status = busy_loop(input);
433 fclose(input);
434 llist_pop(&close_me_list);
435 return status;
436}
437
438/* built-in 'unset VAR' handler */
439static int builtin_unset(struct child_prog *child)
440{
441 if (child->argv[1] == NULL) {
442 printf(bb_msg_requires_arg, "unset");
443 return EXIT_FAILURE;
444 }
445 unsetenv(child->argv[1]);
446 return EXIT_SUCCESS;
447}
448
449#if ENABLE_LASH_JOB_CONTROL
450/* free up all memory from a job */
451static void free_job(struct job *cmd)
452{
453 int i;
454 struct jobset *keep;
455
456 for (i = 0; i < cmd->num_progs; i++) {
457 free(cmd->progs[i].argv);
458#if ENABLE_LASH_PIPE_N_REDIRECTS
459 free(cmd->progs[i].redirects);
460#endif
461 }
462 free(cmd->progs);
463 free(cmd->text);
464 free(cmd->cmdbuf);
465 keep = cmd->job_list;
466 memset(cmd, 0, sizeof(struct job));
467 cmd->job_list = keep;
468}
469
470/* remove a job from a jobset */
471static void remove_job(struct jobset *j_list, struct job *job)
472{
473 struct job *prevjob;
474
475 free_job(job);
476 if (job == j_list->head) {
477 j_list->head = job->next;
478 } else {
479 prevjob = j_list->head;
480 while (prevjob->next != job)
481 prevjob = prevjob->next;
482 prevjob->next = job->next;
483 }
484
485 if (j_list->head)
486 last_jobid = j_list->head->jobid;
487 else
488 last_jobid = 0;
489
490 free(job);
491}
492
493/* Checks to see if any background processes have exited -- if they
494 have, figure out why and see if a job has completed */
495static void checkjobs(struct jobset *j_list)
496{
497 struct job *job;
498 pid_t childpid;
499 int status;
500 int prognum = 0;
501
502 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
503 for (job = j_list->head; job; job = job->next) {
504 prognum = 0;
505 while (prognum < job->num_progs &&
506 job->progs[prognum].pid != childpid) prognum++;
507 if (prognum < job->num_progs)
508 break;
509 }
510
511 /* This happens on backticked commands */
512 if (job == NULL)
513 return;
514
515 if (WIFEXITED(status) || WIFSIGNALED(status)) {
516 /* child exited */
517 job->running_progs--;
518 job->progs[prognum].pid = 0;
519
520 if (!job->running_progs) {
521 printf(JOB_STATUS_FORMAT, job->jobid, "Done", job->text);
522 last_jobid = 0;
523 remove_job(j_list, job);
524 }
525 } else {
526 /* child stopped */
527 job->stopped_progs++;
528 job->progs[prognum].is_stopped = 1;
529 }
530 }
531
532 if (childpid == -1 && errno != ECHILD)
533 bb_perror_msg("waitpid");
534}
535#else
536static void checkjobs(struct jobset *j_list)
537{
538}
539static void free_job(struct job *cmd)
540{
541}
542static void remove_job(struct jobset *j_list, struct job *job)
543{
544}
545#endif
546
547#if ENABLE_LASH_PIPE_N_REDIRECTS
548/* squirrel != NULL means we squirrel away copies of stdin, stdout,
549 * and stderr if they are redirected. */
550static int setup_redirects(struct child_prog *prog, int squirrel[])
551{
552 int i;
553 int openfd;
554 int mode = O_RDONLY;
555 struct redir_struct *redir = prog->redirects;
556
557 for (i = 0; i < prog->num_redirects; i++, redir++) {
558 switch (redir->type) {
559 case REDIRECT_INPUT:
560 mode = O_RDONLY;
561 break;
562 case REDIRECT_OVERWRITE:
563 mode = O_WRONLY | O_CREAT | O_TRUNC;
564 break;
565 case REDIRECT_APPEND:
566 mode = O_WRONLY | O_CREAT | O_APPEND;
567 break;
568 }
569
570 openfd = open_or_warn(redir->filename, mode);
571 if (openfd < 0) {
572 /* this could get lost if stderr has been redirected, but
573 bash and ash both lose it as well (though zsh doesn't!) */
574 return 1;
575 }
576
577 if (openfd != redir->fd) {
578 if (squirrel && redir->fd < 3) {
579 squirrel[redir->fd] = dup(redir->fd);
580 close_on_exec_on(squirrel[redir->fd]);
581 }
582 dup2(openfd, redir->fd);
583 close(openfd);
584 }
585 }
586
587 return 0;
588}
589
590static void restore_redirects(int squirrel[])
591{
592 int i, fd;
593 for (i = 0; i < 3; i++) {
594 fd = squirrel[i];
595 if (fd != -1) {
596 /* No error checking. I sure wouldn't know what
597 * to do with an error if I found one! */
598 dup2(fd, i);
599 close(fd);
600 }
601 }
602}
603#else
604static inline int setup_redirects(struct child_prog *prog, int squirrel[])
605{
606 return 0;
607}
608static inline void restore_redirects(int squirrel[])
609{
610}
611#endif
612
613static inline void cmdedit_set_initial_prompt(void)
614{
615#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
616 PS1 = NULL;
617#else
618 PS1 = getenv("PS1");
619 if (PS1 == 0)
620 PS1 = "\\w \\$ ";
621#endif
622}
623
624static inline const char* setup_prompt_string(void)
625{
626#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
627 /* Set up the prompt */
628 if (shell_context == 0) {
629 char *ns;
630 free((char*)PS1);
631 ns = xmalloc(strlen(cwd)+4);
632 sprintf(ns, "%s %c ", cwd, (geteuid() != 0) ? '$': '#');
633 PS1 = ns;
634 return ns;
635 } else {
636 return PS2;
637 }
638#else
639 return (shell_context == 0)? PS1 : PS2;
640#endif
641}
642
643#if ENABLE_FEATURE_EDITING
644static line_input_t *line_input_state;
645#endif
646
647static int get_command_bufsiz(FILE *source, char *command)
648{
649 const char *prompt_str;
650
651 if (source == NULL) {
652 if (local_pending_command) {
653 /* a command specified (-c option): return it & mark it done */
654 strncpy(command, local_pending_command, BUFSIZ);
655 local_pending_command = NULL;
656 return 0;
657 }
658 return 1;
659 }
660
661 if (source == stdin) {
662 prompt_str = setup_prompt_string();
663
664#if ENABLE_FEATURE_EDITING
665 /*
666 ** enable command line editing only while a command line
667 ** is actually being read; otherwise, we'll end up bequeathing
668 ** atexit() handlers and other unwanted stuff to our
669 ** child processes (rob@sysgo.de)
670 */
671 read_line_input(prompt_str, command, BUFSIZ, line_input_state);
672 return 0;
673#else
674 fputs(prompt_str, stdout);
675#endif
676 }
677
678 if (!fgets(command, BUFSIZ - 2, source)) {
679 if (source == stdin)
680 bb_putchar('\n');
681 return 1;
682 }
683
684 return 0;
685}
686
687static char * strsep_space(char *string, int * ix)
688{
689 /* Short circuit the trivial case */
690 if (!string || ! string[*ix])
691 return NULL;
692
693 /* Find the end of the token. */
694 while (string[*ix] && !isspace(string[*ix]) ) {
695 (*ix)++;
696 }
697
698 /* Find the end of any whitespace trailing behind
699 * the token and let that be part of the token */
700 while (string[*ix] && isspace(string[*ix])) {
701 (*ix)++;
702 }
703
704 if (!*ix) {
705 /* Nothing useful was found */
706 return NULL;
707 }
708
709 return xstrndup(string, *ix);
710}
711
712static int expand_arguments(char *command)
713{
714 static const char out_of_space[] ALIGN1 = "out of space during expansion";
715
716 int total_length = 0, length, i, retval, ix = 0;
717 expand_t expand_result;
718 char *tmpcmd, *cmd, *cmd_copy;
719 char *src, *dst, *var;
720 int flags = GLOB_NOCHECK
721#ifdef GLOB_BRACE
722 | GLOB_BRACE
723#endif
724#ifdef GLOB_TILDE
725 | GLOB_TILDE
726#endif
727 ;
728
729 /* get rid of the terminating \n */
730 chomp(command);
731
732 /* Fix up escape sequences to be the Real Thing(tm) */
733 while (command && command[ix]) {
734 if (command[ix] == '\\') {
735 const char *tmp = command+ix+1;
736 command[ix] = bb_process_escape_sequence( &tmp );
737 memmove(command+ix + 1, tmp, strlen(tmp)+1);
738 }
739 ix++;
740 }
741 /* Use glob and then fixup environment variables and such */
742
743 /* It turns out that glob is very stupid. We have to feed it one word at a
744 * time since it can't cope with a full string. Here we convert command
745 * (char*) into cmd (char**, one word per string) */
746
747 /* We need a clean copy, so strsep can mess up the copy while
748 * we write stuff into the original (in a minute) */
749 cmd = cmd_copy = xstrdup(command);
750 *command = '\0';
751 for (ix = 0, tmpcmd = cmd;
752 (tmpcmd = strsep_space(cmd, &ix)) != NULL; cmd += ix, ix = 0) {
753 if (*tmpcmd == '\0')
754 break;
755 /* we need to trim() the result for glob! */
756 trim(tmpcmd);
757 retval = glob(tmpcmd, flags, NULL, &expand_result);
758 free(tmpcmd); /* Free mem allocated by strsep_space */
759 if (retval == GLOB_NOSPACE) {
760 /* Mem may have been allocated... */
761 globfree(&expand_result);
762 bb_error_msg(out_of_space);
763 return FALSE;
764 } else if (retval != 0) {
765 /* Some other error. GLOB_NOMATCH shouldn't
766 * happen because of the GLOB_NOCHECK flag in
767 * the glob call. */
768 bb_error_msg("syntax error");
769 return FALSE;
770 } else {
771 /* Convert from char** (one word per string) to a simple char*,
772 * but don't overflow command which is BUFSIZ in length */
773 for (i = 0; i < expand_result.gl_pathc; i++) {
774 length = strlen(expand_result.gl_pathv[i]);
775 if (total_length+length+1 >= BUFSIZ) {
776 bb_error_msg(out_of_space);
777 return FALSE;
778 }
779 strcat(command+total_length, " ");
780 total_length += 1;
781 strcat(command+total_length, expand_result.gl_pathv[i]);
782 total_length += length;
783 }
784 globfree(&expand_result);
785 }
786 }
787 free(cmd_copy);
788 trim(command);
789
790 /* Now do the shell variable substitutions which
791 * wordexp can't do for us, namely $? and $! */
792 src = command;
793 while ((dst = strchr(src,'$')) != NULL) {
794 var = NULL;
795 switch (*(dst+1)) {
796 case '?':
797 var = itoa(last_return_code);
798 break;
799 case '!':
800 if (last_bg_pid == -1)
801 *var = '\0';
802 else
803 var = itoa(last_bg_pid);
804 break;
805 /* Everything else like $$, $#, $[0-9], etc. should all be
806 * expanded by wordexp(), so we can in theory skip that stuff
807 * here, but just to be on the safe side (i.e., since uClibc
808 * wordexp doesn't do this stuff yet), lets leave it in for
809 * now. */
810 case '$':
811 var = itoa(getpid());
812 break;
813 case '#':
814 var = itoa(global_argc - 1);
815 break;
816 case '0':case '1':case '2':case '3':case '4':
817 case '5':case '6':case '7':case '8':case '9':
818 {
819 int ixx = *(dst+1)-48+1;
820 if (ixx >= global_argc) {
821 var = '\0';
822 } else {
823 var = global_argv[ixx];
824 }
825 }
826 break;
827
828 }
829 if (var) {
830 /* a single character construction was found, and
831 * already handled in the case statement */
832 src = dst + 2;
833 } else {
834 /* Looks like an environment variable */
835 char delim_hold;
836 int num_skip_chars = 0;
837 int dstlen = strlen(dst);
838 /* Is this a ${foo} type variable? */
839 if (dstlen >= 2 && *(dst+1) == '{') {
840 src = strchr(dst+1, '}');
841 num_skip_chars = 1;
842 } else {
843 src = dst + 1;
844 while (isalnum(*src) || *src == '_') src++;
845 }
846 if (src == NULL) {
847 src = dst+dstlen;
848 }
849 delim_hold = *src;
850 *src = '\0'; /* temporary */
851 var = getenv(dst + 1 + num_skip_chars);
852 *src = delim_hold;
853 src += num_skip_chars;
854 }
855 if (var == NULL) {
856 /* Seems we got an un-expandable variable. So delete it. */
857 var = (char*)"";
858 }
859 {
860 int subst_len = strlen(var);
861 int trail_len = strlen(src);
862 if (dst+subst_len+trail_len >= command+BUFSIZ) {
863 bb_error_msg(out_of_space);
864 return FALSE;
865 }
866 /* Move stuff to the end of the string to accommodate
867 * filling the created gap with the new stuff */
868 memmove(dst+subst_len, src, trail_len+1);
869 /* Now copy in the new stuff */
870 memcpy(dst, var, subst_len);
871 src = dst+subst_len;
872 }
873 }
874
875 return TRUE;
876}
877
878/* Return cmd->num_progs as 0 if no command is present (e.g. an empty
879 line). If a valid command is found, command_ptr is set to point to
880 the beginning of the next command (if the original command had more
881 then one job associated with it) or NULL if no more commands are
882 present. */
883static int parse_command(char **command_ptr, struct job *job, int *inbg)
884{
885 char *command;
886 char *return_command = NULL;
887 char *src, *buf;
888 int argc_l;
889 int flag;
890 int argv_alloced;
891 char quote = '\0';
892 struct child_prog *prog;
893#if ENABLE_LASH_PIPE_N_REDIRECTS
894 int i;
895 char *chptr;
896#endif
897
898 /* skip leading white space */
899 *command_ptr = skip_whitespace(*command_ptr);
900
901 /* this handles empty lines or leading '#' characters */
902 if (!**command_ptr || (**command_ptr == '#')) {
903 job->num_progs = 0;
904 return 0;
905 }
906
907 *inbg = 0;
908 job->num_progs = 1;
909 job->progs = xmalloc(sizeof(*job->progs));
910
911 /* We set the argv elements to point inside of this string. The
912 memory is freed by free_job(). Allocate twice the original
913 length in case we need to quote every single character.
914
915 Getting clean memory relieves us of the task of NULL
916 terminating things and makes the rest of this look a bit
917 cleaner (though it is, admittedly, a tad less efficient) */
918 job->cmdbuf = command = xzalloc(2*strlen(*command_ptr) + 1);
919 job->text = NULL;
920
921 prog = job->progs;
922 prog->num_redirects = 0;
923 prog->is_stopped = 0;
924 prog->family = job;
925#if ENABLE_LASH_PIPE_N_REDIRECTS
926 prog->redirects = NULL;
927#endif
928
929 argv_alloced = 5;
930 prog->argv = xmalloc(sizeof(*prog->argv) * argv_alloced);
931 prog->argv[0] = job->cmdbuf;
932
933 flag = argc_l = 0;
934 buf = command;
935 src = *command_ptr;
936 while (*src && !(flag & LASH_OPT_DONE)) {
937 if (quote == *src) {
938 quote = '\0';
939 } else if (quote) {
940 if (*src == '\\') {
941 src++;
942 if (!*src) {
943 bb_error_msg("character expected after \\");
944 free_job(job);
945 return 1;
946 }
947
948 /* in shell, "\'" should yield \' */
949 if (*src != quote) {
950 *buf++ = '\\';
951 *buf++ = '\\';
952 }
953 } else
954 if (*src == '*' || *src == '?'
955 || *src == '[' || *src == ']'
956 ) {
957 *buf++ = '\\';
958 }
959 *buf++ = *src;
960 } else if (isspace(*src)) {
961 if (*prog->argv[argc_l] || (flag & LASH_OPT_SAW_QUOTE)) {
962 buf++, argc_l++;
963 /* +1 here leaves room for the NULL which ends argv */
964 if ((argc_l + 1) == argv_alloced) {
965 argv_alloced += 5;
966 prog->argv = xrealloc(prog->argv,
967 sizeof(*prog->argv) * argv_alloced);
968 }
969 prog->argv[argc_l] = buf;
970 flag ^= LASH_OPT_SAW_QUOTE;
971 }
972 } else
973 switch (*src) {
974 case '"':
975 case '\'':
976 quote = *src;
977 flag |= LASH_OPT_SAW_QUOTE;
978 break;
979
980 case '#': /* comment */
981 if (*(src-1)== '$')
982 *buf++ = *src;
983 else
984 flag |= LASH_OPT_DONE;
985 break;
986
987#if ENABLE_LASH_PIPE_N_REDIRECTS
988 case '>': /* redirects */
989 case '<':
990 i = prog->num_redirects++;
991 prog->redirects = xrealloc(prog->redirects,
992 sizeof(*prog->redirects) * (i + 1));
993
994 prog->redirects[i].fd = -1;
995 if (buf != prog->argv[argc_l]) {
996 /* the stuff before this character may be the file number
997 being redirected */
998 prog->redirects[i].fd =
999 strtol(prog->argv[argc_l], &chptr, 10);
1000
1001 if (*chptr && *prog->argv[argc_l]) {
1002 buf++, argc_l++;
1003 prog->argv[argc_l] = buf;
1004 }
1005 }
1006
1007 if (prog->redirects[i].fd == -1) {
1008 if (*src == '>')
1009 prog->redirects[i].fd = 1;
1010 else
1011 prog->redirects[i].fd = 0;
1012 }
1013
1014 if (*src++ == '>') {
1015 if (*src == '>')
1016 prog->redirects[i].type =
1017 REDIRECT_APPEND, src++;
1018 else
1019 prog->redirects[i].type = REDIRECT_OVERWRITE;
1020 } else {
1021 prog->redirects[i].type = REDIRECT_INPUT;
1022 }
1023
1024 /* This isn't POSIX sh compliant. Oh well. */
1025 chptr = src;
1026 chptr = skip_whitespace(chptr);
1027
1028 if (!*chptr) {
1029 bb_error_msg("file name expected after %c", *(src-1));
1030 free_job(job);
1031 job->num_progs = 0;
1032 return 1;
1033 }
1034
1035 prog->redirects[i].filename = buf;
1036 while (*chptr && !isspace(*chptr))
1037 *buf++ = *chptr++;
1038
1039 src = chptr - 1; /* we src++ later */
1040 prog->argv[argc_l] = ++buf;
1041 break;
1042
1043 case '|': /* pipe */
1044 /* finish this command */
1045 if (*prog->argv[argc_l] || flag & LASH_OPT_SAW_QUOTE)
1046 argc_l++;
1047 if (!argc_l) {
1048 goto empty_command_in_pipe;
1049 }
1050 prog->argv[argc_l] = NULL;
1051
1052 /* and start the next */
1053 job->num_progs++;
1054 job->progs = xrealloc(job->progs,
1055 sizeof(*job->progs) * job->num_progs);
1056 prog = job->progs + (job->num_progs - 1);
1057 prog->num_redirects = 0;
1058 prog->redirects = NULL;
1059 prog->is_stopped = 0;
1060 prog->family = job;
1061 argc_l = 0;
1062
1063 argv_alloced = 5;
1064 prog->argv = xmalloc(sizeof(*prog->argv) * argv_alloced);
1065 prog->argv[0] = ++buf;
1066
1067 src++;
1068 src = skip_whitespace(src);
1069
1070 if (!*src) {
1071empty_command_in_pipe:
1072 bb_error_msg("empty command in pipe");
1073 free_job(job);
1074 job->num_progs = 0;
1075 return 1;
1076 }
1077 src--; /* we'll ++ it at the end of the loop */
1078
1079 break;
1080#endif
1081
1082#if ENABLE_LASH_JOB_CONTROL
1083 case '&': /* background */
1084 *inbg = 1;
1085 /* fallthrough */
1086#endif
1087 case ';': /* multiple commands */
1088 flag |= LASH_OPT_DONE;
1089 return_command = *command_ptr + (src - *command_ptr) + 1;
1090 break;
1091
1092 case '\\':
1093 src++;
1094 if (!*src) {
1095 bb_error_msg("character expected after \\");
1096 free_job(job);
1097 return 1;
1098 }
1099 if (*src == '*' || *src == '[' || *src == ']'
1100 || *src == '?') *buf++ = '\\';
1101 /* fallthrough */
1102 default:
1103 *buf++ = *src;
1104 }
1105
1106 src++;
1107 }
1108
1109 if (*prog->argv[argc_l] || flag & LASH_OPT_SAW_QUOTE) {
1110 argc_l++;
1111 }
1112 if (!argc_l) {
1113 free_job(job);
1114 return 0;
1115 }
1116 prog->argv[argc_l] = NULL;
1117
1118 if (!return_command) {
1119 job->text = xstrdup(*command_ptr);
1120 } else {
1121 /* This leaves any trailing spaces, which is a bit sloppy */
1122 job->text = xstrndup(*command_ptr, return_command - *command_ptr);
1123 }
1124
1125 *command_ptr = return_command;
1126
1127 return 0;
1128}
1129
1130/* Run the child_prog, no matter what kind of command it uses.
1131 */
1132static int pseudo_exec(struct child_prog *child)
1133{
1134 const struct built_in_command *x;
1135
1136 /* Check if the command matches any of the non-forking builtins.
1137 * Depending on context, this might be redundant. But it's
1138 * easier to waste a few CPU cycles than it is to figure out
1139 * if this is one of those cases.
1140 */
1141 /* Check if the command matches any of the forking builtins. */
1142 for (x = bltins; x <= &VEC_LAST(bltins); x++) {
1143 if (strcmp(child->argv[0], x->cmd) == 0) {
1144 _exit(x->function(child));
1145 }
1146 }
1147
1148 /* Check if the command matches any busybox internal
1149 * commands ("applets") here. Following discussions from
1150 * November 2000 on busybox@busybox.net, don't use
1151 * bb_get_last_path_component_nostrip(). This way explicit
1152 * (with slashes) filenames will never be interpreted as an
1153 * applet, just like with builtins. This way the user can
1154 * override an applet with an explicit filename reference.
1155 * The only downside to this change is that an explicit
1156 * /bin/foo invocation will fork and exec /bin/foo, even if
1157 * /bin/foo is a symlink to busybox.
1158 */
1159 if (ENABLE_FEATURE_SH_STANDALONE) {
1160 run_applet_and_exit(child->argv[0], child->argv);
1161 }
1162
1163 execvp(child->argv[0], child->argv);
1164
1165 /* Do not use bb_perror_msg_and_die() here, since we must not
1166 * call exit() but should call _exit() instead */
1167 bb_simple_perror_msg(child->argv[0]);
1168 _exit(EXIT_FAILURE);
1169}
1170
1171static void insert_job(struct job *newjob, int inbg)
1172{
1173 struct job *thejob;
1174 struct jobset *j_list = newjob->job_list;
1175
1176 /* find the ID for thejob to use */
1177 newjob->jobid = 1;
1178 for (thejob = j_list->head; thejob; thejob = thejob->next)
1179 if (thejob->jobid >= newjob->jobid)
1180 newjob->jobid = thejob->jobid + 1;
1181
1182 /* add thejob to the list of running jobs */
1183 if (!j_list->head) {
1184 thejob = j_list->head = xmalloc(sizeof(*thejob));
1185 } else {
1186 for (thejob = j_list->head; thejob->next; thejob = thejob->next) /* nothing */;
1187 thejob->next = xmalloc(sizeof(*thejob));
1188 thejob = thejob->next;
1189 }
1190
1191 *thejob = *newjob; /* physically copy the struct job */
1192 thejob->next = NULL;
1193 thejob->running_progs = thejob->num_progs;
1194 thejob->stopped_progs = 0;
1195
1196#if ENABLE_LASH_JOB_CONTROL
1197 if (inbg) {
1198 /* we don't wait for background thejobs to return -- append it
1199 to the list of backgrounded thejobs and leave it alone */
1200 printf("[%d] %d\n", thejob->jobid,
1201 newjob->progs[newjob->num_progs - 1].pid);
1202 last_jobid = newjob->jobid;
1203 last_bg_pid = newjob->progs[newjob->num_progs - 1].pid;
1204 } else {
1205 newjob->job_list->fg = thejob;
1206
1207 /* move the new process group into the foreground */
1208 /* Ignore errors since child could have already exited */
1209 tcsetpgrp(shell_terminal, newjob->pgrp);
1210 }
1211#endif
1212}
1213
1214static int run_command(struct job *newjob, int inbg, int outpipe[2])
1215{
1216 /* struct job *thejob; */
1217 int i;
1218 int nextin, nextout;
1219 int pipefds[2]; /* pipefd[0] is for reading */
1220 const struct built_in_command *x;
1221 struct child_prog *child;
1222
1223 nextin = 0;
1224 for (i = 0; i < newjob->num_progs; i++) {
1225 child = &(newjob->progs[i]);
1226
1227 nextout = 1;
1228 if ((i + 1) < newjob->num_progs) {
1229 xpipe(pipefds);
1230 nextout = pipefds[1];
1231 } else if (outpipe[1] != -1) {
1232 nextout = outpipe[1];
1233 }
1234
1235 /* Check if the command matches any non-forking builtins,
1236 * but only if this is a simple command.
1237 * Non-forking builtins within pipes have to fork anyway,
1238 * and are handled in pseudo_exec. "echo foo | read bar"
1239 * is doomed to failure, and doesn't work on bash, either.
1240 */
1241 if (newjob->num_progs == 1) {
1242 int rcode;
1243 int squirrel[] = {-1, -1, -1};
1244
1245 /* Check if the command sets an environment variable. */
1246 if (strchr(child->argv[0], '=') != NULL) {
1247 child->argv[1] = child->argv[0];
1248 return builtin_export(child);
1249 }
1250
1251 for (x = bltins; x <= &VEC_LAST(bltins); x++) {
1252 if (strcmp(child->argv[0], x->cmd) == 0) {
1253 setup_redirects(child, squirrel);
1254 rcode = x->function(child);
1255 restore_redirects(squirrel);
1256 return rcode;
1257 }
1258 }
1259#if ENABLE_FEATURE_SH_STANDALONE
1260 {
1261 int a = find_applet_by_name(child->argv[i]);
1262 if (a >= 0 && APPLET_IS_NOFORK(a)) {
1263 setup_redirects(child, squirrel);
1264 rcode = run_nofork_applet(a, child->argv + i);
1265 restore_redirects(squirrel);
1266 return rcode;
1267 }
1268 }
1269#endif
1270 }
1271
1272#if BB_MMU
1273 child->pid = fork();
1274#else
1275 child->pid = vfork();
1276#endif
1277 if (!child->pid) {
1278 /* Set the handling for job control signals back to the default. */
1279 signal(SIGINT, SIG_DFL);
1280 signal(SIGQUIT, SIG_DFL);
1281 signal(SIGTSTP, SIG_DFL);
1282 signal(SIGTTIN, SIG_DFL);
1283 signal(SIGTTOU, SIG_DFL);
1284 signal(SIGCHLD, SIG_DFL);
1285
1286 /* Close all open filehandles. */
1287 while (close_me_list)
1288 close((long)llist_pop(&close_me_list));
1289
1290 if (outpipe[1] != -1) {
1291 close(outpipe[0]);
1292 }
1293 if (nextin != 0) {
1294 dup2(nextin, 0);
1295 close(nextin);
1296 }
1297
1298 if (nextout != 1) {
1299 dup2(nextout, 1);
1300 dup2(nextout, 2); /* Really? */
1301 close(nextout);
1302 close(pipefds[0]);
1303 }
1304
1305 /* explicit redirects override pipes */
1306 setup_redirects(child,NULL);
1307
1308 pseudo_exec(child);
1309 }
1310 if (outpipe[1] != -1) {
1311 close(outpipe[1]);
1312 }
1313
1314 /* put our child in the process group whose leader is the
1315 first process in this pipe */
1316 setpgid(child->pid, newjob->progs[0].pid);
1317 if (nextin != 0)
1318 close(nextin);
1319 if (nextout != 1)
1320 close(nextout);
1321
1322 /* If there isn't another process, nextin is garbage
1323 but it doesn't matter */
1324 nextin = pipefds[0];
1325 }
1326
1327 newjob->pgrp = newjob->progs[0].pid;
1328
1329 insert_job(newjob, inbg);
1330
1331 return 0;
1332}
1333
1334static int busy_loop(FILE *input)
1335{
1336 char *command;
1337 char *next_command = NULL;
1338 struct job newjob;
1339 int i;
1340 int inbg = 0;
1341 int status;
1342#if ENABLE_LASH_JOB_CONTROL
1343 pid_t parent_pgrp;
1344 /* save current owner of TTY so we can restore it on exit */
1345 parent_pgrp = tcgetpgrp(shell_terminal);
1346#endif
1347 newjob.job_list = &job_list;
1348 newjob.job_context = DEFAULT_CONTEXT;
1349
1350 command = xzalloc(BUFSIZ);
1351
1352 while (1) {
1353 if (!job_list.fg) {
1354 /* no job is in the foreground */
1355
1356 /* see if any background processes have exited */
1357 checkjobs(&job_list);
1358
1359 if (!next_command) {
1360 if (get_command_bufsiz(input, command))
1361 break;
1362 next_command = command;
1363 }
1364
1365 if (!expand_arguments(next_command)) {
1366 free(command);
1367 command = xzalloc(BUFSIZ);
1368 next_command = NULL;
1369 continue;
1370 }
1371
1372 if (!parse_command(&next_command, &newjob, &inbg)
1373 && newjob.num_progs
1374 ) {
1375 int pipefds[2] = { -1, -1 };
1376 debug_printf("job=%p fed to run_command by busy_loop()'\n",
1377 &newjob);
1378 run_command(&newjob, inbg, pipefds);
1379 }
1380 else {
1381 free(command);
1382 command = xzalloc(BUFSIZ);
1383 next_command = NULL;
1384 }
1385 } else {
1386 /* a job is running in the foreground; wait for it */
1387 i = 0;
1388 while (!job_list.fg->progs[i].pid ||
1389 job_list.fg->progs[i].is_stopped == 1) i++;
1390
1391 if (waitpid(job_list.fg->progs[i].pid, &status, WUNTRACED) < 0) {
1392 if (errno != ECHILD) {
1393 bb_perror_msg_and_die("waitpid(%d)", job_list.fg->progs[i].pid);
1394 }
1395 }
1396
1397 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1398 /* the child exited */
1399 job_list.fg->running_progs--;
1400 job_list.fg->progs[i].pid = 0;
1401
1402 last_return_code = WEXITSTATUS(status);
1403
1404 if (!job_list.fg->running_progs) {
1405 /* child exited */
1406 remove_job(&job_list, job_list.fg);
1407 job_list.fg = NULL;
1408 }
1409 }
1410#if ENABLE_LASH_JOB_CONTROL
1411 else {
1412 /* the child was stopped */
1413 job_list.fg->stopped_progs++;
1414 job_list.fg->progs[i].is_stopped = 1;
1415
1416 if (job_list.fg->stopped_progs == job_list.fg->running_progs) {
1417 printf("\n" JOB_STATUS_FORMAT, job_list.fg->jobid,
1418 "Stopped", job_list.fg->text);
1419 job_list.fg = NULL;
1420 }
1421 }
1422
1423 if (!job_list.fg) {
1424 /* move the shell to the foreground */
1425 /* suppress messages when run from /linuxrc mag@sysgo.de */
1426 if (tcsetpgrp(shell_terminal, getpgrp()) && errno != ENOTTY)
1427 bb_perror_msg("tcsetpgrp");
1428 }
1429#endif
1430 }
1431 }
1432 free(command);
1433
1434#if ENABLE_LASH_JOB_CONTROL
1435 /* return controlling TTY back to parent process group before exiting */
1436 if (tcsetpgrp(shell_terminal, parent_pgrp) && errno != ENOTTY)
1437 bb_perror_msg("tcsetpgrp");
1438#endif
1439
1440 /* return exit status if called with "-c" */
1441 if (input == NULL && WIFEXITED(status))
1442 return WEXITSTATUS(status);
1443
1444 return 0;
1445}
1446
1447#if ENABLE_FEATURE_CLEAN_UP
1448static void free_memory(void)
1449{
1450 free(cwd);
1451
1452 if (job_list.fg && !job_list.fg->running_progs) {
1453 remove_job(&job_list, job_list.fg);
1454 }
1455}
1456#else
1457void free_memory(void);
1458#endif
1459
1460#if ENABLE_LASH_JOB_CONTROL
1461/* Make sure we have a controlling tty. If we get started under a job
1462 * aware app (like bash for example), make sure we are now in charge so
1463 * we don't fight over who gets the foreground */
1464static void setup_job_control(void)
1465{
1466 int status;
1467 pid_t shell_pgrp;
1468
1469 /* Loop until we are in the foreground. */
1470 while ((status = tcgetpgrp(shell_terminal)) >= 0) {
1471 shell_pgrp = getpgrp();
1472 if (status == shell_pgrp) {
1473 break;
1474 }
1475 kill(- shell_pgrp, SIGTTIN);
1476 }
1477
1478 /* Ignore interactive and job-control signals. */
1479 signal(SIGINT, SIG_IGN);
1480 signal(SIGQUIT, SIG_IGN);
1481 signal(SIGTSTP, SIG_IGN);
1482 signal(SIGTTIN, SIG_IGN);
1483 signal(SIGTTOU, SIG_IGN);
1484 signal(SIGCHLD, SIG_IGN);
1485
1486 /* Put ourselves in our own process group. */
1487 setsid();
1488 shell_pgrp = getpid();
1489 setpgid(shell_pgrp, shell_pgrp);
1490
1491 /* Grab control of the terminal. */
1492 tcsetpgrp(shell_terminal, shell_pgrp);
1493}
1494#else
1495static inline void setup_job_control(void)
1496{
1497}
1498#endif
1499
1500int lash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1501int lash_main(int argc, char **argv)
1502{
1503 unsigned opt;
1504 FILE *input = stdin;
1505
1506 global_argc = argc;
1507 global_argv = argv;
1508
1509#if ENABLE_FEATURE_EDITING
1510 line_input_state = new_line_input_t(FOR_SHELL);
1511#endif
1512
1513 /* These variables need re-initializing when recursing */
1514 last_jobid = 0;
1515 close_me_list = NULL;
1516 job_list.head = NULL;
1517 job_list.fg = NULL;
1518 last_return_code = 1;
1519
1520 if (global_argv[0] && global_argv[0][0] == '-') {
1521 FILE *prof_input;
1522 prof_input = fopen_for_read("/etc/profile");
1523 if (prof_input) {
1524 llist_add_to(&close_me_list, (void *)(long)fileno(prof_input));
1525 /* Now run the file */
1526 busy_loop(prof_input);
1527 fclose_if_not_stdin(prof_input);
1528 llist_pop(&close_me_list);
1529 }
1530 }
1531
1532 opt = getopt32(argv, "+ic:", &local_pending_command);
1533#define LASH_OPT_i (1<<0)
1534#define LASH_OPT_c (1<<1)
1535 if (opt & LASH_OPT_c) {
1536 input = NULL;
1537 optind++;
1538 global_argv += optind;
1539 }
1540 /* A shell is interactive if the `-i' flag was given, or if all of
1541 * the following conditions are met:
1542 * no -c command
1543 * no arguments remaining or the -s flag given
1544 * standard input is a terminal
1545 * standard output is a terminal
1546 * Refer to Posix.2, the description of the `sh' utility. */
1547 if (global_argv[optind] == NULL && input == stdin
1548 && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)
1549 ) {
1550 opt |= LASH_OPT_i;
1551 }
1552 setup_job_control();
1553 if (opt & LASH_OPT_i) {
1554 /* Looks like they want an interactive shell */
1555 if (!ENABLE_FEATURE_SH_EXTRA_QUIET) {
1556 printf("\n\n%s built-in shell (lash)\n"
1557 "Enter 'help' for a list of built-in commands.\n\n",
1558 bb_banner);
1559 }
1560 } else if (!local_pending_command && global_argv[optind]) {
1561 //printf( "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1562 input = xfopen_for_read(global_argv[optind]);
1563 /* be lazy, never mark this closed */
1564 llist_add_to(&close_me_list, (void *)(long)fileno(input));
1565 }
1566
1567 /* initialize the cwd -- this is never freed...*/
1568 update_cwd();
1569
1570 if (ENABLE_FEATURE_CLEAN_UP) atexit(free_memory);
1571
1572 if (ENABLE_FEATURE_EDITING) cmdedit_set_initial_prompt();
1573 else PS1 = NULL;
1574
1575 return busy_loop(input);
1576}