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