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(data command)
{
    list argv;
    sshell ss;

    b_cast(ss_path(ss), "/bin/sh");

    l_set(argv, ss_argv(ss));

    lb_p_text(argv, "sh");
    lb_p_text(argv, "-c");
    l_l_data(argv, -1, command);

    ss_link(ss);

    return ss_read(ss);
}


void
run_system(data collect, data command)
{
    b_copy(collect, system(command));
}


integer
run_suite(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 (trap(run_system, read, command)) {
	    status = -1;
	} else {
	    if (b_length(read)) {
		if (b_text(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;

    status = 0;

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

    return status;
}
