]> Zhao Yanbai Git Server - minix.git/commitdiff
Importing NetBSD tsort 39/139/7
authorLionel Sambuc <lionel@minix3.org>
Tue, 4 Dec 2012 13:02:11 +0000 (14:02 +0100)
committerLionel Sambuc <lionel@minix3.org>
Fri, 7 Dec 2012 12:58:07 +0000 (13:58 +0100)
Change-Id: I110de8037b9253f4fe53cbe13dc8fc281aeea2ec

14 files changed:
commands/Makefile
commands/tsort/Makefile [deleted file]
commands/tsort/tsort.c [deleted file]
docs/UPDATING
man/man1/Makefile
man/man1/tsort.1 [deleted file]
releasetools/nbsd_ports
share/mk/bsd.lib.mk
tools/Makefile
tools/tsort/Makefile [new file with mode: 0644]
usr.bin/Makefile
usr.bin/tsort/Makefile [new file with mode: 0644]
usr.bin/tsort/tsort.1 [new file with mode: 0644]
usr.bin/tsort/tsort.c [new file with mode: 0644]

index 73e104dd419b731c665f87c3afa50b7863dc72b2..ffd193b64cf99bcb2007755f158fdef5e1471425 100644 (file)
@@ -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 \
diff --git a/commands/tsort/Makefile b/commands/tsort/Makefile
deleted file mode 100644 (file)
index 4a438ab..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-PROG=  tsort
-MAN=
-
-.include <bsd.prog.mk>
diff --git a/commands/tsort/tsort.c b/commands/tsort/tsort.c
deleted file mode 100644 (file)
index 4c9ee24..0000000
+++ /dev/null
@@ -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);
-}
index 841663ed566550aa6d28b1a0b7bd737deb326961..a49924c456568861fd713d9f9c9631ab9cffa892 100644 (file)
@@ -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:
index 0abec304e97df4de45a2469909d0643ea076a1c2..c3df9e45ed0cd6348694c3e4e5a98b517c568dca 100644 (file)
@@ -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
 
diff --git a/man/man1/tsort.1 b/man/man1/tsort.1
deleted file mode 100644 (file)
index 19fa6da..0000000
+++ /dev/null
@@ -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.
index 661bdc03620258fd3ef1cc32f5cfc69516282ee7..a87a49f51dc646c32019304adf1e7c3bca99096c 100644 (file)
@@ -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
index 1db0a82754e5730b803e5386abe6a282a82c40d3..622b7a916749d1d46b06d4ec4c9a5e7b7550641a 100644 (file)
@@ -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)
index e4b8d71d80a0662ae96485f07e1fbd6c51bd01f7..36bd77028e83f9d93255a21b78fceda1c8746dbf 100644 (file)
@@ -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 \
diff --git a/tools/tsort/Makefile b/tools/tsort/Makefile
new file mode 100644 (file)
index 0000000..bb516a7
--- /dev/null
@@ -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"
index 92f1ea20e68cda3905cbab788ac41a364c605a47..53984bb4ce3e7dbe61b41ea7bed35bd4613a2d72 100644 (file)
@@ -20,7 +20,8 @@ SUBDIR= \
        sed seq \
        sort stat su \
        tic \
-       uniq  \
+       tsort \
+       uniq \
        xinstall 
 
 .if !defined(__MINIX)
diff --git a/usr.bin/tsort/Makefile b/usr.bin/tsort/Makefile
new file mode 100644 (file)
index 0000000..06e78c7
--- /dev/null
@@ -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>
diff --git a/usr.bin/tsort/tsort.1 b/usr.bin/tsort/tsort.1
new file mode 100644 (file)
index 0000000..061824b
--- /dev/null
@@ -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.
diff --git a/usr.bin/tsort/tsort.c b/usr.bin/tsort/tsort.c
new file mode 100644 (file)
index 0000000..3e8d569
--- /dev/null
@@ -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);
+}