1 /* $NetBSD: tsort.c,v 1.23 2011/09/06 18:34:37 joerg 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.23 2011/09/06 18:34:37 joerg 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 static NODE
*graph
, **cycle_buf
, **longest_cycle
;
102 static int debug
, longest
, quiet
;
104 static void add_arc(char *, char *);
105 static void clear_cycle(void);
106 static int find_cycle(NODE
*, NODE
*, int, int);
107 static NODE
*get_node(char *);
108 static void *grow_buf(void *, int);
109 static void remove_node(NODE
*);
110 static void tsort(void);
111 __dead
static void usage(void);
114 main(int argc
, char *argv
[])
119 int bsize
, ch
, nused
;
122 setprogname(argv
[0]);
125 while ((ch
= getopt(argc
, argv
, "dlq")) != -1)
148 if ((fp
= fopen(*argv
, "r")) == NULL
)
155 for (b
= bufs
, n
= 2; --n
>= 0; b
++)
156 b
->b_buf
= grow_buf(NULL
, b
->b_bsize
= 1024);
158 /* parse input and build the graph */
159 for (n
= 0, c
= getc(fp
);;) {
160 while (c
!= EOF
&& isspace(c
))
169 b
->b_buf
[nused
++] = c
;
171 b
->b_buf
= grow_buf(b
->b_buf
, bsize
*= 2);
173 } while (c
!= EOF
&& !isspace(c
));
175 b
->b_buf
[nused
] = '\0';
178 add_arc(bufs
[0].b_buf
, bufs
[1].b_buf
);
183 errx(1, "odd data count");
190 /* double the size of oldbuf and return a pointer to the new buffer. */
192 grow_buf(void *bp
, int size
)
196 if ((n
= realloc(bp
, (u_int
)size
)) == NULL
)
203 * add an arc from node s1 to node s2 in the graph. If s1 or s2 are not in
204 * the graph, then add them.
207 add_arc(char *s1
, char *s2
)
221 * Check if this arc is already here.
223 for (i
= 0; i
< n1
->n_narcs
; i
++)
224 if (n1
->n_arcs
[i
] == n2
)
229 if (n1
->n_narcs
== n1
->n_arcsize
) {
232 bsize
= n1
->n_arcsize
* sizeof(*n1
->n_arcs
) * 2;
233 n1
->n_arcs
= grow_buf(n1
->n_arcs
, bsize
);
234 n1
->n_arcsize
= bsize
/ sizeof(*n1
->n_arcs
);
236 n1
->n_arcs
[n1
->n_narcs
++] = n2
;
240 /* Find a node in the graph (insert if not found) and return a pointer to it. */
248 (db
= dbopen(NULL
, O_RDWR
, 0, DB_HASH
, NULL
)) == NULL
)
249 err(1, "db: %s", name
);
252 key
.size
= strlen(name
) + 1;
254 switch ((*db
->get
)(db
, &key
, &data
, 0)) {
256 (void)memmove(&n
, data
.data
, sizeof(n
));
262 err(1, "db: %s", name
);
265 if ((n
= malloc(sizeof(NODE
) + key
.size
)) == NULL
)
273 (void)memmove(n
->n_name
, name
, key
.size
);
275 /* Add to linked list. */
276 if ((n
->n_next
= graph
) != NULL
)
277 graph
->n_prevp
= &n
->n_next
;
281 /* Add to hash table. */
283 data
.size
= sizeof(n
);
284 if ((*db
->put
)(db
, &key
, &data
, 0))
285 err(1, "db: %s", name
);
291 * Clear the NODEST flag from all nodes.
298 for (n
= graph
; n
!= NULL
; n
= n
->n_next
)
299 n
->n_flags
&= ~NF_NODEST
;
302 /* do topological sort on graph */
309 while (graph
!= NULL
) {
311 * Keep getting rid of simple cases until there are none left,
312 * if there are any nodes still in the graph, then there is
316 for (cnt
= 0, n
= graph
; n
!= NULL
; n
= next
) {
318 if (n
->n_refcnt
== 0) {
323 } while (graph
!= NULL
&& cnt
);
330 * Allocate space for two cycle logs - one to be used
331 * as scratch space, the other to save the longest
334 for (cnt
= 0, n
= graph
; n
!= NULL
; n
= n
->n_next
)
336 cycle_buf
= malloc((u_int
)sizeof(NODE
*) * cnt
);
337 longest_cycle
= malloc((u_int
)sizeof(NODE
*) * cnt
);
338 if (cycle_buf
== NULL
|| longest_cycle
== NULL
)
341 for (n
= graph
; n
!= NULL
; n
= n
->n_next
) {
342 if (!(n
->n_flags
& NF_ACYCLIC
)) {
343 if ((cnt
= find_cycle(n
, n
, 0, 0)) != 0) {
345 warnx("cycle in data");
346 for (i
= 0; i
< cnt
; i
++)
348 longest_cycle
[i
]->n_name
);
354 /* to avoid further checks */
355 n
->n_flags
|= NF_ACYCLIC
;
361 errx(1, "internal error -- could not find cycle");
365 /* print node and remove from graph (does not actually free node) */
372 (void)printf("%s\n", n
->n_name
);
373 for (np
= n
->n_arcs
, i
= n
->n_narcs
; --i
>= 0; np
++)
376 *n
->n_prevp
= n
->n_next
;
378 n
->n_next
->n_prevp
= n
->n_prevp
;
382 /* look for the longest? cycle from node from to node to. */
384 find_cycle(NODE
*from
, NODE
*to
, int longest_len
, int depth
)
390 * avoid infinite loops and ignore portions of the graph known
393 if (from
->n_flags
& (NF_NODEST
|NF_MARK
|NF_ACYCLIC
))
395 from
->n_flags
|= NF_MARK
;
397 for (np
= from
->n_arcs
, i
= from
->n_narcs
; --i
>= 0; np
++) {
398 cycle_buf
[depth
] = *np
;
400 if (depth
+ 1 > longest_len
) {
401 longest_len
= depth
+ 1;
402 (void)memcpy(longest_cycle
, cycle_buf
,
403 longest_len
* sizeof(NODE
*));
406 if ((*np
)->n_flags
& (NF_MARK
|NF_ACYCLIC
|NF_NODEST
))
408 len
= find_cycle(*np
, to
, longest_len
, depth
+ 1);
411 (void)printf("%*s %s->%s %d\n", depth
, "",
412 from
->n_name
, to
->n_name
, len
);
415 (*np
)->n_flags
|= NF_NODEST
;
417 if (len
> longest_len
)
420 if (len
> 0 && !longest
)
424 from
->n_flags
&= ~NF_MARK
;
425 return (longest_len
);
431 (void)fprintf(stderr
, "usage: tsort [-lq] [file]\n");