void
read_environment(record environment)
{
    data b;
    file f, g;
    integer e;
    list l;
    text s;

    f_affix(f, "/dev/stdin");

    while (f_line(f, s) ^ -1) {
	if ((e = index(s, '=')) ^ -1) {
	    b_cast(b, project(s, e + 1));
	    f_b_affix(g, b);
	    f_list(g, r_v_list(environment, cut(s, 0, e)), 0);
	}
    }
}


void
display_command(data command)
{
    data prefix, mine;

    b_copy(mine, command);

    b_cast(prefix, "src/");
    if (b_find(mine, prefix)) {
    } else {
	b_scrap(mine, 0, 4);
    }

    if (63 < b_length(mine)) {
	b_size(mine, 60);
	b_suffix(mine, "...");
    }

    o_text(b_string(mine));
    o_space(64 - b_length(mine));
}


void
display_failed(text path, integer index, data command, integer diff, data read,
	       data result)
{
    o_text("[\033[1;31mfailed\033[0;39m]\n");
    v_text(argv(0));
    v_text(": ");
    v_text(path);
    v_text("[");
    v_integer(index);
    v_text("]: failed `");
    v_text(b_string(command));
    v_text("'\n");
    if (diff) {
	v_text(argv(0));
	v_text(": ");
	v_text(path);
	v_text("[");
	v_integer(index);
	v_text("]: output was `");
	v_text(b_string(read));
	v_text("'\n");
	v_text(argv(0));
	v_text(": ");
	v_text(path);
	v_text("[");
	v_integer(index);
	v_text("]:   expected `");
	v_text(b_string(result));
	v_text("'\n");
    }
}


void
display_passed(void)
{
    o_text("[passed]\n");
}


data
system(record environment, data command)
{
    file f;
    integer c, e;
    list argv;
    sshell ss;
    text s;

    l_set(argv, ss_argv(ss));

    f_b_affix(f, command);

    while ((e = f_ever(f, " '", s)) ^ -1) {
	c = f_pick(f);
	if (c == '\'') {
	    f_ever(f, "'", s);
	    lb_p_text(argv, s);
	    f_pick(f);
	} else {
	    if (e) {
		lb_p_text(argv, s);
	    }
	}
    }

    do {
	s = lf_q_text(argv);

	if (initial(s) == '$') {
	    s = delete(s, 0);
	    if (r_key(environment, s)) {
		integer d, k;
		list n;

		n = r_q_list(environment, s);
		b_cast(ss_path(ss), lf_q_text(n));

		d = l_length(n);

		k = 1;
		while (k < d) {
		    l_p_text(argv, k, l_q_text(n, k));
		    k += 1;
		}

		break;
	    }
	}

	b_cast(ss_path(ss), lf_q_text(argv));
    } while (0);

    ss_link(ss);

    return ss_read(ss);
}


integer
run_suite(record environment, text path)
{
    file f;
    integer index, status;
    data command, read, result;

    f_affix(f, path);

    status = 0;

    index = -1;
    while (f_b_line(f, command) ^ -1) {
	index += 2;
	f_b_line(f, result);
	if (wrap(read, system, environment, command)) {
	    status = -1;
	} else {
	    if (b_length(read)) {
		if (b_character(read, -1) == 10) {
		    b_delete(read, -1);
		}
	    }
	}
	display_command(command);
	if (status) {
	} else {
	    if (b_compare(result, read)) {
		status = 1;
	    }
	}
	if (status) {
	    display_failed(path, index, command, status ^ -1, read, result);
	    break;
	} else {
	    display_passed();
	}
    }

    return status;
}


integer
main(void)
{
    integer i, status;
    record environment;

    read_environment(environment);

    status = 0;

    i = 1;
    while (i < argc()) {
	status = run_suite(environment, argv(i));
	if (status) {
	    break;
	} else {
	    i += 1;
	}
    }

    return status;
}
