1 /* vi: set sw=4 ts=4: */
3 * tsort implementation for busybox
5 * public domain -- David Leonard, 2022
8 //config: bool "tsort (2.6 kb)"
11 //config: tsort performs a topological sort.
13 //applet:IF_TSORT(APPLET(tsort, BB_DIR_USR_BIN, BB_SUID_DROP))
15 //kbuild:lib-$(CONFIG_TSORT) += tsort.o
17 /* BB_AUDIT SUSv3 compliant */
18 /* http://www.opengroup.org/onlinepubs/007904975/utilities/tsort.html */
20 //usage:#define tsort_trivial_usage
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"
31 #include "common_bufsiz.h"
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); \
53 get_node(const char *name
)
57 unsigned b
= G
.nodes_len
;
59 /* Binary search for name */
61 unsigned m
= (a
+ b
) / 2;
62 int cmp
= strcmp(name
, G
.nodes
[m
]->name
);
64 return G
.nodes
[m
]; /* found */
72 /* Allocate new node */
73 n
= xzalloc(sizeof(*n
) + strlen(name
));
77 strcpy(n
->name
, name
);
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
));
89 add_edge(struct node
*a
, struct node
*b
)
91 a
->out
= xrealloc_vector(a
->out
, 6, a
->out_count
);
92 a
->out
[a
->out_count
++] = b
;
96 int tsort_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
97 int tsort_main(int argc UNUSED_PARAM
, char **argv
)
105 #if ENABLE_FEATURE_CLEAN_UP
114 if (NOT_LONE_DASH(argv
[1])) {
115 close(STDIN_FILENO
); /* == 0 */
116 xopen(argv
[1], O_RDONLY
); /* fd will be 0 */
120 /* Read in words separated by <blank>s */
124 while ((len
= getline(&line
, &linesz
, stdin
)) != -1) {
126 while (*(s
= skip_whitespace(s
)) != '\0') {
131 s
= skip_non_whitespace(s
);
135 /* Create nodes and edges for each word pair */
146 // Most other tools do not check for input read error (treat them as EOF)
147 // die_if_ferror(in, input_filename);
149 bb_simple_error_msg_and_die("odd input");
154 * - find a node that has no incoming edges, print and remove it
155 * - repeat until the graph is empty
156 * - if any nodes are left, they form cycles.
159 #if ENABLE_FEATURE_CLEAN_UP
160 max_len
= G
.nodes_len
;
162 while (G
.nodes_len
) {
165 /* Search for first node with no incoming edges */
166 for (i
= 0; i
< G
.nodes_len
; i
++) {
167 if (!G
.nodes
[i
]->in_count
)
170 if (i
== G
.nodes_len
) {
171 /* Must be a cycle; arbitraily break it at node 0 */
175 bb_error_msg("cycle at %s", G
.nodes
[i
]->name
);
179 /* Remove the node (need no longer maintain sort) */
181 G
.nodes
[i
] = G
.nodes
[--G
.nodes_len
];
182 #if ENABLE_FEATURE_CLEAN_UP
183 /* Keep reference to removed node so it can be freed */
184 G
.nodes
[G
.nodes_len
] = n
;
187 /* And remove its outgoing edges */
188 for (i
= 0; i
< n
->out_count
; i
++)
189 n
->out
[i
]->in_count
--;
193 #if ENABLE_FEATURE_CLEAN_UP
194 for (i
= 0; i
< max_len
; i
++) {
195 free(G
.nodes
[i
]->out
);
201 fflush_stdout_and_exit(cycles
? 1 : 0);