tsort.c

Go to the documentation of this file.
00001 /* topo - topological sort              Author: Kent Williams */
00002 
00003 /*
00004 ** topo - perform a topological sort of the output of lorder.
00005 **
00006 ** Usage : topo [infile] [outfile]
00007 **
00008 ** Author: Kent Williams (williams@umaxc.weeg.uiowa.edu)
00009 */
00010 
00011 #include <ctype.h>
00012 #include <stdlib.h>
00013 #include <string.h>
00014 #include <stdio.h>
00015 
00016 typedef struct __v {
00017     struct __v *next;   /* link list node                   */
00018     int indegree,       /* number of edges into this vertex */
00019         visited,        /* depth-first search visited flag  */
00020         on_the_path,    /* used to find cycles              */
00021         has_a_cycle;    /* true if a cycle at this vertex   */
00022     struct __e *out;    /* outgoing edges from this vertex  */
00023     char key[1];        /* name of this vertex              */
00024 } vertex;
00025 
00026 typedef struct __e {
00027     struct __e *next;   /* link list node                   */
00028     vertex *v;          /* vertex to which this edge goes   */
00029 } edge;
00030 
00031 _PROTOTYPE(int main, (int argc, char **argv));
00032 _PROTOTYPE(void *xmalloc, (size_t siz));
00033 _PROTOTYPE(edge *new_edge, (vertex *v));
00034 _PROTOTYPE(char *copyupto, (char *name, char *buf, int stop));
00035 _PROTOTYPE(int child_of, (vertex *parent, vertex *child));
00036 _PROTOTYPE(vertex *add_v, (char *s));
00037 _PROTOTYPE(void readin, (void));
00038 _PROTOTYPE(void pushname, (char *s));
00039 _PROTOTYPE(char *popname, (void));
00040 _PROTOTYPE(void topo, (void));
00041 _PROTOTYPE(void print_cycle, (vertex *parent, vertex *child));
00042 _PROTOTYPE(void dfs, (vertex *v));
00043 _PROTOTYPE(void check_cycles, (void));
00044 
00045 /*
00046 ** xmalloc -- standard do or die malloc front end.
00047 */
00048 void *
00049 xmalloc(siz)
00050 size_t siz;
00051 {
00052     void *rval = (void *)malloc(siz);
00053     if(rval == NULL) {
00054         fputs("Out of memory.\n",stderr);
00055         exit(1);
00056     }
00057     return rval;
00058 }
00059 
00060 /*
00061 ** edge allocater.
00062 */
00063 edge *
00064 new_edge(v)
00065 vertex *v;
00066 {
00067     edge *rval;
00068     rval = (edge *)xmalloc(sizeof(edge));
00069     rval->v = v; return rval;
00070 }
00071 
00072 /*
00073 ** copyupto - copy until you see the stop character.
00074 */
00075 char *
00076 copyupto(name,buf,stop)
00077 char *name,*buf,stop;
00078 {
00079     while(*buf != '\0' && *buf != stop)
00080         *name++ = *buf++;
00081     *name = '\0';
00082     while(*buf != '\0' && isspace(*buf))
00083         buf++;
00084     return buf;
00085 }
00086 
00087 /*
00088 ** find out if the vertex child is a child of the vertex parent.
00089 */
00090 int
00091 child_of(parent,child)
00092 vertex *parent,*child;
00093 {
00094     edge *e;
00095     for(e = parent->out; e != NULL && e->v != child; e = e->next)
00096         ;
00097     return e == NULL ? 0 : 1;
00098 }
00099 
00100 /*
00101 ** the vertex set.
00102 **
00103 ** add_v adds a vertex to the set if it's not already there.
00104 */
00105 vertex *vset = NULL;
00106 
00107 vertex *
00108 add_v(s)
00109 char *s;
00110 {
00111     vertex *v,*last;
00112     /*
00113     ** go looking for this key in the vertex set.
00114     */
00115     for(last = v = vset; v != NULL && strcmp(v->key,s) != 0;
00116         last = v, v = v->next)
00117         ;
00118     if(v != NULL) {
00119         /*
00120         ** use the move-to-front heuristic to keep this from being
00121         ** an O(N^2) algorithm.
00122         */
00123         if(last != vset) {
00124             last->next = v->next;
00125             v->next = vset;
00126             vset = v;
00127         }
00128         return v;
00129     }
00130 
00131     v = (vertex *)xmalloc(sizeof(vertex) + strlen(s));
00132 
00133     v->out = NULL;
00134     strcpy(v->key,s);
00135     v->indegree =
00136     v->on_the_path =
00137     v->has_a_cycle =
00138     v->visited = 0;
00139     v->next = vset;
00140     vset = v;
00141     return v;
00142 }
00143 
00144 /*
00145 ** readin -- read in the dependency pairs.
00146 */
00147 void
00148 readin()
00149 {
00150     static char buf[128];
00151     static char name[64];
00152     char *bp;
00153     vertex *child,*parent;
00154     edge *e;
00155     while(fgets(buf,sizeof(buf),stdin) != NULL) {
00156         bp = buf + strlen(buf);
00157         if (bp > buf && bp[-1] == '\n') *--bp = 0;
00158         bp = copyupto(name,buf,' ');
00159         child = add_v(name);
00160         parent = add_v(bp);
00161         if(child != parent && !child_of(parent,child)) {
00162             e = new_edge(child);
00163             e->next = parent->out;
00164             parent->out = e;
00165             child->indegree++;
00166         }
00167     }
00168 }
00169 
00170 /*
00171 ** the topological sort produces names of modules in reverse of
00172 ** the order we want them in, so use a stack to hold the names
00173 ** until we get them all, then pop them off to print them.
00174 */
00175 struct name { struct name *next; char *s; }
00176 *namelist = NULL;
00177 
00178 void
00179 pushname(s)
00180 char *s;
00181 {
00182     struct name *x = (struct name *)xmalloc(sizeof(struct name));
00183     x->s = s;
00184     x->next = namelist;
00185     namelist = x;
00186 }
00187 
00188 char *
00189 popname() {
00190     char *rval;
00191     struct name *tmp;
00192     if(namelist == NULL)
00193         return NULL;
00194     tmp = namelist;
00195     rval = namelist->s;
00196     namelist = namelist->next;
00197     free(tmp);
00198     return rval;
00199 }
00200 
00201 /*
00202 ** topo - do a topological sort of the dependency graph.
00203 */
00204 void topo() {
00205     vertex *x = vset,*n;
00206     edge *e;
00207     vertex *outq = NULL,*tmp;
00208 #define insq(x) ((x->next = outq),(outq = x))
00209 #define deq() ((tmp = outq),(outq = outq->next),tmp)
00210 
00211     /*
00212     ** find all vertices that don't depend on any other vertices
00213     ** Since it breaks the "next" links to insert x into the queue,
00214     ** x->next is saved before insq, to resume the list traversal.
00215     */
00216     while (x != NULL) {
00217         n = x->next;
00218         if(x->indegree == 0) {
00219             insq(x);
00220             pushname(x->key);       
00221         }
00222         x = n;
00223     }
00224 
00225     /*
00226     ** for each vertex V with indegree of zero,
00227     **     for each edge E from vertex V
00228     **        subtract one from the indegree of the vertex V'
00229     **        pointed to by E.  If V' now has an indegree of zero,
00230     **        add it to the set of vertices with indegree zero, and
00231     **        push its name on the output stack.
00232     */
00233     while(outq != NULL) {
00234         x = deq();
00235         e = x->out;
00236         while(e != NULL) {
00237             if(--(e->v->indegree) == 0) {
00238                 insq(e->v);
00239                 pushname(e->v->key);
00240             }
00241             e = e->next;
00242         }
00243     }
00244     
00245     /*
00246     ** print the vertex names in opposite of the order they were
00247     ** encountered.
00248     */
00249     while(namelist != NULL)
00250         puts(popname());
00251 }
00252 
00253 /*
00254 ** print_cycle --
00255 ** A cycle has been detected between parent and child.
00256 ** Start with the child, and look at each of its edges for
00257 ** the parent.
00258 **
00259 ** We know a vertex is on the path from the child to the parent
00260 ** because the depth-first search sets on_the_path true for each
00261 ** vertex it visits.
00262 */
00263 void
00264 print_cycle(parent,child)
00265 vertex *parent, *child;
00266 {
00267     char *s;
00268     vertex *x;
00269     edge *e;
00270     for(x = child; x != parent; ) {
00271         pushname(x->key);
00272         for(e = x->out; e != NULL; e = e->next) {
00273             /*
00274             ** stop looking for the path at the first node found
00275             ** that's on the path.  Watch out for cycles already
00276             ** detected, because if you follow an edge into a cycle,
00277             ** you're stuck in an infinite loop!
00278             */
00279             if(e->v->on_the_path && !e->v->has_a_cycle) {
00280                 x = e->v;
00281                 break;
00282             }
00283         }
00284     }
00285     /*
00286     ** print the name of the parent, and then names of each of the
00287     ** vertices on the path from the child to the parent.
00288     */
00289     fprintf(stderr,"%s\n",x->key);
00290     while((s = popname()) != NULL)
00291         fprintf(stderr,"%s\n",s);
00292 }
00293 
00294 /*
00295 ** depth first search for cycles in the dependency graph.
00296 ** See "Introduction to Algorithms" by Udi Manber Addison-Wesley 1989
00297 */
00298 void
00299 dfs(v)
00300 vertex *v;
00301 {
00302     edge *e;
00303 
00304     if(v->visited)      /* If you've been here before, don't go again! */
00305         return;
00306     v->visited++;
00307     v->on_the_path++;   /* this node is on the path from the root. */
00308 
00309     /*
00310     ** depth-first search all outgoing edges.
00311     */
00312     for(e = v->out; e != NULL; e = e->next) {
00313         if(!e->v->visited)
00314             dfs(e->v);
00315         if(e->v->on_the_path) {
00316             fprintf(stderr,"cycle found between %s and %s\n",
00317                 v->key,e->v->key);
00318             print_cycle(v,e->v);
00319             v->has_a_cycle++;
00320         }
00321     }
00322     v->on_the_path = 0;
00323 }
00324 
00325 /*
00326 ** check cycles starts the recursive depth-first search from
00327 ** each vertex in vset.
00328 */
00329 void
00330 check_cycles()
00331 {
00332     vertex *v;
00333     for(v = vset; v != NULL; v = v->next)
00334         dfs(v);
00335 }
00336 
00337 /*
00338 ** main program.
00339 */
00340 int main(argc,argv)
00341 int argc;
00342 char **argv;
00343 {
00344     if(argc > 1 && freopen(argv[1],"r",stdin) == NULL) {
00345         perror(argv[1]);
00346         exit(0);
00347     }
00348     if(argc > 2 && freopen(argv[2],"w",stdout) == NULL) {
00349         perror(argv[2]);
00350         exit(0);
00351     }
00352     readin();
00353     check_cycles();
00354     topo();
00355     return(0);
00356 }

Generated on Fri Apr 14 22:57:12 2006 for minix by  doxygen 1.4.6