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++;
// break;
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: */
bb_putchar('\n');
G.flag_SIGINT = 1;
@ -2842,6 +2841,31 @@ static struct function *new_function(char *name)
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
#define exec_function(nommu_save, funcp, argv) \
exec_function(funcp, argv)
@ -3223,6 +3247,13 @@ static int checkjobs(struct pipe* fg_pipe)
/* last process gives overall exitstatus */
rcode = WEXITSTATUS(status);
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 {
fg_pipe->cmds[i].is_stopped = 1;
@ -6372,11 +6403,19 @@ static int builtin_exec(char **argv)
static int builtin_exit(char **argv)
{
debug_printf_exec("%s()\n", __func__);
// TODO: bash does it ONLY on top-level sh exit (+interacive only?)
//puts("exit"); /* bash does it */
// TODO: warn if we have background jobs: "There are stopped jobs"
// On second consecutive 'exit', exit anyway.
// perhaps use G.exiting = -1 as indicator "last cmd was exit"
/* interactive bash:
* # trap "echo EEE" EXIT
* # 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 */
if (*++argv == NULL)
@ -6776,28 +6815,34 @@ static int builtin_unset(char **argv)
{
int ret;
char var;
char *arg;
if (!*++argv)
return EXIT_SUCCESS;
var = 'v';
if (argv[0][0] == '-') {
switch (argv[0][1]) {
case 'v':
case 'f':
var = argv[0][1];
break;
default:
bb_error_msg("unset: %s: invalid option", *argv);
return EXIT_FAILURE;
var = 0;
while ((arg = *argv) != NULL && arg[0] == '-') {
while (*++arg) {
switch (*arg) {
case 'v':
case 'f':
if (var == 0 || var == *arg) {
var = *arg;
break;
}
/* 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++;
}
ret = EXIT_SUCCESS;
while (*argv) {
if (var == 'v') {
if (var != 'f') {
if (unset_local_var(*argv)) {
/* unset <nonexistent_var> doesn't fail.
* Error is when one tries to unset RO var.
@ -6805,11 +6850,11 @@ static int builtin_unset(char **argv)
ret = EXIT_FAILURE;
}
}
//#if ENABLE_HUSH_FUNCTIONS
// else {
// unset_local_func(*argv);
// }
//#endif
#if ENABLE_HUSH_FUNCTIONS
else {
unset_func(*argv);
}
#endif
argv++;
}
return ret;