mirror of
https://github.com/sheumann/hush.git
synced 2025-01-05 14:29:56 +00:00
hush: fix a few thinkos in function support; make it work on NOMMU;
functions in child shells now even have $n passed to them! :) (in main shell it still doesn't work)
This commit is contained in:
parent
b7d8c0dbbd
commit
c0ea329297
189
shell/hush.c
189
shell/hush.c
@ -452,11 +452,16 @@ enum {
|
|||||||
BC_CONTINUE = 2,
|
BC_CONTINUE = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if ENABLE_HUSH_FUNCTIONS
|
||||||
struct function {
|
struct function {
|
||||||
struct function *next;
|
struct function *next;
|
||||||
char *name;
|
char *name;
|
||||||
struct pipe *body;
|
struct pipe *body;
|
||||||
|
#if !BB_MMU
|
||||||
|
char *body_as_string;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* "Globals" within this file */
|
/* "Globals" within this file */
|
||||||
@ -510,7 +515,9 @@ struct globals {
|
|||||||
const char *cwd;
|
const char *cwd;
|
||||||
struct variable *top_var; /* = &G.shell_ver (set in main()) */
|
struct variable *top_var; /* = &G.shell_ver (set in main()) */
|
||||||
struct variable shell_ver;
|
struct variable shell_ver;
|
||||||
|
#if ENABLE_HUSH_FUNCTIONS
|
||||||
struct function *top_func;
|
struct function *top_func;
|
||||||
|
#endif
|
||||||
/* Signal and trap handling */
|
/* Signal and trap handling */
|
||||||
// unsigned count_SIGCHLD;
|
// unsigned count_SIGCHLD;
|
||||||
// unsigned handled_SIGCHLD;
|
// unsigned handled_SIGCHLD;
|
||||||
@ -675,11 +682,12 @@ static void xxfree(void *ptr)
|
|||||||
* HUSH_DEBUG >= 2 prints line number in this file where it was detected.
|
* HUSH_DEBUG >= 2 prints line number in this file where it was detected.
|
||||||
*/
|
*/
|
||||||
#if HUSH_DEBUG < 2
|
#if HUSH_DEBUG < 2
|
||||||
# define die_if_script(lineno, fmt...) die_if_script(fmt)
|
# define die_if_script(lineno, fmt...) die_if_script(fmt)
|
||||||
# define syntax_error(lineno, msg) syntax_error(msg)
|
# define syntax_error(lineno, msg) syntax_error(msg)
|
||||||
# define syntax_error_at(lineno, msg) syntax_error_at(msg)
|
# define syntax_error_at(lineno, msg) syntax_error_at(msg)
|
||||||
# define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch)
|
# define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch)
|
||||||
# define syntax_error_unterm_str(lineno, s) syntax_error_unterm_str(s)
|
# define syntax_error_unterm_str(lineno, s) syntax_error_unterm_str(s)
|
||||||
|
# define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void die_if_script(unsigned lineno, const char *fmt, ...)
|
static void die_if_script(unsigned lineno, const char *fmt, ...)
|
||||||
@ -729,18 +737,29 @@ static void syntax_error_unterm_str(unsigned lineno, const char *s)
|
|||||||
die_if_script(lineno, "syntax error: unterminated %s", s);
|
die_if_script(lineno, "syntax error: unterminated %s", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void syntax_error_unexpected_ch(unsigned lineno, char ch)
|
||||||
|
{
|
||||||
|
char msg[2];
|
||||||
|
msg[0] = ch;
|
||||||
|
msg[1] = '\0';
|
||||||
|
die_if_script(lineno, "syntax error: unexpected %s", msg);
|
||||||
|
xfunc_die();
|
||||||
|
}
|
||||||
|
|
||||||
#if HUSH_DEBUG < 2
|
#if HUSH_DEBUG < 2
|
||||||
# undef die_if_script
|
# undef die_if_script
|
||||||
# undef syntax_error
|
# undef syntax_error
|
||||||
# undef syntax_error_at
|
# undef syntax_error_at
|
||||||
# undef syntax_error_unterm_ch
|
# undef syntax_error_unterm_ch
|
||||||
# undef syntax_error_unterm_str
|
# undef syntax_error_unterm_str
|
||||||
|
# undef syntax_error_unexpected_ch
|
||||||
#else
|
#else
|
||||||
# define die_if_script(fmt...) die_if_script(__LINE__, fmt)
|
# define die_if_script(fmt...) die_if_script(__LINE__, fmt)
|
||||||
# define syntax_error(msg) syntax_error(__LINE__, msg)
|
# define syntax_error(msg) syntax_error(__LINE__, msg)
|
||||||
# define syntax_error_at(msg) syntax_error_at(__LINE__, msg)
|
# define syntax_error_at(msg) syntax_error_at(__LINE__, msg)
|
||||||
# define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch)
|
# define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch)
|
||||||
# define syntax_error_unterm_str(s) syntax_error_unterm_str(__LINE__, s)
|
# define syntax_error_unterm_str(s) syntax_error_unterm_str(__LINE__, s)
|
||||||
|
# define syntax_error_unexpected_ch(ch) syntax_error_unexpected_ch(__LINE__, ch)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@ -2234,8 +2253,11 @@ static char **expand_assignments(char **argv, int count)
|
|||||||
|
|
||||||
|
|
||||||
#if BB_MMU
|
#if BB_MMU
|
||||||
void re_execute_shell(const char *s, int is_heredoc); /* never called */
|
/* never called */
|
||||||
|
void re_execute_shell(const char *s, char *argv0, char **argv);
|
||||||
|
|
||||||
#define clean_up_after_re_execute() ((void)0)
|
#define clean_up_after_re_execute() ((void)0)
|
||||||
|
|
||||||
static void reset_traps_to_defaults(void)
|
static void reset_traps_to_defaults(void)
|
||||||
{
|
{
|
||||||
unsigned sig;
|
unsigned sig;
|
||||||
@ -2266,16 +2288,16 @@ static void reset_traps_to_defaults(void)
|
|||||||
|
|
||||||
#else /* !BB_MMU */
|
#else /* !BB_MMU */
|
||||||
|
|
||||||
static void re_execute_shell(const char *s, int is_heredoc) NORETURN;
|
static void re_execute_shell(const char *s, char *g_argv0, char **g_argv) NORETURN;
|
||||||
static void re_execute_shell(const char *s, int is_heredoc)
|
static void re_execute_shell(const char *s, char *g_argv0, char **g_argv)
|
||||||
{
|
{
|
||||||
char param_buf[sizeof("-$%x:%x:%x:%x") + sizeof(unsigned) * 4];
|
char param_buf[sizeof("-$%x:%x:%x:%x") + sizeof(unsigned) * 4];
|
||||||
char *heredoc_argv[4];
|
char *heredoc_argv[4];
|
||||||
struct variable *cur;
|
struct variable *cur;
|
||||||
char **argv, **pp, **pp2;
|
char **argv, **pp;
|
||||||
unsigned cnt;
|
unsigned cnt;
|
||||||
|
|
||||||
if (is_heredoc) {
|
if (!g_argv0) { /* heredoc */
|
||||||
argv = heredoc_argv;
|
argv = heredoc_argv;
|
||||||
argv[0] = (char *) G.argv0_for_re_execing;
|
argv[0] = (char *) G.argv0_for_re_execing;
|
||||||
argv[1] = (char *) "-<";
|
argv[1] = (char *) "-<";
|
||||||
@ -2292,13 +2314,16 @@ static void re_execute_shell(const char *s, int is_heredoc)
|
|||||||
USE_HUSH_LOOPS(, G.depth_of_loop)
|
USE_HUSH_LOOPS(, G.depth_of_loop)
|
||||||
);
|
);
|
||||||
/* 1:hush 2:-$<pid>:<pid>:<exitcode>:<depth> <vars...>
|
/* 1:hush 2:-$<pid>:<pid>:<exitcode>:<depth> <vars...>
|
||||||
* 3:-c 4:<cmd> <argN...> 5:NULL
|
* 3:-c 4:<cmd> 5:<arg0> <argN...> 6:NULL
|
||||||
*/
|
*/
|
||||||
cnt = 5 + G.global_argc;
|
cnt = 6;
|
||||||
for (cur = G.top_var; cur; cur = cur->next) {
|
for (cur = G.top_var; cur; cur = cur->next) {
|
||||||
if (!cur->flg_export || cur->flg_read_only)
|
if (!cur->flg_export || cur->flg_read_only)
|
||||||
cnt += 2;
|
cnt += 2;
|
||||||
}
|
}
|
||||||
|
pp = g_argv;
|
||||||
|
while (*pp++)
|
||||||
|
cnt++;
|
||||||
G.argv_from_re_execing = argv = pp = xzalloc(sizeof(argv[0]) * cnt);
|
G.argv_from_re_execing = argv = pp = xzalloc(sizeof(argv[0]) * cnt);
|
||||||
*pp++ = (char *) G.argv0_for_re_execing;
|
*pp++ = (char *) G.argv0_for_re_execing;
|
||||||
*pp++ = param_buf;
|
*pp++ = param_buf;
|
||||||
@ -2335,9 +2360,9 @@ static void re_execute_shell(const char *s, int is_heredoc)
|
|||||||
*/
|
*/
|
||||||
*pp++ = (char *) "-c";
|
*pp++ = (char *) "-c";
|
||||||
*pp++ = (char *) s;
|
*pp++ = (char *) s;
|
||||||
pp2 = G.global_argv;
|
*pp++ = g_argv0;
|
||||||
while (*pp2)
|
while (*g_argv)
|
||||||
*pp++ = *pp2++;
|
*pp++ = *g_argv++;
|
||||||
/* *pp = NULL; - is already there */
|
/* *pp = NULL; - is already there */
|
||||||
pp = environ;
|
pp = environ;
|
||||||
|
|
||||||
@ -2426,7 +2451,7 @@ static void setup_heredoc(struct redir_struct *redir)
|
|||||||
/* Delegate blocking writes to another process */
|
/* Delegate blocking writes to another process */
|
||||||
disable_restore_tty_pgrp_on_exit();
|
disable_restore_tty_pgrp_on_exit();
|
||||||
xmove_fd(pair.wr, STDOUT_FILENO);
|
xmove_fd(pair.wr, STDOUT_FILENO);
|
||||||
re_execute_shell(heredoc, 1);
|
re_execute_shell(heredoc, NULL, NULL);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
/* parent */
|
/* parent */
|
||||||
@ -2592,6 +2617,8 @@ static void free_pipe_list(struct pipe *head, int indent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int run_list(struct pipe *pi);
|
||||||
|
|
||||||
static const struct built_in_command* find_builtin(const char *name)
|
static const struct built_in_command* find_builtin(const char *name)
|
||||||
{
|
{
|
||||||
const struct built_in_command *x;
|
const struct built_in_command *x;
|
||||||
@ -2604,23 +2631,40 @@ static const struct built_in_command* find_builtin(const char *name)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
# if ENABLE_HUSH_FUNCTIONS
|
#if ENABLE_HUSH_FUNCTIONS
|
||||||
static const struct function *find_function(const char *name)
|
static const struct function *find_function(const char *name)
|
||||||
{
|
{
|
||||||
const struct function *funcp = G.top_func;
|
const struct function *funcp = G.top_func;
|
||||||
while (funcp) {
|
while (funcp) {
|
||||||
if (strcmp(name, funcp->name) != 0)
|
if (strcmp(name, funcp->name) == 0) {
|
||||||
continue;
|
break;
|
||||||
return funcp;
|
}
|
||||||
debug_printf_exec("found function '%s'\n", name);
|
funcp = funcp->next;
|
||||||
}
|
}
|
||||||
return NULL;
|
debug_printf_exec("found function '%s'\n", name);
|
||||||
|
return funcp;
|
||||||
|
}
|
||||||
|
static void exec_function(const struct function *funcp, char **argv) NORETURN;
|
||||||
|
static void exec_function(const struct function *funcp, char **argv)
|
||||||
|
{
|
||||||
|
# if BB_MMU
|
||||||
|
int n = 1;
|
||||||
|
|
||||||
|
argv[0] = G.global_argv[0];
|
||||||
|
G.global_argv = argv;
|
||||||
|
while (*++argv)
|
||||||
|
n++;
|
||||||
|
G.global_argc = n;
|
||||||
|
n = run_list(funcp->body);
|
||||||
|
fflush(NULL);
|
||||||
|
_exit(n);
|
||||||
|
# else
|
||||||
|
re_execute_shell(funcp->body_as_string, G.global_argv[0], argv + 1);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static int run_list(struct pipe *pi);
|
|
||||||
|
|
||||||
#if BB_MMU
|
#if BB_MMU
|
||||||
#define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \
|
#define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \
|
||||||
pseudo_exec_argv(argv, assignment_cnt, argv_expanded)
|
pseudo_exec_argv(argv, assignment_cnt, argv_expanded)
|
||||||
@ -2688,18 +2732,15 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save,
|
|||||||
_exit(rcode);
|
_exit(rcode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# if ENABLE_HUSH_FUNCTIONS
|
#endif
|
||||||
|
#if ENABLE_HUSH_FUNCTIONS
|
||||||
/* Check if the command matches any functions */
|
/* Check if the command matches any functions */
|
||||||
{
|
{
|
||||||
int rcode;
|
|
||||||
const struct function *funcp = find_function(argv[0]);
|
const struct function *funcp = find_function(argv[0]);
|
||||||
if (funcp) {
|
if (funcp) {
|
||||||
rcode = run_list(funcp->body);
|
exec_function(funcp, argv);
|
||||||
fflush(NULL);
|
|
||||||
_exit(rcode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ENABLE_FEATURE_SH_STANDALONE
|
#if ENABLE_FEATURE_SH_STANDALONE
|
||||||
@ -2723,7 +2764,9 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
|
||||||
skip:
|
skip:
|
||||||
|
#endif
|
||||||
debug_printf_exec("execing '%s'\n", argv[0]);
|
debug_printf_exec("execing '%s'\n", argv[0]);
|
||||||
sigprocmask(SIG_SETMASK, &G.inherited_set, NULL);
|
sigprocmask(SIG_SETMASK, &G.inherited_set, NULL);
|
||||||
execvp(argv[0], argv);
|
execvp(argv[0], argv);
|
||||||
@ -2761,7 +2804,9 @@ static void pseudo_exec(nommu_save_t *nommu_save,
|
|||||||
* since this process is about to exit */
|
* since this process is about to exit */
|
||||||
_exit(rcode);
|
_exit(rcode);
|
||||||
#else
|
#else
|
||||||
re_execute_shell(command->group_as_string, 0);
|
re_execute_shell(command->group_as_string,
|
||||||
|
G.global_argv[0],
|
||||||
|
G.global_argv + 1);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3085,19 +3130,23 @@ static int run_pipe(struct pipe *pi)
|
|||||||
|
|
||||||
while ((funcp = *funcpp) != NULL) {
|
while ((funcp = *funcpp) != NULL) {
|
||||||
if (strcmp(funcp->name, command->argv[0]) == 0) {
|
if (strcmp(funcp->name, command->argv[0]) == 0) {
|
||||||
debug_printf_exec("replacing function '%s'", funcp->name);
|
debug_printf_exec("replacing function '%s'\n", funcp->name);
|
||||||
free(funcp->name);
|
free(funcp->name);
|
||||||
free_pipe_list(funcp->body, /* indent: */ 0);
|
free_pipe_list(funcp->body, /* indent: */ 0);
|
||||||
goto skip;
|
goto skip;
|
||||||
}
|
}
|
||||||
funcpp = &funcp->next;
|
funcpp = &funcp->next;
|
||||||
}
|
}
|
||||||
debug_printf_exec("remembering new function '%s'", funcp->name);
|
debug_printf_exec("remembering new function '%s'\n", command->argv[0]);
|
||||||
funcp = *funcpp = xzalloc(sizeof(*funcp));
|
funcp = *funcpp = xzalloc(sizeof(*funcp));
|
||||||
/*funcp->next = NULL;*/
|
/*funcp->next = NULL;*/
|
||||||
skip:
|
skip:
|
||||||
funcp->name = command->argv[0];
|
funcp->name = command->argv[0];
|
||||||
funcp->body = command->group;
|
funcp->body = command->group;
|
||||||
|
#if !BB_MMU
|
||||||
|
funcp->body_as_string = command->group_as_string;
|
||||||
|
command->group_as_string = NULL;
|
||||||
|
#endif
|
||||||
command->group = NULL;
|
command->group = NULL;
|
||||||
command->argv[0] = NULL;
|
command->argv[0] = NULL;
|
||||||
free_strings(command->argv);
|
free_strings(command->argv);
|
||||||
@ -3160,6 +3209,7 @@ static int run_pipe(struct pipe *pi)
|
|||||||
|
|
||||||
x = find_builtin(argv_expanded[0]);
|
x = find_builtin(argv_expanded[0]);
|
||||||
#if ENABLE_HUSH_FUNCTIONS
|
#if ENABLE_HUSH_FUNCTIONS
|
||||||
|
funcp = NULL;
|
||||||
if (!x)
|
if (!x)
|
||||||
funcp = find_function(argv_expanded[0]);
|
funcp = find_function(argv_expanded[0]);
|
||||||
#endif
|
#endif
|
||||||
@ -3171,7 +3221,6 @@ static int run_pipe(struct pipe *pi)
|
|||||||
goto clean_up_and_ret1;
|
goto clean_up_and_ret1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug_printf("builtin inline %s\n", argv_expanded[0]);
|
|
||||||
/* XXX setup_redirects acts on file descriptors, not FILEs.
|
/* XXX setup_redirects acts on file descriptors, not FILEs.
|
||||||
* This is perfect for work that comes after exec().
|
* This is perfect for work that comes after exec().
|
||||||
* Is it really safe for inline use? Experimentally,
|
* Is it really safe for inline use? Experimentally,
|
||||||
@ -3681,7 +3730,7 @@ static int run_list(struct pipe *pi)
|
|||||||
/* We only ran a builtin: rcode is already known
|
/* We only ran a builtin: rcode is already known
|
||||||
* and we don't need to wait for anything. */
|
* and we don't need to wait for anything. */
|
||||||
G.last_exitcode = rcode;
|
G.last_exitcode = rcode;
|
||||||
debug_printf_exec(": builtin exitcode %d\n", rcode);
|
debug_printf_exec(": builtin/func exitcode %d\n", rcode);
|
||||||
check_and_run_traps(0);
|
check_and_run_traps(0);
|
||||||
#if ENABLE_HUSH_LOOPS
|
#if ENABLE_HUSH_LOOPS
|
||||||
/* Was it "break" or "continue"? */
|
/* Was it "break" or "continue"? */
|
||||||
@ -4497,7 +4546,9 @@ static FILE *generate_stream_from_string(const char *s)
|
|||||||
* huge=`cat BIG` # was blocking here forever
|
* huge=`cat BIG` # was blocking here forever
|
||||||
* echo OK
|
* echo OK
|
||||||
*/
|
*/
|
||||||
re_execute_shell(s, 0);
|
re_execute_shell(s,
|
||||||
|
G.global_argv[0],
|
||||||
|
G.global_argv + 1);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4561,34 +4612,34 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
|
|||||||
|
|
||||||
debug_printf_parse("parse_group entered\n");
|
debug_printf_parse("parse_group entered\n");
|
||||||
#if ENABLE_HUSH_FUNCTIONS
|
#if ENABLE_HUSH_FUNCTIONS
|
||||||
if (ch == '(') {
|
if (ch == '(' && !dest->o_quoted) {
|
||||||
if (!dest->o_quoted) {
|
if (dest->length)
|
||||||
if (dest->length)
|
done_word(dest, ctx);
|
||||||
done_word(dest, ctx);
|
if (!command->argv)
|
||||||
if (!command->argv)
|
goto skip; /* (... */
|
||||||
goto skip; /* (... */
|
if (command->argv[1]) { /* word word ... (... */
|
||||||
if (command->argv[1]) { /* word word ... (... */
|
syntax_error_unexpected_ch('(');
|
||||||
syntax_error("unexpected character (");
|
return 1;
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
/* it is "word(..." or "word (..." */
|
|
||||||
do
|
|
||||||
ch = i_getch(input);
|
|
||||||
while (ch == ' ' || ch == '\t');
|
|
||||||
if (ch != ')') {
|
|
||||||
syntax_error("unexpected character X");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
do
|
|
||||||
ch = i_getch(input);
|
|
||||||
while (ch == ' ' || ch == '\t' || ch == '\n');
|
|
||||||
if (ch != '{') {
|
|
||||||
syntax_error("unexpected character X");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
command->grp_type = GRP_FUNCTION;
|
|
||||||
goto skip;
|
|
||||||
}
|
}
|
||||||
|
/* it is "word(..." or "word (..." */
|
||||||
|
do
|
||||||
|
ch = i_getch(input);
|
||||||
|
while (ch == ' ' || ch == '\t');
|
||||||
|
if (ch != ')') {
|
||||||
|
syntax_error_unexpected_ch(ch);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
nommu_addchr(&ctx->as_string, ch);
|
||||||
|
do
|
||||||
|
ch = i_getch(input);
|
||||||
|
while (ch == ' ' || ch == '\t' || ch == '\n');
|
||||||
|
if (ch != '{') {
|
||||||
|
syntax_error_unexpected_ch(ch);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
nommu_addchr(&ctx->as_string, ch);
|
||||||
|
command->grp_type = GRP_FUNCTION;
|
||||||
|
goto skip;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (command->argv /* word [word]{... */
|
if (command->argv /* word [word]{... */
|
||||||
@ -5452,7 +5503,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
/* proper use of this character is caught by end_trigger:
|
/* proper use of this character is caught by end_trigger:
|
||||||
* if we see {, we call parse_group(..., end_trigger='}')
|
* if we see {, we call parse_group(..., end_trigger='}')
|
||||||
* and it will match } earlier (not here). */
|
* and it will match } earlier (not here). */
|
||||||
syntax_error("unexpected } or )");
|
syntax_error_unexpected_ch(ch);
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
default:
|
default:
|
||||||
if (HUSH_DEBUG)
|
if (HUSH_DEBUG)
|
||||||
|
Loading…
Reference in New Issue
Block a user