master xplshn/aruu / cmd / posix / tsort.c
  1/* See LICENSE file for copyright and license details. */
  2
  3
  4#include <stdio.h>
  5#include <string.h>
  6#include <stdlib.h>
  7#include <ctype.h>
  8
  9#include "util.h"
 10
 11enum { WHITE = 0, GREY, BLACK };
 12
 13struct vertex;
 14
 15struct edge {
 16	struct vertex *to;
 17	struct edge *next;
 18};
 19
 20struct vertex {
 21	char *name;
 22	struct vertex *next;
 23	struct edge edges;
 24	size_t in_edges;
 25	int colour;
 26};
 27
 28static struct vertex graph;
 29
 30static void
 31find_vertex(const char *name, struct vertex **it, struct vertex **prev)
 32{
 33	for (*prev = &graph; (*it = (*prev)->next); *prev = *it) {
 34		int cmp = strcmp(name, (*it)->name);
 35		if (cmp > 0)
 36			continue;
 37		if (cmp < 0)
 38			*it = 0;
 39		return;
 40	}
 41}
 42
 43static void
 44find_edge(struct vertex *from, const char *to, struct edge **it, struct edge **prev)
 45{
 46	for (*prev = &(from->edges); (*it = (*prev)->next); *prev = *it) {
 47		int cmp = strcmp(to, (*it)->to->name);
 48		if (cmp > 0)
 49			continue;
 50		if (cmp < 0)
 51			*it = 0;
 52		return;
 53	}
 54}
 55
 56static struct vertex *
 57add_vertex(char *name)
 58{
 59	struct vertex *vertex;
 60	struct vertex *prev;
 61
 62	find_vertex(name, &vertex, &prev);
 63	if (vertex)
 64		return vertex;
 65
 66	vertex = encalloc(2, 1, sizeof(*vertex));
 67	vertex->name = name;
 68	vertex->next = prev->next;
 69	prev->next = vertex;
 70
 71	return vertex;
 72}
 73
 74static struct edge *
 75add_edge(struct vertex *from, struct vertex* to)
 76{
 77	struct edge *edge;
 78	struct edge *prev;
 79
 80	find_edge(from, to->name, &edge, &prev);
 81	if (edge)
 82		return edge;
 83
 84	edge = encalloc(2, 1, sizeof(*edge));
 85	edge->to = to;
 86	edge->next = prev->next;
 87	prev->next = edge;
 88	to->in_edges += 1;
 89
 90	return edge;
 91}
 92
 93static void
 94load_graph(FILE *fp)
 95{
 96#define SKIP(VAR, START, FUNC)  for (VAR = START; FUNC(*VAR) && *VAR; VAR++)
 97#define TOKEN_END(P)            do { if (*P) *P++ = 0; else P = 0; } while (0)
 98
 99	char *line = 0;
100	size_t size = 0;
101	ssize_t len;
102	char *p;
103	char *name;
104	struct vertex *from = 0;
105
106	while ((len = getline(&line, &size, fp)) != -1) {
107		if (line[len - 1] == '\n')
108			line[--len] = 0;
109		for (p = line; p;) {
110			SKIP(name, p, isspace);
111			if (!*name)
112				break;
113			SKIP(p, name, !isspace);
114			TOKEN_END(p);
115			if (!from) {
116				from = add_vertex(enstrdup(2, name));
117			} else if (strcmp(from->name, name)) {
118				add_edge(from, add_vertex(enstrdup(2, name)));
119				from = 0;
120			} else {
121				from = 0;
122			}
123		}
124	}
125
126	free(line);
127
128	if (from)
129		enprintf(2, "odd number of tokens in input\n");
130}
131
132static int
133sort_graph_visit(struct vertex *u)
134{
135	struct edge *e = &(u->edges);
136	struct vertex *v;
137	int r = 0;
138	u->colour = GREY;
139	printf("%s\n", u->name);
140	while ((e = e->next)) {
141		v = e->to;
142		if (v->colour == WHITE) {
143			v->in_edges -= 1;
144			if (v->in_edges == 0)
145				r |= sort_graph_visit(v);
146		} else if (v->colour == GREY) {
147			r = 1;
148			fprintf(stderr, "%s: loop detected between %s and %s\n",
149			        argv0, u->name, v->name);
150		}
151	}
152	u->colour = BLACK;
153	return r;
154}
155
156static int
157sort_graph(void)
158{
159	struct vertex *u, *prev;
160	int r = 0;
161	size_t in_edges;
162	for (in_edges = 0; graph.next; in_edges++) {
163		for (prev = &graph; (u = prev->next); prev = u) {
164			if (u->colour != WHITE)
165				goto unlist;
166			if (u->in_edges > in_edges)
167				continue;
168			r |= sort_graph_visit(u);
169		unlist:
170			prev->next = u->next;
171			u = prev;
172		}
173	}
174	return r;
175}
176
177static void
178usage(void)
179{
180	enprintf(2, "usage: %s [file]\n", argv0);
181}
182
183// ?man tsort: topological sort
184// ?man arguments: file
185// ?man perform a topological sort on input pairs
186int
187main(int argc, char *argv[])
188{
189	FILE *fp = stdin;
190	const char *fn = "<stdin>";
191	int ret = 0;
192
193	ARGBEGIN {
194	default:
195		usage();
196	} ARGEND
197
198	if (argc > 1)
199		usage();
200	if (argc && strcmp(*argv, "-"))
201		if (!(fp = fopen(fn = *argv, "r")))
202			enprintf(2, "fopen %s:", *argv);
203
204	memset(&graph, 0, sizeof(graph));
205	load_graph(fp);
206	enfshut(2, fp, fn);
207
208	ret = sort_graph();
209
210	if (fshut(stdout, "<stdout>") | fshut(stderr, "<stderr>"))
211		ret = 2;
212
213	return ret;
214}