*** empty log message ***
[coreutils.git] / src / tsort.c
blob270632ffeae7dd2b4543a96aa2538457441783d2
1 /* tsort - topological sort.
2 Copyright (C) 1998, 1999 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 /* Written by Mark Kettenis <kettenis@phys.uva.nl>. */
20 /* The topological sort is done according to Algorithm T (Topological
21 sort) in Donald E. Knuth, The Art of Computer Programming, Volume
22 1/Fundamental Algorithms, page 262. */
24 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif
28 #include <stdio.h>
29 #include <assert.h>
30 #include <getopt.h>
32 #include "long-options.h"
33 #include "system.h"
34 #include "error.h"
35 #include "readtokens.h"
37 /* Token delimiters when reading from a file. */
38 #define DELIM " \t\n"
40 char *xstrdup ();
42 /* Members of the list of successors. */
43 struct successor
45 struct item *suc;
46 struct successor *next;
49 /* Each string is held in core as the head of a list of successors. */
50 struct item
52 const char *str;
53 struct item *left, *right;
54 int balance;
55 int count;
56 struct item *qlink;
57 struct successor *top;
60 /* The name this program was run with. */
61 char *program_name;
63 /* Nonzero if any of the input files are the standard input. */
64 static int have_read_stdin;
66 /* The head of the sorted list. */
67 static struct item *head = NULL;
69 /* The tail of the list of `zeros', strings that have no predecessors. */
70 static struct item *zeros = NULL;
72 /* The number of strings to sort. */
73 static int n_strings = 0;
75 static struct option const long_options[] =
77 { NULL, 0, NULL, 0}
80 void
81 usage (int status)
83 if (status != 0)
84 fprintf (stderr, _("Try `%s --help' for more information.\n"),
85 program_name);
86 else
88 printf (_("\
89 Usage: %s [OPTION] [FILE]\n\
90 Write totally ordered list consistent with the partial ordering in FILE.\n\
91 With no FILE, or when FILE is -, read standard input.\n\
92 \n\
93 --help display this help and exit\n\
94 --version output version information and exit\n"),
95 program_name);
96 puts (_("\nReport bugs to <textutils-bugs@gnu.org>."));
99 exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
102 /* Create a new item/node for STR. */
103 static struct item *
104 new_item (const char *str)
106 struct item *k = xmalloc (sizeof (struct item));
108 k->str = (str ? xstrdup (str): NULL);
109 k->left = k->right = NULL;
110 k->balance = 0;
112 /* T1. Initialize (COUNT[k] <- 0 and TOP[k] <- ^). */
113 k->count = 0;
114 k->qlink = NULL;
115 k->top = NULL;
117 return k;
120 /* Search binary tree rooted at *ROOT for STR. Allocate a new tree if
121 *ROOT is NULL. Insert a node/item for STR if not found. Return
122 the node/item found/created for STR.
124 This is done according to Algorithm A (Balanced tree search and
125 insertion) in Donald E. Knuth, The Art of Computer Programming,
126 Volume 3/Searching and Sorting, pages 455--457. */
128 static struct item *
129 search_item (struct item *root, const char *str)
131 struct item *p, *q, *r, *s, *t;
132 int a;
134 assert (root);
136 /* Make sure the tree is not empty, since that is what the algorithm
137 below expects. */
138 if (root->right == NULL)
139 return (root->right = new_item (str));
141 /* A1. Initialize. */
142 t = root;
143 s = p = root->right;
145 for (;;)
147 /* A2. Compare. */
148 a = strcmp (str, p->str);
149 if (a == 0)
150 return p;
152 /* A3 & A4. Move left & right. */
153 if (a < 0)
154 q = p->left;
155 else
156 q = p->right;
158 if (q == NULL)
160 /* A5. Insert. */
161 q = new_item (str);
163 /* A3 & A4. (continued). */
164 if (a < 0)
165 p->left = q;
166 else
167 p->right = q;
169 /* A6. Adjust balance factors. */
170 assert (!STREQ (str, s->str));
171 if (strcmp (str, s->str) < 0)
173 r = p = s->left;
174 a = -1;
176 else
178 r = p = s->right;
179 a = +1;
182 while (p != q)
184 assert (!STREQ (str, p->str));
185 if (strcmp (str, p->str) < 0)
187 p->balance = -1;
188 p = p->left;
190 else
192 p->balance = +1;
193 p = p->right;
197 /* A7. Balancing act. */
198 if (s->balance == 0 || s->balance == -a)
200 s->balance += a;
201 return q;
204 if (r->balance == a)
206 /* A8. Single Rotation. */
207 p = r;
208 if (a < 0)
210 s->left = r->right;
211 r->right = s;
213 else
215 s->right = r->left;
216 r->left = s;
218 s->balance = r->balance = 0;
220 else
222 /* A9. Double rotation. */
223 if (a < 0)
225 p = r->right;
226 r->right = p->left;
227 p->left = r;
228 s->left = p->right;
229 p->right = s;
231 else
233 p = r->left;
234 r->left = p->right;
235 p->right = r;
236 s->right = p->left;
237 p->left = s;
240 s->balance = 0;
241 r->balance = 0;
242 if (p->balance == a)
243 s->balance = -a;
244 else if (p->balance == -a)
245 r->balance = a;
246 p->balance = 0;
249 /* A10. Finishing touch. */
250 if (s == t->right)
251 t->right = p;
252 else
253 t->left = p;
255 return q;
258 /* A3 & A4. (continued). */
259 if (q->balance)
261 t = p;
262 s = q;
265 p = q;
268 /* NOTREACHED */
271 /* Record the fact that J precedes K. */
273 static void
274 record_relation (struct item *j, struct item *k)
276 struct successor *p;
278 if (!STREQ (j->str, k->str))
280 k->count++;
281 p = xmalloc (sizeof (struct successor));
282 p->suc = k;
283 p->next = j->top;
284 j->top = p;
288 static void
289 count_items (struct item *k)
291 n_strings++;
294 static void
295 scan_zeros (struct item *k)
297 if (k->count == 0)
299 if (head == NULL)
300 head = k;
301 else
302 zeros->qlink = k;
304 zeros = k;
308 /* If K is part of a loop, print the loop on standard error, and exit. */
310 static void
311 detect_loop (struct item *k)
313 if (k->count > 0)
315 while (k && k->count > 0)
317 k->count = 0;
318 fprintf (stderr, "%s: %s\n", program_name, k->str);
319 k = k->top->suc;
322 exit (EXIT_FAILURE);
326 /* Recurse (sub)tree rooted at ROOT, calling ACTION for each node. */
328 static void
329 recurse_tree (struct item *root, void (*action) (struct item *))
331 if (root->left == NULL && root->right == NULL)
332 (*action) (root);
333 else
335 if (root->left != NULL)
336 recurse_tree (root->left, action);
337 (*action) (root);
338 if (root->right != NULL)
339 recurse_tree (root->right, action);
343 /* Walk the tree specified by the head ROOT, calling ACTION for
344 each node. */
346 static void
347 walk_tree (struct item *root, void (*action) (struct item *))
349 if (root->right)
350 recurse_tree (root->right, action);
353 /* Do a topological sort on FILE. */
355 static void
356 tsort (const char *file)
358 struct item *root;
359 struct item *j = NULL;
360 struct item *k = NULL;
361 register FILE *fp;
362 token_buffer tokenbuffer;
364 /* Intialize the head of the tree will hold the strings we're sorting. */
365 root = new_item (NULL);
367 if (STREQ (file, "-"))
369 fp = stdin;
370 have_read_stdin = 1;
372 else
374 fp = fopen (file, "r");
375 if (fp == NULL)
376 error (EXIT_FAILURE, errno, "%s", file);
379 init_tokenbuffer (&tokenbuffer);
381 while (1)
383 long int len;
385 /* T2. Next Relation. */
386 len = readtoken (fp, DELIM, sizeof (DELIM) - 1, &tokenbuffer);
387 if (len < 0)
388 break;
390 assert (len != 0);
392 k = search_item (root, tokenbuffer.buffer);
393 if (j)
395 /* T3. Record the relation. */
396 record_relation (j, k);
397 k = NULL;
400 j = k;
403 /* T1. Initialize (N <- n). */
404 walk_tree (root, count_items);
406 /* T4. Scan for zeros. */
407 walk_tree (root, scan_zeros);
409 while (head)
411 struct successor *p = head->top;
413 /* T5. Output front of queue. */
414 printf ("%s\n", head->str);
415 n_strings--;
417 /* T6. Erase relations. */
418 while (p)
420 p->suc->count--;
421 if (p->suc->count == 0)
423 zeros->qlink = p->suc;
424 zeros = p->suc;
427 p = p->next;
430 /* T7. Remove from queue. */
431 head = head->qlink;
434 /* T8. End of process. */
435 assert (n_strings >= 0);
436 if (n_strings > 0)
438 error (0, 0, _("%s: input contains a loop:\n"),
439 (have_read_stdin ? "-" : file));
441 /* Print out loop. */
442 walk_tree (root, detect_loop);
444 /* Should not happen. */
445 error (EXIT_FAILURE, 0, _("could not find loop"));
450 main (int argc, char **argv)
452 int opt;
454 program_name = argv[0];
455 setlocale (LC_ALL, "");
456 bindtextdomain (PACKAGE, LOCALEDIR);
457 textdomain (PACKAGE);
459 parse_long_options (argc, argv, "tsort", GNU_PACKAGE, VERSION, usage);
461 while ((opt = getopt_long (argc, argv, "", long_options, NULL)) != -1)
462 switch (opt)
464 case 0: /* long option */
465 break;
466 default:
467 usage (EXIT_FAILURE);
470 have_read_stdin = 0;
472 if (optind + 1 < argc)
474 error (0, 0, _("only one argument may be specified"));
475 usage (EXIT_FAILURE);
478 if (optind < argc)
479 tsort (argv[optind]);
480 else
481 tsort ("-");
483 if (fclose (stdout) == EOF)
484 error (EXIT_FAILURE, errno, _("write error"));
486 if (have_read_stdin && fclose (stdin) == EOF)
487 error (EXIT_FAILURE, errno, _("standard input"));
489 exit (EXIT_SUCCESS);