# define PIPE_BUF 4096 /* amount of buffering in a pipe */
#endif
-/* Not every libc has sighandler_t. Fix it */
-typedef void (*hush_sighandler_t)(int);
-#define sighandler_t hush_sighandler_t
-
//config:config HUSH
//config: bool "hush"
//config: default y
# define ENABLE_FEATURE_EDITING 0
# undef ENABLE_FEATURE_EDITING_FANCY_PROMPT
# define ENABLE_FEATURE_EDITING_FANCY_PROMPT 0
+# undef ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
+# define ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 0
#endif
/* Do we support ANY keywords? */
struct command {
pid_t pid; /* 0 if exited */
int assignment_cnt; /* how many argv[i] are assignments? */
- smallint is_stopped; /* is the command currently running? */
smallint cmd_type; /* CMD_xxx */
#define CMD_NORMAL 0
#define CMD_SUBSHELL 1
#if ENABLE_HUSH_HELP
static int builtin_help(char **argv) FAST_FUNC;
#endif
+#if MAX_HISTORY && ENABLE_FEATURE_EDITING
+static int builtin_history(char **argv) FAST_FUNC;
+#endif
#if ENABLE_HUSH_LOCAL
static int builtin_local(char **argv) FAST_FUNC;
#endif
#if ENABLE_HUSH_HELP
BLTIN("help" , builtin_help , NULL),
#endif
+#if MAX_HISTORY && ENABLE_FEATURE_EDITING
+ BLTIN("history" , builtin_history , "Show command history"),
+#endif
#if ENABLE_HUSH_JOB
BLTIN("jobs" , builtin_jobs , "List jobs"),
#endif
* backgrounds (i.e. stops) or kills all members of currently running
* pipe.
*
- * Wait builtin in interruptible by signals for which user trap is set
+ * Wait builtin is interruptible by signals for which user trap is set
* or by SIGINT in interactive shell.
*
* Trap handlers will execute even within trap handlers. (right?)
* are set to '' (ignore) are NOT reset to defaults. We do the same.
*
* Problem: the above approach makes it unwieldy to catch signals while
- * we are in read builtin, of while we read commands from stdin:
+ * we are in read builtin, or while we read commands from stdin:
* masked signals are not visible!
*
* New implementation
* for them - a bit like emulating kernel pending signal mask in userspace.
* We are interested in: signals which need to have special handling
* as described above, and all signals which have traps set.
- * Signals are rocorded in pending_set.
+ * Signals are recorded in pending_set.
* After each pipe execution, we extract any pending signals
* and act on them.
*
static void hush_exit(int exitcode) NORETURN;
static void hush_exit(int exitcode)
{
+#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
+ save_history(G.line_input_state);
+#endif
+
fflush_all();
if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) {
char *argv[3];
* _during_ shell execution, not only if it was set when
* shell was started. Therefore, re-check LANG every time:
*/
- reinit_unicode(get_local_var_value("LANG"));
+ const char *s = get_local_var_value("LC_ALL");
+ if (!s) s = get_local_var_value("LC_CTYPE");
+ if (!s) s = get_local_var_value("LANG");
+ reinit_unicode(s);
G.flag_SIGINT = 0;
/* buglet: SIGINT will not make new prompt to appear _at once_,
/* (this makes bare "&" cmd a no-op.
* bash says: "syntax error near unexpected token '&'") */
if (pi->num_cmds == 0
- IF_HAS_KEYWORDS( && pi->res_word == RES_NONE)
+ IF_HAS_KEYWORDS(&& pi->res_word == RES_NONE)
) {
free_pipe_list(pi);
pi = NULL;
debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
/* Do we sit outside of any if's, loops or case's? */
if (!HAS_KEYWORDS
- IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0))
+ IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0))
) {
o_free(&dest);
#if !BB_MMU
}
fg_pipe->cmds[i].cmd_exitcode = ex;
} else {
- fg_pipe->cmds[i].is_stopped = 1;
fg_pipe->stopped_cmds++;
}
debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n",
}
} else {
/* child stopped */
- pi->cmds[i].is_stopped = 1;
pi->stopped_cmds++;
}
#endif
* and we should not execute CMD */
debug_printf_exec("skipped cmd because of || or &&\n");
last_followup = pi->followup;
- continue;
+ goto dont_check_jobs_but_continue;
}
}
last_followup = pi->followup;
G.flag_break_continue = 0;
/* else: e.g. "continue 2" should *break* once, *then* continue */
} /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */
- if (G.depth_break_continue != 0 || fbc == BC_BREAK)
- goto check_jobs_and_break;
+ if (G.depth_break_continue != 0 || fbc == BC_BREAK) {
+ checkjobs(NULL);
+ break;
+ }
/* "continue": simulate end of loop */
rword = RES_DONE;
continue;
#endif
#if ENABLE_HUSH_FUNCTIONS
if (G.flag_return_in_progress == 1) {
- /* same as "goto check_jobs_and_break" */
checkjobs(NULL);
break;
}
if (rword == RES_IF || rword == RES_ELIF)
cond_code = rcode;
#endif
+ check_jobs_and_continue:
+ checkjobs(NULL);
+ dont_check_jobs_but_continue: ;
#if ENABLE_HUSH_LOOPS
/* Beware of "while false; true; do ..."! */
if (pi->next
/* "while false; do...done" - exitcode 0 */
G.last_exitcode = rcode = EXIT_SUCCESS;
debug_printf_exec(": while expr is false: breaking (exitcode:EXIT_SUCCESS)\n");
- goto check_jobs_and_break;
+ break;
}
}
if (rword == RES_UNTIL) {
if (!rcode) {
debug_printf_exec(": until expr is true: breaking\n");
- check_jobs_and_break:
- checkjobs(NULL);
break;
}
}
}
#endif
-
- check_jobs_and_continue:
- checkjobs(NULL);
} /* for (pi) */
#if ENABLE_HUSH_JOB
*/
#if ENABLE_FEATURE_EDITING
- G.line_input_state = new_line_input_t(FOR_SHELL & ~SAVE_HISTORY);
+ G.line_input_state = new_line_input_t(FOR_SHELL);
#endif
/* Initialize some more globals to non-zero values */
}
if (hp) {
G.line_input_state->hist_file = hp;
- G.line_input_state->flags |= SAVE_HISTORY;
//set_local_var(xasprintf("HISTFILE=%s", ...));
}
# if ENABLE_FEATURE_SH_HISTFILESIZE
* (if there are _stopped_ jobs, running ones don't count)
* # exit
* exit
- # EEE (then bash exits)
+ * EEE (then bash exits)
*
* TODO: we can use G.exiting = -1 as indicator "last cmd was exit"
*/
debug_printf_jobs("reviving %d procs, pgrp %d\n", pi->num_cmds, pi->pgrp);
for (i = 0; i < pi->num_cmds; i++) {
debug_printf_jobs("reviving pid %d\n", pi->cmds[i].pid);
- pi->cmds[i].is_stopped = 0;
}
pi->stopped_cmds = 0;
}
#endif
+#if MAX_HISTORY && ENABLE_FEATURE_EDITING
+static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM)
+{
+ show_history(G.line_input_state);
+ return EXIT_SUCCESS;
+}
+#endif
+
#if ENABLE_HUSH_JOB
static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM)
{
free(arg_path);
if (!input) {
/* bb_perror_msg("%s", *argv); - done by fopen_or_warn */
+ /* POSIX: non-interactive shell should abort here,
+ * not merely fail. So far no one complained :)
+ */
return EXIT_FAILURE;
}
close_on_exec_on(fileno(input));
/* "we are inside sourced file, ok to use return" */
G.flag_return_in_progress = -1;
#endif
- save_and_replace_G_args(&sv, argv);
+ if (argv[1])
+ save_and_replace_G_args(&sv, argv);
parse_and_run_file(input);
fclose(input);
- restore_G_args(&sv, argv);
+ if (argv[1])
+ restore_G_args(&sv, argv);
#if ENABLE_HUSH_FUNCTIONS
G.flag_return_in_progress = sv_flg;
#endif