aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--shell/hush.c711
-rw-r--r--shell/hush_leaktool.sh2
2 files changed, 358 insertions, 355 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 0b92c29d4..e64cc476d 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -244,7 +244,6 @@ typedef enum {
244 PIPE_BG = 4, 244 PIPE_BG = 4,
245} pipe_style; 245} pipe_style;
246 246
247/* might eventually control execution */
248typedef enum { 247typedef enum {
249 RES_NONE = 0, 248 RES_NONE = 0,
250#if ENABLE_HUSH_IF 249#if ENABLE_HUSH_IF
@@ -265,6 +264,7 @@ typedef enum {
265 RES_XXXX = 12, 264 RES_XXXX = 12,
266 RES_SNTX = 13 265 RES_SNTX = 13
267} reserved_style; 266} reserved_style;
267
268/* This holds pointers to the various results of parsing */ 268/* This holds pointers to the various results of parsing */
269struct p_context { 269struct p_context {
270 struct child_prog *child; 270 struct child_prog *child;
@@ -298,6 +298,7 @@ struct child_prog {
298 * and on execution these are substituted with their values. 298 * and on execution these are substituted with their values.
299 * Substitution can make _several_ words out of one argv[n]! 299 * Substitution can make _several_ words out of one argv[n]!
300 * Example: argv[0]=='.^C*^C.' here: echo .$*. 300 * Example: argv[0]=='.^C*^C.' here: echo .$*.
301 * References of the form ^C`cmd arg^C are `cmd arg` substitutions.
301 */ 302 */
302 303
303struct pipe { 304struct pipe {
@@ -341,8 +342,8 @@ typedef struct {
341 smallint nonnull; 342 smallint nonnull;
342 smallint has_empty_slot; 343 smallint has_empty_slot;
343} o_string; 344} o_string;
345/* Used for initialization: o_string foo = NULL_O_STRING; */
344#define NULL_O_STRING { NULL } 346#define NULL_O_STRING { NULL }
345/* used for initialization: o_string foo = NULL_O_STRING; */
346 347
347/* I can almost use ordinary FILE *. Is open_memstream() universally 348/* I can almost use ordinary FILE *. Is open_memstream() universally
348 * available? Where is it documented? */ 349 * available? Where is it documented? */
@@ -572,7 +573,7 @@ static int glob_needed(const char *s)
572 return 0; 573 return 0;
573} 574}
574 575
575static char **add_strings_to_strings(int need_xstrdup, char **strings, char **add) 576static char **add_malloced_strings_to_strings(char **strings, char **add)
576{ 577{
577 int i; 578 int i;
578 unsigned count1; 579 unsigned count1;
@@ -597,19 +598,18 @@ static char **add_strings_to_strings(int need_xstrdup, char **strings, char **ad
597 v[count1 + count2] = NULL; 598 v[count1 + count2] = NULL;
598 i = count2; 599 i = count2;
599 while (--i >= 0) 600 while (--i >= 0)
600 v[count1 + i] = need_xstrdup ? xstrdup(add[i]) : add[i]; 601 v[count1 + i] = add[i];
601 return v; 602 return v;
602} 603}
603 604
604/* 'add' should be a malloced pointer */ 605static char **add_malloced_string_to_strings(char **strings, char *add)
605static char **add_string_to_strings(char **strings, char *add)
606{ 606{
607 char *v[2]; 607 char *v[2];
608 608
609 v[0] = add; 609 v[0] = add;
610 v[1] = NULL; 610 v[1] = NULL;
611 611
612 return add_strings_to_strings(0, strings, v); 612 return add_malloced_strings_to_strings(strings, v);
613} 613}
614 614
615static void free_strings(char **strings) 615static void free_strings(char **strings)
@@ -713,6 +713,7 @@ static const struct built_in_command bltins[] = {
713#endif 713#endif
714}; 714};
715 715
716
716/* Signals are grouped, we handle them in batches */ 717/* Signals are grouped, we handle them in batches */
717static void set_misc_sighandler(void (*handler)(int)) 718static void set_misc_sighandler(void (*handler)(int))
718{ 719{
@@ -846,333 +847,6 @@ static const char *set_cwd(void)
846} 847}
847 848
848 849
849/* built-in 'true' handler */
850static int builtin_true(char **argv ATTRIBUTE_UNUSED)
851{
852 return 0;
853}
854
855/* built-in 'test' handler */
856static int builtin_test(char **argv)
857{
858 int argc = 0;
859 while (*argv) {
860 argc++;
861 argv++;
862 }
863 return test_main(argc, argv - argc);
864}
865
866/* built-in 'test' handler */
867static int builtin_echo(char **argv)
868{
869 int argc = 0;
870 while (*argv) {
871 argc++;
872 argv++;
873 }
874 return echo_main(argc, argv - argc);
875}
876
877/* built-in 'eval' handler */
878static int builtin_eval(char **argv)
879{
880 int rcode = EXIT_SUCCESS;
881
882 if (argv[1]) {
883 char *str = expand_strvec_to_string(argv + 1);
884 parse_and_run_string(str, PARSEFLAG_EXIT_FROM_LOOP);
885 free(str);
886 rcode = last_return_code;
887 }
888 return rcode;
889}
890
891/* built-in 'cd <path>' handler */
892static int builtin_cd(char **argv)
893{
894 const char *newdir;
895 if (argv[1] == NULL) {
896 // bash does nothing (exitcode 0) if HOME is ""; if it's unset,
897 // bash says "bash: cd: HOME not set" and does nothing (exitcode 1)
898 newdir = getenv("HOME") ? : "/";
899 } else
900 newdir = argv[1];
901 if (chdir(newdir)) {
902 printf("cd: %s: %s\n", newdir, strerror(errno));
903 return EXIT_FAILURE;
904 }
905 set_cwd();
906 return EXIT_SUCCESS;
907}
908
909/* built-in 'exec' handler */
910static int builtin_exec(char **argv)
911{
912 if (argv[1] == NULL)
913 return EXIT_SUCCESS; /* bash does this */
914 {
915#if !BB_MMU
916 char **ptrs2free = alloc_ptrs(argv);
917#endif
918// FIXME: if exec fails, bash does NOT exit! We do...
919 pseudo_exec_argv(ptrs2free, argv + 1);
920 /* never returns */
921 }
922}
923
924/* built-in 'exit' handler */
925static int builtin_exit(char **argv)
926{
927// TODO: bash does it ONLY on top-level sh exit (+interacive only?)
928 //puts("exit"); /* bash does it */
929// TODO: warn if we have background jobs: "There are stopped jobs"
930// On second consecutive 'exit', exit anyway.
931 if (argv[1] == NULL)
932 hush_exit(last_return_code);
933 /* mimic bash: exit 123abc == exit 255 + error msg */
934 xfunc_error_retval = 255;
935 /* bash: exit -2 == exit 254, no error msg */
936 hush_exit(xatoi(argv[1]) & 0xff);
937}
938
939/* built-in 'export VAR=value' handler */
940static int builtin_export(char **argv)
941{
942 const char *value;
943 char *name = argv[1];
944
945 if (name == NULL) {
946 // TODO:
947 // ash emits: export VAR='VAL'
948 // bash: declare -x VAR="VAL"
949 // (both also escape as needed (quotes, $, etc))
950 char **e = environ;
951 if (e)
952 while (*e)
953 puts(*e++);
954 return EXIT_SUCCESS;
955 }
956
957 value = strchr(name, '=');
958 if (!value) {
959 /* They are exporting something without a =VALUE */
960 struct variable *var;
961
962 var = get_local_var(name);
963 if (var) {
964 var->flg_export = 1;
965 putenv(var->varstr);
966 }
967 /* bash does not return an error when trying to export
968 * an undefined variable. Do likewise. */
969 return EXIT_SUCCESS;
970 }
971
972 set_local_var(xstrdup(name), 1);
973 return EXIT_SUCCESS;
974}
975
976#if ENABLE_HUSH_JOB
977/* built-in 'fg' and 'bg' handler */
978static int builtin_fg_bg(char **argv)
979{
980 int i, jobnum;
981 struct pipe *pi;
982
983 if (!interactive_fd)
984 return EXIT_FAILURE;
985 /* If they gave us no args, assume they want the last backgrounded task */
986 if (!argv[1]) {
987 for (pi = job_list; pi; pi = pi->next) {
988 if (pi->jobid == last_jobid) {
989 goto found;
990 }
991 }
992 bb_error_msg("%s: no current job", argv[0]);
993 return EXIT_FAILURE;
994 }
995 if (sscanf(argv[1], "%%%d", &jobnum) != 1) {
996 bb_error_msg("%s: bad argument '%s'", argv[0], argv[1]);
997 return EXIT_FAILURE;
998 }
999 for (pi = job_list; pi; pi = pi->next) {
1000 if (pi->jobid == jobnum) {
1001 goto found;
1002 }
1003 }
1004 bb_error_msg("%s: %d: no such job", argv[0], jobnum);
1005 return EXIT_FAILURE;
1006 found:
1007 // TODO: bash prints a string representation
1008 // of job being foregrounded (like "sleep 1 | cat")
1009 if (*argv[0] == 'f') {
1010 /* Put the job into the foreground. */
1011 tcsetpgrp(interactive_fd, pi->pgrp);
1012 }
1013
1014 /* Restart the processes in the job */
1015 debug_printf_jobs("reviving %d procs, pgrp %d\n", pi->num_progs, pi->pgrp);
1016 for (i = 0; i < pi->num_progs; i++) {
1017 debug_printf_jobs("reviving pid %d\n", pi->progs[i].pid);
1018 pi->progs[i].is_stopped = 0;
1019 }
1020 pi->stopped_progs = 0;
1021
1022 i = kill(- pi->pgrp, SIGCONT);
1023 if (i < 0) {
1024 if (errno == ESRCH) {
1025 delete_finished_bg_job(pi);
1026 return EXIT_SUCCESS;
1027 } else {
1028 bb_perror_msg("kill (SIGCONT)");
1029 }
1030 }
1031
1032 if (*argv[0] == 'f') {
1033 remove_bg_job(pi);
1034 return checkjobs_and_fg_shell(pi);
1035 }
1036 return EXIT_SUCCESS;
1037}
1038#endif
1039
1040/* built-in 'help' handler */
1041#if ENABLE_HUSH_HELP
1042static int builtin_help(char **argv ATTRIBUTE_UNUSED)
1043{
1044 const struct built_in_command *x;
1045
1046 printf("\nBuilt-in commands:\n");
1047 printf("-------------------\n");
1048 for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) {
1049 printf("%s\t%s\n", x->cmd, x->descr);
1050 }
1051 printf("\n\n");
1052 return EXIT_SUCCESS;
1053}
1054#endif
1055
1056#if ENABLE_HUSH_JOB
1057/* built-in 'jobs' handler */
1058static int builtin_jobs(char **argv ATTRIBUTE_UNUSED)
1059{
1060 struct pipe *job;
1061 const char *status_string;
1062
1063 for (job = job_list; job; job = job->next) {
1064 if (job->running_progs == job->stopped_progs)
1065 status_string = "Stopped";
1066 else
1067 status_string = "Running";
1068
1069 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext);
1070 }
1071 return EXIT_SUCCESS;
1072}
1073#endif
1074
1075/* built-in 'pwd' handler */
1076static int builtin_pwd(char **argv ATTRIBUTE_UNUSED)
1077{
1078 puts(set_cwd());
1079 return EXIT_SUCCESS;
1080}
1081
1082/* built-in 'read VAR' handler */
1083static int builtin_read(char **argv)
1084{
1085 char *string;
1086 const char *name = argv[1] ? argv[1] : "REPLY";
1087
1088 string = xmalloc_reads(STDIN_FILENO, xasprintf("%s=", name), NULL);
1089 return set_local_var(string, 0);
1090}
1091
1092/* built-in 'set [VAR=value]' handler */
1093static int builtin_set(char **argv)
1094{
1095 char *temp = argv[1];
1096 struct variable *e;
1097
1098 if (temp == NULL)
1099 for (e = top_var; e; e = e->next)
1100 puts(e->varstr);
1101 else
1102 set_local_var(xstrdup(temp), 0);
1103
1104 return EXIT_SUCCESS;
1105}
1106
1107
1108/* Built-in 'shift' handler */
1109static int builtin_shift(char **argv)
1110{
1111 int n = 1;
1112 if (argv[1]) {
1113 n = atoi(argv[1]);
1114 }
1115 if (n >= 0 && n < global_argc) {
1116 global_argv[n] = global_argv[0];
1117 global_argc -= n;
1118 global_argv += n;
1119 return EXIT_SUCCESS;
1120 }
1121 return EXIT_FAILURE;
1122}
1123
1124/* Built-in '.' handler (read-in and execute commands from file) */
1125static int builtin_source(char **argv)
1126{
1127 FILE *input;
1128 int status;
1129
1130 if (argv[1] == NULL)
1131 return EXIT_FAILURE;
1132
1133 /* XXX search through $PATH is missing */
1134 input = fopen(argv[1], "r");
1135 if (!input) {
1136 bb_error_msg("cannot open '%s'", argv[1]);
1137 return EXIT_FAILURE;
1138 }
1139 close_on_exec_on(fileno(input));
1140
1141 /* Now run the file */
1142 /* XXX argv and argc are broken; need to save old global_argv
1143 * (pointer only is OK!) on this stack frame,
1144 * set global_argv=argv+1, recurse, and restore. */
1145 status = parse_and_run_file(input);
1146 fclose(input);
1147 return status;
1148}
1149
1150static int builtin_umask(char **argv)
1151{
1152 mode_t new_umask;
1153 const char *arg = argv[1];
1154 char *end;
1155 if (arg) {
1156 new_umask = strtoul(arg, &end, 8);
1157 if (*end != '\0' || end == arg) {
1158 return EXIT_FAILURE;
1159 }
1160 } else {
1161 new_umask = umask(0);
1162 printf("%.3o\n", (unsigned) new_umask);
1163 }
1164 umask(new_umask);
1165 return EXIT_SUCCESS;
1166}
1167
1168/* built-in 'unset VAR' handler */
1169static int builtin_unset(char **argv)
1170{
1171 /* bash always returns true */
1172 unset_local_var(argv[1]);
1173 return EXIT_SUCCESS;
1174}
1175
1176/* 850/*
1177 * o_string support 851 * o_string support
1178 */ 852 */
@@ -1282,7 +956,7 @@ static void o_addQstr(o_string *o, const char *str, int len)
1282 956
1283/* A special kind of o_string for $VAR and `cmd` expansion. 957/* A special kind of o_string for $VAR and `cmd` expansion.
1284 * It contains char* list[] at the beginning, which is grown in 16 element 958 * It contains char* list[] at the beginning, which is grown in 16 element
1285 * increments. Actual string data starts at the next multiple of 16. 959 * increments. Actual string data starts at the next multiple of 16 * (char*).
1286 * list[i] contains an INDEX (int!) into this string data. 960 * list[i] contains an INDEX (int!) into this string data.
1287 * It means that if list[] needs to grow, data needs to be moved higher up 961 * It means that if list[] needs to grow, data needs to be moved higher up
1288 * but list[i]'s need not be modified. 962 * but list[i]'s need not be modified.
@@ -1355,7 +1029,8 @@ static int o_get_last_ptr(o_string *o, int n)
1355 return ((int)list[n-1]) + string_start; 1029 return ((int)list[n-1]) + string_start;
1356} 1030}
1357 1031
1358/* Convert every \x to x in-place, return ptr past NUL. */ 1032/* o_glob performs globbing on last list[], saving each result
1033 * as a new list[]. unbackslash() is just a helper */
1359static char *unbackslash(char *src) 1034static char *unbackslash(char *src)
1360{ 1035{
1361 char *dst = src; 1036 char *dst = src;
@@ -1367,7 +1042,6 @@ static char *unbackslash(char *src)
1367 } 1042 }
1368 return dst; 1043 return dst;
1369} 1044}
1370
1371static int o_glob(o_string *o, int n) 1045static int o_glob(o_string *o, int n)
1372{ 1046{
1373 glob_t globdata; 1047 glob_t globdata;
@@ -1416,8 +1090,8 @@ static int o_glob(o_string *o, int n)
1416 return n; 1090 return n;
1417} 1091}
1418 1092
1419/* o_save_ptr_helper + but glob the string so far remembered 1093/* If o->o_glob == 1, glob the string so far remembered.
1420 * if o->o_glob == 1 */ 1094 * Otherwise, just finish current list[] and start new */
1421static int o_save_ptr(o_string *o, int n) 1095static int o_save_ptr(o_string *o, int n)
1422{ 1096{
1423 if (o->o_glob) 1097 if (o->o_glob)
@@ -1462,6 +1136,7 @@ static int static_peek(struct in_str *i)
1462} 1136}
1463 1137
1464#if ENABLE_HUSH_INTERACTIVE 1138#if ENABLE_HUSH_INTERACTIVE
1139
1465#if ENABLE_FEATURE_EDITING 1140#if ENABLE_FEATURE_EDITING
1466static void cmdedit_set_initial_prompt(void) 1141static void cmdedit_set_initial_prompt(void)
1467{ 1142{
@@ -1521,6 +1196,7 @@ static void get_user_input(struct in_str *i)
1521#endif 1196#endif
1522 i->p = user_input_buf; 1197 i->p = user_input_buf;
1523} 1198}
1199
1524#endif /* INTERACTIVE */ 1200#endif /* INTERACTIVE */
1525 1201
1526/* This is the magic location that prints prompts 1202/* This is the magic location that prints prompts
@@ -1604,6 +1280,7 @@ static void setup_string_in_str(struct in_str *i, const char *s)
1604 i->eof_flag = 0; 1280 i->eof_flag = 0;
1605} 1281}
1606 1282
1283
1607/* squirrel != NULL means we squirrel away copies of stdin, stdout, 1284/* squirrel != NULL means we squirrel away copies of stdin, stdout,
1608 * and stderr if they are redirected. */ 1285 * and stderr if they are redirected. */
1609static int setup_redirects(struct child_prog *prog, int squirrel[]) 1286static int setup_redirects(struct child_prog *prog, int squirrel[])
@@ -1659,6 +1336,7 @@ static void restore_redirects(int squirrel[])
1659 } 1336 }
1660} 1337}
1661 1338
1339
1662/* Called after [v]fork() in run_pipe(), or from builtin_exec(). 1340/* Called after [v]fork() in run_pipe(), or from builtin_exec().
1663 * Never returns. 1341 * Never returns.
1664 * XXX no exit() here. If you don't exec, use _exit instead. 1342 * XXX no exit() here. If you don't exec, use _exit instead.
@@ -2589,6 +2267,7 @@ static int run_and_free_list(struct pipe *pi)
2589 return rcode; 2267 return rcode;
2590} 2268}
2591 2269
2270
2592/* expand_strvec_to_strvec() takes a list of strings, expands 2271/* expand_strvec_to_strvec() takes a list of strings, expands
2593 * all variable references within and returns a pointer to 2272 * all variable references within and returns a pointer to
2594 * a list of expanded strings, possibly with larger number 2273 * a list of expanded strings, possibly with larger number
@@ -2737,7 +2416,9 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
2737 } else 2416 } else
2738 val = lookup_param(arg); 2417 val = lookup_param(arg);
2739 arg[0] = first_ch; 2418 arg[0] = first_ch;
2419#if ENABLE_HUSH_TICK
2740 store_val: 2420 store_val:
2421#endif
2741 *p = SPECIAL_VAR_SYMBOL; 2422 *p = SPECIAL_VAR_SYMBOL;
2742 if (!(first_ch & 0x80)) { /* unquoted $VAR */ 2423 if (!(first_ch & 0x80)) { /* unquoted $VAR */
2743 if (val) { 2424 if (val) {
@@ -2801,7 +2482,7 @@ static char **expand_strvec_to_strvec(char **argv)
2801 return expand_variables(argv, 0x100); 2482 return expand_variables(argv, 0x100);
2802} 2483}
2803 2484
2804/* used for expansion of right hand of assignments */ 2485/* Used for expansion of right hand of assignments */
2805/* NB: should NOT do globbing! "export v=/bin/c*; env | grep ^v=" outputs 2486/* NB: should NOT do globbing! "export v=/bin/c*; env | grep ^v=" outputs
2806 * "v=/bin/c*" */ 2487 * "v=/bin/c*" */
2807static char *expand_string_to_string(const char *str) 2488static char *expand_string_to_string(const char *str)
@@ -2820,7 +2501,7 @@ static char *expand_string_to_string(const char *str)
2820 return (char*)list; 2501 return (char*)list;
2821} 2502}
2822 2503
2823/* used for eval */ 2504/* Used for "eval" builtin */
2824static char* expand_strvec_to_string(char **argv) 2505static char* expand_strvec_to_string(char **argv)
2825{ 2506{
2826 char **list; 2507 char **list;
@@ -2842,7 +2523,8 @@ static char* expand_strvec_to_string(char **argv)
2842 return (char*)list; 2523 return (char*)list;
2843} 2524}
2844 2525
2845/* This is used to get/check local shell variables */ 2526
2527/* Used to get/check local shell variables */
2846static struct variable *get_local_var(const char *name) 2528static struct variable *get_local_var(const char *name)
2847{ 2529{
2848 struct variable *cur; 2530 struct variable *cur;
@@ -3099,12 +2781,16 @@ static int reserved_word(const o_string *word, struct p_context *ctx)
3099 continue; 2781 continue;
3100 debug_printf("found reserved word %s, res %d\n", r->literal, r->res); 2782 debug_printf("found reserved word %s, res %d\n", r->literal, r->res);
3101 if (r->flag == 0) { /* '!' */ 2783 if (r->flag == 0) { /* '!' */
2784#if ENABLE_HUSH_LOOPS
3102 if (ctx->res_w == RES_IN) { 2785 if (ctx->res_w == RES_IN) {
3103 /* 'for a in ! a b c; ...' - ! isn't a keyword here */ 2786 /* 'for a in ! a b c; ...' - ! isn't a keyword here */
3104 break; 2787 break;
3105 } 2788 }
3106 if (ctx->res_w == RES_FOR /* example: 'for ! a' */ 2789#endif
3107 || ctx->ctx_inverted /* bash doesn't accept '! ! true' */ 2790 if (ctx->ctx_inverted /* bash doesn't accept '! ! true' */
2791#if ENABLE_HUSH_LOOPS
2792 || ctx->res_w == RES_FOR /* example: 'for ! a' */
2793#endif
3108 ) { 2794 ) {
3109 syntax(NULL); 2795 syntax(NULL);
3110 ctx->res_w = RES_SNTX; 2796 ctx->res_w = RES_SNTX;
@@ -3152,8 +2838,7 @@ static int reserved_word(const o_string *word, struct p_context *ctx)
3152#endif 2838#endif
3153 2839
3154/* Word is complete, look at it and update parsing context. 2840/* Word is complete, look at it and update parsing context.
3155 * Normal return is 0. 2841 * Normal return is 0. Syntax errors return 1. */
3156 * Syntax or xglob errors return 1. */
3157static int done_word(o_string *word, struct p_context *ctx) 2842static int done_word(o_string *word, struct p_context *ctx)
3158{ 2843{
3159 struct child_prog *child = ctx->child; 2844 struct child_prog *child = ctx->child;
@@ -3184,7 +2869,7 @@ static int done_word(o_string *word, struct p_context *ctx)
3184 } 2869 }
3185 2870
3186 if (word->length || word->nonnull) { 2871 if (word->length || word->nonnull) {
3187 *glob_target = add_string_to_strings(*glob_target, xstrdup(word->data)); 2872 *glob_target = add_malloced_string_to_strings(*glob_target, xstrdup(word->data));
3188 debug_print_strings("glob_target appended", *glob_target); 2873 debug_print_strings("glob_target appended", *glob_target);
3189 } 2874 }
3190 2875
@@ -3282,9 +2967,9 @@ static void done_pipe(struct p_context *ctx, pipe_style type)
3282 debug_printf_parse("done_pipe return\n"); 2967 debug_printf_parse("done_pipe return\n");
3283} 2968}
3284 2969
3285/* peek ahead in the in_str to find out if we have a "&n" construct, 2970/* Peek ahead in the in_str to find out if we have a "&n" construct,
3286 * as in "2>&1", that represents duplicating a file descriptor. 2971 * as in "2>&1", that represents duplicating a file descriptor.
3287 * returns either -2 (syntax error), -1 (no &), or the number found. 2972 * Return either -2 (syntax error), -1 (no &), or the number found.
3288 */ 2973 */
3289static int redirect_dup_num(struct in_str *input) 2974static int redirect_dup_num(struct in_str *input)
3290{ 2975{
@@ -3395,7 +3080,7 @@ static int process_command_subs(o_string *dest,
3395 3080
3396 initialize_context(&inner); 3081 initialize_context(&inner);
3397 3082
3398 /* recursion to generate command */ 3083 /* Recursion to generate command */
3399 retcode = parse_stream(&result, &inner, input, subst_end); 3084 retcode = parse_stream(&result, &inner, input, subst_end);
3400 if (retcode != 0) 3085 if (retcode != 0)
3401 return retcode; /* syntax error or EOF */ 3086 return retcode; /* syntax error or EOF */
@@ -3409,7 +3094,7 @@ static int process_command_subs(o_string *dest,
3409 close_on_exec_on(fileno(p)); 3094 close_on_exec_on(fileno(p));
3410 setup_file_in_str(&pipe_str, p); 3095 setup_file_in_str(&pipe_str, p);
3411 3096
3412 /* now send results of command back into original context */ 3097 /* Now send results of command back into original context */
3413 eol_cnt = 0; 3098 eol_cnt = 0;
3414 while ((ch = i_getch(&pipe_str)) != EOF) { 3099 while ((ch = i_getch(&pipe_str)) != EOF) {
3415 if (ch == '\n') { 3100 if (ch == '\n') {
@@ -3596,7 +3281,7 @@ static void add_till_closing_curly_brace(o_string *dest, struct in_str *input)
3596} 3281}
3597#endif /* ENABLE_HUSH_TICK */ 3282#endif /* ENABLE_HUSH_TICK */
3598 3283
3599/* return code: 0 for OK, 1 for syntax error */ 3284/* Return code: 0 for OK, 1 for syntax error */
3600static int handle_dollar(o_string *dest, struct in_str *input) 3285static int handle_dollar(o_string *dest, struct in_str *input)
3601{ 3286{
3602 int ch = i_peek(input); /* first character after the $ */ 3287 int ch = i_peek(input); /* first character after the $ */
@@ -3674,7 +3359,7 @@ static int handle_dollar(o_string *dest, struct in_str *input)
3674 return 0; 3359 return 0;
3675} 3360}
3676 3361
3677/* return code is 0 for normal exit, 1 for syntax error */ 3362/* Return code is 0 for normal exit, 1 for syntax error */
3678static int parse_stream(o_string *dest, struct p_context *ctx, 3363static int parse_stream(o_string *dest, struct p_context *ctx,
3679 struct in_str *input, const char *end_trigger) 3364 struct in_str *input, const char *end_trigger)
3680{ 3365{
@@ -3937,7 +3622,7 @@ static void update_charmap(void)
3937 set_in_charmap(ifs, CHAR_IFS); /* are ordinary if quoted */ 3622 set_in_charmap(ifs, CHAR_IFS); /* are ordinary if quoted */
3938} 3623}
3939 3624
3940/* most recursion does not come through here, the exception is 3625/* Most recursion does not come through here, the exception is
3941 * from builtin_source() and builtin_eval() */ 3626 * from builtin_source() and builtin_eval() */
3942static int parse_and_run_stream(struct in_str *inp, int parse_flag) 3627static int parse_and_run_stream(struct in_str *inp, int parse_flag)
3943{ 3628{
@@ -3975,7 +3660,9 @@ static int parse_and_run_stream(struct in_str *inp, int parse_flag)
3975 free_pipe_list(ctx.list_head, /* indent: */ 0); 3660 free_pipe_list(ctx.list_head, /* indent: */ 0);
3976 /* Discard all unprocessed line input, force prompt on */ 3661 /* Discard all unprocessed line input, force prompt on */
3977 inp->p = NULL; 3662 inp->p = NULL;
3663#if ENABLE_HUSH_INTERACTIVE
3978 inp->promptme = 1; 3664 inp->promptme = 1;
3665#endif
3979 } 3666 }
3980 o_free(&temp); 3667 o_free(&temp);
3981 /* loop on syntax errors, return on EOF: */ 3668 /* loop on syntax errors, return on EOF: */
@@ -4034,6 +3721,7 @@ static void setup_job_control(void)
4034} 3721}
4035#endif 3722#endif
4036 3723
3724
4037int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 3725int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
4038int hush_main(int argc, char **argv) 3726int hush_main(int argc, char **argv)
4039{ 3727{
@@ -4230,3 +3918,318 @@ int lash_main(int argc, char **argv)
4230 return hush_main(argc, argv); 3918 return hush_main(argc, argv);
4231} 3919}
4232#endif 3920#endif
3921
3922
3923/*
3924 * Built-ins
3925 */
3926static int builtin_true(char **argv ATTRIBUTE_UNUSED)
3927{
3928 return 0;
3929}
3930
3931static int builtin_test(char **argv)
3932{
3933 int argc = 0;
3934 while (*argv) {
3935 argc++;
3936 argv++;
3937 }
3938 return test_main(argc, argv - argc);
3939}
3940
3941static int builtin_echo(char **argv)
3942{
3943 int argc = 0;
3944 while (*argv) {
3945 argc++;
3946 argv++;
3947 }
3948 return echo_main(argc, argv - argc);
3949}
3950
3951static int builtin_eval(char **argv)
3952{
3953 int rcode = EXIT_SUCCESS;
3954
3955 if (argv[1]) {
3956 char *str = expand_strvec_to_string(argv + 1);
3957 parse_and_run_string(str, PARSEFLAG_EXIT_FROM_LOOP);
3958 free(str);
3959 rcode = last_return_code;
3960 }
3961 return rcode;
3962}
3963
3964static int builtin_cd(char **argv)
3965{
3966 const char *newdir;
3967 if (argv[1] == NULL) {
3968 // bash does nothing (exitcode 0) if HOME is ""; if it's unset,
3969 // bash says "bash: cd: HOME not set" and does nothing (exitcode 1)
3970 newdir = getenv("HOME") ? : "/";
3971 } else
3972 newdir = argv[1];
3973 if (chdir(newdir)) {
3974 printf("cd: %s: %s\n", newdir, strerror(errno));
3975 return EXIT_FAILURE;
3976 }
3977 set_cwd();
3978 return EXIT_SUCCESS;
3979}
3980
3981static int builtin_exec(char **argv)
3982{
3983 if (argv[1] == NULL)
3984 return EXIT_SUCCESS; /* bash does this */
3985 {
3986#if !BB_MMU
3987 char **ptrs2free = alloc_ptrs(argv);
3988#endif
3989// FIXME: if exec fails, bash does NOT exit! We do...
3990 pseudo_exec_argv(ptrs2free, argv + 1);
3991 /* never returns */
3992 }
3993}
3994
3995static int builtin_exit(char **argv)
3996{
3997// TODO: bash does it ONLY on top-level sh exit (+interacive only?)
3998 //puts("exit"); /* bash does it */
3999// TODO: warn if we have background jobs: "There are stopped jobs"
4000// On second consecutive 'exit', exit anyway.
4001 if (argv[1] == NULL)
4002 hush_exit(last_return_code);
4003 /* mimic bash: exit 123abc == exit 255 + error msg */
4004 xfunc_error_retval = 255;
4005 /* bash: exit -2 == exit 254, no error msg */
4006 hush_exit(xatoi(argv[1]) & 0xff);
4007}
4008
4009static int builtin_export(char **argv)
4010{
4011 const char *value;
4012 char *name = argv[1];
4013
4014 if (name == NULL) {
4015 // TODO:
4016 // ash emits: export VAR='VAL'
4017 // bash: declare -x VAR="VAL"
4018 // (both also escape as needed (quotes, $, etc))
4019 char **e = environ;
4020 if (e)
4021 while (*e)
4022 puts(*e++);
4023 return EXIT_SUCCESS;
4024 }
4025
4026 value = strchr(name, '=');
4027 if (!value) {
4028 /* They are exporting something without a =VALUE */
4029 struct variable *var;
4030
4031 var = get_local_var(name);
4032 if (var) {
4033 var->flg_export = 1;
4034 putenv(var->varstr);
4035 }
4036 /* bash does not return an error when trying to export
4037 * an undefined variable. Do likewise. */
4038 return EXIT_SUCCESS;
4039 }
4040
4041 set_local_var(xstrdup(name), 1);
4042 return EXIT_SUCCESS;
4043}
4044
4045#if ENABLE_HUSH_JOB
4046/* built-in 'fg' and 'bg' handler */
4047static int builtin_fg_bg(char **argv)
4048{
4049 int i, jobnum;
4050 struct pipe *pi;
4051
4052 if (!interactive_fd)
4053 return EXIT_FAILURE;
4054 /* If they gave us no args, assume they want the last backgrounded task */
4055 if (!argv[1]) {
4056 for (pi = job_list; pi; pi = pi->next) {
4057 if (pi->jobid == last_jobid) {
4058 goto found;
4059 }
4060 }
4061 bb_error_msg("%s: no current job", argv[0]);
4062 return EXIT_FAILURE;
4063 }
4064 if (sscanf(argv[1], "%%%d", &jobnum) != 1) {
4065 bb_error_msg("%s: bad argument '%s'", argv[0], argv[1]);
4066 return EXIT_FAILURE;
4067 }
4068 for (pi = job_list; pi; pi = pi->next) {
4069 if (pi->jobid == jobnum) {
4070 goto found;
4071 }
4072 }
4073 bb_error_msg("%s: %d: no such job", argv[0], jobnum);
4074 return EXIT_FAILURE;
4075 found:
4076 // TODO: bash prints a string representation
4077 // of job being foregrounded (like "sleep 1 | cat")
4078 if (*argv[0] == 'f') {
4079 /* Put the job into the foreground. */
4080 tcsetpgrp(interactive_fd, pi->pgrp);
4081 }
4082
4083 /* Restart the processes in the job */
4084 debug_printf_jobs("reviving %d procs, pgrp %d\n", pi->num_progs, pi->pgrp);
4085 for (i = 0; i < pi->num_progs; i++) {
4086 debug_printf_jobs("reviving pid %d\n", pi->progs[i].pid);
4087 pi->progs[i].is_stopped = 0;
4088 }
4089 pi->stopped_progs = 0;
4090
4091 i = kill(- pi->pgrp, SIGCONT);
4092 if (i < 0) {
4093 if (errno == ESRCH) {
4094 delete_finished_bg_job(pi);
4095 return EXIT_SUCCESS;
4096 } else {
4097 bb_perror_msg("kill (SIGCONT)");
4098 }
4099 }
4100
4101 if (*argv[0] == 'f') {
4102 remove_bg_job(pi);
4103 return checkjobs_and_fg_shell(pi);
4104 }
4105 return EXIT_SUCCESS;
4106}
4107#endif
4108
4109#if ENABLE_HUSH_HELP
4110static int builtin_help(char **argv ATTRIBUTE_UNUSED)
4111{
4112 const struct built_in_command *x;
4113
4114 printf("\nBuilt-in commands:\n");
4115 printf("-------------------\n");
4116 for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) {
4117 printf("%s\t%s\n", x->cmd, x->descr);
4118 }
4119 printf("\n\n");
4120 return EXIT_SUCCESS;
4121}
4122#endif
4123
4124#if ENABLE_HUSH_JOB
4125static int builtin_jobs(char **argv ATTRIBUTE_UNUSED)
4126{
4127 struct pipe *job;
4128 const char *status_string;
4129
4130 for (job = job_list; job; job = job->next) {
4131 if (job->running_progs == job->stopped_progs)
4132 status_string = "Stopped";
4133 else
4134 status_string = "Running";
4135
4136 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext);
4137 }
4138 return EXIT_SUCCESS;
4139}
4140#endif
4141
4142static int builtin_pwd(char **argv ATTRIBUTE_UNUSED)
4143{
4144 puts(set_cwd());
4145 return EXIT_SUCCESS;
4146}
4147
4148static int builtin_read(char **argv)
4149{
4150 char *string;
4151 const char *name = argv[1] ? argv[1] : "REPLY";
4152
4153 string = xmalloc_reads(STDIN_FILENO, xasprintf("%s=", name), NULL);
4154 return set_local_var(string, 0);
4155}
4156
4157/* built-in 'set [VAR=value]' handler */
4158static int builtin_set(char **argv)
4159{
4160 char *temp = argv[1];
4161 struct variable *e;
4162
4163 if (temp == NULL)
4164 for (e = top_var; e; e = e->next)
4165 puts(e->varstr);
4166 else
4167 set_local_var(xstrdup(temp), 0);
4168
4169 return EXIT_SUCCESS;
4170}
4171
4172static int builtin_shift(char **argv)
4173{
4174 int n = 1;
4175 if (argv[1]) {
4176 n = atoi(argv[1]);
4177 }
4178 if (n >= 0 && n < global_argc) {
4179 global_argv[n] = global_argv[0];
4180 global_argc -= n;
4181 global_argv += n;
4182 return EXIT_SUCCESS;
4183 }
4184 return EXIT_FAILURE;
4185}
4186
4187static int builtin_source(char **argv)
4188{
4189 FILE *input;
4190 int status;
4191
4192 if (argv[1] == NULL)
4193 return EXIT_FAILURE;
4194
4195 /* XXX search through $PATH is missing */
4196 input = fopen(argv[1], "r");
4197 if (!input) {
4198 bb_error_msg("cannot open '%s'", argv[1]);
4199 return EXIT_FAILURE;
4200 }
4201 close_on_exec_on(fileno(input));
4202
4203 /* Now run the file */
4204 /* XXX argv and argc are broken; need to save old global_argv
4205 * (pointer only is OK!) on this stack frame,
4206 * set global_argv=argv+1, recurse, and restore. */
4207 status = parse_and_run_file(input);
4208 fclose(input);
4209 return status;
4210}
4211
4212static int builtin_umask(char **argv)
4213{
4214 mode_t new_umask;
4215 const char *arg = argv[1];
4216 char *end;
4217 if (arg) {
4218 new_umask = strtoul(arg, &end, 8);
4219 if (*end != '\0' || end == arg) {
4220 return EXIT_FAILURE;
4221 }
4222 } else {
4223 new_umask = umask(0);
4224 printf("%.3o\n", (unsigned) new_umask);
4225 }
4226 umask(new_umask);
4227 return EXIT_SUCCESS;
4228}
4229
4230static int builtin_unset(char **argv)
4231{
4232 /* bash always returns true */
4233 unset_local_var(argv[1]);
4234 return EXIT_SUCCESS;
4235}
diff --git a/shell/hush_leaktool.sh b/shell/hush_leaktool.sh
index 54a19aa6f..54161b3e9 100644
--- a/shell/hush_leaktool.sh
+++ b/shell/hush_leaktool.sh
@@ -8,6 +8,6 @@ freelist=`grep 'free 0x' "$output" | cut -d' ' -f2 | sort | uniq | xargs`
8grep -v free "$output" >temp1 8grep -v free "$output" >temp1
9for freed in $freelist; do 9for freed in $freelist; do
10 echo Dropping $freed 10 echo Dropping $freed
11 cat temp1 | grep -v $freed >temp2 11 grep -v $freed <temp1 >temp2
12 mv temp2 temp1 12 mv temp2 temp1
13done 13done