diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-06-17 05:43:38 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-06-17 05:43:38 +0000 |
commit | c7985b76c5174c4c224b67e29dd554443f80f47e (patch) | |
tree | 45c1a89347719a50c642be4be8564bd3dd777862 | |
parent | b61e13d24741536fc713dab1c10a4925197b2072 (diff) | |
download | busybox-w32-c7985b76c5174c4c224b67e29dd554443f80f47e.tar.gz busybox-w32-c7985b76c5174c4c224b67e29dd554443f80f47e.tar.bz2 busybox-w32-c7985b76c5174c4c224b67e29dd554443f80f47e.zip |
hush: cleanup pass, the biggest is - moved builtins to the end of the file,
they really annoy in the middle of parser code. no real code changes.
-rw-r--r-- | shell/hush.c | 711 | ||||
-rw-r--r-- | shell/hush_leaktool.sh | 2 |
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 */ | ||
248 | typedef enum { | 247 | typedef 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 */ |
269 | struct p_context { | 269 | struct 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 | ||
303 | struct pipe { | 304 | struct 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 | ||
575 | static char **add_strings_to_strings(int need_xstrdup, char **strings, char **add) | 576 | static 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 */ | 605 | static char **add_malloced_string_to_strings(char **strings, char *add) |
605 | static 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 | ||
615 | static void free_strings(char **strings) | 615 | static 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 */ |
717 | static void set_misc_sighandler(void (*handler)(int)) | 718 | static 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 */ | ||
850 | static int builtin_true(char **argv ATTRIBUTE_UNUSED) | ||
851 | { | ||
852 | return 0; | ||
853 | } | ||
854 | |||
855 | /* built-in 'test' handler */ | ||
856 | static 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 */ | ||
867 | static 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 */ | ||
878 | static 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 */ | ||
892 | static 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 */ | ||
910 | static 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 */ | ||
925 | static 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 */ | ||
940 | static 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 */ | ||
978 | static 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 | ||
1042 | static 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 */ | ||
1058 | static 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 */ | ||
1076 | static int builtin_pwd(char **argv ATTRIBUTE_UNUSED) | ||
1077 | { | ||
1078 | puts(set_cwd()); | ||
1079 | return EXIT_SUCCESS; | ||
1080 | } | ||
1081 | |||
1082 | /* built-in 'read VAR' handler */ | ||
1083 | static 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 */ | ||
1093 | static 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 */ | ||
1109 | static 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) */ | ||
1125 | static 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 | |||
1150 | static 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 */ | ||
1169 | static 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 */ | ||
1359 | static char *unbackslash(char *src) | 1034 | static 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 | |||
1371 | static int o_glob(o_string *o, int n) | 1045 | static 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 */ |
1421 | static int o_save_ptr(o_string *o, int n) | 1095 | static 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 |
1466 | static void cmdedit_set_initial_prompt(void) | 1141 | static 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. */ |
1609 | static int setup_redirects(struct child_prog *prog, int squirrel[]) | 1286 | static 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*" */ |
2807 | static char *expand_string_to_string(const char *str) | 2488 | static 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 */ |
2824 | static char* expand_strvec_to_string(char **argv) | 2505 | static 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 */ | ||
2846 | static struct variable *get_local_var(const char *name) | 2528 | static 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. */ | ||
3157 | static int done_word(o_string *word, struct p_context *ctx) | 2842 | static 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 | */ |
3289 | static int redirect_dup_num(struct in_str *input) | 2974 | static 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 */ |
3600 | static int handle_dollar(o_string *dest, struct in_str *input) | 3285 | static 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 */ |
3678 | static int parse_stream(o_string *dest, struct p_context *ctx, | 3363 | static 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() */ |
3942 | static int parse_and_run_stream(struct in_str *inp, int parse_flag) | 3627 | static 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 | |||
4037 | int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 3725 | int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
4038 | int hush_main(int argc, char **argv) | 3726 | int 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 | */ | ||
3926 | static int builtin_true(char **argv ATTRIBUTE_UNUSED) | ||
3927 | { | ||
3928 | return 0; | ||
3929 | } | ||
3930 | |||
3931 | static 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 | |||
3941 | static 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 | |||
3951 | static 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 | |||
3964 | static 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 | |||
3981 | static 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 | |||
3995 | static 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 | |||
4009 | static 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 */ | ||
4047 | static 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 | ||
4110 | static 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 | ||
4125 | static 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 | |||
4142 | static int builtin_pwd(char **argv ATTRIBUTE_UNUSED) | ||
4143 | { | ||
4144 | puts(set_cwd()); | ||
4145 | return EXIT_SUCCESS; | ||
4146 | } | ||
4147 | |||
4148 | static 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 */ | ||
4158 | static 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 | |||
4172 | static 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 | |||
4187 | static 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 | |||
4212 | static 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 | |||
4230 | static 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` | |||
8 | grep -v free "$output" >temp1 | 8 | grep -v free "$output" >temp1 |
9 | for freed in $freelist; do | 9 | for 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 |
13 | done | 13 | done |