hush: deal with some easier TODOs
function old new delta is_well_formed_var_name - 87 +87 builtin_read 49 86 +37 die_if_script - 31 +31 syntax_error_unterminated - 28 +28 syntax_error 26 51 +25 done_word 768 788 +20 syntax_error_at - 12 +12 parse_stream_dquoted 320 328 +8 expand_variables 2064 2063 -1 run_list 1225 1220 -5 add_till_closing_paren 308 303 -5 add_till_backquote 111 106 -5 handle_dollar 812 803 -9 parse_stream 2378 2356 -22 parse_redirect 408 372 -36 maybe_die 44 - -44 is_assignment 215 134 -81 ------------------------------------------------------------------------------ (add/remove: 4/1 grow/shrink: 4/8 up/down: 248/-208) Total: 40 bytes
This commit is contained in:
parent
1943aec2ec
commit
05d3b7cc0d
289
shell/hush.c
289
shell/hush.c
@ -65,8 +65,6 @@
|
|||||||
* Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
|
* Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
|
||||||
*/
|
*/
|
||||||
#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */
|
#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */
|
||||||
//TODO: pull in some .h and find out whether we have SINGLE_APPLET_MAIN?
|
|
||||||
//#include "applet_tables.h" doesn't work
|
|
||||||
#include <glob.h>
|
#include <glob.h>
|
||||||
/* #include <dmalloc.h> */
|
/* #include <dmalloc.h> */
|
||||||
#if ENABLE_HUSH_CASE
|
#if ENABLE_HUSH_CASE
|
||||||
@ -80,11 +78,18 @@
|
|||||||
|
|
||||||
|
|
||||||
/* Debug build knobs */
|
/* Debug build knobs */
|
||||||
//#define LEAK_HUNTING 1
|
#define LEAK_HUNTING 0
|
||||||
//#define WANT_TO_TEST_NOMMU 1
|
#define BUILD_AS_NOMMU 0
|
||||||
|
/* Enable/disable sanity checks. Ok to enable in production,
|
||||||
|
* only adds a bit of bloat. Set to >1 to get non-production level verbosity.
|
||||||
|
* Keeping 1 for now even in released versions.
|
||||||
|
*/
|
||||||
|
#define HUSH_DEBUG 1
|
||||||
|
/* In progress... */
|
||||||
|
#define ENABLE_HUSH_FUNCTIONS 0
|
||||||
|
|
||||||
|
|
||||||
#ifdef WANT_TO_TEST_NOMMU
|
#if BUILD_AS_NOMMU
|
||||||
# undef BB_MMU
|
# undef BB_MMU
|
||||||
# undef USE_FOR_NOMMU
|
# undef USE_FOR_NOMMU
|
||||||
# undef USE_FOR_MMU
|
# undef USE_FOR_MMU
|
||||||
@ -122,15 +127,6 @@
|
|||||||
#define IF_HAS_NO_KEYWORDS(...) __VA_ARGS__
|
#define IF_HAS_NO_KEYWORDS(...) __VA_ARGS__
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Enable/disable sanity checks. Ok to enable in production,
|
|
||||||
* only adds a bit of bloat.
|
|
||||||
* Keeping unconditionally on for now.
|
|
||||||
*/
|
|
||||||
#define HUSH_DEBUG 1
|
|
||||||
/* In progress... */
|
|
||||||
#define ENABLE_HUSH_FUNCTIONS 0
|
|
||||||
|
|
||||||
|
|
||||||
/* If you comment out one of these below, it will be #defined later
|
/* If you comment out one of these below, it will be #defined later
|
||||||
* to perform debug printfs to stderr: */
|
* to perform debug printfs to stderr: */
|
||||||
#define debug_printf(...) do {} while (0)
|
#define debug_printf(...) do {} while (0)
|
||||||
@ -216,40 +212,6 @@ static void debug_print_strings(const char *prefix, char **vv)
|
|||||||
#define debug_print_strings(prefix, vv) ((void)0)
|
#define debug_print_strings(prefix, vv) ((void)0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
|
||||||
* Leak hunting. Use hush_leaktool.sh for post-processing.
|
|
||||||
*/
|
|
||||||
#ifdef LEAK_HUNTING
|
|
||||||
static void *xxmalloc(int lineno, size_t size)
|
|
||||||
{
|
|
||||||
void *ptr = xmalloc((size + 0xff) & ~0xff);
|
|
||||||
fdprintf(2, "line %d: malloc %p\n", lineno, ptr);
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
static void *xxrealloc(int lineno, void *ptr, size_t size)
|
|
||||||
{
|
|
||||||
ptr = xrealloc(ptr, (size + 0xff) & ~0xff);
|
|
||||||
fdprintf(2, "line %d: realloc %p\n", lineno, ptr);
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
static char *xxstrdup(int lineno, const char *str)
|
|
||||||
{
|
|
||||||
char *ptr = xstrdup(str);
|
|
||||||
fdprintf(2, "line %d: strdup %p\n", lineno, ptr);
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
static void xxfree(void *ptr)
|
|
||||||
{
|
|
||||||
fdprintf(2, "free %p\n", ptr);
|
|
||||||
free(ptr);
|
|
||||||
}
|
|
||||||
#define xmalloc(s) xxmalloc(__LINE__, s)
|
|
||||||
#define xrealloc(p, s) xxrealloc(__LINE__, p, s)
|
|
||||||
#define xstrdup(s) xxstrdup(__LINE__, s)
|
|
||||||
#define free(p) xxfree(p)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#define ERR_PTR ((void*)(long)1)
|
#define ERR_PTR ((void*)(long)1)
|
||||||
|
|
||||||
#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
|
#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
|
||||||
@ -662,28 +624,100 @@ static const struct built_in_command bltins[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static void maybe_die(const char *notice, const char *msg)
|
/* Leak hunting. Use hush_leaktool.sh for post-processing.
|
||||||
|
*/
|
||||||
|
#if LEAK_HUNTING
|
||||||
|
static void *xxmalloc(int lineno, size_t size)
|
||||||
|
{
|
||||||
|
void *ptr = xmalloc((size + 0xff) & ~0xff);
|
||||||
|
fdprintf(2, "line %d: malloc %p\n", lineno, ptr);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
static void *xxrealloc(int lineno, void *ptr, size_t size)
|
||||||
|
{
|
||||||
|
ptr = xrealloc(ptr, (size + 0xff) & ~0xff);
|
||||||
|
fdprintf(2, "line %d: realloc %p\n", lineno, ptr);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
static char *xxstrdup(int lineno, const char *str)
|
||||||
|
{
|
||||||
|
char *ptr = xstrdup(str);
|
||||||
|
fdprintf(2, "line %d: strdup %p\n", lineno, ptr);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
static void xxfree(void *ptr)
|
||||||
|
{
|
||||||
|
fdprintf(2, "free %p\n", ptr);
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
#define xmalloc(s) xxmalloc(__LINE__, s)
|
||||||
|
#define xrealloc(p, s) xxrealloc(__LINE__, p, s)
|
||||||
|
#define xstrdup(s) xxstrdup(__LINE__, s)
|
||||||
|
#define free(p) xxfree(p)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Syntax and runtime errors. They always abort scripts.
|
||||||
|
* In interactive use they usually discard unparsed and/or unexecuted commands
|
||||||
|
* and return to the prompt.
|
||||||
|
* HUSH_DEBUG >= 2 prints line number in this file where it was detected.
|
||||||
|
*/
|
||||||
|
#if HUSH_DEBUG < 2
|
||||||
|
# define die_if_script(lineno, fmt, msg) die_if_script(fmt, msg)
|
||||||
|
# define syntax_error(lineno, msg) syntax_error(msg)
|
||||||
|
# define syntax_error_at(lineno, msg) syntax_error_at(msg)
|
||||||
|
# define syntax_error_unterminated(lineno, ch) syntax_error_unterminated(ch)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void die_if_script(unsigned lineno, const char *fmt, const char *msg)
|
||||||
{
|
{
|
||||||
/* Was using fancy stuff:
|
|
||||||
* (G_interactive_fd ? bb_error_msg : bb_error_msg_and_die)(...params...)
|
|
||||||
* but it SEGVs. ?! Oh well... explicit temp ptr works around that */
|
|
||||||
void FAST_FUNC (*fp)(const char *s, ...) = bb_error_msg_and_die;
|
void FAST_FUNC (*fp)(const char *s, ...) = bb_error_msg_and_die;
|
||||||
#if ENABLE_HUSH_INTERACTIVE
|
#if ENABLE_HUSH_INTERACTIVE
|
||||||
if (G_interactive_fd)
|
if (G_interactive_fd)
|
||||||
fp = bb_error_msg;
|
fp = bb_error_msg;
|
||||||
#endif
|
#endif
|
||||||
fp(msg ? "%s: %s" : notice, notice, msg);
|
#if HUSH_DEBUG >= 2
|
||||||
|
bb_error_msg("hush.c:%u", lineno);
|
||||||
|
#endif
|
||||||
|
fp(fmt, msg);
|
||||||
}
|
}
|
||||||
#if 1
|
|
||||||
#define syntax(msg) maybe_die("syntax error", msg);
|
static void syntax_error(unsigned lineno, const char *msg)
|
||||||
|
{
|
||||||
|
if (msg)
|
||||||
|
die_if_script(lineno, "syntax error: %s", msg);
|
||||||
|
else
|
||||||
|
die_if_script(lineno, "syntax error", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void syntax_error_at(unsigned lineno, const char *msg)
|
||||||
|
{
|
||||||
|
die_if_script(lineno, "syntax error at '%s'", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void syntax_error_unterminated(unsigned lineno, char ch)
|
||||||
|
{
|
||||||
|
char msg[2];
|
||||||
|
msg[0] = ch;
|
||||||
|
msg[1] = '\0';
|
||||||
|
die_if_script(lineno, "syntax error: unterminated %s", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if HUSH_DEBUG < 2
|
||||||
|
# undef die_if_script
|
||||||
|
# undef syntax_error
|
||||||
|
# undef syntax_error_at
|
||||||
|
# undef syntax_error_unterminated
|
||||||
#else
|
#else
|
||||||
/* Debug -- trick gcc to expand __LINE__ and convert to string */
|
# define die_if_script(fmt, msg) die_if_script(__LINE__, fmt, msg)
|
||||||
#define __syntax(msg, line) maybe_die("syntax error hush.c:" # line, msg)
|
# define syntax_error(msg) syntax_error(__LINE__, msg)
|
||||||
#define _syntax(msg, line) __syntax(msg, line)
|
# define syntax_error_at(msg) syntax_error_at(__LINE__, msg)
|
||||||
#define syntax(msg) _syntax(msg, __LINE__)
|
# define syntax_error_unterminated(ch) syntax_error_unterminated(__LINE__, ch)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Utility functions
|
||||||
|
*/
|
||||||
static int glob_needed(const char *s)
|
static int glob_needed(const char *s)
|
||||||
{
|
{
|
||||||
while (*s) {
|
while (*s) {
|
||||||
@ -696,14 +730,14 @@ static int glob_needed(const char *s)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_assignment(const char *s)
|
static int is_well_formed_var_name(const char *s, char terminator)
|
||||||
{
|
{
|
||||||
if (!s || !(isalpha(*s) || *s == '_'))
|
if (!s || !(isalpha(*s) || *s == '_'))
|
||||||
return 0;
|
return 0;
|
||||||
s++;
|
s++;
|
||||||
while (isalnum(*s) || *s == '_')
|
while (isalnum(*s) || *s == '_')
|
||||||
s++;
|
s++;
|
||||||
return *s == '=';
|
return *s == terminator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Replace each \x with x in place, return ptr past NUL. */
|
/* Replace each \x with x in place, return ptr past NUL. */
|
||||||
@ -747,7 +781,7 @@ static char **add_strings_to_strings(char **strings, char **add, int need_to_dup
|
|||||||
v[count1 + i] = (need_to_dup ? xstrdup(add[i]) : add[i]);
|
v[count1 + i] = (need_to_dup ? xstrdup(add[i]) : add[i]);
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
#ifdef LEAK_HUNTING
|
#if LEAK_HUNTING
|
||||||
static char **xx_add_strings_to_strings(int lineno, char **strings, char **add, int need_to_dup)
|
static char **xx_add_strings_to_strings(int lineno, char **strings, char **add, int need_to_dup)
|
||||||
{
|
{
|
||||||
char **ptr = add_strings_to_strings(strings, add, need_to_dup);
|
char **ptr = add_strings_to_strings(strings, add, need_to_dup);
|
||||||
@ -765,7 +799,7 @@ static char **add_string_to_strings(char **strings, char *add)
|
|||||||
v[1] = NULL;
|
v[1] = NULL;
|
||||||
return add_strings_to_strings(strings, v, /*dup:*/ 0);
|
return add_strings_to_strings(strings, v, /*dup:*/ 0);
|
||||||
}
|
}
|
||||||
#ifdef LEAK_HUNTING
|
#if LEAK_HUNTING
|
||||||
static char **xx_add_string_to_strings(int lineno, char **strings, char *add)
|
static char **xx_add_string_to_strings(int lineno, char **strings, char *add)
|
||||||
{
|
{
|
||||||
char **ptr = add_string_to_strings(strings, add);
|
char **ptr = add_string_to_strings(strings, add);
|
||||||
@ -1911,12 +1945,19 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
|
|||||||
free(exp_str);
|
free(exp_str);
|
||||||
|
|
||||||
if (errcode < 0) {
|
if (errcode < 0) {
|
||||||
|
const char *msg = "error in arithmetic";
|
||||||
switch (errcode) {
|
switch (errcode) {
|
||||||
case -3: maybe_die("arith", "exponent less than 0"); break;
|
case -3:
|
||||||
case -2: maybe_die("arith", "divide by zero"); break;
|
msg = "exponent less than 0";
|
||||||
case -5: maybe_die("arith", "expression recursion loop detected"); break;
|
break;
|
||||||
default: maybe_die("arith", "syntax error"); break;
|
case -2:
|
||||||
|
msg = "divide by 0";
|
||||||
|
break;
|
||||||
|
case -5:
|
||||||
|
msg = "expression recursion loop detected";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
die_if_script(msg, NULL);
|
||||||
}
|
}
|
||||||
debug_printf_subst("ARITH RES '"arith_t_fmt"'\n", res);
|
debug_printf_subst("ARITH RES '"arith_t_fmt"'\n", res);
|
||||||
sprintf(arith_buf, arith_t_fmt, res);
|
sprintf(arith_buf, arith_t_fmt, res);
|
||||||
@ -1951,7 +1992,8 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
|
|||||||
exp_save = var[exp_off];
|
exp_save = var[exp_off];
|
||||||
exp_null = exp_save == ':';
|
exp_null = exp_save == ':';
|
||||||
exp_word = var + exp_off;
|
exp_word = var + exp_off;
|
||||||
if (exp_null) ++exp_word;
|
if (exp_null)
|
||||||
|
++exp_word;
|
||||||
exp_op = *exp_word++;
|
exp_op = *exp_word++;
|
||||||
var[exp_off] = '\0';
|
var[exp_off] = '\0';
|
||||||
}
|
}
|
||||||
@ -1995,17 +2037,28 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
|
|||||||
exp_null ? "true" : "false", exp_test);
|
exp_null ? "true" : "false", exp_test);
|
||||||
if (exp_test) {
|
if (exp_test) {
|
||||||
if (exp_op == '?')
|
if (exp_op == '?')
|
||||||
maybe_die(var, *exp_word ? exp_word : "parameter null or not set");
|
//TODO: what does interactive bash
|
||||||
|
/* ${var?[error_msg_if_unset]} */
|
||||||
|
/* ${var:?[error_msg_if_unset_or_null]} */
|
||||||
|
/* mimic bash message */
|
||||||
|
if (*exp_word) {
|
||||||
|
char *msg = xasprintf("%s: %s", var, exp_word);
|
||||||
|
die_if_script("%s", msg);
|
||||||
|
free(msg);
|
||||||
|
} else {
|
||||||
|
die_if_script("%s: parameter null or not set", var);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
val = exp_word;
|
val = exp_word;
|
||||||
|
|
||||||
if (exp_op == '=') {
|
if (exp_op == '=') {
|
||||||
|
/* ${var=[word]} or ${var:=[word]} */
|
||||||
if (isdigit(var[0]) || var[0] == '#') {
|
if (isdigit(var[0]) || var[0] == '#') {
|
||||||
maybe_die(var, "special vars cannot assign in this way");
|
/* mimic bash message */
|
||||||
|
die_if_script("$%s: cannot assign in this way", var);
|
||||||
val = NULL;
|
val = NULL;
|
||||||
} else {
|
} else {
|
||||||
char *new_var = xmalloc(strlen(var) + strlen(val) + 2);
|
char *new_var = xasprintf("%s=%s", var, val);
|
||||||
sprintf(new_var, "%s=%s", var, val);
|
|
||||||
set_local_var(new_var, -1, 0);
|
set_local_var(new_var, -1, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3280,7 +3333,7 @@ static int run_list(struct pipe *pi)
|
|||||||
continue;
|
continue;
|
||||||
/* current word is FOR or IN (BOLD in comments below) */
|
/* current word is FOR or IN (BOLD in comments below) */
|
||||||
if (cpipe->next == NULL) {
|
if (cpipe->next == NULL) {
|
||||||
syntax("malformed for");
|
syntax_error("malformed for");
|
||||||
debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
|
debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -3291,7 +3344,7 @@ static int run_list(struct pipe *pi)
|
|||||||
if (cpipe->res_word == RES_IN /* "for v IN a b; not_do..."? */
|
if (cpipe->res_word == RES_IN /* "for v IN a b; not_do..."? */
|
||||||
|| cpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */
|
|| cpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */
|
||||||
) {
|
) {
|
||||||
syntax("malformed for");
|
syntax_error("malformed for");
|
||||||
debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
|
debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -3443,7 +3496,7 @@ static int run_list(struct pipe *pi)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* Insert next value from for_lcur */
|
/* Insert next value from for_lcur */
|
||||||
//TODO: does it need escaping?
|
/* note: *for_lcur already has quotes removed, $var expanded, etc */
|
||||||
pi->cmds[0].argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++);
|
pi->cmds[0].argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++);
|
||||||
pi->cmds[0].assignment_cnt = 1;
|
pi->cmds[0].assignment_cnt = 1;
|
||||||
}
|
}
|
||||||
@ -3846,7 +3899,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx)
|
|||||||
#endif
|
#endif
|
||||||
if (r->flag == 0) { /* '!' */
|
if (r->flag == 0) { /* '!' */
|
||||||
if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */
|
if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */
|
||||||
syntax("! ! command");
|
syntax_error("! ! command");
|
||||||
IF_HAS_KEYWORDS(ctx->ctx_res_w = RES_SNTX;)
|
IF_HAS_KEYWORDS(ctx->ctx_res_w = RES_SNTX;)
|
||||||
}
|
}
|
||||||
ctx->ctx_inverted = 1;
|
ctx->ctx_inverted = 1;
|
||||||
@ -3860,7 +3913,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx)
|
|||||||
initialize_context(ctx);
|
initialize_context(ctx);
|
||||||
ctx->stack = old;
|
ctx->stack = old;
|
||||||
} else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) {
|
} else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) {
|
||||||
syntax(word->data);
|
syntax_error_at(word->data);
|
||||||
ctx->ctx_res_w = RES_SNTX;
|
ctx->ctx_res_w = RES_SNTX;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -3943,7 +3996,7 @@ static int done_word(o_string *word, struct parse_context *ctx)
|
|||||||
* while if false; then false; fi; do; break; done
|
* while if false; then false; fi; do; break; done
|
||||||
* TODO? */
|
* TODO? */
|
||||||
if (command->group) {
|
if (command->group) {
|
||||||
syntax(word->data);
|
syntax_error_at(word->data);
|
||||||
debug_printf_parse("done_word return 1: syntax error, "
|
debug_printf_parse("done_word return 1: syntax error, "
|
||||||
"groups and arglists don't mix\n");
|
"groups and arglists don't mix\n");
|
||||||
return 1;
|
return 1;
|
||||||
@ -4008,7 +4061,10 @@ static int done_word(o_string *word, struct parse_context *ctx)
|
|||||||
* as it is "for v; in ...". FOR and IN become two pipe structs
|
* as it is "for v; in ...". FOR and IN become two pipe structs
|
||||||
* in parse tree. */
|
* in parse tree. */
|
||||||
if (ctx->ctx_res_w == RES_FOR) {
|
if (ctx->ctx_res_w == RES_FOR) {
|
||||||
//TODO: check that command->argv[0] is a valid variable name!
|
if (!is_well_formed_var_name(command->argv[0], '\0')) {
|
||||||
|
syntax_error("malformed variable name in for");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
done_pipe(ctx, PIPE_SEQ);
|
done_pipe(ctx, PIPE_SEQ);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -4091,12 +4147,6 @@ static int parse_redirect(struct parse_context *ctx,
|
|||||||
nommu_addchr(&ctx->as_string, ch);
|
nommu_addchr(&ctx->as_string, ch);
|
||||||
ch = i_peek(input);
|
ch = i_peek(input);
|
||||||
}
|
}
|
||||||
/* <<[-] word is the same as <<[-]word */
|
|
||||||
while (ch == ' ' || ch == '\t') {
|
|
||||||
ch = i_getch(input);
|
|
||||||
nommu_addchr(&ctx->as_string, ch);
|
|
||||||
ch = i_peek(input);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (style == REDIRECT_OVERWRITE && dup_num == -1) {
|
if (style == REDIRECT_OVERWRITE && dup_num == -1) {
|
||||||
@ -4222,7 +4272,7 @@ static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_
|
|||||||
{
|
{
|
||||||
struct pipe *pi = ctx->list_head;
|
struct pipe *pi = ctx->list_head;
|
||||||
|
|
||||||
while (pi) {
|
while (pi && heredoc_cnt) {
|
||||||
int i;
|
int i;
|
||||||
struct command *cmd = pi->cmds;
|
struct command *cmd = pi->cmds;
|
||||||
|
|
||||||
@ -4238,16 +4288,12 @@ static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_
|
|||||||
if (redir->rd_type == REDIRECT_HEREDOC) {
|
if (redir->rd_type == REDIRECT_HEREDOC) {
|
||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
if (heredoc_cnt <= 0) {
|
|
||||||
syntax("heredoc BUG 1");
|
|
||||||
return 1; /* error */
|
|
||||||
}
|
|
||||||
redir->rd_type = REDIRECT_HEREDOC2;
|
redir->rd_type = REDIRECT_HEREDOC2;
|
||||||
/* redir->dup is (ab)used to indicate <<- */
|
/* redir->dup is (ab)used to indicate <<- */
|
||||||
p = fetch_till_str(&ctx->as_string, input,
|
p = fetch_till_str(&ctx->as_string, input,
|
||||||
redir->rd_filename, redir->rd_dup & HEREDOC_SKIPTABS);
|
redir->rd_filename, redir->rd_dup & HEREDOC_SKIPTABS);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
syntax("unexpected EOF in here document");
|
syntax_error("unexpected EOF in here document");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
free(redir->rd_filename);
|
free(redir->rd_filename);
|
||||||
@ -4260,10 +4306,12 @@ static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_
|
|||||||
}
|
}
|
||||||
pi = pi->next;
|
pi = pi->next;
|
||||||
}
|
}
|
||||||
|
#if 0
|
||||||
/* Should be 0. If it isn't, it's a parse error */
|
/* Should be 0. If it isn't, it's a parse error */
|
||||||
if (heredoc_cnt)
|
if (heredoc_cnt)
|
||||||
syntax("heredoc BUG 2");
|
bb_error_msg_and_die("heredoc BUG 2");
|
||||||
return heredoc_cnt;
|
#endif
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -4388,7 +4436,7 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
|
|||||||
|| dest->length /* word(... */
|
|| dest->length /* word(... */
|
||||||
|| dest->o_quoted /* ""(... */
|
|| dest->o_quoted /* ""(... */
|
||||||
) {
|
) {
|
||||||
syntax(NULL);
|
syntax_error(NULL);
|
||||||
debug_printf_parse("parse_group return 1: "
|
debug_printf_parse("parse_group return 1: "
|
||||||
"syntax error, groups and arglists don't mix\n");
|
"syntax error, groups and arglists don't mix\n");
|
||||||
return 1;
|
return 1;
|
||||||
@ -4412,7 +4460,7 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
|
|||||||
#if !BB_MMU
|
#if !BB_MMU
|
||||||
free(as_string);
|
free(as_string);
|
||||||
#endif
|
#endif
|
||||||
syntax(NULL);
|
syntax_error(NULL);
|
||||||
debug_printf_parse("parse_group return 1: "
|
debug_printf_parse("parse_group return 1: "
|
||||||
"parse_stream returned %p\n", pipe_list);
|
"parse_stream returned %p\n", pipe_list);
|
||||||
return 1;
|
return 1;
|
||||||
@ -4439,7 +4487,7 @@ static int add_till_single_quote(o_string *dest, struct in_str *input)
|
|||||||
while (1) {
|
while (1) {
|
||||||
int ch = i_getch(input);
|
int ch = i_getch(input);
|
||||||
if (ch == EOF) {
|
if (ch == EOF) {
|
||||||
syntax("unterminated '");
|
syntax_error_unterminated('\'');
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (ch == '\'')
|
if (ch == '\'')
|
||||||
@ -4453,7 +4501,7 @@ static int add_till_double_quote(o_string *dest, struct in_str *input)
|
|||||||
while (1) {
|
while (1) {
|
||||||
int ch = i_getch(input);
|
int ch = i_getch(input);
|
||||||
if (ch == EOF) {
|
if (ch == EOF) {
|
||||||
syntax("unterminated \"");
|
syntax_error_unterminated('"');
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (ch == '"')
|
if (ch == '"')
|
||||||
@ -4491,7 +4539,7 @@ static int add_till_backquote(o_string *dest, struct in_str *input)
|
|||||||
while (1) {
|
while (1) {
|
||||||
int ch = i_getch(input);
|
int ch = i_getch(input);
|
||||||
if (ch == EOF) {
|
if (ch == EOF) {
|
||||||
syntax("unterminated `");
|
syntax_error_unterminated('`');
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (ch == '`')
|
if (ch == '`')
|
||||||
@ -4500,7 +4548,7 @@ static int add_till_backquote(o_string *dest, struct in_str *input)
|
|||||||
/* \x. Copy both chars unless it is \` */
|
/* \x. Copy both chars unless it is \` */
|
||||||
int ch2 = i_getch(input);
|
int ch2 = i_getch(input);
|
||||||
if (ch2 == EOF) {
|
if (ch2 == EOF) {
|
||||||
syntax("unterminated `");
|
syntax_error_unterminated('`');
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (ch2 != '`' && ch2 != '$' && ch2 != '\\')
|
if (ch2 != '`' && ch2 != '$' && ch2 != '\\')
|
||||||
@ -4528,7 +4576,7 @@ static int add_till_closing_paren(o_string *dest, struct in_str *input, bool dbl
|
|||||||
while (1) {
|
while (1) {
|
||||||
int ch = i_getch(input);
|
int ch = i_getch(input);
|
||||||
if (ch == EOF) {
|
if (ch == EOF) {
|
||||||
syntax("unterminated )");
|
syntax_error_unterminated(')');
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (ch == '(')
|
if (ch == '(')
|
||||||
@ -4560,7 +4608,7 @@ static int add_till_closing_paren(o_string *dest, struct in_str *input, bool dbl
|
|||||||
/* \x. Copy verbatim. Important for \(, \) */
|
/* \x. Copy verbatim. Important for \(, \) */
|
||||||
ch = i_getch(input);
|
ch = i_getch(input);
|
||||||
if (ch == EOF) {
|
if (ch == EOF) {
|
||||||
syntax("unterminated )");
|
syntax_error_unterminated(')');
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
o_addchr(dest, ch);
|
o_addchr(dest, ch);
|
||||||
@ -4678,7 +4726,7 @@ static int handle_dollar(o_string *as_string,
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
case_default:
|
case_default:
|
||||||
syntax("unterminated ${name}");
|
syntax_error("unterminated ${name}");
|
||||||
debug_printf_parse("handle_dollar return 1: unterminated ${name}\n");
|
debug_printf_parse("handle_dollar return 1: unterminated ${name}\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -4783,7 +4831,7 @@ static int parse_stream_dquoted(o_string *as_string,
|
|||||||
}
|
}
|
||||||
/* note: can't move it above ch == dquote_end check! */
|
/* note: can't move it above ch == dquote_end check! */
|
||||||
if (ch == EOF) {
|
if (ch == EOF) {
|
||||||
syntax("unterminated \"");
|
syntax_error_unterminated('"');
|
||||||
debug_printf_parse("parse_stream_dquoted return 1: unterminated \"\n");
|
debug_printf_parse("parse_stream_dquoted return 1: unterminated \"\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -4794,8 +4842,9 @@ static int parse_stream_dquoted(o_string *as_string,
|
|||||||
debug_printf_parse(": ch=%c (%d) escape=%d\n",
|
debug_printf_parse(": ch=%c (%d) escape=%d\n",
|
||||||
ch, ch, dest->o_escape);
|
ch, ch, dest->o_escape);
|
||||||
if (ch == '\\') {
|
if (ch == '\\') {
|
||||||
|
//TODO: check interactive behavior
|
||||||
if (next == EOF) {
|
if (next == EOF) {
|
||||||
syntax("\\<eof>");
|
syntax_error("\\<eof>");
|
||||||
debug_printf_parse("parse_stream_dquoted return 1: \\<eof>\n");
|
debug_printf_parse("parse_stream_dquoted return 1: \\<eof>\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -4839,7 +4888,7 @@ static int parse_stream_dquoted(o_string *as_string,
|
|||||||
if (ch == '='
|
if (ch == '='
|
||||||
&& (dest->o_assignment == MAYBE_ASSIGNMENT
|
&& (dest->o_assignment == MAYBE_ASSIGNMENT
|
||||||
|| dest->o_assignment == WORD_IS_KEYWORD)
|
|| dest->o_assignment == WORD_IS_KEYWORD)
|
||||||
&& is_assignment(dest->data)
|
&& is_well_formed_var_name(dest->data, '=')
|
||||||
) {
|
) {
|
||||||
dest->o_assignment = DEFINITELY_ASSIGNMENT;
|
dest->o_assignment = DEFINITELY_ASSIGNMENT;
|
||||||
}
|
}
|
||||||
@ -4905,7 +4954,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
struct pipe *pi;
|
struct pipe *pi;
|
||||||
|
|
||||||
if (heredoc_cnt) {
|
if (heredoc_cnt) {
|
||||||
syntax("unterminated here document");
|
syntax_error("unterminated here document");
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
if (done_word(&dest, &ctx)) {
|
if (done_word(&dest, &ctx)) {
|
||||||
@ -4943,7 +4992,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
if ((dest.o_assignment == MAYBE_ASSIGNMENT
|
if ((dest.o_assignment == MAYBE_ASSIGNMENT
|
||||||
|| dest.o_assignment == WORD_IS_KEYWORD)
|
|| dest.o_assignment == WORD_IS_KEYWORD)
|
||||||
&& ch == '='
|
&& ch == '='
|
||||||
&& is_assignment(dest.data)
|
&& is_well_formed_var_name(dest.data, '=')
|
||||||
) {
|
) {
|
||||||
dest.o_assignment = DEFINITELY_ASSIGNMENT;
|
dest.o_assignment = DEFINITELY_ASSIGNMENT;
|
||||||
}
|
}
|
||||||
@ -4994,7 +5043,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
* We require heredoc to be in enclosing {}/(),
|
* We require heredoc to be in enclosing {}/(),
|
||||||
* if any.
|
* if any.
|
||||||
*/
|
*/
|
||||||
syntax("unterminated here document");
|
syntax_error("unterminated here document");
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
if (done_word(&dest, &ctx)) {
|
if (done_word(&dest, &ctx)) {
|
||||||
@ -5051,7 +5100,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
break;
|
break;
|
||||||
case '\\':
|
case '\\':
|
||||||
if (next == EOF) {
|
if (next == EOF) {
|
||||||
syntax("\\<eof>");
|
syntax_error("\\<eof>");
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
o_addchr(&dest, '\\');
|
o_addchr(&dest, '\\');
|
||||||
@ -5074,7 +5123,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
while (1) {
|
while (1) {
|
||||||
ch = i_getch(input);
|
ch = i_getch(input);
|
||||||
if (ch == EOF) {
|
if (ch == EOF) {
|
||||||
syntax("unterminated '");
|
syntax_error_unterminated('\'');
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
nommu_addchr(&ctx.as_string, ch);
|
nommu_addchr(&ctx.as_string, ch);
|
||||||
@ -5126,7 +5175,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
}
|
}
|
||||||
#if 0
|
#if 0
|
||||||
else if (next == '(') {
|
else if (next == '(') {
|
||||||
syntax(">(process) not supported");
|
syntax_error(">(process) not supported");
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -5152,7 +5201,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
}
|
}
|
||||||
#if 0
|
#if 0
|
||||||
else if (next == '(') {
|
else if (next == '(') {
|
||||||
syntax("<(process) not supported");
|
syntax_error("<(process) not supported");
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -5246,7 +5295,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
ch = i_getch(input);
|
ch = i_getch(input);
|
||||||
} while (ch == ' ' || ch == '\n');
|
} while (ch == ' ' || ch == '\n');
|
||||||
if (ch != '{') {
|
if (ch != '{') {
|
||||||
syntax("was expecting {");
|
syntax_error("was expecting {");
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
ch = 'F'; /* magic value */
|
ch = 'F'; /* magic value */
|
||||||
@ -5266,7 +5315,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("unexpected } or )");
|
syntax_error("unexpected } or )");
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
default:
|
default:
|
||||||
if (HUSH_DEBUG)
|
if (HUSH_DEBUG)
|
||||||
@ -6111,8 +6160,16 @@ static int builtin_pwd(char **argv UNUSED_PARAM)
|
|||||||
static int builtin_read(char **argv)
|
static int builtin_read(char **argv)
|
||||||
{
|
{
|
||||||
char *string;
|
char *string;
|
||||||
const char *name = argv[1] ? argv[1] : "REPLY";
|
const char *name = "REPLY";
|
||||||
//TODO: check that argv[1] is a valid variable name
|
|
||||||
|
if (argv[1]) {
|
||||||
|
name = argv[1];
|
||||||
|
if (!is_well_formed_var_name(name, '\0')) {
|
||||||
|
/* Mimic bash message */
|
||||||
|
bb_error_msg("read: '%s': not a valid identifier", name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
string = xmalloc_reads(STDIN_FILENO, xasprintf("%s=", name), NULL);
|
string = xmalloc_reads(STDIN_FILENO, xasprintf("%s=", name), NULL);
|
||||||
return set_local_var(string, 0, 0);
|
return set_local_var(string, 0, 0);
|
||||||
|
@ -55,28 +55,28 @@ Format: 'expected actual'
|
|||||||
30 30
|
30 30
|
||||||
20 20
|
20 20
|
||||||
30 30
|
30 30
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
6 6
|
6 6
|
||||||
6,5,3 6,5,3
|
6,5,3 6,5,3
|
||||||
263 263
|
263 263
|
||||||
255 255
|
255 255
|
||||||
40 40
|
40 40
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
hush: arith: divide by zero
|
hush: divide by 0
|
||||||
hush: can't exec 'let': No such file or directory
|
hush: can't exec 'let': No such file or directory
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
hush: can't exec 'let': No such file or directory
|
hush: can't exec 'let': No such file or directory
|
||||||
abc
|
abc
|
||||||
def
|
def
|
||||||
ghi
|
ghi
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
16 16
|
16 16
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
9 9
|
9 9
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
9 9
|
9 9
|
||||||
9 9
|
9 9
|
||||||
9 9
|
9 9
|
||||||
@ -97,18 +97,18 @@ hush: arith: syntax error
|
|||||||
3 3
|
3 3
|
||||||
4 4
|
4 4
|
||||||
4 4
|
4 4
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
4 4
|
4 4
|
||||||
7 7
|
7 7
|
||||||
-7 -7
|
-7 -7
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
6 6
|
6 6
|
||||||
3 3
|
3 3
|
||||||
7 7
|
7 7
|
||||||
@ -119,19 +119,19 @@ hush: arith: syntax error
|
|||||||
2 2
|
2 2
|
||||||
-2 -2
|
-2 -2
|
||||||
1 1
|
1 1
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
5 5
|
5 5
|
||||||
1 1
|
1 1
|
||||||
4 4
|
4 4
|
||||||
0 0
|
0 0
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
8 12
|
8 12
|
||||||
hush: arith: syntax error
|
hush: error in arithmetic
|
||||||
42
|
42
|
||||||
42
|
42
|
||||||
42
|
42
|
||||||
|
@ -2,10 +2,10 @@ hush: syntax error: unterminated ${name}
|
|||||||
hush: syntax error: unterminated ${name}
|
hush: syntax error: unterminated ${name}
|
||||||
0
|
0
|
||||||
0
|
0
|
||||||
hush: 1: special vars cannot assign in this way
|
hush: $1: cannot assign in this way
|
||||||
hush: 1: special vars cannot assign in this way
|
hush: $1: cannot assign in this way
|
||||||
hush: 1: special vars cannot assign in this way
|
hush: $1: cannot assign in this way
|
||||||
hush: 1: special vars cannot assign in this way
|
hush: $1: cannot assign in this way
|
||||||
_aa
|
_aa
|
||||||
_aa
|
_aa
|
||||||
_aa
|
_aa
|
||||||
|
@ -5,8 +5,8 @@ hush: syntax error: unterminated ${name}
|
|||||||
_
|
_
|
||||||
hush: 1: parameter null or not set
|
hush: 1: parameter null or not set
|
||||||
hush: 1: parameter null or not set
|
hush: 1: parameter null or not set
|
||||||
hush: 1: word
|
hush: 1: message1
|
||||||
hush: 1: word
|
hush: 1: message1
|
||||||
_aaaa
|
_aaaa
|
||||||
_aaaa
|
_aaaa
|
||||||
_aaaa
|
_aaaa
|
||||||
@ -15,13 +15,13 @@ _aaaa
|
|||||||
_
|
_
|
||||||
hush: f: parameter null or not set
|
hush: f: parameter null or not set
|
||||||
hush: f: parameter null or not set
|
hush: f: parameter null or not set
|
||||||
hush: f: word
|
hush: f: message3
|
||||||
hush: f: word
|
hush: f: message3
|
||||||
_
|
_
|
||||||
_
|
_
|
||||||
hush: f: parameter null or not set
|
hush: f: parameter null or not set
|
||||||
_
|
_
|
||||||
hush: f: word
|
hush: f: message4
|
||||||
_fff
|
_fff
|
||||||
_fff
|
_fff
|
||||||
_fff
|
_fff
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
"$THIS_SH" -c 'set --; echo _$1'
|
"$THIS_SH" -c 'set --; echo _$1'
|
||||||
"$THIS_SH" -c 'set --; echo _${1?}'
|
"$THIS_SH" -c 'set --; echo _${1?}'
|
||||||
"$THIS_SH" -c 'set --; echo _${1:?}'
|
"$THIS_SH" -c 'set --; echo _${1:?}'
|
||||||
"$THIS_SH" -c 'set --; echo _${1?word}'
|
"$THIS_SH" -c 'set --; echo _${1?message1}'
|
||||||
"$THIS_SH" -c 'set --; echo _${1:?word}'
|
"$THIS_SH" -c 'set --; echo _${1:?message1}'
|
||||||
|
|
||||||
"$THIS_SH" -c 'set -- aaaa; echo _$1'
|
"$THIS_SH" -c 'set -- aaaa; echo _$1'
|
||||||
"$THIS_SH" -c 'set -- aaaa; echo _${1?}'
|
"$THIS_SH" -c 'set -- aaaa; echo _${1?}'
|
||||||
@ -24,14 +24,14 @@
|
|||||||
"$THIS_SH" -c 'unset f; echo _$f'
|
"$THIS_SH" -c 'unset f; echo _$f'
|
||||||
"$THIS_SH" -c 'unset f; echo _${f?}'
|
"$THIS_SH" -c 'unset f; echo _${f?}'
|
||||||
"$THIS_SH" -c 'unset f; echo _${f:?}'
|
"$THIS_SH" -c 'unset f; echo _${f:?}'
|
||||||
"$THIS_SH" -c 'unset f; echo _${f?word}'
|
"$THIS_SH" -c 'unset f; echo _${f?message3}'
|
||||||
"$THIS_SH" -c 'unset f; echo _${f:?word}'
|
"$THIS_SH" -c 'unset f; echo _${f:?message3}'
|
||||||
|
|
||||||
"$THIS_SH" -c 'f=; echo _$f'
|
"$THIS_SH" -c 'f=; echo _$f'
|
||||||
"$THIS_SH" -c 'f=; echo _${f?}'
|
"$THIS_SH" -c 'f=; echo _${f?}'
|
||||||
"$THIS_SH" -c 'f=; echo _${f:?}'
|
"$THIS_SH" -c 'f=; echo _${f:?}'
|
||||||
"$THIS_SH" -c 'f=; echo _${f?word}'
|
"$THIS_SH" -c 'f=; echo _${f?word}'
|
||||||
"$THIS_SH" -c 'f=; echo _${f:?word}'
|
"$THIS_SH" -c 'f=; echo _${f:?message4}'
|
||||||
|
|
||||||
"$THIS_SH" -c 'f=fff; echo _$f'
|
"$THIS_SH" -c 'f=fff; echo _$f'
|
||||||
"$THIS_SH" -c 'f=fff; echo _${f?}'
|
"$THIS_SH" -c 'f=fff; echo _${f?}'
|
||||||
|
Loading…
Reference in New Issue
Block a user