2016-02-17 00:17:33 +00:00
|
|
|
/* See LICENSE file for copyright and license details. */
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
#include "util.h"
|
|
|
|
|
2016-02-24 15:56:39 +00:00
|
|
|
enum { WHITE = 0, GREY, BLACK };
|
2016-02-17 00:17:33 +00:00
|
|
|
|
|
|
|
struct vertex;
|
|
|
|
|
2016-02-17 08:27:41 +00:00
|
|
|
struct edge {
|
2016-02-17 00:17:33 +00:00
|
|
|
struct vertex *to;
|
|
|
|
struct edge *next;
|
|
|
|
};
|
|
|
|
|
2016-02-17 08:27:41 +00:00
|
|
|
struct vertex {
|
2016-02-17 00:17:33 +00:00
|
|
|
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
|
2016-02-24 15:56:39 +00:00
|
|
|
find_edge(struct vertex *from, const char *to, struct edge **it, struct edge **prev)
|
2016-02-17 00:17:33 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2016-02-24 15:26:29 +00:00
|
|
|
vertex = encalloc(2, 1, sizeof(*vertex));
|
2016-02-17 00:17:33 +00:00
|
|
|
vertex->name = name;
|
|
|
|
vertex->next = prev->next;
|
|
|
|
prev->next = vertex;
|
|
|
|
|
|
|
|
return vertex;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct edge *
|
2016-02-24 15:56:39 +00:00
|
|
|
add_edge(struct vertex *from, struct vertex* to)
|
2016-02-17 00:17:33 +00:00
|
|
|
{
|
|
|
|
struct edge *edge;
|
|
|
|
struct edge *prev;
|
|
|
|
|
|
|
|
find_edge(from, to->name, &edge, &prev);
|
|
|
|
if (edge)
|
|
|
|
return edge;
|
|
|
|
|
2016-02-24 15:26:29 +00:00
|
|
|
edge = encalloc(2, 1, sizeof(*edge));
|
2016-02-17 00:17:33 +00:00
|
|
|
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) {
|
2016-02-24 15:56:39 +00:00
|
|
|
if (line[len - 1] == '\n')
|
|
|
|
line[--len] = 0;
|
2016-02-17 00:17:33 +00:00
|
|
|
for (p = line; p;) {
|
|
|
|
SKIP(name, p, isspace);
|
|
|
|
if (!*name)
|
|
|
|
break;
|
|
|
|
SKIP(p, name, !isspace);
|
|
|
|
TOKEN_END(p);
|
|
|
|
if (!from) {
|
2016-02-24 15:26:29 +00:00
|
|
|
from = add_vertex(enstrdup(2, name));
|
2016-02-17 00:17:33 +00:00
|
|
|
} else if (strcmp(from->name, name)) {
|
2016-02-24 15:26:29 +00:00
|
|
|
add_edge(from, add_vertex(enstrdup(2, name)));
|
2016-02-17 00:17:33 +00:00
|
|
|
from = 0;
|
|
|
|
} else {
|
|
|
|
from = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(line);
|
|
|
|
|
|
|
|
if (from)
|
2016-02-24 15:26:29 +00:00
|
|
|
enprintf(2, "odd number of tokens in input\n");
|
2016-02-17 00:17:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2016-02-24 15:26:29 +00:00
|
|
|
enprintf(2, "usage: %s [file]\n", argv0);
|
2016-02-17 00:17:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
FILE *fp = stdin;
|
|
|
|
const char *fn = "<stdin>";
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
ARGBEGIN {
|
|
|
|
default:
|
|
|
|
usage();
|
2019-06-30 01:39:07 +00:00
|
|
|
} ARGEND
|
2016-02-17 00:17:33 +00:00
|
|
|
|
|
|
|
if (argc > 1)
|
|
|
|
usage();
|
|
|
|
if (argc && strcmp(*argv, "-"))
|
|
|
|
if (!(fp = fopen(fn = *argv, "r")))
|
2016-02-24 15:26:29 +00:00
|
|
|
enprintf(2, "fopen %s:", *argv);
|
2016-02-17 00:17:33 +00:00
|
|
|
|
|
|
|
memset(&graph, 0, sizeof(graph));
|
|
|
|
load_graph(fp);
|
2016-02-24 15:26:29 +00:00
|
|
|
enfshut(2, fp, fn);
|
2016-02-17 00:17:33 +00:00
|
|
|
|
|
|
|
ret = sort_graph();
|
|
|
|
|
|
|
|
if (fshut(stdout, "<stdout>") | fshut(stderr, "<stderr>"))
|
|
|
|
ret = 2;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|