tsort.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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. //applet:IF_TSORT(APPLET(tsort, BB_DIR_USR_BIN, BB_SUID_DROP))
  13. //kbuild:lib-$(CONFIG_TSORT) += tsort.o
  14. /* BB_AUDIT SUSv3 compliant */
  15. /* http://www.opengroup.org/onlinepubs/007904975/utilities/tsort.html */
  16. //usage:#define tsort_trivial_usage
  17. //usage: "[FILE]"
  18. //usage:#define tsort_full_usage "\n\n"
  19. //usage: "Topological sort"
  20. //usage:#define tsort_example_usage
  21. //usage: "$ echo -e \"a b\\nb c\" | tsort\n"
  22. //usage: "a\n"
  23. //usage: "b\n"
  24. //usage: "c\n"
  25. #include "libbb.h"
  26. #include "common_bufsiz.h"
  27. struct node {
  28. unsigned in_count;
  29. unsigned out_count;
  30. struct node **out;
  31. char name[1];
  32. };
  33. struct globals {
  34. struct node **nodes;
  35. unsigned nodes_len;
  36. };
  37. #define G (*(struct globals*)bb_common_bufsiz1)
  38. #define INIT_G() do { \
  39. setup_common_bufsiz(); \
  40. BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
  41. G.nodes = NULL; \
  42. G.nodes_len = 0; \
  43. } while (0)
  44. static struct node *
  45. get_node(const char *name)
  46. {
  47. struct node *n;
  48. unsigned a = 0;
  49. unsigned b = G.nodes_len;
  50. /* Binary search for name */
  51. while (a != b) {
  52. unsigned m = (a + b) / 2;
  53. int cmp = strcmp(name, G.nodes[m]->name);
  54. if (cmp == 0)
  55. return G.nodes[m]; /* found */
  56. if (cmp < 0) {
  57. b = m;
  58. } else {
  59. a = m + 1;
  60. }
  61. }
  62. /* Allocate new node */
  63. n = xzalloc(sizeof(*n) + strlen(name));
  64. //n->in_count = 0;
  65. //n->out_count = 0;
  66. //n->out = NULL;
  67. strcpy(n->name, name);
  68. /* Insert to maintain sort */
  69. G.nodes = xrealloc(G.nodes, (G.nodes_len + 1) * sizeof(*G.nodes));
  70. memmove(&G.nodes[a + 1], &G.nodes[a],
  71. (G.nodes_len - a) * sizeof(*G.nodes));
  72. G.nodes[a] = n;
  73. G.nodes_len++;
  74. return n;
  75. }
  76. static void
  77. add_edge(struct node *a, struct node *b)
  78. {
  79. a->out = xrealloc_vector(a->out, 6, a->out_count);
  80. a->out[a->out_count++] = b;
  81. b->in_count++;
  82. }
  83. int tsort_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  84. int tsort_main(int argc UNUSED_PARAM, char **argv)
  85. {
  86. char *line;
  87. size_t linesz;
  88. ssize_t len;
  89. struct node *a;
  90. int cycles;
  91. INIT_G();
  92. if (argv[1]) {
  93. if (argv[2])
  94. bb_show_usage();
  95. if (NOT_LONE_DASH(argv[1])) {
  96. close(STDIN_FILENO); /* == 0 */
  97. xopen(argv[1], O_RDONLY); /* fd will be 0 */
  98. }
  99. }
  100. /* Read in words separated by <blank>s */
  101. a = NULL;
  102. line = NULL;
  103. linesz = 0;
  104. while ((len = getline(&line, &linesz, stdin)) != -1) {
  105. char *s = line;
  106. while (*(s = skip_whitespace(s)) != '\0') {
  107. struct node *b;
  108. char *word;
  109. word = s;
  110. s = skip_non_whitespace(s);
  111. if (*s)
  112. *s++ = '\0';
  113. /* Create nodes and edges for each word pair */
  114. b = get_node(word);
  115. if (!a) {
  116. a = b;
  117. } else {
  118. if (a != b)
  119. add_edge(a, b);
  120. a = NULL;
  121. }
  122. }
  123. }
  124. // Most other tools do not check for input read error (treat them as EOF)
  125. // die_if_ferror(in, input_filename);
  126. if (a)
  127. bb_simple_error_msg_and_die("odd input");
  128. free(line);
  129. /*
  130. * Kahn's algorithm:
  131. * - find a node that has no incoming edges, print and remove it
  132. * - repeat until the graph is empty
  133. * - if any nodes are left, they form cycles.
  134. */
  135. cycles = 0;
  136. while (G.nodes_len) {
  137. struct node *n;
  138. unsigned i;
  139. /* Search for first node with no incoming edges */
  140. for (i = 0; i < G.nodes_len; i++) {
  141. if (!G.nodes[i]->in_count)
  142. break;
  143. }
  144. if (i == G.nodes_len) {
  145. /* Must be a cycle; arbitraily break it at node 0 */
  146. cycles++;
  147. i = 0;
  148. #ifndef TINY
  149. bb_error_msg("cycle at %s", G.nodes[i]->name);
  150. #endif
  151. }
  152. /* Remove the node (need no longer maintain sort) */
  153. n = G.nodes[i];
  154. G.nodes[i] = G.nodes[--G.nodes_len];
  155. /* And remove its outgoing edges */
  156. for (i = 0; i < n->out_count; i++)
  157. n->out[i]->in_count--;
  158. free(n->out);
  159. puts(n->name);
  160. free(n);
  161. }
  162. free(G.nodes);
  163. fflush_stdout_and_exit(cycles ? 1 : 0);
  164. }