aboutsummaryrefslogtreecommitdiff
path: root/coreutils
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2022-05-12 08:11:27 +0100
committerRon Yorston <rmy@pobox.com>2022-05-12 08:11:27 +0100
commit7c8c7681a9c8fac1fb8cf77f5950d32885ebb08c (patch)
tree4e21c0c676bc424ba10e616d9f97de76bfe4409c /coreutils
parentf637f37e0bd2e295936a7b4836676846693219aa (diff)
parent1099a27696cd733041db97f99da4e22ecd2424e5 (diff)
downloadbusybox-w32-7c8c7681a9c8fac1fb8cf77f5950d32885ebb08c.tar.gz
busybox-w32-7c8c7681a9c8fac1fb8cf77f5950d32885ebb08c.tar.bz2
busybox-w32-7c8c7681a9c8fac1fb8cf77f5950d32885ebb08c.zip
Merge branch 'busybox' into merge
Diffstat (limited to 'coreutils')
-rw-r--r--coreutils/tsort.c188
1 files changed, 188 insertions, 0 deletions
diff --git a/coreutils/tsort.c b/coreutils/tsort.c
new file mode 100644
index 000000000..dedb65b15
--- /dev/null
+++ b/coreutils/tsort.c
@@ -0,0 +1,188 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * tsort implementation for busybox
4 *
5 * public domain -- David Leonard, 2022
6 */
7//config:config TSORT
8//config: bool "tsort (0.7 kb)"
9//config: default y
10//config: help
11//config: tsort performs a topological sort.
12
13//applet:IF_TSORT(APPLET(tsort, BB_DIR_USR_BIN, BB_SUID_DROP))
14
15//kbuild:lib-$(CONFIG_TSORT) += tsort.o
16
17/* BB_AUDIT SUSv3 compliant */
18/* http://www.opengroup.org/onlinepubs/007904975/utilities/tsort.html */
19
20//usage:#define tsort_trivial_usage
21//usage: "[FILE]"
22//usage:#define tsort_full_usage "\n\n"
23//usage: "Topological sort"
24//usage:#define tsort_example_usage
25//usage: "$ echo -e \"a b\\nb c\" | tsort\n"
26//usage: "a\n"
27//usage: "b\n"
28//usage: "c\n"
29
30#include "libbb.h"
31#include "common_bufsiz.h"
32
33struct node {
34 unsigned in_count;
35 unsigned out_count;
36 struct node **out;
37 char name[1];
38};
39
40struct globals {
41 struct node **nodes;
42 unsigned nodes_len;
43};
44#define G (*(struct globals*)bb_common_bufsiz1)
45#define INIT_G() do { \
46 setup_common_bufsiz(); \
47 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
48 G.nodes = NULL; \
49 G.nodes_len = 0; \
50} while (0)
51
52static struct node *
53get_node(const char *name)
54{
55 struct node *n;
56 unsigned a = 0;
57 unsigned b = G.nodes_len;
58
59 /* Binary search for name */
60 while (a != b) {
61 unsigned m = (a + b) / 2;
62 int cmp = strcmp(name, G.nodes[m]->name);
63 if (cmp == 0)
64 return G.nodes[m]; /* found */
65 if (cmp < 0) {
66 b = m;
67 } else {
68 a = m + 1;
69 }
70 }
71
72 /* Allocate new node */
73 n = xzalloc(sizeof(*n) + strlen(name));
74 //n->in_count = 0;
75 //n->out_count = 0;
76 //n->out = NULL;
77 strcpy(n->name, name);
78
79 /* Insert to maintain sort */
80 G.nodes = xrealloc(G.nodes, (G.nodes_len + 1) * sizeof(*G.nodes));
81 memmove(&G.nodes[a + 1], &G.nodes[a],
82 (G.nodes_len - a) * sizeof(*G.nodes));
83 G.nodes[a] = n;
84 G.nodes_len++;
85 return n;
86}
87
88static void
89add_edge(struct node *a, struct node *b)
90{
91 a->out = xrealloc_vector(a->out, 6, a->out_count);
92 a->out[a->out_count++] = b;
93 b->in_count++;
94}
95
96int tsort_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
97int tsort_main(int argc UNUSED_PARAM, char **argv)
98{
99 char *line;
100 size_t linesz;
101 ssize_t len;
102 struct node *a;
103 int cycles;
104
105 INIT_G();
106
107 if (argv[1]) {
108 if (argv[2])
109 bb_show_usage();
110 if (NOT_LONE_DASH(argv[1])) {
111 close(STDIN_FILENO); /* == 0 */
112 xopen(argv[1], O_RDONLY); /* fd will be 0 */
113 }
114 }
115
116 /* Read in words separated by <blank>s */
117 a = NULL;
118 line = NULL;
119 linesz = 0;
120 while ((len = getline(&line, &linesz, stdin)) != -1) {
121 char *s = line;
122 while (*(s = skip_whitespace(s)) != '\0') {
123 struct node *b;
124 char *word;
125
126 word = s;
127 s = skip_non_whitespace(s);
128 if (*s)
129 *s++ = '\0';
130
131 /* Create nodes and edges for each word pair */
132 b = get_node(word);
133 if (!a) {
134 a = b;
135 } else {
136 if (a != b)
137 add_edge(a, b);
138 a = NULL;
139 }
140 }
141 }
142// Most other tools do not check for input read error (treat them as EOF)
143// die_if_ferror(in, input_filename);
144 if (a)
145 bb_simple_error_msg_and_die("odd input");
146 free(line);
147
148 /*
149 * Kahn's algorithm:
150 * - find a node that has no incoming edges, print and remove it
151 * - repeat until the graph is empty
152 * - if any nodes are left, they form cycles.
153 */
154 cycles = 0;
155 while (G.nodes_len) {
156 struct node *n;
157 unsigned i;
158
159 /* Search for first node with no incoming edges */
160 for (i = 0; i < G.nodes_len; i++) {
161 if (!G.nodes[i]->in_count)
162 break;
163 }
164 if (i == G.nodes_len) {
165 /* Must be a cycle; arbitraily break it at node 0 */
166 cycles++;
167 i = 0;
168#ifndef TINY
169 bb_error_msg("cycle at %s", G.nodes[i]->name);
170#endif
171 }
172
173 /* Remove the node (need no longer maintain sort) */
174 n = G.nodes[i];
175 G.nodes[i] = G.nodes[--G.nodes_len];
176
177 /* And remove its outgoing edges */
178 for (i = 0; i < n->out_count; i++)
179 n->out[i]->in_count--;
180 free(n->out);
181
182 puts(n->name);
183 free(n);
184 }
185 free(G.nodes);
186
187 fflush_stdout_and_exit(cycles ? 1 : 0);
188}