src/plugins.{c,h}, src/tsort.{c,h}: proper error handling in dependency handling
[vlock.git] / src / tsort.c
blobfff2ed11d5986a29502d55662f12b249ac2c34fc
1 /* tsort.c -- topological sort for vlock, the VT locking program for linux
3 * This program is copyright (C) 2007 Frank Benkstein, and is free
4 * software which is freely distributable under the terms of the
5 * GNU General Public License version 2, included as the file COPYING in this
6 * distribution. It is NOT public domain software, and any
7 * redistribution not permitted by the GNU General Public License is
8 * expressly forbidden without prior written permission from
9 * the author.
13 #include <stdlib.h>
14 #include <errno.h>
16 #include "list.h"
17 #include "util.h"
19 #include "tsort.h"
21 /* Get the zeros of the graph, i.e. nodes with no incoming edges. */
22 static struct list *get_zeros(struct list *nodes, struct list *edges)
24 struct list *zeros = list_copy(nodes);
26 if (zeros == NULL)
27 return NULL;
29 list_for_each(edges, edge_item) {
30 struct edge *e = edge_item->data;
31 list_delete(zeros, e->successor);
34 return zeros;
37 /* Check if the given node is a zero. */
38 static bool is_zero(void *node, struct list *edges)
40 list_for_each(edges, edge_item) {
41 struct edge *e = edge_item->data;
43 if (e->successor == node)
44 return false;
47 return true;
50 /* For the given directed graph, generate a topological sort of the nodes.
52 * Sorts the list and deletes all edges. If there are circles found in the
53 * graph or there are edges that have no corresponding nodes the erroneous
54 * edges are left.
56 * The algorithm is taken from the Wikipedia:
58 * http://en.wikipedia.org/w/index.php?title=Topological_sorting&oldid=153157450#Algorithms
61 struct list *tsort(struct list *nodes, struct list *edges)
63 struct list *sorted_nodes = list_new();
64 /* Retrieve all zeros. */
65 struct list *zeros;
67 if (sorted_nodes == NULL)
68 return NULL;
70 zeros = get_zeros(nodes, edges);
72 if (zeros == NULL) {
73 GUARD_ERRNO(list_free(sorted_nodes));
74 return NULL;
77 /* While the list of zeros is not empty, take the first zero and remove it
78 * and ... */
79 list_delete_for_each(zeros, zero_item) {
80 void *zero = zero_item->data;
81 /* ... add it to the list of sorted nodes. */
82 if (!list_append(sorted_nodes, zero))
83 goto error;
85 /* Then look at each edge ... */
86 list_for_each_manual(edges, edge_item) {
87 struct edge *e = edge_item->data;
89 /* ... that has this zero as its predecessor ... */
90 if (e->predecessor == zero) {
91 /* ... and remove it. */
92 edge_item = list_delete_item(edges, edge_item);
94 /* If the successor has become a zero now ... */
95 if (is_zero(e->successor, edges))
96 /* ... add it to the list of zeros. */
97 if (!list_append(zeros, e->successor))
98 goto error;
100 free(e);
101 } else {
102 edge_item = edge_item->next;
107 /* If all edges were deleted the algorithm was successful. */
108 if (!list_is_empty(edges)) {
109 list_free(sorted_nodes);
110 sorted_nodes = NULL;
113 list_free(zeros);
114 errno = 0;
116 return sorted_nodes;;
118 error:
120 int errsv = errno;
121 list_free(sorted_nodes);
122 list_free(zeros);
124 errno = errsv;
125 return NULL;