Importing NetBSD tsort

Change-Id: I110de8037b9253f4fe53cbe13dc8fc281aeea2ec
This commit is contained in:
Lionel Sambuc 2012-12-04 14:02:11 +01:00
parent 6e0ed9c90c
commit 8e5df35e84
14 changed files with 545 additions and 396 deletions

View 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 \

View file

@ -1,4 +0,0 @@
PROG= tsort
MAN=
.include <bsd.prog.mk>

View file

@ -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);
}

View 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:

View 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

View file

@ -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.

View 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

View 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)

View 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 \

6
tools/tsort/Makefile Normal file
View 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"

View file

@ -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
View 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
View 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
View 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);
}