minix/commands/simple/tsort.c

357 lines
8.1 KiB
C
Raw Normal View History

2005-04-21 16:53:53 +02:00
/* topo - topological sort Author: Kent Williams */
/*
** topo - perform a topological sort of the output of lorder.
**
** Usage : topo [infile] [outfile]
**
** Author: Kent Williams (williams@umaxc.weeg.uiowa.edu)
*/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
typedef struct __v {
struct __v *next; /* link list node */
int indegree, /* number of edges into this vertex */
visited, /* depth-first search visited flag */
on_the_path, /* used to find cycles */
has_a_cycle; /* true if a cycle at this vertex */
struct __e *out; /* outgoing edges from this vertex */
char key[1]; /* name of this vertex */
} vertex;
typedef struct __e {
struct __e *next; /* link list node */
vertex *v; /* vertex to which this edge goes */
} edge;
_PROTOTYPE(int main, (int argc, char **argv));
_PROTOTYPE(void *xmalloc, (size_t siz));
_PROTOTYPE(edge *new_edge, (vertex *v));
_PROTOTYPE(char *copyupto, (char *name, char *buf, int stop));
_PROTOTYPE(int child_of, (vertex *parent, vertex *child));
_PROTOTYPE(vertex *add_v, (char *s));
_PROTOTYPE(void readin, (void));
_PROTOTYPE(void pushname, (char *s));
_PROTOTYPE(char *popname, (void));
_PROTOTYPE(void topo, (void));
_PROTOTYPE(void print_cycle, (vertex *parent, vertex *child));
_PROTOTYPE(void dfs, (vertex *v));
_PROTOTYPE(void check_cycles, (void));
/*
** xmalloc -- standard do or die malloc front end.
*/
void *
xmalloc(siz)
size_t siz;
{
void *rval = (void *)malloc(siz);
if(rval == NULL) {
fputs("Out of memory.\n",stderr);
exit(1);
}
return rval;
}
/*
** edge allocater.
*/
edge *
new_edge(v)
vertex *v;
{
edge *rval;
rval = (edge *)xmalloc(sizeof(edge));
rval->v = v; return rval;
}
/*
** copyupto - copy until you see the stop character.
*/
char *
copyupto(name,buf,stop)
char *name,*buf,stop;
{
while(*buf != '\0' && *buf != stop)
*name++ = *buf++;
*name = '\0';
while(*buf != '\0' && isspace(*buf))
buf++;
return buf;
}
/*
** find out if the vertex child is a child of the vertex parent.
*/
int
child_of(parent,child)
vertex *parent,*child;
{
edge *e;
for(e = parent->out; e != NULL && e->v != child; e = e->next)
;
return e == NULL ? 0 : 1;
}
/*
** the vertex set.
**
** add_v adds a vertex to the set if it's not already there.
*/
vertex *vset = NULL;
vertex *
add_v(s)
char *s;
{
vertex *v,*last;
/*
** go looking for this key in the vertex set.
*/
for(last = v = vset; v != NULL && strcmp(v->key,s) != 0;
last = v, v = v->next)
;
if(v != NULL) {
/*
** use the move-to-front heuristic to keep this from being
** an O(N^2) algorithm.
*/
if(last != vset) {
last->next = v->next;
v->next = vset;
vset = v;
}
return v;
}
v = (vertex *)xmalloc(sizeof(vertex) + strlen(s));
v->out = NULL;
strcpy(v->key,s);
v->indegree =
v->on_the_path =
v->has_a_cycle =
v->visited = 0;
v->next = vset;
vset = v;
return v;
}
/*
** readin -- read in the dependency pairs.
*/
void
readin()
{
static char buf[128];
static char name[64];
char *bp;
vertex *child,*parent;
edge *e;
while(fgets(buf,sizeof(buf),stdin) != NULL) {
bp = buf + strlen(buf);
if (bp > buf && bp[-1] == '\n') *--bp = 0;
bp = copyupto(name,buf,' ');
child = add_v(name);
parent = add_v(bp);
if(child != parent && !child_of(parent,child)) {
e = new_edge(child);
e->next = parent->out;
parent->out = e;
child->indegree++;
}
}
}
/*
** the topological sort produces names of modules in reverse of
** the order we want them in, so use a stack to hold the names
** until we get them all, then pop them off to print them.
*/
struct name { struct name *next; char *s; }
*namelist = NULL;
void
pushname(s)
char *s;
{
struct name *x = (struct name *)xmalloc(sizeof(struct name));
x->s = s;
x->next = namelist;
namelist = x;
}
char *
popname() {
char *rval;
struct name *tmp;
if(namelist == NULL)
return NULL;
tmp = namelist;
rval = namelist->s;
namelist = namelist->next;
free(tmp);
return rval;
}
/*
** topo - do a topological sort of the dependency graph.
*/
void topo() {
vertex *x = vset,*n;
edge *e;
vertex *outq = NULL,*tmp;
#define insq(x) ((x->next = outq),(outq = x))
#define deq() ((tmp = outq),(outq = outq->next),tmp)
/*
** find all vertices that don't depend on any other vertices
** Since it breaks the "next" links to insert x into the queue,
** x->next is saved before insq, to resume the list traversal.
*/
while (x != NULL) {
n = x->next;
if(x->indegree == 0) {
insq(x);
pushname(x->key);
}
x = n;
}
/*
** for each vertex V with indegree of zero,
** for each edge E from vertex V
** subtract one from the indegree of the vertex V'
** pointed to by E. If V' now has an indegree of zero,
** add it to the set of vertices with indegree zero, and
** push its name on the output stack.
*/
while(outq != NULL) {
x = deq();
e = x->out;
while(e != NULL) {
if(--(e->v->indegree) == 0) {
insq(e->v);
pushname(e->v->key);
}
e = e->next;
}
}
/*
** print the vertex names in opposite of the order they were
** encountered.
*/
while(namelist != NULL)
puts(popname());
}
/*
** print_cycle --
** A cycle has been detected between parent and child.
** Start with the child, and look at each of its edges for
** the parent.
**
** We know a vertex is on the path from the child to the parent
** because the depth-first search sets on_the_path true for each
** vertex it visits.
*/
void
print_cycle(parent,child)
vertex *parent, *child;
{
char *s;
vertex *x;
edge *e;
for(x = child; x != parent; ) {
pushname(x->key);
for(e = x->out; e != NULL; e = e->next) {
/*
** stop looking for the path at the first node found
** that's on the path. Watch out for cycles already
** detected, because if you follow an edge into a cycle,
** you're stuck in an infinite loop!
*/
if(e->v->on_the_path && !e->v->has_a_cycle) {
x = e->v;
break;
}
}
}
/*
** print the name of the parent, and then names of each of the
** vertices on the path from the child to the parent.
*/
fprintf(stderr,"%s\n",x->key);
while((s = popname()) != NULL)
fprintf(stderr,"%s\n",s);
}
/*
** depth first search for cycles in the dependency graph.
** See "Introduction to Algorithms" by Udi Manber Addison-Wesley 1989
*/
void
dfs(v)
vertex *v;
{
edge *e;
if(v->visited) /* If you've been here before, don't go again! */
return;
v->visited++;
v->on_the_path++; /* this node is on the path from the root. */
/*
** depth-first search all outgoing edges.
*/
for(e = v->out; e != NULL; e = e->next) {
if(!e->v->visited)
dfs(e->v);
if(e->v->on_the_path) {
fprintf(stderr,"cycle found between %s and %s\n",
v->key,e->v->key);
print_cycle(v,e->v);
v->has_a_cycle++;
}
}
v->on_the_path = 0;
}
/*
** check cycles starts the recursive depth-first search from
** each vertex in vset.
*/
void
check_cycles()
{
vertex *v;
for(v = vset; v != NULL; v = v->next)
dfs(v);
}
/*
** main program.
*/
int main(argc,argv)
int argc;
char **argv;
{
if(argc > 1 && freopen(argv[1],"r",stdin) == NULL) {
perror(argv[1]);
exit(0);
}
if(argc > 2 && freopen(argv[2],"w",stdout) == NULL) {
perror(argv[2]);
exit(0);
}
readin();
check_cycles();
topo();
return(0);
}