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 \
 | ||||
| 	syslogd tail tcpd tcpdp tcpstat tee telnet \
 | ||||
| 	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 \
 | ||||
| 	whereis which who write writeisofs fetch \
 | ||||
| 	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: | ||||
| 	lorder requires a newer version of sort, so to ensure it is present | ||||
| 	do the following: | ||||
|  | ||||
| @ -19,7 +19,7 @@ MAN=	ash.1 at.1 banner.1 basename.1 \ | ||||
| 	split.1 stty.1 svc.1 svrctl.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 \
 | ||||
| 	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 \
 | ||||
| 	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/sed | ||||
| 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/indent | ||||
| 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/passwd/Makefile | ||||
| 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.sbin/Makefile | ||||
| 2012/10/17 12:00:00,usr.sbin/Makefile.inc | ||||
|  | ||||
| @ -510,12 +510,7 @@ _INSTRANLIB=${empty(PRESERVE):?-a "${RANLIB} -t":} | ||||
| __archivebuild: .USE | ||||
| 	${_MKTARGET_BUILD} | ||||
| 	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}` | ||||
| .endif # defined(__MINIX)
 | ||||
| .endif | ||||
| 
 | ||||
| .if !target(__archiveinstall) | ||||
|  | ||||
| @ -61,7 +61,7 @@ LINT_BITS= lint lint2 | ||||
| SUBDIR=	host-mkdep .WAIT compat .WAIT \
 | ||||
| 	binstall .WAIT mktemp .WAIT sed .WAIT \
 | ||||
| 		genassym join \
 | ||||
| 		lorder makewhatis mkdep mtree nbperf .WAIT \
 | ||||
| 		lorder makewhatis mkdep mtree nbperf .WAIT tsort \
 | ||||
| 		m4 \
 | ||||
| 	.WAIT mkfs.mfs \
 | ||||
| 	.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,7 +20,8 @@ SUBDIR= \ | ||||
| 	sed seq \
 | ||||
| 	sort stat su \
 | ||||
| 	tic \
 | ||||
| 	uniq  \
 | ||||
| 	tsort \
 | ||||
| 	uniq \
 | ||||
| 	xinstall  | ||||
| 
 | ||||
| .if !defined(__MINIX) | ||||
|  | ||||
							
								
								
									
										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