*** empty log message ***
[coreutils.git] / src / tsort.c
blob0eb9b57961f3a771393e1c48cac32b0e7a8232ec
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 "system.h"
33 #include "long-options.h"
34 #include "error.h"
35 #include "readtokens.h"
37 /* The official name of this program (e.g., no `g' prefix). */
38 #define PROGRAM_NAME "tsort"
40 #define AUTHORS "Mark Kettenis"
42 /* Token delimiters when reading from a file. */
43 #define DELIM " \t\n"
45 char *xstrdup ();
47 /* Members of the list of successors. */
48 struct successor
50 struct item *suc;
51 struct successor *next;
54 /* Each string is held in core as the head of a list of successors. */
55 struct item
57 const char *str;
58 struct item *left, *right;
59 int balance;
60 int count;
61 struct item *qlink;
62 struct successor *top;
65 /* The name this program was run with. */
66 char *program_name;
68 /* Nonzero if any of the input files are the standard input. */
69 static int have_read_stdin;
71 /* The head of the sorted list. */
72 static struct item *head = NULL;
74 /* The tail of the list of `zeros', strings that have no predecessors. */
75 static struct item *zeros = NULL;
77 /* The number of strings to sort. */
78 static int n_strings = 0;
80 static struct option const long_options[] =
82 { NULL, 0, NULL, 0}
85 void
86 usage (int status)
88 if (status != 0)
89 fprintf (stderr, _("Try `%s --help' for more information.\n"),
90 program_name);
91 else
93 printf (_("\
94 Usage: %s [OPTION] [FILE]\n\
95 Write totally ordered list consistent with the partial ordering in FILE.\n\
96 With no FILE, or when FILE is -, read standard input.\n\
97 \n\
98 --help display this help and exit\n\
99 --version output version information and exit\n"),
100 program_name);
101 puts (_("\nReport bugs to <textutils-bugs@gnu.org>."));
104 exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
107 /* Create a new item/node for STR. */
108 static struct item *
109 new_item (const char *str)
111 struct item *k = xmalloc (sizeof (struct item));
113 k->str = (str ? xstrdup (str): NULL);
114 k->left = k->right = NULL;
115 k->balance = 0;
117 /* T1. Initialize (COUNT[k] <- 0 and TOP[k] <- ^). */
118 k->count = 0;
119 k->qlink = NULL;
120 k->top = NULL;
122 return k;
125 /* Search binary tree rooted at *ROOT for STR. Allocate a new tree if
126 *ROOT is NULL. Insert a node/item for STR if not found. Return
127 the node/item found/created for STR.
129 This is done according to Algorithm A (Balanced tree search and
130 insertion) in Donald E. Knuth, The Art of Computer Programming,
131 Volume 3/Searching and Sorting, pages 455--457. */
133 static struct item *
134 search_item (struct item *root, const char *str)
136 struct item *p, *q, *r, *s, *t;
137 int a;
139 assert (root);
141 /* Make sure the tree is not empty, since that is what the algorithm
142 below expects. */
143 if (root->right == NULL)
144 return (root->right = new_item (str));
146 /* A1. Initialize. */
147 t = root;
148 s = p = root->right;
150 for (;;)
152 /* A2. Compare. */
153 a = strcmp (str, p->str);
154 if (a == 0)
155 return p;
157 /* A3 & A4. Move left & right. */
158 if (a < 0)
159 q = p->left;
160 else
161 q = p->right;
163 if (q == NULL)
165 /* A5. Insert. */
166 q = new_item (str);
168 /* A3 & A4. (continued). */
169 if (a < 0)
170 p->left = q;
171 else
172 p->right = q;
174 /* A6. Adjust balance factors. */
175 assert (!STREQ (str, s->str));
176 if (strcmp (str, s->str) < 0)
178 r = p = s->left;
179 a = -1;
181 else
183 r = p = s->right;
184 a = 1;
187 while (p != q)
189 assert (!STREQ (str, p->str));
190 if (strcmp (str, p->str) < 0)
192 p->balance = -1;
193 p = p->left;
195 else
197 p->balance = 1;
198 p = p->right;
202 /* A7. Balancing act. */
203 if (s->balance == 0 || s->balance == -a)
205 s->balance += a;
206 return q;
209 if (r->balance == a)
211 /* A8. Single Rotation. */
212 p = r;
213 if (a < 0)
215 s->left = r->right;
216 r->right = s;
218 else
220 s->right = r->left;
221 r->left = s;
223 s->balance = r->balance = 0;
225 else
227 /* A9. Double rotation. */
228 if (a < 0)
230 p = r->right;
231 r->right = p->left;
232 p->left = r;
233 s->left = p->right;
234 p->right = s;
236 else
238 p = r->left;
239 r->left = p->right;
240 p->right = r;
241 s->right = p->left;
242 p->left = s;
245 s->balance = 0;
246 r->balance = 0;
247 if (p->balance == a)
248 s->balance = -a;
249 else if (p->balance == -a)
250 r->balance = a;
251 p->balance = 0;
254 /* A10. Finishing touch. */
255 if (s == t->right)
256 t->right = p;
257 else
258 t->left = p;
260 return q;
263 /* A3 & A4. (continued). */
264 if (q->balance)
266 t = p;
267 s = q;
270 p = q;
273 /* NOTREACHED */
276 /* Record the fact that J precedes K. */
278 static void
279 record_relation (struct item *j, struct item *k)
281 struct successor *p;
283 if (!STREQ (j->str, k->str))
285 k->count++;
286 p = xmalloc (sizeof (struct successor));
287 p->suc = k;
288 p->next = j->top;
289 j->top = p;
293 static void
294 count_items (struct item *k)
296 n_strings++;
299 static void
300 scan_zeros (struct item *k)
302 if (k->count == 0)
304 if (head == NULL)
305 head = k;
306 else
307 zeros->qlink = k;
309 zeros = k;
313 /* If K is part of a loop, print the loop on standard error, and exit. */
315 static void
316 detect_loop (struct item *k)
318 if (k->count > 0)
320 while (k && k->count > 0)
322 k->count = 0;
323 fprintf (stderr, "%s: %s\n", program_name, k->str);
324 k = k->top->suc;
327 exit (EXIT_FAILURE);
331 /* Recurse (sub)tree rooted at ROOT, calling ACTION for each node. */
333 static void
334 recurse_tree (struct item *root, void (*action) (struct item *))
336 if (root->left == NULL && root->right == NULL)
337 (*action) (root);
338 else
340 if (root->left != NULL)
341 recurse_tree (root->left, action);
342 (*action) (root);
343 if (root->right != NULL)
344 recurse_tree (root->right, action);
348 /* Walk the tree specified by the head ROOT, calling ACTION for
349 each node. */
351 static void
352 walk_tree (struct item *root, void (*action) (struct item *))
354 if (root->right)
355 recurse_tree (root->right, action);
358 /* Do a topological sort on FILE. */
360 static void
361 tsort (const char *file)
363 struct item *root;
364 struct item *j = NULL;
365 struct item *k = NULL;
366 register FILE *fp;
367 token_buffer tokenbuffer;
369 /* Intialize the head of the tree will hold the strings we're sorting. */
370 root = new_item (NULL);
372 if (STREQ (file, "-"))
374 fp = stdin;
375 have_read_stdin = 1;
377 else
379 fp = fopen (file, "r");
380 if (fp == NULL)
381 error (EXIT_FAILURE, errno, "%s", file);
384 init_tokenbuffer (&tokenbuffer);
386 while (1)
388 long int len;
390 /* T2. Next Relation. */
391 len = readtoken (fp, DELIM, sizeof (DELIM) - 1, &tokenbuffer);
392 if (len < 0)
393 break;
395 assert (len != 0);
397 k = search_item (root, tokenbuffer.buffer);
398 if (j)
400 /* T3. Record the relation. */
401 record_relation (j, k);
402 k = NULL;
405 j = k;
408 /* T1. Initialize (N <- n). */
409 walk_tree (root, count_items);
411 /* T4. Scan for zeros. */
412 walk_tree (root, scan_zeros);
414 while (head)
416 struct successor *p = head->top;
418 /* T5. Output front of queue. */
419 printf ("%s\n", head->str);
420 n_strings--;
422 /* T6. Erase relations. */
423 while (p)
425 p->suc->count--;
426 if (p->suc->count == 0)
428 zeros->qlink = p->suc;
429 zeros = p->suc;
432 p = p->next;
435 /* T7. Remove from queue. */
436 head = head->qlink;
439 /* T8. End of process. */
440 assert (n_strings >= 0);
441 if (n_strings > 0)
443 error (0, 0, _("%s: input contains a loop:\n"),
444 (have_read_stdin ? "-" : file));
446 /* Print out loop. */
447 walk_tree (root, detect_loop);
449 /* Should not happen. */
450 error (EXIT_FAILURE, 0, _("could not find loop"));
455 main (int argc, char **argv)
457 int opt;
459 program_name = argv[0];
460 setlocale (LC_ALL, "");
461 bindtextdomain (PACKAGE, LOCALEDIR);
462 textdomain (PACKAGE);
464 parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
465 AUTHORS, usage);
467 while ((opt = getopt_long (argc, argv, "", long_options, NULL)) != -1)
468 switch (opt)
470 case 0: /* long option */
471 break;
472 default:
473 usage (EXIT_FAILURE);
476 have_read_stdin = 0;
478 if (optind + 1 < argc)
480 error (0, 0, _("only one argument may be specified"));
481 usage (EXIT_FAILURE);
484 if (optind < argc)
485 tsort (argv[optind]);
486 else
487 tsort ("-");
489 if (fclose (stdout) == EOF)
490 error (EXIT_FAILURE, errno, _("write error"));
492 if (have_read_stdin && fclose (stdin) == EOF)
493 error (EXIT_FAILURE, errno, _("standard input"));
495 exit (EXIT_SUCCESS);