Importing NetBSD tsort
Change-Id: I110de8037b9253f4fe53cbe13dc8fc281aeea2ec
This commit is contained in:
		
							parent
							
								
									6e0ed9c90c
								
							
						
					
					
						commit
						8e5df35e84
					
				| @ -27,7 +27,7 @@ SUBDIR=	add_route arp ash at backup banner basename btrace cal \ | |||||||
| 	stty svclog svrctl swifi sync synctree sysenv \
 | 	stty svclog svrctl swifi sync synctree sysenv \
 | ||||||
| 	syslogd tail tcpd tcpdp tcpstat tee telnet \
 | 	syslogd tail tcpd tcpdp tcpstat tee telnet \
 | ||||||
| 	telnetd term termcap tget time touch tr \
 | 	telnetd term termcap tget time touch tr \
 | ||||||
| 	truncate tsort tty udpstat umount uname unexpand \
 | 	truncate tty udpstat umount uname unexpand \
 | ||||||
| 	unstack update uud uue version vol wc \
 | 	unstack update uud uue version vol wc \
 | ||||||
| 	whereis which who write writeisofs fetch \
 | 	whereis which who write writeisofs fetch \
 | ||||||
| 	xargs yes zdump zmodem pkgin_cd \
 | 	xargs yes zdump zmodem pkgin_cd \
 | ||||||
|  | |||||||
| @ -1,4 +0,0 @@ | |||||||
| PROG=	tsort |  | ||||||
| MAN= |  | ||||||
| 
 |  | ||||||
| .include <bsd.prog.mk> |  | ||||||
| @ -1,356 +0,0 @@ | |||||||
| /* 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; |  | ||||||
| 
 |  | ||||||
| int main(int argc, char **argv); |  | ||||||
| void *xmalloc(size_t siz); |  | ||||||
| edge *new_edge(vertex *v); |  | ||||||
| char *copyupto(char *name, char *buf, int stop); |  | ||||||
| int child_of(vertex *parent, vertex *child); |  | ||||||
| vertex *add_v(char *s); |  | ||||||
| void readin(void); |  | ||||||
| void pushname(char *s); |  | ||||||
| char *popname(void); |  | ||||||
| void topo(void); |  | ||||||
| void print_cycle(vertex *parent, vertex *child); |  | ||||||
| void dfs(vertex *v); |  | ||||||
| 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); |  | ||||||
| } |  | ||||||
| @ -1,3 +1,9 @@ | |||||||
|  | 20121205: | ||||||
|  | 	The tsort tool has been also upgraded and is now also used during | ||||||
|  | 	the build: | ||||||
|  | 	# make -C usr.bin/tsort all install | ||||||
|  | 	# cp share/mk/* /usr/share/mk | ||||||
|  | 
 | ||||||
| 20121205: | 20121205: | ||||||
| 	lorder requires a newer version of sort, so to ensure it is present | 	lorder requires a newer version of sort, so to ensure it is present | ||||||
| 	do the following: | 	do the following: | ||||||
|  | |||||||
| @ -19,7 +19,7 @@ MAN=	ash.1 at.1 banner.1 basename.1 \ | |||||||
| 	split.1 stty.1 svc.1 svrctl.1 \
 | 	split.1 stty.1 svc.1 svrctl.1 \
 | ||||||
| 	synctree.1 sysenv.1 sz.1 tail.1 tee.1 telnet.1 template.1 \
 | 	synctree.1 sysenv.1 sz.1 tail.1 tee.1 telnet.1 template.1 \
 | ||||||
| 	term.1 termcap.1 tget.1 time.1 tr.1 true.1 \
 | 	term.1 termcap.1 tget.1 time.1 tr.1 true.1 \
 | ||||||
| 	truncate.1 tsort.1 tty.1 umount.1 uname.1 unexpand.1 \
 | 	truncate.1 tty.1 umount.1 uname.1 unexpand.1 \
 | ||||||
| 	uud.1 uue.1 vol.1 wc.1 whereis.1 which.1 \
 | 	uud.1 uue.1 vol.1 wc.1 whereis.1 which.1 \
 | ||||||
| 	who.1 write.1 xargs.1 yap.1 yes.1 linkfarm.1 pkg_view.1 | 	who.1 write.1 xargs.1 yap.1 yes.1 linkfarm.1 pkg_view.1 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,27 +0,0 @@ | |||||||
| .TH TSORT 1 |  | ||||||
| .SH NAME |  | ||||||
| tsort \- topological sort [IBM] |  | ||||||
| .SH SYNOPSIS |  | ||||||
| \fBtsort \fIfile\fR |  | ||||||
| .br |  | ||||||
| .de FL |  | ||||||
| .TP |  | ||||||
| \\fB\\$1\\fR |  | ||||||
| \\$2 |  | ||||||
| .. |  | ||||||
| .de EX |  | ||||||
| .TP 20 |  | ||||||
| \\fB\\$1\\fR |  | ||||||
| # \\$2 |  | ||||||
| .. |  | ||||||
| .SH EXAMPLES |  | ||||||
| .TP 20 |  | ||||||
| .B lorder *.s | tsort |  | ||||||
| # Give library ordering |  | ||||||
| .TP 20 |  | ||||||
| .B ar cr libc.a \`lorder *.s | tsort\` |  | ||||||
| # Build library |  | ||||||
| .SH DESCRIPTION |  | ||||||
| .PP |  | ||||||
| \fITsort\fR accepts a file of lines containing ordered pairs and builds a |  | ||||||
| total ordering from the partial orderings. |  | ||||||
| @ -67,6 +67,7 @@ | |||||||
| 2012/10/17 12:00:00,tools/nbperf | 2012/10/17 12:00:00,tools/nbperf | ||||||
| 2012/10/17 12:00:00,tools/sed | 2012/10/17 12:00:00,tools/sed | ||||||
| 2012/10/17 12:00:00,tools/tic | 2012/10/17 12:00:00,tools/tic | ||||||
|  | 2012/10/17 12:00:00,tools/tsort | ||||||
| 2012/10/17 12:00:00,usr.bin/gzip/Makefile | 2012/10/17 12:00:00,usr.bin/gzip/Makefile | ||||||
| 2012/10/17 12:00:00,usr.bin/indent | 2012/10/17 12:00:00,usr.bin/indent | ||||||
| 2012/10/17 12:00:00,usr.bin/join | 2012/10/17 12:00:00,usr.bin/join | ||||||
| @ -76,6 +77,7 @@ | |||||||
| 2012/10/17 12:00:00,usr.bin/nbperf | 2012/10/17 12:00:00,usr.bin/nbperf | ||||||
| 2012/10/17 12:00:00,usr.bin/passwd/Makefile | 2012/10/17 12:00:00,usr.bin/passwd/Makefile | ||||||
| 2012/10/17 12:00:00,usr.bin/sort | 2012/10/17 12:00:00,usr.bin/sort | ||||||
|  | 2012/10/17 12:00:00,usr.bin/tsort | ||||||
| 2012/10/17 12:00:00,usr.bin/xinstall | 2012/10/17 12:00:00,usr.bin/xinstall | ||||||
| 2012/10/17 12:00:00,usr.sbin/Makefile | 2012/10/17 12:00:00,usr.sbin/Makefile | ||||||
| 2012/10/17 12:00:00,usr.sbin/Makefile.inc | 2012/10/17 12:00:00,usr.sbin/Makefile.inc | ||||||
|  | |||||||
| @ -510,12 +510,7 @@ _INSTRANLIB=${empty(PRESERVE):?-a "${RANLIB} -t":} | |||||||
| __archivebuild: .USE | __archivebuild: .USE | ||||||
| 	${_MKTARGET_BUILD} | 	${_MKTARGET_BUILD} | ||||||
| 	rm -f ${.TARGET} | 	rm -f ${.TARGET} | ||||||
| .if defined(__MINIX) |  | ||||||
| 	# LSC FIXME MINIX: We do not have yet imported tsort nor lorder |  | ||||||
| 	${AR} ${_ARFL} ${.TARGET} ${.ALLSRC:M*o} |  | ||||||
| .else |  | ||||||
| 	${AR} ${_ARFL} ${.TARGET} `NM=${NM} ${LORDER} ${.ALLSRC:M*o} | ${TSORT}` | 	${AR} ${_ARFL} ${.TARGET} `NM=${NM} ${LORDER} ${.ALLSRC:M*o} | ${TSORT}` | ||||||
| .endif # defined(__MINIX)
 |  | ||||||
| .endif | .endif | ||||||
| 
 | 
 | ||||||
| .if !target(__archiveinstall) | .if !target(__archiveinstall) | ||||||
|  | |||||||
| @ -61,7 +61,7 @@ LINT_BITS= lint lint2 | |||||||
| SUBDIR=	host-mkdep .WAIT compat .WAIT \
 | SUBDIR=	host-mkdep .WAIT compat .WAIT \
 | ||||||
| 	binstall .WAIT mktemp .WAIT sed .WAIT \
 | 	binstall .WAIT mktemp .WAIT sed .WAIT \
 | ||||||
| 		genassym join \
 | 		genassym join \
 | ||||||
| 		lorder makewhatis mkdep mtree nbperf .WAIT \
 | 		lorder makewhatis mkdep mtree nbperf .WAIT tsort \
 | ||||||
| 		m4 \
 | 		m4 \
 | ||||||
| 	.WAIT mkfs.mfs \
 | 	.WAIT mkfs.mfs \
 | ||||||
| 	.WAIT yacc \
 | 	.WAIT yacc \
 | ||||||
|  | |||||||
							
								
								
									
										6
									
								
								tools/tsort/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								tools/tsort/Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | #	$NetBSD: Makefile,v 1.4 2002/12/08 20:20:06 thorpej Exp $
 | ||||||
|  | 
 | ||||||
|  | HOSTPROGNAME=	${_TOOL_PREFIX}tsort | ||||||
|  | HOST_SRCDIR=	usr.bin/tsort | ||||||
|  | 
 | ||||||
|  | .include "${.CURDIR}/../Makefile.host" | ||||||
| @ -20,6 +20,7 @@ SUBDIR= \ | |||||||
| 	sed seq \
 | 	sed seq \
 | ||||||
| 	sort stat su \
 | 	sort stat su \
 | ||||||
| 	tic \
 | 	tic \
 | ||||||
|  | 	tsort \
 | ||||||
| 	uniq \
 | 	uniq \
 | ||||||
| 	xinstall  | 	xinstall  | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										6
									
								
								usr.bin/tsort/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								usr.bin/tsort/Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | #	$NetBSD: Makefile,v 1.7 2009/04/14 22:15:27 lukem Exp $
 | ||||||
|  | #	@(#)Makefile	8.1 (Berkeley) 6/9/93
 | ||||||
|  | 
 | ||||||
|  | PROG=	tsort | ||||||
|  | 
 | ||||||
|  | .include <bsd.prog.mk> | ||||||
							
								
								
									
										87
									
								
								usr.bin/tsort/tsort.1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								usr.bin/tsort/tsort.1
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,87 @@ | |||||||
|  | .\"	$NetBSD: tsort.1,v 1.10 2003/08/07 11:16:50 agc Exp $ | ||||||
|  | .\" | ||||||
|  | .\" Copyright (c) 1990, 1993, 1994 | ||||||
|  | .\"	The Regents of the University of California.  All rights reserved. | ||||||
|  | .\" | ||||||
|  | .\" This manual is derived from one contributed to Berkeley by | ||||||
|  | .\" Michael Rendell of Memorial University of Newfoundland. | ||||||
|  | .\" | ||||||
|  | .\" Redistribution and use in source and binary forms, with or without | ||||||
|  | .\" modification, are permitted provided that the following conditions | ||||||
|  | .\" are met: | ||||||
|  | .\" 1. Redistributions of source code must retain the above copyright | ||||||
|  | .\"    notice, this list of conditions and the following disclaimer. | ||||||
|  | .\" 2. Redistributions in binary form must reproduce the above copyright | ||||||
|  | .\"    notice, this list of conditions and the following disclaimer in the | ||||||
|  | .\"    documentation and/or other materials provided with the distribution. | ||||||
|  | .\" 3. Neither the name of the University nor the names of its contributors | ||||||
|  | .\"    may be used to endorse or promote products derived from this software | ||||||
|  | .\"    without specific prior written permission. | ||||||
|  | .\" | ||||||
|  | .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||||||
|  | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||||
|  | .\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||||||
|  | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||||||
|  | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
|  | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||||
|  | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||||
|  | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||||
|  | .\" SUCH DAMAGE. | ||||||
|  | .\" | ||||||
|  | .\"     @(#)tsort.1	8.3 (Berkeley) 4/1/94 | ||||||
|  | .\" | ||||||
|  | .Dd April 1, 1994 | ||||||
|  | .Dt TSORT 1 | ||||||
|  | .Os | ||||||
|  | .Sh NAME | ||||||
|  | .Nm tsort | ||||||
|  | .Nd topological sort of a directed graph | ||||||
|  | .Sh SYNOPSIS | ||||||
|  | .Nm | ||||||
|  | .Op Fl l | ||||||
|  | .Op Fl q | ||||||
|  | .Op Ar file | ||||||
|  | .Sh DESCRIPTION | ||||||
|  | .Nm | ||||||
|  | takes a list of pairs of node names representing directed arcs in | ||||||
|  | a graph and prints the nodes in topological order on standard output. | ||||||
|  | Input is taken from the named | ||||||
|  | .Ar file , | ||||||
|  | or from standard input if no file | ||||||
|  | is given. | ||||||
|  | .Pp | ||||||
|  | Node names in the input are separated by white space and there must | ||||||
|  | be an even number of node names. | ||||||
|  | .Pp | ||||||
|  | Presence of a node in a graph can be represented by an arc from the node | ||||||
|  | to itself. | ||||||
|  | This is useful when a node is not connected to any other nodes. | ||||||
|  | .Pp | ||||||
|  | If the graph contains a cycle (and therefore cannot be properly sorted), | ||||||
|  | one of the arcs in the cycle is ignored and the sort continues. | ||||||
|  | Cycles are reported on standard error. | ||||||
|  | .Pp | ||||||
|  | The options are as follows: | ||||||
|  | .Bl -tag -width Ds | ||||||
|  | .It Fl l | ||||||
|  | Search for and display the longest cycle. | ||||||
|  | Can take a very long time. | ||||||
|  | .It Fl q | ||||||
|  | Do not display informational messages about cycles. | ||||||
|  | This is primarily | ||||||
|  | intended for building libraries, where optimal ordering is not critical, | ||||||
|  | and cycles occur often. | ||||||
|  | .El | ||||||
|  | .Sh SEE ALSO | ||||||
|  | .Xr ar 1 | ||||||
|  | .Sh HISTORY | ||||||
|  | A | ||||||
|  | .Nm | ||||||
|  | command appeared in | ||||||
|  | .At v7 . | ||||||
|  | This | ||||||
|  | .Nm | ||||||
|  | command and manual page are derived from sources contributed to Berkeley by | ||||||
|  | Michael Rendell of Memorial University of Newfoundland. | ||||||
							
								
								
									
										433
									
								
								usr.bin/tsort/tsort.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										433
									
								
								usr.bin/tsort/tsort.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,433 @@ | |||||||
|  | /*	$NetBSD: tsort.c,v 1.23 2011/09/06 18:34:37 joerg Exp $	*/ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 1989, 1993, 1994 | ||||||
|  |  *	The Regents of the University of California.  All rights reserved. | ||||||
|  |  * | ||||||
|  |  * This code is derived from software contributed to Berkeley by | ||||||
|  |  * Michael Rendell of Memorial University of Newfoundland. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions | ||||||
|  |  * are met: | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright | ||||||
|  |  *    notice, this list of conditions and the following disclaimer. | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright | ||||||
|  |  *    notice, this list of conditions and the following disclaimer in the | ||||||
|  |  *    documentation and/or other materials provided with the distribution. | ||||||
|  |  * 3. Neither the name of the University nor the names of its contributors | ||||||
|  |  *    may be used to endorse or promote products derived from this software | ||||||
|  |  *    without specific prior written permission. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||||||
|  |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||||
|  |  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||||||
|  |  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
|  |  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||||
|  |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||||
|  |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||||
|  |  * SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #if HAVE_NBTOOL_CONFIG_H | ||||||
|  | #include "nbtool_config.h" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #include <sys/cdefs.h> | ||||||
|  | #if !defined(lint) | ||||||
|  | __COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\
 | ||||||
|  |  The Regents of the University of California.  All rights reserved."); | ||||||
|  | #if 0 | ||||||
|  | static char sccsid[] = "@(#)tsort.c	8.3 (Berkeley) 5/4/95"; | ||||||
|  | #endif | ||||||
|  | __RCSID("$NetBSD: tsort.c,v 1.23 2011/09/06 18:34:37 joerg Exp $"); | ||||||
|  | #endif /* not lint */ | ||||||
|  | 
 | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <ctype.h> | ||||||
|  | #include <db.h> | ||||||
|  | #include <err.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  *  Topological sort.  Input is a list of pairs of strings separated by | ||||||
|  |  *  white space (spaces, tabs, and/or newlines); strings are written to | ||||||
|  |  *  standard output in sorted order, one per line. | ||||||
|  |  * | ||||||
|  |  *  usage: | ||||||
|  |  *     tsort [-l] [inputfile] | ||||||
|  |  *  If no input file is specified, standard input is read. | ||||||
|  |  * | ||||||
|  |  *  Should be compatible with AT&T tsort HOWEVER the output is not identical | ||||||
|  |  *  (i.e. for most graphs there is more than one sorted order, and this tsort | ||||||
|  |  *  usually generates a different one then the AT&T tsort).  Also, cycle | ||||||
|  |  *  reporting seems to be more accurate in this version (the AT&T tsort | ||||||
|  |  *  sometimes says a node is in a cycle when it isn't). | ||||||
|  |  * | ||||||
|  |  *  Michael Rendell, michael@stretch.cs.mun.ca - Feb 26, '90 | ||||||
|  |  */ | ||||||
|  | #define	HASHSIZE	53		/* doesn't need to be big */ | ||||||
|  | #define	NF_MARK		0x1		/* marker for cycle detection */ | ||||||
|  | #define	NF_ACYCLIC	0x2		/* this node is cycle free */ | ||||||
|  | #define	NF_NODEST	0x4		/* Unreachable */ | ||||||
|  | 
 | ||||||
|  | typedef struct node_str NODE; | ||||||
|  | 
 | ||||||
|  | struct node_str { | ||||||
|  | 	NODE **n_prevp;			/* pointer to previous node's n_next */ | ||||||
|  | 	NODE *n_next;			/* next node in graph */ | ||||||
|  | 	NODE **n_arcs;			/* array of arcs to other nodes */ | ||||||
|  | 	int n_narcs;			/* number of arcs in n_arcs[] */ | ||||||
|  | 	int n_arcsize;			/* size of n_arcs[] array */ | ||||||
|  | 	int n_refcnt;			/* # of arcs pointing to this node */ | ||||||
|  | 	int n_flags;			/* NF_* */ | ||||||
|  | 	char n_name[1];			/* name of this node */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct _buf { | ||||||
|  | 	char *b_buf; | ||||||
|  | 	int b_bsize; | ||||||
|  | } BUF; | ||||||
|  | 
 | ||||||
|  | static DB *db; | ||||||
|  | static NODE *graph, **cycle_buf, **longest_cycle; | ||||||
|  | static int debug, longest, quiet; | ||||||
|  | 
 | ||||||
|  | static void	 add_arc(char *, char *); | ||||||
|  | static void	 clear_cycle(void); | ||||||
|  | static int	 find_cycle(NODE *, NODE *, int, int); | ||||||
|  | static NODE	*get_node(char *); | ||||||
|  | static void	*grow_buf(void *, int); | ||||||
|  | static void	 remove_node(NODE *); | ||||||
|  | static void	 tsort(void); | ||||||
|  | __dead static void	 usage(void); | ||||||
|  | 
 | ||||||
|  | int | ||||||
|  | main(int argc, char *argv[]) | ||||||
|  | { | ||||||
|  | 	BUF *b; | ||||||
|  | 	int c, n; | ||||||
|  | 	FILE *fp; | ||||||
|  | 	int bsize, ch, nused; | ||||||
|  | 	BUF bufs[2]; | ||||||
|  | 
 | ||||||
|  | 	setprogname(argv[0]); | ||||||
|  | 
 | ||||||
|  | 	fp = NULL; | ||||||
|  | 	while ((ch = getopt(argc, argv, "dlq")) != -1) | ||||||
|  | 		switch (ch) { | ||||||
|  | 		case 'd': | ||||||
|  | 			debug = 1; | ||||||
|  | 			break; | ||||||
|  | 		case 'l': | ||||||
|  | 			longest = 1; | ||||||
|  | 			break; | ||||||
|  | 		case 'q': | ||||||
|  | 			quiet = 1; | ||||||
|  | 			break; | ||||||
|  | 		case '?': | ||||||
|  | 		default: | ||||||
|  | 			usage(); | ||||||
|  | 		} | ||||||
|  | 	argc -= optind; | ||||||
|  | 	argv += optind; | ||||||
|  | 
 | ||||||
|  | 	switch (argc) { | ||||||
|  | 	case 0: | ||||||
|  | 		fp = stdin; | ||||||
|  | 		break; | ||||||
|  | 	case 1: | ||||||
|  | 		if ((fp = fopen(*argv, "r")) == NULL) | ||||||
|  | 			err(1, "%s", *argv); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		usage(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (b = bufs, n = 2; --n >= 0; b++) | ||||||
|  | 		b->b_buf = grow_buf(NULL, b->b_bsize = 1024); | ||||||
|  | 
 | ||||||
|  | 	/* parse input and build the graph */ | ||||||
|  | 	for (n = 0, c = getc(fp);;) { | ||||||
|  | 		while (c != EOF && isspace(c)) | ||||||
|  | 			c = getc(fp); | ||||||
|  | 		if (c == EOF) | ||||||
|  | 			break; | ||||||
|  | 
 | ||||||
|  | 		nused = 0; | ||||||
|  | 		b = &bufs[n]; | ||||||
|  | 		bsize = b->b_bsize; | ||||||
|  | 		do { | ||||||
|  | 			b->b_buf[nused++] = c; | ||||||
|  | 			if (nused == bsize) | ||||||
|  | 				b->b_buf = grow_buf(b->b_buf, bsize *= 2); | ||||||
|  | 			c = getc(fp); | ||||||
|  | 		} while (c != EOF && !isspace(c)); | ||||||
|  | 
 | ||||||
|  | 		b->b_buf[nused] = '\0'; | ||||||
|  | 		b->b_bsize = bsize; | ||||||
|  | 		if (n) | ||||||
|  | 			add_arc(bufs[0].b_buf, bufs[1].b_buf); | ||||||
|  | 		n = !n; | ||||||
|  | 	} | ||||||
|  | 	(void)fclose(fp); | ||||||
|  | 	if (n) | ||||||
|  | 		errx(1, "odd data count"); | ||||||
|  | 
 | ||||||
|  | 	/* do the sort */ | ||||||
|  | 	tsort(); | ||||||
|  | 	return(0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* double the size of oldbuf and return a pointer to the new buffer. */ | ||||||
|  | static void * | ||||||
|  | grow_buf(void *bp, int size) | ||||||
|  | { | ||||||
|  | 	void *n; | ||||||
|  | 
 | ||||||
|  | 	if ((n = realloc(bp, (u_int)size)) == NULL) | ||||||
|  | 		err(1, "realloc"); | ||||||
|  | 	bp = n; | ||||||
|  | 	return (bp); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * add an arc from node s1 to node s2 in the graph.  If s1 or s2 are not in | ||||||
|  |  * the graph, then add them. | ||||||
|  |  */ | ||||||
|  | static void | ||||||
|  | add_arc(char *s1, char *s2) | ||||||
|  | { | ||||||
|  | 	NODE *n1; | ||||||
|  | 	NODE *n2; | ||||||
|  | 	int bsize, i; | ||||||
|  | 
 | ||||||
|  | 	n1 = get_node(s1); | ||||||
|  | 
 | ||||||
|  | 	if (!strcmp(s1, s2)) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	n2 = get_node(s2); | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Check if this arc is already here. | ||||||
|  | 	 */ | ||||||
|  | 	for (i = 0; i < n1->n_narcs; i++) | ||||||
|  | 		if (n1->n_arcs[i] == n2) | ||||||
|  | 			return; | ||||||
|  | 	/*
 | ||||||
|  | 	 * Add it. | ||||||
|  | 	 */ | ||||||
|  | 	if (n1->n_narcs == n1->n_arcsize) { | ||||||
|  | 		if (!n1->n_arcsize) | ||||||
|  | 			n1->n_arcsize = 10; | ||||||
|  | 		bsize = n1->n_arcsize * sizeof(*n1->n_arcs) * 2; | ||||||
|  | 		n1->n_arcs = grow_buf(n1->n_arcs, bsize); | ||||||
|  | 		n1->n_arcsize = bsize / sizeof(*n1->n_arcs); | ||||||
|  | 	} | ||||||
|  | 	n1->n_arcs[n1->n_narcs++] = n2; | ||||||
|  | 	++n2->n_refcnt; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Find a node in the graph (insert if not found) and return a pointer to it. */ | ||||||
|  | static NODE * | ||||||
|  | get_node(char *name) | ||||||
|  | { | ||||||
|  | 	DBT data, key; | ||||||
|  | 	NODE *n; | ||||||
|  | 
 | ||||||
|  | 	if (db == NULL && | ||||||
|  | 	    (db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == NULL) | ||||||
|  | 		err(1, "db: %s", name); | ||||||
|  | 
 | ||||||
|  | 	key.data = name; | ||||||
|  | 	key.size = strlen(name) + 1; | ||||||
|  | 
 | ||||||
|  | 	switch ((*db->get)(db, &key, &data, 0)) { | ||||||
|  | 	case 0: | ||||||
|  | 		(void)memmove(&n, data.data, sizeof(n)); | ||||||
|  | 		return (n); | ||||||
|  | 	case 1: | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 	case -1: | ||||||
|  | 		err(1, "db: %s", name); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if ((n = malloc(sizeof(NODE) + key.size)) == NULL) | ||||||
|  | 		err(1, "malloc"); | ||||||
|  | 
 | ||||||
|  | 	n->n_narcs = 0; | ||||||
|  | 	n->n_arcsize = 0; | ||||||
|  | 	n->n_arcs = NULL; | ||||||
|  | 	n->n_refcnt = 0; | ||||||
|  | 	n->n_flags = 0; | ||||||
|  | 	(void)memmove(n->n_name, name, key.size); | ||||||
|  | 
 | ||||||
|  | 	/* Add to linked list. */ | ||||||
|  | 	if ((n->n_next = graph) != NULL) | ||||||
|  | 		graph->n_prevp = &n->n_next; | ||||||
|  | 	n->n_prevp = &graph; | ||||||
|  | 	graph = n; | ||||||
|  | 
 | ||||||
|  | 	/* Add to hash table. */ | ||||||
|  | 	data.data = &n; | ||||||
|  | 	data.size = sizeof(n); | ||||||
|  | 	if ((*db->put)(db, &key, &data, 0)) | ||||||
|  | 		err(1, "db: %s", name); | ||||||
|  | 	return (n); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Clear the NODEST flag from all nodes. | ||||||
|  |  */ | ||||||
|  | static void | ||||||
|  | clear_cycle(void) | ||||||
|  | { | ||||||
|  | 	NODE *n; | ||||||
|  | 
 | ||||||
|  | 	for (n = graph; n != NULL; n = n->n_next) | ||||||
|  | 		n->n_flags &= ~NF_NODEST; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* do topological sort on graph */ | ||||||
|  | static void | ||||||
|  | tsort(void) | ||||||
|  | { | ||||||
|  | 	NODE *n, *next; | ||||||
|  | 	int cnt, i; | ||||||
|  | 
 | ||||||
|  | 	while (graph != NULL) { | ||||||
|  | 		/*
 | ||||||
|  | 		 * Keep getting rid of simple cases until there are none left, | ||||||
|  | 		 * if there are any nodes still in the graph, then there is | ||||||
|  | 		 * a cycle in it. | ||||||
|  | 		 */ | ||||||
|  | 		do { | ||||||
|  | 			for (cnt = 0, n = graph; n != NULL; n = next) { | ||||||
|  | 				next = n->n_next; | ||||||
|  | 				if (n->n_refcnt == 0) { | ||||||
|  | 					remove_node(n); | ||||||
|  | 					++cnt; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} while (graph != NULL && cnt); | ||||||
|  | 
 | ||||||
|  | 		if (graph == NULL) | ||||||
|  | 			break; | ||||||
|  | 
 | ||||||
|  | 		if (!cycle_buf) { | ||||||
|  | 			/*
 | ||||||
|  | 			 * Allocate space for two cycle logs - one to be used | ||||||
|  | 			 * as scratch space, the other to save the longest | ||||||
|  | 			 * cycle. | ||||||
|  | 			 */ | ||||||
|  | 			for (cnt = 0, n = graph; n != NULL; n = n->n_next) | ||||||
|  | 				++cnt; | ||||||
|  | 			cycle_buf = malloc((u_int)sizeof(NODE *) * cnt); | ||||||
|  | 			longest_cycle = malloc((u_int)sizeof(NODE *) * cnt); | ||||||
|  | 			if (cycle_buf == NULL || longest_cycle == NULL) | ||||||
|  | 				err(1, "malloc"); | ||||||
|  | 		} | ||||||
|  | 		for (n = graph; n != NULL; n = n->n_next) { | ||||||
|  | 			if (!(n->n_flags & NF_ACYCLIC)) { | ||||||
|  | 				if ((cnt = find_cycle(n, n, 0, 0)) != 0) { | ||||||
|  | 					if (!quiet) { | ||||||
|  | 						warnx("cycle in data"); | ||||||
|  | 						for (i = 0; i < cnt; i++) | ||||||
|  | 							warnx("%s",  | ||||||
|  | 							    longest_cycle[i]->n_name); | ||||||
|  | 					} | ||||||
|  | 					remove_node(n); | ||||||
|  | 					clear_cycle(); | ||||||
|  | 					break; | ||||||
|  | 				} else { | ||||||
|  | 					/* to avoid further checks */ | ||||||
|  | 					n->n_flags  |= NF_ACYCLIC; | ||||||
|  | 					clear_cycle(); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if (n == NULL) | ||||||
|  | 			errx(1, "internal error -- could not find cycle"); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* print node and remove from graph (does not actually free node) */ | ||||||
|  | static void | ||||||
|  | remove_node(NODE *n) | ||||||
|  | { | ||||||
|  | 	NODE **np; | ||||||
|  | 	int i; | ||||||
|  | 
 | ||||||
|  | 	(void)printf("%s\n", n->n_name); | ||||||
|  | 	for (np = n->n_arcs, i = n->n_narcs; --i >= 0; np++) | ||||||
|  | 		--(*np)->n_refcnt; | ||||||
|  | 	n->n_narcs = 0; | ||||||
|  | 	*n->n_prevp = n->n_next; | ||||||
|  | 	if (n->n_next) | ||||||
|  | 		n->n_next->n_prevp = n->n_prevp; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* look for the longest? cycle from node from to node to. */ | ||||||
|  | static int | ||||||
|  | find_cycle(NODE *from, NODE *to, int longest_len, int depth) | ||||||
|  | { | ||||||
|  | 	NODE **np; | ||||||
|  | 	int i, len; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * avoid infinite loops and ignore portions of the graph known | ||||||
|  | 	 * to be acyclic | ||||||
|  | 	 */ | ||||||
|  | 	if (from->n_flags & (NF_NODEST|NF_MARK|NF_ACYCLIC)) | ||||||
|  | 		return (0); | ||||||
|  | 	from->n_flags |= NF_MARK; | ||||||
|  | 
 | ||||||
|  | 	for (np = from->n_arcs, i = from->n_narcs; --i >= 0; np++) { | ||||||
|  | 		cycle_buf[depth] = *np; | ||||||
|  | 		if (*np == to) { | ||||||
|  | 			if (depth + 1 > longest_len) { | ||||||
|  | 				longest_len = depth + 1; | ||||||
|  | 				(void)memcpy(longest_cycle, cycle_buf, | ||||||
|  | 				    longest_len * sizeof(NODE *)); | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			if ((*np)->n_flags & (NF_MARK|NF_ACYCLIC|NF_NODEST)) | ||||||
|  | 				continue; | ||||||
|  | 			len = find_cycle(*np, to, longest_len, depth + 1); | ||||||
|  | 
 | ||||||
|  | 			if (debug) | ||||||
|  | 				(void)printf("%*s %s->%s %d\n", depth, "", | ||||||
|  | 				    from->n_name, to->n_name, len); | ||||||
|  | 
 | ||||||
|  | 			if (len == 0) | ||||||
|  | 				(*np)->n_flags |= NF_NODEST; | ||||||
|  | 
 | ||||||
|  | 			if (len > longest_len) | ||||||
|  | 				longest_len = len; | ||||||
|  | 
 | ||||||
|  | 			if (len > 0 && !longest) | ||||||
|  | 				break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	from->n_flags &= ~NF_MARK; | ||||||
|  | 	return (longest_len); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | usage(void) | ||||||
|  | { | ||||||
|  | 	(void)fprintf(stderr, "usage: tsort [-lq] [file]\n"); | ||||||
|  | 	exit(1); | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Lionel Sambuc
						Lionel Sambuc