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 \
+++ /dev/null
-PROG= tsort
-MAN=
-
-.include <bsd.prog.mk>
+++ /dev/null
-/* 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);
-}
+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:
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
+++ /dev/null
-.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.
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
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
__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)
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 \
--- /dev/null
+# $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"
sed seq \
sort stat su \
tic \
- uniq \
+ tsort \
+ uniq \
xinstall
.if !defined(__MINIX)
--- /dev/null
+# $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>
--- /dev/null
+.\" $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.
--- /dev/null
+/* $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);
+}