#include #include #include #include #include #define lenof(x) (sizeof((x)) / sizeof(*(x))) #define MAX_LINE_LEN 256 #define MAX_RCOL 32 #define RCOL_LEN 64 #define RED "\033[0;31m" #define GREEN "\033[0;32m" #define YELLOW "\033[0;33m" #define HRED "\033[0;91m" #define HGREEN "\033[0;92m" #define HYELLOW "\033[0;93m" #define RESET "\033[0m" unsigned hash(char const *buf) { unsigned res = 5381; for (; *buf; buf++) { res = (res << 5) + res + *buf; } return res; } void err_misfmt_input(char const *reason) { fprintf(stderr, "error: misformatted input: %s\n", reason); exit(1); } char buf[MAX_LINE_LEN]; struct counters { unsigned char pfs[3]; }; unsigned num_rcol = 0; unsigned rcol_hashes[MAX_RCOL] = {0}; char rcol_values[MAX_RCOL][RCOL_LEN]; struct counters rcol_ctr[MAX_RCOL] = {0}; unsigned rcol_lut(unsigned hash) { for (unsigned *p = rcol_hashes; *p; p++) if (*p == hash) return p - rcol_hashes + 1; return 0; } void rstrip(char *buf) { char *sub; for (; *buf; buf++) { if (isspace(*buf)) { for (sub = buf + 1; *sub && isspace(*sub); sub++) ; if (!*sub) { *buf = 0; break; } } } } void put(int pfs_mode) { if (pfs_mode == 0) printf(HGREEN "* " RESET); else if (pfs_mode == 1) printf(HRED "X " RESET); else printf(HYELLOW "- " RESET); } void finish(int last_lc, unsigned last_rcol_idx) { if (last_lc == 0) return; for (; last_rcol_idx < num_rcol; last_rcol_idx++) { put(2); rcol_ctr[last_rcol_idx].pfs[2] += 1; } } int main(int argc, char **argv) { int lcol_width; int last_lc = 0; char *p, *lcol, *rcol; unsigned lcol_hash, rcol_hash, rcol_idx, last_rcol_idx = 0, actual_last_rcol_idx, i, j, pfs_mode; struct counters sum = {0}; if (argc != 2 || !((lcol_width = atoi(argv[1])))) { fprintf(stderr, "usage: %s \n", *argv); return 1; } while (buf == fgets(buf, lenof(buf), stdin)) { p = buf; for (; isspace(*p); p++) ; if (*p == '#' || !*p) continue; lcol = strtok(p, " \t"); rcol = strtok(0, " \t"); p = strtok(0, " \t"); rstrip(p); if (!p || p[1]) err_misfmt_input("3rd column invalid"); if (*p == '0') pfs_mode = 1; else if (*p == '1') pfs_mode = 0; else if (*p == '-') pfs_mode = 2; else err_misfmt_input("3rd column invalid"); lcol_hash = hash(lcol); rcol_hash = hash(rcol); actual_last_rcol_idx = last_rcol_idx; if (lcol_hash != last_lc) { last_rcol_idx = 0; } rcol_idx = rcol_lut(rcol_hash); if (rcol_idx == 0) { rcol_idx = ++num_rcol; // TODO: validate length rcol_hashes[rcol_idx - 1] = rcol_hash; // TODO: validate length strcpy(rcol_values[rcol_idx - 1], rcol); } else if (rcol_idx <= last_rcol_idx && last_rcol_idx != 0) { err_misfmt_input("ordering different from last time"); } rcol_ctr[rcol_idx - 1].pfs[pfs_mode] += 1; if (lcol[0] == '-' && lcol[1] == 0) continue; if (lcol_hash != last_lc) { finish(last_lc, actual_last_rcol_idx); if (last_lc != 0) putchar('\n'); last_lc = lcol_hash; last_rcol_idx = 0; printf("%*s ", lcol_width, lcol); } for (i = last_rcol_idx + 1; i < rcol_idx; i++) { put(2); rcol_ctr[i - 1].pfs[2]++; } put(pfs_mode); last_rcol_idx = rcol_idx; fflush(stdout); } finish(last_lc, last_rcol_idx); if (last_lc != 0) putchar('\n'); for (i = 0; i <= num_rcol; i++) { for (j = 0; j < lcol_width + (num_rcol - i) * 2; j++) putchar(' '); if (i != num_rcol) { putchar('^'); sum.pfs[0] += rcol_ctr[i].pfs[0]; sum.pfs[1] += rcol_ctr[i].pfs[1]; sum.pfs[2] += rcol_ctr[i].pfs[2]; } else putchar(' '); if (i != 0) { printf(" %s \t(%u/%u passed", rcol_values[num_rcol - i], rcol_ctr[num_rcol - i].pfs[0], rcol_ctr[num_rcol - i].pfs[0] + rcol_ctr[num_rcol - i].pfs[1]); if (rcol_ctr[num_rcol - i].pfs[2]) printf(", %u skipped", rcol_ctr[num_rcol - i].pfs[2]); printf(")"); } putchar('\n'); } printf("\nSummary:\n"); if (sum.pfs[0]) printf(HGREEN " %u passed" RESET "\n", sum.pfs[0]); if (sum.pfs[1]) printf(HRED " %u failed" RESET "\n", sum.pfs[1]); if (sum.pfs[2]) printf(HYELLOW " %u skipped" RESET "\n", sum.pfs[2]); }