mirror of
https://github.com/sheumann/hush.git
synced 2024-12-22 14:30:31 +00:00
hush: fix bug in interactive shell introduced yesterday
hush: fix `process subst` (2 bugs) NB: will delete and re-add hush_test in order to change file modes
This commit is contained in:
parent
e0a336747c
commit
3e9aaae5dc
86
shell/hush.c
86
shell/hush.c
@ -317,10 +317,10 @@ typedef struct {
|
||||
/* I can almost use ordinary FILE *. Is open_memstream() universally
|
||||
* available? Where is it documented? */
|
||||
struct in_str {
|
||||
union {
|
||||
const char *p;
|
||||
int cached_ch;
|
||||
};
|
||||
/* eof_flag=1: last char in ->p is really an EOF */
|
||||
char eof_flag; /* meaningless if ->p == NULL */
|
||||
char peek_buf[2];
|
||||
#if ENABLE_HUSH_INTERACTIVE
|
||||
int __promptme;
|
||||
int promptmode;
|
||||
@ -976,7 +976,7 @@ static int b_check_space(o_string *o, int len)
|
||||
|
||||
static int b_addchr(o_string *o, int ch)
|
||||
{
|
||||
debug_printf("b_addchr: %c %d %p\n", ch, o->length, o);
|
||||
debug_printf("b_addchr: '%c' o->lengtt=%d o=%p\n", ch, o->length, o);
|
||||
if (b_check_space(o, 1))
|
||||
return B_NOSPAC;
|
||||
o->data[o->length] = ch;
|
||||
@ -1079,12 +1079,13 @@ static const char* setup_prompt_string(int promptmode)
|
||||
static line_input_t *line_input_state;
|
||||
#endif
|
||||
|
||||
static int get_user_input(struct in_str *i)
|
||||
static void get_user_input(struct in_str *i)
|
||||
{
|
||||
static char the_command[ENABLE_FEATURE_EDITING ? BUFSIZ : 2];
|
||||
|
||||
int r;
|
||||
const char *prompt_str;
|
||||
|
||||
prompt_str = setup_prompt_string(i->promptmode);
|
||||
#if ENABLE_FEATURE_EDITING
|
||||
/*
|
||||
@ -1094,15 +1095,19 @@ static int get_user_input(struct in_str *i)
|
||||
** child processes (rob@sysgo.de)
|
||||
*/
|
||||
r = read_line_input(prompt_str, the_command, BUFSIZ-1, line_input_state);
|
||||
i->eof_flag = (r < 0);
|
||||
if (i->eof_flag) { /* EOF/error detected */
|
||||
the_command[0] = EOF; /* yes, it will be truncated, it's ok */
|
||||
the_command[1] = '\0';
|
||||
}
|
||||
#else
|
||||
fputs(prompt_str, stdout);
|
||||
fflush(stdout);
|
||||
the_command[0] = r = fgetc(i->file);
|
||||
/*the_command[1] = '\0'; - already is and never changed */
|
||||
i->eof_flag = (r == EOF);
|
||||
#endif
|
||||
fflush(stdout);
|
||||
i->p = the_command;
|
||||
return r; /* < 0 == EOF. Not meaningful otherwise */
|
||||
}
|
||||
#endif /* INTERACTIVE */
|
||||
|
||||
@ -1112,33 +1117,30 @@ static int file_get(struct in_str *i)
|
||||
{
|
||||
int ch;
|
||||
|
||||
ch = 0;
|
||||
/* If there is data waiting, eat it up */
|
||||
if (i->cached_ch) {
|
||||
ch = i->cached_ch ^ 0x100;
|
||||
if (ch != EOF)
|
||||
i->cached_ch = 0;
|
||||
if (i->p && *i->p) {
|
||||
take_cached:
|
||||
ch = *i->p++;
|
||||
if (i->eof_flag && !*i->p)
|
||||
ch = EOF;
|
||||
} else {
|
||||
/* need to double check i->file because we might be doing something
|
||||
* more complicated by now, like sourcing or substituting. */
|
||||
#if ENABLE_HUSH_INTERACTIVE
|
||||
if (interactive_fd && i->__promptme && i->file == stdin) {
|
||||
while (!i->p || !(interactive_fd && i->p[0])) {
|
||||
if (get_user_input(i) < 0)
|
||||
return EOF;
|
||||
}
|
||||
do {
|
||||
get_user_input(i);
|
||||
} while (!*i->p); /* need non-empty line */
|
||||
i->promptmode = 2;
|
||||
i->__promptme = 0;
|
||||
if (i->p && *i->p) {
|
||||
ch = *i->p++;
|
||||
}
|
||||
goto take_cached;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
ch = fgetc(i->file);
|
||||
}
|
||||
debug_printf("file_get: got a %d\n", ch);
|
||||
}
|
||||
debug_printf("file_get: got a '%c' %d\n", ch, ch);
|
||||
#if ENABLE_HUSH_INTERACTIVE
|
||||
if (ch == '\n')
|
||||
i->__promptme = 1;
|
||||
@ -1152,12 +1154,17 @@ static int file_get(struct in_str *i)
|
||||
static int file_peek(struct in_str *i)
|
||||
{
|
||||
int ch;
|
||||
if (i->cached_ch) {
|
||||
return i->cached_ch ^ 0x100;
|
||||
if (i->p && *i->p) {
|
||||
if (i->eof_flag && !i->p[1])
|
||||
return EOF;
|
||||
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);
|
||||
i->eof_flag = (ch == EOF);
|
||||
i->peek_buf[0] = ch;
|
||||
i->peek_buf[1] = '\0';
|
||||
i->p = i->peek_buf;
|
||||
debug_printf("file_peek: got a '%c' %d\n", *i->p, *i->p);
|
||||
return ch;
|
||||
}
|
||||
|
||||
@ -1182,6 +1189,7 @@ static void setup_string_in_str(struct in_str *i, const char *s)
|
||||
i->promptmode = 1;
|
||||
#endif
|
||||
i->p = s;
|
||||
i->eof_flag = 0;
|
||||
}
|
||||
|
||||
static void mark_open(int fd)
|
||||
@ -2846,11 +2854,12 @@ static FILE *generate_stream_from_list(struct pipe *head)
|
||||
static int process_command_subs(o_string *dest, struct p_context *ctx,
|
||||
struct in_str *input, const char *subst_end)
|
||||
{
|
||||
int retcode;
|
||||
int retcode, ch, eol_cnt;
|
||||
o_string result = NULL_O_STRING;
|
||||
struct p_context inner;
|
||||
FILE *p;
|
||||
struct in_str pipe_str;
|
||||
|
||||
initialize_context(&inner);
|
||||
|
||||
/* recursion to generate command */
|
||||
@ -2863,25 +2872,20 @@ static int process_command_subs(o_string *dest, struct p_context *ctx,
|
||||
p = generate_stream_from_list(inner.list_head);
|
||||
if (p == NULL) return 1;
|
||||
mark_open(fileno(p));
|
||||
// FIXME: need to flag pipe_str to somehow discard all trailing newlines.
|
||||
// Example: echo "TEST`date;echo;echo`BEST"
|
||||
// must produce one line: TEST<date>BEST
|
||||
setup_file_in_str(&pipe_str, p);
|
||||
|
||||
/* now send results of command back into original context */
|
||||
// FIXME: must not do quote parsing of the output!
|
||||
// Example: echo "TEST`echo '$(echo ZZ)'`BEST"
|
||||
// must produce TEST$(echo ZZ)BEST, not TESTZZBEST.
|
||||
// Example: echo "TEST`echo "'"`BEST"
|
||||
// must produce TEST'BEST
|
||||
// (maybe by setting all chars flagged as literals in map[]?)
|
||||
|
||||
retcode = parse_stream(dest, ctx, &pipe_str, NULL);
|
||||
/* XXX In case of a syntax error, should we try to kill the child?
|
||||
* That would be tough to do right, so just read until EOF. */
|
||||
if (retcode == 1) {
|
||||
while (b_getch(&pipe_str) != EOF)
|
||||
/* discard */;
|
||||
eol_cnt = 0;
|
||||
while ((ch = b_getch(&pipe_str)) != EOF) {
|
||||
if (ch == '\n') {
|
||||
eol_cnt++;
|
||||
continue;
|
||||
}
|
||||
while (eol_cnt) {
|
||||
b_addqchr(dest, '\n', dest->quote);
|
||||
eol_cnt--;
|
||||
}
|
||||
b_addqchr(dest, ch, dest->quote);
|
||||
}
|
||||
|
||||
debug_printf("done reading from pipe, pclose()ing\n");
|
||||
|
@ -1 +0,0 @@
|
||||
HELLO
|
@ -1,2 +0,0 @@
|
||||
# next line has no EOL!
|
||||
echo HELLO
|
@ -1,4 +0,0 @@
|
||||
http://busybox.net
|
||||
http://busybox.net_abc
|
||||
1
|
||||
0
|
@ -1,10 +0,0 @@
|
||||
URL=http://busybox.net
|
||||
|
||||
echo $URL
|
||||
echo ${URL}_abc
|
||||
|
||||
true
|
||||
false; echo $?
|
||||
true
|
||||
# BUG: prints 0, must be 1
|
||||
{ false; echo $?; }
|
@ -1,59 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
test -x hush || { echo "No ./hush?!"; exit; }
|
||||
|
||||
PATH="$PWD:$PATH" # for hush and recho/zecho/printenv
|
||||
export PATH
|
||||
|
||||
THIS_SH="$PWD/hush"
|
||||
export THIS_SH
|
||||
|
||||
do_test()
|
||||
{
|
||||
test -d "$1" || return 0
|
||||
(
|
||||
cd "$1" || { echo "cannot cd $1!"; exit 1; }
|
||||
for x in run-*; do
|
||||
test -f "$x" || continue
|
||||
case "$x" in
|
||||
"$0"|run-minimal|run-gprof) ;;
|
||||
*.orig|*~) ;;
|
||||
#*) echo $x ; sh $x ;;
|
||||
*)
|
||||
sh "$x" >"../$1-$x.fail" 2>&1 && \
|
||||
{ echo "$1/$x: ok"; rm "../$1-$x.fail"; } || echo "$1/$x: fail";
|
||||
;;
|
||||
esac
|
||||
done
|
||||
# Many bash run-XXX scripts just do this,
|
||||
# no point in duplication it all over the place
|
||||
for x in *.tests; do
|
||||
test -x "$x" || continue
|
||||
name="${x%%.tests}"
|
||||
test -f "$name.right" || continue
|
||||
{
|
||||
"$THIS_SH" "./$x" >"$name.xx" 2>&1
|
||||
diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail"
|
||||
} && echo "$1/$x: ok" || echo "$1/$x: fail"
|
||||
done
|
||||
)
|
||||
}
|
||||
|
||||
# main part of this script
|
||||
# Usage: run-all [directories]
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
# All sub directories
|
||||
modules=`ls -d hush-*`
|
||||
|
||||
for module in $modules; do
|
||||
do_test $module
|
||||
done
|
||||
else
|
||||
while [ $# -ge 1 ]; do
|
||||
if [ -d $1 ]; then
|
||||
do_test $1
|
||||
fi
|
||||
shift
|
||||
done
|
||||
fi
|
Loading…
Reference in New Issue
Block a user