|
|
|
@@ -317,8 +317,10 @@ typedef struct {
|
|
|
|
|
/* I can almost use ordinary FILE *. Is open_memstream() universally
|
|
|
|
|
* available? Where is it documented? */
|
|
|
|
|
struct in_str {
|
|
|
|
|
const char *p;
|
|
|
|
|
char peek_buf[2];
|
|
|
|
|
union {
|
|
|
|
|
const char *p;
|
|
|
|
|
int cached_ch;
|
|
|
|
|
};
|
|
|
|
|
#if ENABLE_HUSH_INTERACTIVE
|
|
|
|
|
int __promptme;
|
|
|
|
|
int promptmode;
|
|
|
|
@@ -1112,8 +1114,10 @@ static int file_get(struct in_str *i)
|
|
|
|
|
|
|
|
|
|
ch = 0;
|
|
|
|
|
/* If there is data waiting, eat it up */
|
|
|
|
|
if (i->p && *i->p) {
|
|
|
|
|
ch = *i->p++;
|
|
|
|
|
if (i->cached_ch) {
|
|
|
|
|
ch = i->cached_ch ^ 0x100;
|
|
|
|
|
if (ch != EOF)
|
|
|
|
|
i->cached_ch = 0;
|
|
|
|
|
} else {
|
|
|
|
|
/* need to double check i->file because we might be doing something
|
|
|
|
|
* more complicated by now, like sourcing or substituting. */
|
|
|
|
@@ -1133,7 +1137,7 @@ static int file_get(struct in_str *i)
|
|
|
|
|
{
|
|
|
|
|
ch = fgetc(i->file);
|
|
|
|
|
}
|
|
|
|
|
debug_printf("b_getch: got a %d\n", ch);
|
|
|
|
|
debug_printf("file_get: got a %d\n", ch);
|
|
|
|
|
}
|
|
|
|
|
#if ENABLE_HUSH_INTERACTIVE
|
|
|
|
|
if (ch == '\n')
|
|
|
|
@@ -1147,14 +1151,14 @@ static int file_get(struct in_str *i)
|
|
|
|
|
*/
|
|
|
|
|
static int file_peek(struct in_str *i)
|
|
|
|
|
{
|
|
|
|
|
if (i->p && *i->p) {
|
|
|
|
|
return *i->p;
|
|
|
|
|
int ch;
|
|
|
|
|
if (i->cached_ch) {
|
|
|
|
|
return i->cached_ch ^ 0x100;
|
|
|
|
|
}
|
|
|
|
|
i->peek_buf[0] = fgetc(i->file);
|
|
|
|
|
i->peek_buf[1] = '\0';
|
|
|
|
|
i->p = i->peek_buf;
|
|
|
|
|
debug_printf("b_peek: got a %d\n", *i->p);
|
|
|
|
|
return *i->p;
|
|
|
|
|
ch = fgetc(i->file);
|
|
|
|
|
i->cached_ch = ch ^ 0x100; /* ^ 0x100 so that it is never 0 */
|
|
|
|
|
debug_printf("file_peek: got a %d '%c'\n", ch, ch);
|
|
|
|
|
return ch;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void setup_file_in_str(struct in_str *i, FILE *f)
|
|
|
|
@@ -1670,10 +1674,10 @@ static int run_pipe_real(struct pipe *pi)
|
|
|
|
|
int export_me = 0;
|
|
|
|
|
char *name, *value;
|
|
|
|
|
name = xstrdup(argv[i]);
|
|
|
|
|
debug_printf("Local environment set: %s\n", name);
|
|
|
|
|
debug_printf("local environment set: %s\n", name);
|
|
|
|
|
value = strchr(name, '=');
|
|
|
|
|
if (value)
|
|
|
|
|
*value = 0;
|
|
|
|
|
*value = '\0';
|
|
|
|
|
if (get_local_var(name)) {
|
|
|
|
|
export_me = 1;
|
|
|
|
|
}
|
|
|
|
@@ -2364,9 +2368,10 @@ static const char *get_local_var(const char *s)
|
|
|
|
|
|
|
|
|
|
if (!s)
|
|
|
|
|
return NULL;
|
|
|
|
|
for (cur = top_vars; cur; cur = cur->next)
|
|
|
|
|
for (cur = top_vars; cur; cur = cur->next) {
|
|
|
|
|
if (strcmp(cur->name, s) == 0)
|
|
|
|
|
return cur->value;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -2380,7 +2385,7 @@ static int set_local_var(const char *s, int flg_export)
|
|
|
|
|
int result = 0;
|
|
|
|
|
struct variables *cur;
|
|
|
|
|
|
|
|
|
|
name = strdup(s);
|
|
|
|
|
name = xstrdup(s);
|
|
|
|
|
|
|
|
|
|
/* Assume when we enter this function that we are already in
|
|
|
|
|
* NAME=VALUE format. So the first order of business is to
|
|
|
|
@@ -2394,48 +2399,46 @@ static int set_local_var(const char *s, int flg_export)
|
|
|
|
|
*value++ = '\0';
|
|
|
|
|
|
|
|
|
|
for (cur = top_vars; cur; cur = cur->next) {
|
|
|
|
|
if (strcmp(cur->name, name) == 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cur) {
|
|
|
|
|
if (strcmp(cur->value, value) == 0) {
|
|
|
|
|
if (flg_export > 0 && cur->flg_export == 0)
|
|
|
|
|
cur->flg_export = flg_export;
|
|
|
|
|
else
|
|
|
|
|
result++;
|
|
|
|
|
} else if (cur->flg_read_only) {
|
|
|
|
|
bb_error_msg("%s: readonly variable", name);
|
|
|
|
|
result = -1;
|
|
|
|
|
} else {
|
|
|
|
|
if (flg_export > 0 || cur->flg_export > 1)
|
|
|
|
|
cur->flg_export = 1;
|
|
|
|
|
free((char*)cur->value);
|
|
|
|
|
|
|
|
|
|
cur->value = strdup(value);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cur = malloc(sizeof(struct variables));
|
|
|
|
|
if (!cur) {
|
|
|
|
|
result = -1;
|
|
|
|
|
} else {
|
|
|
|
|
cur->name = strdup(name);
|
|
|
|
|
if (!cur->name) {
|
|
|
|
|
free(cur);
|
|
|
|
|
if (strcmp(cur->name, name) == 0) {
|
|
|
|
|
if (strcmp(cur->value, value) == 0) {
|
|
|
|
|
if (flg_export > 0 && cur->flg_export == 0)
|
|
|
|
|
cur->flg_export = flg_export;
|
|
|
|
|
else
|
|
|
|
|
result++;
|
|
|
|
|
} else if (cur->flg_read_only) {
|
|
|
|
|
bb_error_msg("%s: readonly variable", name);
|
|
|
|
|
result = -1;
|
|
|
|
|
} else {
|
|
|
|
|
struct variables *bottom = top_vars;
|
|
|
|
|
if (flg_export > 0 || cur->flg_export > 1)
|
|
|
|
|
cur->flg_export = 1;
|
|
|
|
|
free((char*)cur->value);
|
|
|
|
|
cur->value = strdup(value);
|
|
|
|
|
cur->next = 0;
|
|
|
|
|
cur->flg_export = flg_export;
|
|
|
|
|
cur->flg_read_only = 0;
|
|
|
|
|
while (bottom->next)
|
|
|
|
|
bottom = bottom->next;
|
|
|
|
|
bottom->next = cur;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
goto skip;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: need simpler/generic rollback on malloc failure - see ash
|
|
|
|
|
cur = malloc(sizeof(*cur));
|
|
|
|
|
if (!cur) {
|
|
|
|
|
result = -1;
|
|
|
|
|
} else {
|
|
|
|
|
cur->name = strdup(name);
|
|
|
|
|
if (!cur->name) {
|
|
|
|
|
free(cur);
|
|
|
|
|
result = -1;
|
|
|
|
|
} else {
|
|
|
|
|
struct variables *bottom = top_vars;
|
|
|
|
|
cur->value = strdup(value);
|
|
|
|
|
cur->next = 0;
|
|
|
|
|
cur->flg_export = flg_export;
|
|
|
|
|
cur->flg_read_only = 0;
|
|
|
|
|
while (bottom->next)
|
|
|
|
|
bottom = bottom->next;
|
|
|
|
|
bottom->next = cur;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
skip:
|
|
|
|
|
if (result == 0 && cur->flg_export == 1) {
|
|
|
|
|
*(value-1) = '=';
|
|
|
|
|
result = putenv(name);
|
|
|
|
@@ -2975,11 +2978,16 @@ static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *i
|
|
|
|
|
int i, advance = 0;
|
|
|
|
|
char sep[] = " ";
|
|
|
|
|
int ch = input->peek(input); /* first character after the $ */
|
|
|
|
|
debug_printf("handle_dollar: ch=%c\n", ch);
|
|
|
|
|
|
|
|
|
|
debug_printf_parse("handle_dollar entered: ch='%c'\n", ch);
|
|
|
|
|
if (isalpha(ch)) {
|
|
|
|
|
b_addchr(dest, SPECIAL_VAR_SYMBOL);
|
|
|
|
|
ctx->child->sp++;
|
|
|
|
|
while (ch = b_peek(input), isalnum(ch) || ch == '_') {
|
|
|
|
|
while (1) {
|
|
|
|
|
ch = b_peek(input);
|
|
|
|
|
if (!isalnum(ch) && ch != '_')
|
|
|
|
|
break;
|
|
|
|
|
debug_printf_parse(": '%c'\n", ch);
|
|
|
|
|
b_getch(input);
|
|
|
|
|
b_addchr(dest, ch);
|
|
|
|
|
}
|
|
|
|
@@ -3014,14 +3022,16 @@ static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *i
|
|
|
|
|
/* XXX maybe someone will try to escape the '}' */
|
|
|
|
|
while (1) {
|
|
|
|
|
ch = b_getch(input);
|
|
|
|
|
if (ch == EOF || ch == '}')
|
|
|
|
|
if (ch == EOF) {
|
|
|
|
|
syntax();
|
|
|
|
|
debug_printf_parse("handle_dollar return 1: unterminated ${name}\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (ch == '}')
|
|
|
|
|
break;
|
|
|
|
|
debug_printf_parse(": '%c'\n", ch);
|
|
|
|
|
b_addchr(dest, ch);
|
|
|
|
|
}
|
|
|
|
|
if (ch != '}') {
|
|
|
|
|
syntax();
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
b_addchr(dest, SPECIAL_VAR_SYMBOL);
|
|
|
|
|
break;
|
|
|
|
|
case '(':
|
|
|
|
@@ -3052,6 +3062,7 @@ static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *i
|
|
|
|
|
* a nice size-optimized program. Hah! That'll be the day.
|
|
|
|
|
*/
|
|
|
|
|
if (advance) b_getch(input);
|
|
|
|
|
debug_printf_parse("handle_dollar return 0\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -3079,7 +3090,7 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|
|
|
|
|
|
|
|
|
while ((ch = b_getch(input)) != EOF) {
|
|
|
|
|
m = map[ch];
|
|
|
|
|
next = (ch == '\n') ? 0 : b_peek(input);
|
|
|
|
|
next = (ch == '\n') ? '\0' : b_peek(input);
|
|
|
|
|
debug_printf_parse(": ch=%c (%d) m=%d quote=%d\n",
|
|
|
|
|
ch, ch, m, dest->quote);
|
|
|
|
|
if (m == MAP_ORDINARY
|
|
|
|
|