xref: /minix3/usr.bin/tsort/tsort.c (revision 8e5df35e84e62bb6fe3ec211e177c205ccdbfe00)
1*8e5df35eSLionel Sambuc /*	$NetBSD: tsort.c,v 1.23 2011/09/06 18:34:37 joerg Exp $	*/
2*8e5df35eSLionel Sambuc 
3*8e5df35eSLionel Sambuc /*
4*8e5df35eSLionel Sambuc  * Copyright (c) 1989, 1993, 1994
5*8e5df35eSLionel Sambuc  *	The Regents of the University of California.  All rights reserved.
6*8e5df35eSLionel Sambuc  *
7*8e5df35eSLionel Sambuc  * This code is derived from software contributed to Berkeley by
8*8e5df35eSLionel Sambuc  * Michael Rendell of Memorial University of Newfoundland.
9*8e5df35eSLionel Sambuc  *
10*8e5df35eSLionel Sambuc  * Redistribution and use in source and binary forms, with or without
11*8e5df35eSLionel Sambuc  * modification, are permitted provided that the following conditions
12*8e5df35eSLionel Sambuc  * are met:
13*8e5df35eSLionel Sambuc  * 1. Redistributions of source code must retain the above copyright
14*8e5df35eSLionel Sambuc  *    notice, this list of conditions and the following disclaimer.
15*8e5df35eSLionel Sambuc  * 2. Redistributions in binary form must reproduce the above copyright
16*8e5df35eSLionel Sambuc  *    notice, this list of conditions and the following disclaimer in the
17*8e5df35eSLionel Sambuc  *    documentation and/or other materials provided with the distribution.
18*8e5df35eSLionel Sambuc  * 3. Neither the name of the University nor the names of its contributors
19*8e5df35eSLionel Sambuc  *    may be used to endorse or promote products derived from this software
20*8e5df35eSLionel Sambuc  *    without specific prior written permission.
21*8e5df35eSLionel Sambuc  *
22*8e5df35eSLionel Sambuc  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23*8e5df35eSLionel Sambuc  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24*8e5df35eSLionel Sambuc  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25*8e5df35eSLionel Sambuc  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26*8e5df35eSLionel Sambuc  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27*8e5df35eSLionel Sambuc  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28*8e5df35eSLionel Sambuc  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29*8e5df35eSLionel Sambuc  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30*8e5df35eSLionel Sambuc  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31*8e5df35eSLionel Sambuc  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32*8e5df35eSLionel Sambuc  * SUCH DAMAGE.
33*8e5df35eSLionel Sambuc  */
34*8e5df35eSLionel Sambuc 
35*8e5df35eSLionel Sambuc #if HAVE_NBTOOL_CONFIG_H
36*8e5df35eSLionel Sambuc #include "nbtool_config.h"
37*8e5df35eSLionel Sambuc #endif
38*8e5df35eSLionel Sambuc 
39*8e5df35eSLionel Sambuc #include <sys/cdefs.h>
40*8e5df35eSLionel Sambuc #if !defined(lint)
41*8e5df35eSLionel Sambuc __COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\
42*8e5df35eSLionel Sambuc  The Regents of the University of California.  All rights reserved.");
43*8e5df35eSLionel Sambuc #if 0
44*8e5df35eSLionel Sambuc static char sccsid[] = "@(#)tsort.c	8.3 (Berkeley) 5/4/95";
45*8e5df35eSLionel Sambuc #endif
46*8e5df35eSLionel Sambuc __RCSID("$NetBSD: tsort.c,v 1.23 2011/09/06 18:34:37 joerg Exp $");
47*8e5df35eSLionel Sambuc #endif /* not lint */
48*8e5df35eSLionel Sambuc 
49*8e5df35eSLionel Sambuc #include <sys/types.h>
50*8e5df35eSLionel Sambuc #include <ctype.h>
51*8e5df35eSLionel Sambuc #include <db.h>
52*8e5df35eSLionel Sambuc #include <err.h>
53*8e5df35eSLionel Sambuc #include <errno.h>
54*8e5df35eSLionel Sambuc #include <fcntl.h>
55*8e5df35eSLionel Sambuc #include <stdio.h>
56*8e5df35eSLionel Sambuc #include <stdlib.h>
57*8e5df35eSLionel Sambuc #include <string.h>
58*8e5df35eSLionel Sambuc #include <unistd.h>
59*8e5df35eSLionel Sambuc 
60*8e5df35eSLionel Sambuc /*
61*8e5df35eSLionel Sambuc  *  Topological sort.  Input is a list of pairs of strings separated by
62*8e5df35eSLionel Sambuc  *  white space (spaces, tabs, and/or newlines); strings are written to
63*8e5df35eSLionel Sambuc  *  standard output in sorted order, one per line.
64*8e5df35eSLionel Sambuc  *
65*8e5df35eSLionel Sambuc  *  usage:
66*8e5df35eSLionel Sambuc  *     tsort [-l] [inputfile]
67*8e5df35eSLionel Sambuc  *  If no input file is specified, standard input is read.
68*8e5df35eSLionel Sambuc  *
69*8e5df35eSLionel Sambuc  *  Should be compatible with AT&T tsort HOWEVER the output is not identical
70*8e5df35eSLionel Sambuc  *  (i.e. for most graphs there is more than one sorted order, and this tsort
71*8e5df35eSLionel Sambuc  *  usually generates a different one then the AT&T tsort).  Also, cycle
72*8e5df35eSLionel Sambuc  *  reporting seems to be more accurate in this version (the AT&T tsort
73*8e5df35eSLionel Sambuc  *  sometimes says a node is in a cycle when it isn't).
74*8e5df35eSLionel Sambuc  *
75*8e5df35eSLionel Sambuc  *  Michael Rendell, michael@stretch.cs.mun.ca - Feb 26, '90
76*8e5df35eSLionel Sambuc  */
77*8e5df35eSLionel Sambuc #define	HASHSIZE	53		/* doesn't need to be big */
78*8e5df35eSLionel Sambuc #define	NF_MARK		0x1		/* marker for cycle detection */
79*8e5df35eSLionel Sambuc #define	NF_ACYCLIC	0x2		/* this node is cycle free */
80*8e5df35eSLionel Sambuc #define	NF_NODEST	0x4		/* Unreachable */
81*8e5df35eSLionel Sambuc 
82*8e5df35eSLionel Sambuc typedef struct node_str NODE;
83*8e5df35eSLionel Sambuc 
84*8e5df35eSLionel Sambuc struct node_str {
85*8e5df35eSLionel Sambuc 	NODE **n_prevp;			/* pointer to previous node's n_next */
86*8e5df35eSLionel Sambuc 	NODE *n_next;			/* next node in graph */
87*8e5df35eSLionel Sambuc 	NODE **n_arcs;			/* array of arcs to other nodes */
88*8e5df35eSLionel Sambuc 	int n_narcs;			/* number of arcs in n_arcs[] */
89*8e5df35eSLionel Sambuc 	int n_arcsize;			/* size of n_arcs[] array */
90*8e5df35eSLionel Sambuc 	int n_refcnt;			/* # of arcs pointing to this node */
91*8e5df35eSLionel Sambuc 	int n_flags;			/* NF_* */
92*8e5df35eSLionel Sambuc 	char n_name[1];			/* name of this node */
93*8e5df35eSLionel Sambuc };
94*8e5df35eSLionel Sambuc 
95*8e5df35eSLionel Sambuc typedef struct _buf {
96*8e5df35eSLionel Sambuc 	char *b_buf;
97*8e5df35eSLionel Sambuc 	int b_bsize;
98*8e5df35eSLionel Sambuc } BUF;
99*8e5df35eSLionel Sambuc 
100*8e5df35eSLionel Sambuc static DB *db;
101*8e5df35eSLionel Sambuc static NODE *graph, **cycle_buf, **longest_cycle;
102*8e5df35eSLionel Sambuc static int debug, longest, quiet;
103*8e5df35eSLionel Sambuc 
104*8e5df35eSLionel Sambuc static void	 add_arc(char *, char *);
105*8e5df35eSLionel Sambuc static void	 clear_cycle(void);
106*8e5df35eSLionel Sambuc static int	 find_cycle(NODE *, NODE *, int, int);
107*8e5df35eSLionel Sambuc static NODE	*get_node(char *);
108*8e5df35eSLionel Sambuc static void	*grow_buf(void *, int);
109*8e5df35eSLionel Sambuc static void	 remove_node(NODE *);
110*8e5df35eSLionel Sambuc static void	 tsort(void);
111*8e5df35eSLionel Sambuc __dead static void	 usage(void);
112*8e5df35eSLionel Sambuc 
113*8e5df35eSLionel Sambuc int
main(int argc,char * argv[])114*8e5df35eSLionel Sambuc main(int argc, char *argv[])
115*8e5df35eSLionel Sambuc {
116*8e5df35eSLionel Sambuc 	BUF *b;
117*8e5df35eSLionel Sambuc 	int c, n;
118*8e5df35eSLionel Sambuc 	FILE *fp;
119*8e5df35eSLionel Sambuc 	int bsize, ch, nused;
120*8e5df35eSLionel Sambuc 	BUF bufs[2];
121*8e5df35eSLionel Sambuc 
122*8e5df35eSLionel Sambuc 	setprogname(argv[0]);
123*8e5df35eSLionel Sambuc 
124*8e5df35eSLionel Sambuc 	fp = NULL;
125*8e5df35eSLionel Sambuc 	while ((ch = getopt(argc, argv, "dlq")) != -1)
126*8e5df35eSLionel Sambuc 		switch (ch) {
127*8e5df35eSLionel Sambuc 		case 'd':
128*8e5df35eSLionel Sambuc 			debug = 1;
129*8e5df35eSLionel Sambuc 			break;
130*8e5df35eSLionel Sambuc 		case 'l':
131*8e5df35eSLionel Sambuc 			longest = 1;
132*8e5df35eSLionel Sambuc 			break;
133*8e5df35eSLionel Sambuc 		case 'q':
134*8e5df35eSLionel Sambuc 			quiet = 1;
135*8e5df35eSLionel Sambuc 			break;
136*8e5df35eSLionel Sambuc 		case '?':
137*8e5df35eSLionel Sambuc 		default:
138*8e5df35eSLionel Sambuc 			usage();
139*8e5df35eSLionel Sambuc 		}
140*8e5df35eSLionel Sambuc 	argc -= optind;
141*8e5df35eSLionel Sambuc 	argv += optind;
142*8e5df35eSLionel Sambuc 
143*8e5df35eSLionel Sambuc 	switch (argc) {
144*8e5df35eSLionel Sambuc 	case 0:
145*8e5df35eSLionel Sambuc 		fp = stdin;
146*8e5df35eSLionel Sambuc 		break;
147*8e5df35eSLionel Sambuc 	case 1:
148*8e5df35eSLionel Sambuc 		if ((fp = fopen(*argv, "r")) == NULL)
149*8e5df35eSLionel Sambuc 			err(1, "%s", *argv);
150*8e5df35eSLionel Sambuc 		break;
151*8e5df35eSLionel Sambuc 	default:
152*8e5df35eSLionel Sambuc 		usage();
153*8e5df35eSLionel Sambuc 	}
154*8e5df35eSLionel Sambuc 
155*8e5df35eSLionel Sambuc 	for (b = bufs, n = 2; --n >= 0; b++)
156*8e5df35eSLionel Sambuc 		b->b_buf = grow_buf(NULL, b->b_bsize = 1024);
157*8e5df35eSLionel Sambuc 
158*8e5df35eSLionel Sambuc 	/* parse input and build the graph */
159*8e5df35eSLionel Sambuc 	for (n = 0, c = getc(fp);;) {
160*8e5df35eSLionel Sambuc 		while (c != EOF && isspace(c))
161*8e5df35eSLionel Sambuc 			c = getc(fp);
162*8e5df35eSLionel Sambuc 		if (c == EOF)
163*8e5df35eSLionel Sambuc 			break;
164*8e5df35eSLionel Sambuc 
165*8e5df35eSLionel Sambuc 		nused = 0;
166*8e5df35eSLionel Sambuc 		b = &bufs[n];
167*8e5df35eSLionel Sambuc 		bsize = b->b_bsize;
168*8e5df35eSLionel Sambuc 		do {
169*8e5df35eSLionel Sambuc 			b->b_buf[nused++] = c;
170*8e5df35eSLionel Sambuc 			if (nused == bsize)
171*8e5df35eSLionel Sambuc 				b->b_buf = grow_buf(b->b_buf, bsize *= 2);
172*8e5df35eSLionel Sambuc 			c = getc(fp);
173*8e5df35eSLionel Sambuc 		} while (c != EOF && !isspace(c));
174*8e5df35eSLionel Sambuc 
175*8e5df35eSLionel Sambuc 		b->b_buf[nused] = '\0';
176*8e5df35eSLionel Sambuc 		b->b_bsize = bsize;
177*8e5df35eSLionel Sambuc 		if (n)
178*8e5df35eSLionel Sambuc 			add_arc(bufs[0].b_buf, bufs[1].b_buf);
179*8e5df35eSLionel Sambuc 		n = !n;
180*8e5df35eSLionel Sambuc 	}
181*8e5df35eSLionel Sambuc 	(void)fclose(fp);
182*8e5df35eSLionel Sambuc 	if (n)
183*8e5df35eSLionel Sambuc 		errx(1, "odd data count");
184*8e5df35eSLionel Sambuc 
185*8e5df35eSLionel Sambuc 	/* do the sort */
186*8e5df35eSLionel Sambuc 	tsort();
187*8e5df35eSLionel Sambuc 	return(0);
188*8e5df35eSLionel Sambuc }
189*8e5df35eSLionel Sambuc 
190*8e5df35eSLionel Sambuc /* double the size of oldbuf and return a pointer to the new buffer. */
191*8e5df35eSLionel Sambuc static void *
grow_buf(void * bp,int size)192*8e5df35eSLionel Sambuc grow_buf(void *bp, int size)
193*8e5df35eSLionel Sambuc {
194*8e5df35eSLionel Sambuc 	void *n;
195*8e5df35eSLionel Sambuc 
196*8e5df35eSLionel Sambuc 	if ((n = realloc(bp, (u_int)size)) == NULL)
197*8e5df35eSLionel Sambuc 		err(1, "realloc");
198*8e5df35eSLionel Sambuc 	bp = n;
199*8e5df35eSLionel Sambuc 	return (bp);
200*8e5df35eSLionel Sambuc }
201*8e5df35eSLionel Sambuc 
202*8e5df35eSLionel Sambuc /*
203*8e5df35eSLionel Sambuc  * add an arc from node s1 to node s2 in the graph.  If s1 or s2 are not in
204*8e5df35eSLionel Sambuc  * the graph, then add them.
205*8e5df35eSLionel Sambuc  */
206*8e5df35eSLionel Sambuc static void
add_arc(char * s1,char * s2)207*8e5df35eSLionel Sambuc add_arc(char *s1, char *s2)
208*8e5df35eSLionel Sambuc {
209*8e5df35eSLionel Sambuc 	NODE *n1;
210*8e5df35eSLionel Sambuc 	NODE *n2;
211*8e5df35eSLionel Sambuc 	int bsize, i;
212*8e5df35eSLionel Sambuc 
213*8e5df35eSLionel Sambuc 	n1 = get_node(s1);
214*8e5df35eSLionel Sambuc 
215*8e5df35eSLionel Sambuc 	if (!strcmp(s1, s2))
216*8e5df35eSLionel Sambuc 		return;
217*8e5df35eSLionel Sambuc 
218*8e5df35eSLionel Sambuc 	n2 = get_node(s2);
219*8e5df35eSLionel Sambuc 
220*8e5df35eSLionel Sambuc 	/*
221*8e5df35eSLionel Sambuc 	 * Check if this arc is already here.
222*8e5df35eSLionel Sambuc 	 */
223*8e5df35eSLionel Sambuc 	for (i = 0; i < n1->n_narcs; i++)
224*8e5df35eSLionel Sambuc 		if (n1->n_arcs[i] == n2)
225*8e5df35eSLionel Sambuc 			return;
226*8e5df35eSLionel Sambuc 	/*
227*8e5df35eSLionel Sambuc 	 * Add it.
228*8e5df35eSLionel Sambuc 	 */
229*8e5df35eSLionel Sambuc 	if (n1->n_narcs == n1->n_arcsize) {
230*8e5df35eSLionel Sambuc 		if (!n1->n_arcsize)
231*8e5df35eSLionel Sambuc 			n1->n_arcsize = 10;
232*8e5df35eSLionel Sambuc 		bsize = n1->n_arcsize * sizeof(*n1->n_arcs) * 2;
233*8e5df35eSLionel Sambuc 		n1->n_arcs = grow_buf(n1->n_arcs, bsize);
234*8e5df35eSLionel Sambuc 		n1->n_arcsize = bsize / sizeof(*n1->n_arcs);
235*8e5df35eSLionel Sambuc 	}
236*8e5df35eSLionel Sambuc 	n1->n_arcs[n1->n_narcs++] = n2;
237*8e5df35eSLionel Sambuc 	++n2->n_refcnt;
238*8e5df35eSLionel Sambuc }
239*8e5df35eSLionel Sambuc 
240*8e5df35eSLionel Sambuc /* Find a node in the graph (insert if not found) and return a pointer to it. */
241*8e5df35eSLionel Sambuc static NODE *
get_node(char * name)242*8e5df35eSLionel Sambuc get_node(char *name)
243*8e5df35eSLionel Sambuc {
244*8e5df35eSLionel Sambuc 	DBT data, key;
245*8e5df35eSLionel Sambuc 	NODE *n;
246*8e5df35eSLionel Sambuc 
247*8e5df35eSLionel Sambuc 	if (db == NULL &&
248*8e5df35eSLionel Sambuc 	    (db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == NULL)
249*8e5df35eSLionel Sambuc 		err(1, "db: %s", name);
250*8e5df35eSLionel Sambuc 
251*8e5df35eSLionel Sambuc 	key.data = name;
252*8e5df35eSLionel Sambuc 	key.size = strlen(name) + 1;
253*8e5df35eSLionel Sambuc 
254*8e5df35eSLionel Sambuc 	switch ((*db->get)(db, &key, &data, 0)) {
255*8e5df35eSLionel Sambuc 	case 0:
256*8e5df35eSLionel Sambuc 		(void)memmove(&n, data.data, sizeof(n));
257*8e5df35eSLionel Sambuc 		return (n);
258*8e5df35eSLionel Sambuc 	case 1:
259*8e5df35eSLionel Sambuc 		break;
260*8e5df35eSLionel Sambuc 	default:
261*8e5df35eSLionel Sambuc 	case -1:
262*8e5df35eSLionel Sambuc 		err(1, "db: %s", name);
263*8e5df35eSLionel Sambuc 	}
264*8e5df35eSLionel Sambuc 
265*8e5df35eSLionel Sambuc 	if ((n = malloc(sizeof(NODE) + key.size)) == NULL)
266*8e5df35eSLionel Sambuc 		err(1, "malloc");
267*8e5df35eSLionel Sambuc 
268*8e5df35eSLionel Sambuc 	n->n_narcs = 0;
269*8e5df35eSLionel Sambuc 	n->n_arcsize = 0;
270*8e5df35eSLionel Sambuc 	n->n_arcs = NULL;
271*8e5df35eSLionel Sambuc 	n->n_refcnt = 0;
272*8e5df35eSLionel Sambuc 	n->n_flags = 0;
273*8e5df35eSLionel Sambuc 	(void)memmove(n->n_name, name, key.size);
274*8e5df35eSLionel Sambuc 
275*8e5df35eSLionel Sambuc 	/* Add to linked list. */
276*8e5df35eSLionel Sambuc 	if ((n->n_next = graph) != NULL)
277*8e5df35eSLionel Sambuc 		graph->n_prevp = &n->n_next;
278*8e5df35eSLionel Sambuc 	n->n_prevp = &graph;
279*8e5df35eSLionel Sambuc 	graph = n;
280*8e5df35eSLionel Sambuc 
281*8e5df35eSLionel Sambuc 	/* Add to hash table. */
282*8e5df35eSLionel Sambuc 	data.data = &n;
283*8e5df35eSLionel Sambuc 	data.size = sizeof(n);
284*8e5df35eSLionel Sambuc 	if ((*db->put)(db, &key, &data, 0))
285*8e5df35eSLionel Sambuc 		err(1, "db: %s", name);
286*8e5df35eSLionel Sambuc 	return (n);
287*8e5df35eSLionel Sambuc }
288*8e5df35eSLionel Sambuc 
289*8e5df35eSLionel Sambuc 
290*8e5df35eSLionel Sambuc /*
291*8e5df35eSLionel Sambuc  * Clear the NODEST flag from all nodes.
292*8e5df35eSLionel Sambuc  */
293*8e5df35eSLionel Sambuc static void
clear_cycle(void)294*8e5df35eSLionel Sambuc clear_cycle(void)
295*8e5df35eSLionel Sambuc {
296*8e5df35eSLionel Sambuc 	NODE *n;
297*8e5df35eSLionel Sambuc 
298*8e5df35eSLionel Sambuc 	for (n = graph; n != NULL; n = n->n_next)
299*8e5df35eSLionel Sambuc 		n->n_flags &= ~NF_NODEST;
300*8e5df35eSLionel Sambuc }
301*8e5df35eSLionel Sambuc 
302*8e5df35eSLionel Sambuc /* do topological sort on graph */
303*8e5df35eSLionel Sambuc static void
tsort(void)304*8e5df35eSLionel Sambuc tsort(void)
305*8e5df35eSLionel Sambuc {
306*8e5df35eSLionel Sambuc 	NODE *n, *next;
307*8e5df35eSLionel Sambuc 	int cnt, i;
308*8e5df35eSLionel Sambuc 
309*8e5df35eSLionel Sambuc 	while (graph != NULL) {
310*8e5df35eSLionel Sambuc 		/*
311*8e5df35eSLionel Sambuc 		 * Keep getting rid of simple cases until there are none left,
312*8e5df35eSLionel Sambuc 		 * if there are any nodes still in the graph, then there is
313*8e5df35eSLionel Sambuc 		 * a cycle in it.
314*8e5df35eSLionel Sambuc 		 */
315*8e5df35eSLionel Sambuc 		do {
316*8e5df35eSLionel Sambuc 			for (cnt = 0, n = graph; n != NULL; n = next) {
317*8e5df35eSLionel Sambuc 				next = n->n_next;
318*8e5df35eSLionel Sambuc 				if (n->n_refcnt == 0) {
319*8e5df35eSLionel Sambuc 					remove_node(n);
320*8e5df35eSLionel Sambuc 					++cnt;
321*8e5df35eSLionel Sambuc 				}
322*8e5df35eSLionel Sambuc 			}
323*8e5df35eSLionel Sambuc 		} while (graph != NULL && cnt);
324*8e5df35eSLionel Sambuc 
325*8e5df35eSLionel Sambuc 		if (graph == NULL)
326*8e5df35eSLionel Sambuc 			break;
327*8e5df35eSLionel Sambuc 
328*8e5df35eSLionel Sambuc 		if (!cycle_buf) {
329*8e5df35eSLionel Sambuc 			/*
330*8e5df35eSLionel Sambuc 			 * Allocate space for two cycle logs - one to be used
331*8e5df35eSLionel Sambuc 			 * as scratch space, the other to save the longest
332*8e5df35eSLionel Sambuc 			 * cycle.
333*8e5df35eSLionel Sambuc 			 */
334*8e5df35eSLionel Sambuc 			for (cnt = 0, n = graph; n != NULL; n = n->n_next)
335*8e5df35eSLionel Sambuc 				++cnt;
336*8e5df35eSLionel Sambuc 			cycle_buf = malloc((u_int)sizeof(NODE *) * cnt);
337*8e5df35eSLionel Sambuc 			longest_cycle = malloc((u_int)sizeof(NODE *) * cnt);
338*8e5df35eSLionel Sambuc 			if (cycle_buf == NULL || longest_cycle == NULL)
339*8e5df35eSLionel Sambuc 				err(1, "malloc");
340*8e5df35eSLionel Sambuc 		}
341*8e5df35eSLionel Sambuc 		for (n = graph; n != NULL; n = n->n_next) {
342*8e5df35eSLionel Sambuc 			if (!(n->n_flags & NF_ACYCLIC)) {
343*8e5df35eSLionel Sambuc 				if ((cnt = find_cycle(n, n, 0, 0)) != 0) {
344*8e5df35eSLionel Sambuc 					if (!quiet) {
345*8e5df35eSLionel Sambuc 						warnx("cycle in data");
346*8e5df35eSLionel Sambuc 						for (i = 0; i < cnt; i++)
347*8e5df35eSLionel Sambuc 							warnx("%s",
348*8e5df35eSLionel Sambuc 							    longest_cycle[i]->n_name);
349*8e5df35eSLionel Sambuc 					}
350*8e5df35eSLionel Sambuc 					remove_node(n);
351*8e5df35eSLionel Sambuc 					clear_cycle();
352*8e5df35eSLionel Sambuc 					break;
353*8e5df35eSLionel Sambuc 				} else {
354*8e5df35eSLionel Sambuc 					/* to avoid further checks */
355*8e5df35eSLionel Sambuc 					n->n_flags  |= NF_ACYCLIC;
356*8e5df35eSLionel Sambuc 					clear_cycle();
357*8e5df35eSLionel Sambuc 				}
358*8e5df35eSLionel Sambuc 			}
359*8e5df35eSLionel Sambuc 		}
360*8e5df35eSLionel Sambuc 		if (n == NULL)
361*8e5df35eSLionel Sambuc 			errx(1, "internal error -- could not find cycle");
362*8e5df35eSLionel Sambuc 	}
363*8e5df35eSLionel Sambuc }
364*8e5df35eSLionel Sambuc 
365*8e5df35eSLionel Sambuc /* print node and remove from graph (does not actually free node) */
366*8e5df35eSLionel Sambuc static void
remove_node(NODE * n)367*8e5df35eSLionel Sambuc remove_node(NODE *n)
368*8e5df35eSLionel Sambuc {
369*8e5df35eSLionel Sambuc 	NODE **np;
370*8e5df35eSLionel Sambuc 	int i;
371*8e5df35eSLionel Sambuc 
372*8e5df35eSLionel Sambuc 	(void)printf("%s\n", n->n_name);
373*8e5df35eSLionel Sambuc 	for (np = n->n_arcs, i = n->n_narcs; --i >= 0; np++)
374*8e5df35eSLionel Sambuc 		--(*np)->n_refcnt;
375*8e5df35eSLionel Sambuc 	n->n_narcs = 0;
376*8e5df35eSLionel Sambuc 	*n->n_prevp = n->n_next;
377*8e5df35eSLionel Sambuc 	if (n->n_next)
378*8e5df35eSLionel Sambuc 		n->n_next->n_prevp = n->n_prevp;
379*8e5df35eSLionel Sambuc }
380*8e5df35eSLionel Sambuc 
381*8e5df35eSLionel Sambuc 
382*8e5df35eSLionel Sambuc /* look for the longest? cycle from node from to node to. */
383*8e5df35eSLionel Sambuc static int
find_cycle(NODE * from,NODE * to,int longest_len,int depth)384*8e5df35eSLionel Sambuc find_cycle(NODE *from, NODE *to, int longest_len, int depth)
385*8e5df35eSLionel Sambuc {
386*8e5df35eSLionel Sambuc 	NODE **np;
387*8e5df35eSLionel Sambuc 	int i, len;
388*8e5df35eSLionel Sambuc 
389*8e5df35eSLionel Sambuc 	/*
390*8e5df35eSLionel Sambuc 	 * avoid infinite loops and ignore portions of the graph known
391*8e5df35eSLionel Sambuc 	 * to be acyclic
392*8e5df35eSLionel Sambuc 	 */
393*8e5df35eSLionel Sambuc 	if (from->n_flags & (NF_NODEST|NF_MARK|NF_ACYCLIC))
394*8e5df35eSLionel Sambuc 		return (0);
395*8e5df35eSLionel Sambuc 	from->n_flags |= NF_MARK;
396*8e5df35eSLionel Sambuc 
397*8e5df35eSLionel Sambuc 	for (np = from->n_arcs, i = from->n_narcs; --i >= 0; np++) {
398*8e5df35eSLionel Sambuc 		cycle_buf[depth] = *np;
399*8e5df35eSLionel Sambuc 		if (*np == to) {
400*8e5df35eSLionel Sambuc 			if (depth + 1 > longest_len) {
401*8e5df35eSLionel Sambuc 				longest_len = depth + 1;
402*8e5df35eSLionel Sambuc 				(void)memcpy(longest_cycle, cycle_buf,
403*8e5df35eSLionel Sambuc 				    longest_len * sizeof(NODE *));
404*8e5df35eSLionel Sambuc 			}
405*8e5df35eSLionel Sambuc 		} else {
406*8e5df35eSLionel Sambuc 			if ((*np)->n_flags & (NF_MARK|NF_ACYCLIC|NF_NODEST))
407*8e5df35eSLionel Sambuc 				continue;
408*8e5df35eSLionel Sambuc 			len = find_cycle(*np, to, longest_len, depth + 1);
409*8e5df35eSLionel Sambuc 
410*8e5df35eSLionel Sambuc 			if (debug)
411*8e5df35eSLionel Sambuc 				(void)printf("%*s %s->%s %d\n", depth, "",
412*8e5df35eSLionel Sambuc 				    from->n_name, to->n_name, len);
413*8e5df35eSLionel Sambuc 
414*8e5df35eSLionel Sambuc 			if (len == 0)
415*8e5df35eSLionel Sambuc 				(*np)->n_flags |= NF_NODEST;
416*8e5df35eSLionel Sambuc 
417*8e5df35eSLionel Sambuc 			if (len > longest_len)
418*8e5df35eSLionel Sambuc 				longest_len = len;
419*8e5df35eSLionel Sambuc 
420*8e5df35eSLionel Sambuc 			if (len > 0 && !longest)
421*8e5df35eSLionel Sambuc 				break;
422*8e5df35eSLionel Sambuc 		}
423*8e5df35eSLionel Sambuc 	}
424*8e5df35eSLionel Sambuc 	from->n_flags &= ~NF_MARK;
425*8e5df35eSLionel Sambuc 	return (longest_len);
426*8e5df35eSLionel Sambuc }
427*8e5df35eSLionel Sambuc 
428*8e5df35eSLionel Sambuc static void
usage(void)429*8e5df35eSLionel Sambuc usage(void)
430*8e5df35eSLionel Sambuc {
431*8e5df35eSLionel Sambuc 	(void)fprintf(stderr, "usage: tsort [-lq] [file]\n");
432*8e5df35eSLionel Sambuc 	exit(1);
433*8e5df35eSLionel Sambuc }
434