hush: implement $LINENO bashism

function                                             old     new   delta
run_pipe                                            1697    1774     +77
unset_local_var_len                                  258     301     +43
hush_main                                           1009    1051     +42
set_local_var                                        516     554     +38
parse_and_run_file                                    42      75     +33
i_getch                                               96     116     +20
.rodata                                            18858   18876     +18
done_command                                         106     122     +16
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 8/0 up/down: 287/0)             Total: 287 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2018-01-19 15:37:04 +01:00
parent 7c44b600f9
commit 6aad1ddcc9
3 changed files with 68 additions and 2 deletions

View File

@ -361,6 +361,7 @@
#define BASH_SUBSTR ENABLE_HUSH_BASH_COMPAT #define BASH_SUBSTR ENABLE_HUSH_BASH_COMPAT
#define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT #define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT
#define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT #define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT
#define BASH_LINENO_VAR ENABLE_HUSH_BASH_COMPAT
#define BASH_TEST2 (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST) #define BASH_TEST2 (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST)
#define BASH_READ_D ENABLE_HUSH_BASH_COMPAT #define BASH_READ_D ENABLE_HUSH_BASH_COMPAT
@ -612,6 +613,9 @@ typedef enum redir_type {
struct command { struct command {
pid_t pid; /* 0 if exited */ pid_t pid; /* 0 if exited */
int assignment_cnt; /* how many argv[i] are assignments? */ int assignment_cnt; /* how many argv[i] are assignments? */
#if BASH_LINENO_VAR
unsigned lineno;
#endif
smallint cmd_type; /* CMD_xxx */ smallint cmd_type; /* CMD_xxx */
#define CMD_NORMAL 0 #define CMD_NORMAL 0
#define CMD_SUBSHELL 1 #define CMD_SUBSHELL 1
@ -930,6 +934,13 @@ struct globals {
unsigned count_SIGCHLD; unsigned count_SIGCHLD;
unsigned handled_SIGCHLD; unsigned handled_SIGCHLD;
smallint we_have_children; smallint we_have_children;
#endif
#if BASH_LINENO_VAR
unsigned lineno;
char *lineno_var;
# define G_lineno_var G.lineno_var
#else
# define G_lineno_var ((char*)NULL)
#endif #endif
struct FILE_list *FILE_list; struct FILE_list *FILE_list;
/* Which signals have non-DFL handler (even with no traps set)? /* Which signals have non-DFL handler (even with no traps set)?
@ -2135,6 +2146,13 @@ static int set_local_var(char *str, unsigned flags)
} }
name_len = eq_sign - str + 1; /* including '=' */ name_len = eq_sign - str + 1; /* including '=' */
#if BASH_LINENO_VAR
if (G.lineno_var) {
if (name_len == 7 && strncmp("LINENO", str, 6) == 0)
G.lineno_var = NULL;
}
#endif
var_pp = &G.top_var; var_pp = &G.top_var;
while ((cur = *var_pp) != NULL) { while ((cur = *var_pp) != NULL) {
if (strncmp(cur->varstr, str, name_len) != 0) { if (strncmp(cur->varstr, str, name_len) != 0) {
@ -2256,10 +2274,16 @@ static int unset_local_var_len(const char *name, int name_len)
if (!name) if (!name)
return EXIT_SUCCESS; return EXIT_SUCCESS;
#if ENABLE_HUSH_GETOPTS #if ENABLE_HUSH_GETOPTS
if (name_len == 6 && strncmp(name, "OPTIND", 6) == 0) if (name_len == 6 && strncmp(name, "OPTIND", 6) == 0)
G.getopt_count = 0; G.getopt_count = 0;
#endif #endif
#if BASH_LINENO_VAR
if (name_len == 6 && G.lineno_var && strncmp(name, "LINENO", 6) == 0)
G.lineno_var = NULL;
#endif
var_pp = &G.top_var; var_pp = &G.top_var;
while ((cur = *var_pp) != NULL) { while ((cur = *var_pp) != NULL) {
if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') { if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') {
@ -2578,6 +2602,10 @@ static int i_getch(struct in_str *i)
out: out:
debug_printf("file_get: got '%c' %d\n", ch, ch); debug_printf("file_get: got '%c' %d\n", ch, ch);
i->last_char = ch; i->last_char = ch;
#if BASH_LINENO_VAR
if (ch == '\n')
G.lineno++;
#endif
return ch; return ch;
} }
@ -3460,6 +3488,9 @@ static int done_command(struct parse_context *ctx)
ctx->command = command = &pi->cmds[pi->num_cmds]; ctx->command = command = &pi->cmds[pi->num_cmds];
clear_and_ret: clear_and_ret:
memset(command, 0, sizeof(*command)); memset(command, 0, sizeof(*command));
#if BASH_LINENO_VAR
command->lineno = G.lineno;
#endif
return pi->num_cmds; /* used only for 0/nonzero check */ return pi->num_cmds; /* used only for 0/nonzero check */
} }
@ -6520,8 +6551,13 @@ static void parse_and_run_string(const char *s)
static void parse_and_run_file(FILE *f) static void parse_and_run_file(FILE *f)
{ {
struct in_str input; struct in_str input;
unsigned sv;
setup_file_in_str(&input, f); setup_file_in_str(&input, f);
sv = G.lineno;
G.lineno = 1;
parse_and_run_stream(&input, ';'); parse_and_run_stream(&input, ';');
G.lineno = sv;
} }
#if ENABLE_HUSH_TICK #if ENABLE_HUSH_TICK
@ -8068,6 +8104,9 @@ static NOINLINE int run_pipe(struct pipe *pi)
char **new_env = NULL; char **new_env = NULL;
struct variable *old_vars = NULL; struct variable *old_vars = NULL;
if (G_lineno_var)
strcpy(G_lineno_var + sizeof("LINENO=")-1, utoa(command->lineno));
if (argv[command->assignment_cnt] == NULL) { if (argv[command->assignment_cnt] == NULL) {
/* Assignments, but no command */ /* Assignments, but no command */
/* Ensure redirects take effect (that is, create files). /* Ensure redirects take effect (that is, create files).
@ -8272,6 +8311,9 @@ static NOINLINE int run_pipe(struct pipe *pi)
if (cmd_no < pi->num_cmds) if (cmd_no < pi->num_cmds)
xpiped_pair(pipefds); xpiped_pair(pipefds);
if (G_lineno_var)
strcpy(G_lineno_var + sizeof("LINENO=")-1, utoa(command->lineno));
command->pid = BB_MMU ? fork() : vfork(); command->pid = BB_MMU ? fork() : vfork();
if (!command->pid) { /* child */ if (!command->pid) { /* child */
#if ENABLE_HUSH_JOB #if ENABLE_HUSH_JOB
@ -8907,17 +8949,19 @@ int hush_main(int argc, char **argv)
#if !BB_MMU #if !BB_MMU
G.argv0_for_re_execing = argv[0]; G.argv0_for_re_execing = argv[0];
#endif #endif
/* Deal with HUSH_VERSION */ /* Deal with HUSH_VERSION */
debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
unsetenv("HUSH_VERSION"); /* in case it exists in initial env */
shell_ver = xzalloc(sizeof(*shell_ver)); shell_ver = xzalloc(sizeof(*shell_ver));
shell_ver->flg_export = 1; shell_ver->flg_export = 1;
shell_ver->flg_read_only = 1; shell_ver->flg_read_only = 1;
/* Code which handles ${var<op>...} needs writable values for all variables, /* Code which handles ${var<op>...} needs writable values for all variables,
* therefore we xstrdup: */ * therefore we xstrdup: */
shell_ver->varstr = xstrdup(hush_version_str); shell_ver->varstr = xstrdup(hush_version_str);
/* Create shell local variables from the values /* Create shell local variables from the values
* currently living in the environment */ * currently living in the environment */
debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
unsetenv("HUSH_VERSION"); /* in case it exists in initial env */
G.top_var = shell_ver; G.top_var = shell_ver;
cur_var = G.top_var; cur_var = G.top_var;
e = environ; e = environ;
@ -8983,6 +9027,14 @@ int hush_main(int argc, char **argv)
*/ */
#endif #endif
#if BASH_LINENO_VAR
if (BASH_LINENO_VAR) {
char *p = xasprintf("LINENO=%*s", (int)(sizeof(int)*3), "");
set_local_var(p, /*flags*/ 0);
G.lineno_var = p; /* can't assign before set_local_var("LINENO=...") */
}
#endif
#if ENABLE_FEATURE_EDITING #if ENABLE_FEATURE_EDITING
G.line_input_state = new_line_input_t(FOR_SHELL); G.line_input_state = new_line_input_t(FOR_SHELL);
#endif #endif

View File

@ -0,0 +1,8 @@
2:2
3:3
4:4
5:5
2:2
3:3
4:4
5:5

View File

@ -0,0 +1,6 @@
env | grep LINENO
echo 2:$LINENO
echo 3:$LINENO >&2 \
| { sleep 0.1; echo 4:$LINENO; }
echo 5:$LINENO
test "$1" || . ./var_LINENO1.tests norepeat