/* See LICENSE file for copyright and license details. */ #include #include #include #include #include "util.h" enum { WHITE = 0, GREY, BLACK }; struct vertex; struct edge { struct vertex *to; struct edge *next; }; struct vertex { char *name; struct vertex *next; struct edge edges; size_t in_edges; int colour; }; static struct vertex graph; static void find_vertex(const char *name, struct vertex **it, struct vertex **prev) { for (*prev = &graph; (*it = (*prev)->next); *prev = *it) { int cmp = strcmp(name, (*it)->name); if (cmp > 0) continue; if (cmp < 0) *it = 0; return; } } static void find_edge(struct vertex *from, const char *to, struct edge **it, struct edge **prev) { for (*prev = &(from->edges); (*it = (*prev)->next); *prev = *it) { int cmp = strcmp(to, (*it)->to->name); if (cmp > 0) continue; if (cmp < 0) *it = 0; return; } } static struct vertex * add_vertex(char *name) { struct vertex *vertex; struct vertex *prev; find_vertex(name, &vertex, &prev); if (vertex) return vertex; vertex = encalloc(2, 1, sizeof(*vertex)); vertex->name = name; vertex->next = prev->next; prev->next = vertex; return vertex; } static struct edge * add_edge(struct vertex *from, struct vertex* to) { struct edge *edge; struct edge *prev; find_edge(from, to->name, &edge, &prev); if (edge) return edge; edge = encalloc(2, 1, sizeof(*edge)); edge->to = to; edge->next = prev->next; prev->next = edge; to->in_edges += 1; return edge; } static void load_graph(FILE *fp) { #define SKIP(VAR, START, FUNC) for (VAR = START; FUNC(*VAR) && *VAR; VAR++) #define TOKEN_END(P) do { if (*P) *P++ = 0; else P = 0; } while (0) char *line = 0; size_t size = 0; ssize_t len; char *p; char *name; struct vertex *from = 0; while ((len = getline(&line, &size, fp)) != -1) { if (line[len - 1] == '\n') line[--len] = 0; for (p = line; p;) { SKIP(name, p, isspace); if (!*name) break; SKIP(p, name, !isspace); TOKEN_END(p); if (!from) { from = add_vertex(enstrdup(2, name)); } else if (strcmp(from->name, name)) { add_edge(from, add_vertex(enstrdup(2, name))); from = 0; } else { from = 0; } } } free(line); if (from) enprintf(2, "odd number of tokens in input\n"); } static int sort_graph_visit(struct vertex *u) { struct edge *e = &(u->edges); struct vertex *v; int r = 0; u->colour = GREY; printf("%s\n", u->name); while ((e = e->next)) { v = e->to; if (v->colour == WHITE) { v->in_edges -= 1; if (v->in_edges == 0) r |= sort_graph_visit(v); } else if (v->colour == GREY) { r = 1; fprintf(stderr, "%s: loop detected between %s and %s\n", argv0, u->name, v->name); } } u->colour = BLACK; return r; } static int sort_graph(void) { struct vertex *u, *prev; int r = 0; size_t in_edges; for (in_edges = 0; graph.next; in_edges++) { for (prev = &graph; (u = prev->next); prev = u) { if (u->colour != WHITE) goto unlist; if (u->in_edges > in_edges) continue; r |= sort_graph_visit(u); unlist: prev->next = u->next; u = prev; } } return r; } static void usage(void) { enprintf(2, "usage: %s [file]\n", argv0); } int main(int argc, char *argv[]) { FILE *fp = stdin; const char *fn = ""; int ret = 0; ARGBEGIN { default: usage(); } ARGEND; if (argc > 1) usage(); if (argc && strcmp(*argv, "-")) if (!(fp = fopen(fn = *argv, "r"))) enprintf(2, "fopen %s:", *argv); memset(&graph, 0, sizeof(graph)); load_graph(fp); enfshut(2, fp, fn); ret = sort_graph(); if (fshut(stdout, "") | fshut(stderr, "")) ret = 2; return ret; }