2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
6 * Michael Rendell of Memorial University of Newfoundland.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * $FreeBSD: src/usr.bin/tsort/tsort.c,v 1.10.2.1 2001/03/04 09:18:23 kris Exp $
37 * $DragonFly: src/usr.bin/tsort/tsort.c,v 1.4 2004/12/21 20:00:57 cpressey Exp $
39 * @(#) Copyright (c) 1989, 1993, 1994 The Regents of the University of California. All rights reserved.
40 * @(#)tsort.c 8.3 (Berkeley) 5/4/95
43 #include <sys/types.h>
56 * Topological sort. Input is a list of pairs of strings separated by
57 * white space (spaces, tabs, and/or newlines); strings are written to
58 * standard output in sorted order, one per line.
61 * tsort [-dlq] [inputfile]
62 * If no input file is specified, standard input is read.
64 * Should be compatible with AT&T tsort HOWEVER the output is not identical
65 * (i.e. for most graphs there is more than one sorted order, and this tsort
66 * usually generates a different one then the AT&T tsort). Also, cycle
67 * reporting seems to be more accurate in this version (the AT&T tsort
68 * sometimes says a node is in a cycle when it isn't).
70 * Michael Rendell, michael@stretch.cs.mun.ca - Feb 26, '90
72 #define HASHSIZE 53 /* doesn't need to be big */
73 #define NF_MARK 0x1 /* marker for cycle detection */
74 #define NF_ACYCLIC 0x2 /* this node is cycle free */
75 #define NF_NODEST 0x4 /* Unreachable */
78 typedef struct node_str NODE
;
81 NODE
**n_prevp
; /* pointer to previous node's n_next */
82 NODE
*n_next
; /* next node in graph */
83 NODE
**n_arcs
; /* array of arcs to other nodes */
84 int n_narcs
; /* number of arcs in n_arcs[] */
85 int n_arcsize
; /* size of n_arcs[] array */
86 int n_refcnt
; /* # of arcs pointing to this node */
87 int n_flags
; /* NF_* */
88 char n_name
[1]; /* name of this node */
97 NODE
*graph
, **cycle_buf
, **longest_cycle
;
98 int debug
, longest
, quiet
;
100 static void add_arc(char *, char *);
101 static void clear_cycle(void);
102 static int find_cycle(NODE
*, NODE
*, int, int);
103 static NODE
*get_node(char *);
104 static void *grow_buf(void *, size_t);
105 static void remove_node(NODE
*);
106 static void tsort(void);
107 static void usage(void);
110 main(int argc
, char **argv
)
115 int bsize
, ch
, nused
;
118 while ((ch
= getopt(argc
, argv
, "dlq")) != -1) {
142 if ((fp
= fopen(*argv
, "r")) == NULL
)
150 for (b
= bufs
, n
= 2; --n
>= 0; b
++)
151 b
->b_buf
= grow_buf(NULL
, b
->b_bsize
= 1024);
153 /* Parse input and build the graph. */
154 for (n
= 0, c
= getc(fp
);;) {
155 while (c
!= EOF
&& isspace(c
))
164 b
->b_buf
[nused
++] = c
;
166 b
->b_buf
= grow_buf(b
->b_buf
, bsize
*= 2);
168 } while (c
!= EOF
&& !isspace(c
));
170 b
->b_buf
[nused
] = '\0';
173 add_arc(bufs
[0].b_buf
, bufs
[1].b_buf
);
178 errx(1, "odd data count");
185 /* Double the size of oldbuf and return a pointer to the new buffer. */
187 grow_buf(void *bp
, size_t size
)
189 if ((bp
= realloc(bp
, size
)) == NULL
)
195 * Add an arc from node s1 to node s2 in the graph. If s1 or s2 are not in
196 * the graph, then add them.
199 add_arc(char *s1
, char *s2
)
208 if (strcmp(s1
, s2
) == 0)
214 * Check if this arc is already here.
216 for (i
= 0; i
< n1
->n_narcs
; i
++)
217 if (n1
->n_arcs
[i
] == n2
)
222 if (n1
->n_narcs
== n1
->n_arcsize
) {
225 bsize
= n1
->n_arcsize
* sizeof(*n1
->n_arcs
) * 2;
226 n1
->n_arcs
= grow_buf(n1
->n_arcs
, bsize
);
227 n1
->n_arcsize
= bsize
/ sizeof(*n1
->n_arcs
);
229 n1
->n_arcs
[n1
->n_narcs
++] = n2
;
233 /* Find a node in the graph (insert if not found) and return a pointer to it. */
241 (db
= dbopen(NULL
, O_RDWR
, 0, DB_HASH
, NULL
)) == NULL
)
242 err(1, "db: %s", name
);
245 key
.size
= strlen(name
) + 1;
247 switch ((*db
->get
)(db
, &key
, &data
, 0)) {
249 bcopy(data
.data
, &n
, sizeof(n
));
255 err(1, "db: %s", name
);
258 if ((n
= malloc(sizeof(NODE
) + key
.size
)) == NULL
)
266 bcopy(name
, n
->n_name
, key
.size
);
268 /* Add to linked list. */
269 if ((n
->n_next
= graph
) != NULL
)
270 graph
->n_prevp
= &n
->n_next
;
274 /* Add to hash table. */
276 data
.size
= sizeof(n
);
277 if ((*db
->put
)(db
, &key
, &data
, 0))
278 err(1, "db: %s", name
);
284 * Clear the NODEST flag from all nodes.
291 for (n
= graph
; n
!= NULL
; n
= n
->n_next
)
292 n
->n_flags
&= ~NF_NODEST
;
295 /* Perform a topological sort of the graph. */
302 while (graph
!= NULL
) {
304 * Keep getting rid of simple cases until there are none left,
305 * and if there are any nodes still in the graph, then there
309 for (cnt
= 0, n
= graph
; n
!= NULL
; n
= next
) {
311 if (n
->n_refcnt
== 0) {
316 } while (graph
!= NULL
&& cnt
!= 0);
323 * Allocate space for two cycle logs - one to be used
324 * as scratch space, the other to save the longest
327 for (cnt
= 0, n
= graph
; n
!= NULL
; n
= n
->n_next
)
329 cycle_buf
= malloc(sizeof(NODE
*) * cnt
);
330 longest_cycle
= malloc(sizeof(NODE
*) * cnt
);
331 if (cycle_buf
== NULL
|| longest_cycle
== NULL
)
334 for (n
= graph
; n
!= NULL
; n
= n
->n_next
)
335 if (!(n
->n_flags
& NF_ACYCLIC
)) {
336 if ((cnt
= find_cycle(n
, n
, 0, 0))) {
338 warnx("cycle in data");
339 for (i
= 0; i
< cnt
; i
++)
341 longest_cycle
[i
]->n_name
);
347 /* To avoid further checks: */
348 n
->n_flags
|= NF_ACYCLIC
;
354 errx(1, "internal error -- could not find cycle");
359 * Print a node and remove it from the graph (does not actually free
368 printf("%s\n", n
->n_name
);
369 for (np
= n
->n_arcs
, i
= n
->n_narcs
; --i
>= 0; np
++)
372 *n
->n_prevp
= n
->n_next
;
373 if (n
->n_next
!= NULL
)
374 n
->n_next
->n_prevp
= n
->n_prevp
;
378 /* Look for the (longest?) cycle from node from to node to. */
380 find_cycle(NODE
*from
, NODE
*to
, int longest_len
, int depth
)
386 * Avoid infinite loops and ignore portions of the graph known
389 if (from
->n_flags
& (NF_NODEST
|NF_MARK
|NF_ACYCLIC
))
391 from
->n_flags
|= NF_MARK
;
393 for (np
= from
->n_arcs
, i
= from
->n_narcs
; --i
>= 0; np
++) {
394 cycle_buf
[depth
] = *np
;
396 if (depth
+ 1 > longest_len
) {
397 longest_len
= depth
+ 1;
398 memcpy((char *)longest_cycle
,
400 longest_len
* sizeof(NODE
*));
403 if ((*np
)->n_flags
& (NF_MARK
|NF_ACYCLIC
|NF_NODEST
))
405 len
= find_cycle(*np
, to
, longest_len
, depth
+ 1);
408 printf("%*s %s->%s %d\n", depth
, "",
409 from
->n_name
, to
->n_name
, len
);
413 (*np
)->n_flags
|= NF_NODEST
;
415 if (len
> longest_len
)
418 if (len
> 0 && !longest
)
422 from
->n_flags
&= ~NF_MARK
;
423 return (longest_len
);
429 fprintf(stderr
, "usage: tsort [-dlq] [file]\n");