sbase

suckless unix tools
git clone git://git.2f30.org/sbase.git
Log | Files | Refs | README | LICENSE

tsort.c (3727B)


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