hush: implement unset -f; beautify the handling of signal-killed pipe

four TODOs are gone

function                                             old     new   delta
builtin_unset                                        271     364     +93
checkjobs                                            394     428     +34
builtin_exit                                          49      47      -2
This commit is contained in:
Denis Vlasenko 2009-04-18 11:23:38 +00:00
parent eb85849b50
commit 40e84374ec

View File

@ -1185,7 +1185,6 @@ static int check_and_run_traps(int sig)
// G.count_SIGCHLD++; // G.count_SIGCHLD++;
// break; // break;
case SIGINT: case SIGINT:
//TODO: add putchar('\n') also when we detect that child was killed (sleep 5 + ^C)
/* Builtin was ^C'ed, make it look prettier: */ /* Builtin was ^C'ed, make it look prettier: */
bb_putchar('\n'); bb_putchar('\n');
G.flag_SIGINT = 1; G.flag_SIGINT = 1;
@ -2842,6 +2841,31 @@ static struct function *new_function(char *name)
return funcp; return funcp;
} }
static void unset_func(const char *name)
{
struct function *funcp;
struct function **funcpp = &G.top_func;
while ((funcp = *funcpp) != NULL) {
if (strcmp(funcp->name, name) == 0) {
*funcpp = funcp->next;
/* funcp is unlinked now, deleting it */
free(funcp->name);
/* Note: if !funcp->body, do not free body_as_string!
* This is a special case of "-F name body" function:
* body_as_string was not malloced! */
if (funcp->body) {
free_pipe_list(funcp->body);
#if !BB_MMU
free(funcp->body_as_string);
#endif
}
free(funcp);
break;
}
}
}
#if BB_MMU #if BB_MMU
#define exec_function(nommu_save, funcp, argv) \ #define exec_function(nommu_save, funcp, argv) \
exec_function(funcp, argv) exec_function(funcp, argv)
@ -3223,6 +3247,13 @@ static int checkjobs(struct pipe* fg_pipe)
/* last process gives overall exitstatus */ /* last process gives overall exitstatus */
rcode = WEXITSTATUS(status); rcode = WEXITSTATUS(status);
IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;) IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;)
/* bash prints killing signal's name for *last*
* process in pipe (prints just newline for SIGINT).
* we just print newline for any sig:
*/
if (WIFSIGNALED(status)) {
bb_putchar('\n');
}
} }
} else { } else {
fg_pipe->cmds[i].is_stopped = 1; fg_pipe->cmds[i].is_stopped = 1;
@ -6372,11 +6403,19 @@ static int builtin_exec(char **argv)
static int builtin_exit(char **argv) static int builtin_exit(char **argv)
{ {
debug_printf_exec("%s()\n", __func__); debug_printf_exec("%s()\n", __func__);
// TODO: bash does it ONLY on top-level sh exit (+interacive only?)
//puts("exit"); /* bash does it */ /* interactive bash:
// TODO: warn if we have background jobs: "There are stopped jobs" * # trap "echo EEE" EXIT
// On second consecutive 'exit', exit anyway. * # exit
// perhaps use G.exiting = -1 as indicator "last cmd was exit" * exit
* There are stopped jobs.
* (if there are _stopped_ jobs, running ones don't count)
* # exit
* exit
# EEE (then bash exits)
*
* we can use G.exiting = -1 as indicator "last cmd was exit"
*/
/* note: EXIT trap is run by hush_exit */ /* note: EXIT trap is run by hush_exit */
if (*++argv == NULL) if (*++argv == NULL)
@ -6776,28 +6815,34 @@ static int builtin_unset(char **argv)
{ {
int ret; int ret;
char var; char var;
char *arg;
if (!*++argv) if (!*++argv)
return EXIT_SUCCESS; return EXIT_SUCCESS;
var = 'v'; var = 0;
if (argv[0][0] == '-') { while ((arg = *argv) != NULL && arg[0] == '-') {
switch (argv[0][1]) { while (*++arg) {
case 'v': switch (*arg) {
case 'f': case 'v':
var = argv[0][1]; case 'f':
break; if (var == 0 || var == *arg) {
default: var = *arg;
bb_error_msg("unset: %s: invalid option", *argv); break;
return EXIT_FAILURE; }
/* else: unset -vf, which is illegal.
* fall through */
default:
bb_error_msg("unset: %s: invalid option", *argv);
return EXIT_FAILURE;
}
} }
//TODO: disallow "unset -vf ..." too
argv++; argv++;
} }
ret = EXIT_SUCCESS; ret = EXIT_SUCCESS;
while (*argv) { while (*argv) {
if (var == 'v') { if (var != 'f') {
if (unset_local_var(*argv)) { if (unset_local_var(*argv)) {
/* unset <nonexistent_var> doesn't fail. /* unset <nonexistent_var> doesn't fail.
* Error is when one tries to unset RO var. * Error is when one tries to unset RO var.
@ -6805,11 +6850,11 @@ static int builtin_unset(char **argv)
ret = EXIT_FAILURE; ret = EXIT_FAILURE;
} }
} }
//#if ENABLE_HUSH_FUNCTIONS #if ENABLE_HUSH_FUNCTIONS
// else { else {
// unset_local_func(*argv); unset_func(*argv);
// } }
//#endif #endif
argv++; argv++;
} }
return ret; return ret;