/* 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;
- };
+ const char *p;
+ /* 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;
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;
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
/*
** 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 */
{
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;
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;
}
i->promptmode = 1;
#endif
i->p = s;
+ i->eof_flag = 0;
}
static void mark_open(int fd)
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 */
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");
+++ /dev/null
-#!/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