1 /* $NetBSD: tsort.c,v 1.21 2005/07/19 23:18:31 christos Exp $ */
4 * Copyright (c) 1989, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * Michael Rendell of Memorial University of Newfoundland.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 #if HAVE_NBTOOL_CONFIG_H
36 #include "nbtool_config.h"
39 #include <sys/cdefs.h>
41 __COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\
42 The Regents of the University of California. All rights reserved.");
44 static char sccsid
[] = "@(#)tsort.c 8.3 (Berkeley) 5/4/95";
46 __RCSID("$NetBSD: tsort.c,v 1.21 2005/07/19 23:18:31 christos Exp $");
49 #include <sys/types.h>
61 * Topological sort. Input is a list of pairs of strings separated by
62 * white space (spaces, tabs, and/or newlines); strings are written to
63 * standard output in sorted order, one per line.
66 * tsort [-l] [inputfile]
67 * If no input file is specified, standard input is read.
69 * Should be compatible with AT&T tsort HOWEVER the output is not identical
70 * (i.e. for most graphs there is more than one sorted order, and this tsort
71 * usually generates a different one then the AT&T tsort). Also, cycle
72 * reporting seems to be more accurate in this version (the AT&T tsort
73 * sometimes says a node is in a cycle when it isn't).
75 * Michael Rendell, michael@stretch.cs.mun.ca - Feb 26, '90
77 #define HASHSIZE 53 /* doesn't need to be big */
78 #define NF_MARK 0x1 /* marker for cycle detection */
79 #define NF_ACYCLIC 0x2 /* this node is cycle free */
80 #define NF_NODEST 0x4 /* Unreachable */
82 typedef struct node_str NODE
;
85 NODE
**n_prevp
; /* pointer to previous node's n_next */
86 NODE
*n_next
; /* next node in graph */
87 NODE
**n_arcs
; /* array of arcs to other nodes */
88 int n_narcs
; /* number of arcs in n_arcs[] */
89 int n_arcsize
; /* size of n_arcs[] array */
90 int n_refcnt
; /* # of arcs pointing to this node */
91 int n_flags
; /* NF_* */
92 char n_name
[1]; /* name of this node */
101 NODE
*graph
, **cycle_buf
, **longest_cycle
;
102 int debug
, longest
, quiet
;
104 void add_arc
__P((char *, char *));
105 void clear_cycle
__P((void));
106 int find_cycle
__P((NODE
*, NODE
*, int, int));
107 NODE
*get_node
__P((char *));
108 void *grow_buf
__P((void *, int));
109 int main
__P((int, char **));
110 void remove_node
__P((NODE
*));
111 void tsort
__P((void));
112 void usage
__P((void));
122 int bsize
, ch
, nused
;
125 setprogname(argv
[0]);
128 while ((ch
= getopt(argc
, argv
, "dlq")) != -1)
151 if ((fp
= fopen(*argv
, "r")) == NULL
)
158 for (b
= bufs
, n
= 2; --n
>= 0; b
++)
159 b
->b_buf
= grow_buf(NULL
, b
->b_bsize
= 1024);
161 /* parse input and build the graph */
162 for (n
= 0, c
= getc(fp
);;) {
163 while (c
!= EOF
&& isspace(c
))
172 b
->b_buf
[nused
++] = c
;
174 b
->b_buf
= grow_buf(b
->b_buf
, bsize
*= 2);
176 } while (c
!= EOF
&& !isspace(c
));
178 b
->b_buf
[nused
] = '\0';
181 add_arc(bufs
[0].b_buf
, bufs
[1].b_buf
);
186 errx(1, "odd data count");
193 /* double the size of oldbuf and return a pointer to the new buffer. */
201 if ((n
= realloc(bp
, (u_int
)size
)) == NULL
)
208 * add an arc from node s1 to node s2 in the graph. If s1 or s2 are not in
209 * the graph, then add them.
227 * Check if this arc is already here.
229 for (i
= 0; i
< n1
->n_narcs
; i
++)
230 if (n1
->n_arcs
[i
] == n2
)
235 if (n1
->n_narcs
== n1
->n_arcsize
) {
238 bsize
= n1
->n_arcsize
* sizeof(*n1
->n_arcs
) * 2;
239 n1
->n_arcs
= grow_buf(n1
->n_arcs
, bsize
);
240 n1
->n_arcsize
= bsize
/ sizeof(*n1
->n_arcs
);
242 n1
->n_arcs
[n1
->n_narcs
++] = n2
;
246 /* Find a node in the graph (insert if not found) and return a pointer to it. */
255 (db
= dbopen(NULL
, O_RDWR
, 0, DB_HASH
, NULL
)) == NULL
)
256 err(1, "db: %s", name
);
259 key
.size
= strlen(name
) + 1;
261 switch ((*db
->get
)(db
, &key
, &data
, 0)) {
263 (void)memmove(&n
, data
.data
, sizeof(n
));
269 err(1, "db: %s", name
);
272 if ((n
= malloc(sizeof(NODE
) + key
.size
)) == NULL
)
280 (void)memmove(n
->n_name
, name
, key
.size
);
282 /* Add to linked list. */
283 if ((n
->n_next
= graph
) != NULL
)
284 graph
->n_prevp
= &n
->n_next
;
288 /* Add to hash table. */
290 data
.size
= sizeof(n
);
291 if ((*db
->put
)(db
, &key
, &data
, 0))
292 err(1, "db: %s", name
);
298 * Clear the NODEST flag from all nodes.
305 for (n
= graph
; n
!= NULL
; n
= n
->n_next
)
306 n
->n_flags
&= ~NF_NODEST
;
309 /* do topological sort on graph */
316 while (graph
!= NULL
) {
318 * Keep getting rid of simple cases until there are none left,
319 * if there are any nodes still in the graph, then there is
323 for (cnt
= 0, n
= graph
; n
!= NULL
; n
= next
) {
325 if (n
->n_refcnt
== 0) {
330 } while (graph
!= NULL
&& cnt
);
337 * Allocate space for two cycle logs - one to be used
338 * as scratch space, the other to save the longest
341 for (cnt
= 0, n
= graph
; n
!= NULL
; n
= n
->n_next
)
343 cycle_buf
= malloc((u_int
)sizeof(NODE
*) * cnt
);
344 longest_cycle
= malloc((u_int
)sizeof(NODE
*) * cnt
);
345 if (cycle_buf
== NULL
|| longest_cycle
== NULL
)
348 for (n
= graph
; n
!= NULL
; n
= n
->n_next
) {
349 if (!(n
->n_flags
& NF_ACYCLIC
)) {
350 if ((cnt
= find_cycle(n
, n
, 0, 0)) != 0) {
352 warnx("cycle in data");
353 for (i
= 0; i
< cnt
; i
++)
355 longest_cycle
[i
]->n_name
);
361 /* to avoid further checks */
362 n
->n_flags
|= NF_ACYCLIC
;
368 errx(1, "internal error -- could not find cycle");
372 /* print node and remove from graph (does not actually free node) */
380 (void)printf("%s\n", n
->n_name
);
381 for (np
= n
->n_arcs
, i
= n
->n_narcs
; --i
>= 0; np
++)
384 *n
->n_prevp
= n
->n_next
;
386 n
->n_next
->n_prevp
= n
->n_prevp
;
390 /* look for the longest? cycle from node from to node to. */
392 find_cycle(from
, to
, longest_len
, depth
)
394 int depth
, longest_len
;
400 * avoid infinite loops and ignore portions of the graph known
403 if (from
->n_flags
& (NF_NODEST
|NF_MARK
|NF_ACYCLIC
))
405 from
->n_flags
|= NF_MARK
;
407 for (np
= from
->n_arcs
, i
= from
->n_narcs
; --i
>= 0; np
++) {
408 cycle_buf
[depth
] = *np
;
410 if (depth
+ 1 > longest_len
) {
411 longest_len
= depth
+ 1;
412 (void)memcpy(longest_cycle
, cycle_buf
,
413 longest_len
* sizeof(NODE
*));
416 if ((*np
)->n_flags
& (NF_MARK
|NF_ACYCLIC
|NF_NODEST
))
418 len
= find_cycle(*np
, to
, longest_len
, depth
+ 1);
421 (void)printf("%*s %s->%s %d\n", depth
, "",
422 from
->n_name
, to
->n_name
, len
);
425 (*np
)->n_flags
|= NF_NODEST
;
427 if (len
> longest_len
)
430 if (len
> 0 && !longest
)
434 from
->n_flags
&= ~NF_MARK
;
435 return (longest_len
);
441 (void)fprintf(stderr
, "usage: tsort [-lq] [file]\n");