1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1989-2011 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <gsf@research.att.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21
22 static const char usage[] =
23 "[-?\n@(#)$Id: tsort (AT&T Research) 2000-03-23 $\n]"
24 USAGE_LICENSE
25 "[+NAME?tsort - topological sort]"
26 "[+DESCRIPTION?\btsort\b writes to the standard output a totally ordered"
27 " list of items consistent with a partial ordering of items contained"
28 " in the input \afile\a. If \afile\a is omitted then the standard"
29 " input is read.]"
30 "[+?The input consists of pairs of items (non-empty strings) separated by"
31 " blanks. Pairs of different items indicate ordering. Pairs of"
32 " identical items indicate presence, but not ordering.]"
33
34 "\n"
35 "\n[ file ]\n"
36 "\n"
37
38 "[+SEE ALSO?\bcomm\b(1), \bsort\b(1), \buniq\b(1)]"
39 ;
40
41 #include <ast.h>
42 #include <error.h>
43 #include <hash.h>
44
45 #define NODE_INIT 0
46 #define NODE_CYCLE 1
47 #define NODE_DONE 2
48
49 struct List_s;
50
51 typedef struct Node_s
52 {
53 HASH_HEADER;
54 struct List_s* prereqs;
55 int state;
56 } Node_t;
57
58 typedef struct List_s
59 {
60 struct List_s* next;
61 Node_t* node;
62 } List_t;
63
64 static int
visit(register Node_t * x)65 visit(register Node_t* x)
66 {
67 register List_t* p;
68 int cycle = 0;
69
70 switch (x->state)
71 {
72 case NODE_CYCLE:
73 error(1, "cycle in data");
74 cycle = 1;
75 break;
76 case NODE_INIT:
77 x->state = NODE_CYCLE;
78 for (p = x->prereqs; p; p = p->next)
79 if (visit(p->node))
80 {
81 cycle = 1;
82 error(2, " %s", hashname((Hash_bucket_t*)x));
83 break;
84 }
85 x->state = NODE_DONE;
86 sfputr(sfstdout, hashname((Hash_bucket_t*)x), '\n');
87 break;
88 }
89 return cycle;
90 }
91
92 static void
tsort(Sfio_t * ip)93 tsort(Sfio_t* ip)
94 {
95 register int c;
96 register char* s;
97 register char* b;
98 Node_t* head = 0;
99 Node_t* x;
100 List_t* p;
101 Hash_table_t* tab;
102 Hash_position_t* pos;
103
104 if (!(tab = hashalloc(NiL, HASH_set, HASH_ALLOCATE, 0)))
105 error(ERROR_exit(1), "out of space [hash table]");
106 while (s = sfgetr(ip, '\n', 1))
107 {
108 do
109 {
110 while (*s == ' ' || *s == '\t')
111 s++;
112 if (*s == 0)
113 break;
114 for (b = s; (c = *s) && c != ' ' && c != '\t'; s++);
115 *s++ = 0;
116 if (!(x = (Node_t*)hashlook(tab, b, HASH_CREATE|HASH_SIZE(sizeof(Node_t)), 0)))
117 error(ERROR_exit(1), "out of space [hash entry]");
118 if (head)
119 {
120 if (head != x)
121 {
122 if (!(p = newof(0, List_t, 1, 0)))
123 error(ERROR_exit(1), "out of space [hash list]");
124 p->node = head;
125 p->next = x->prereqs;
126 x->prereqs = p;
127 }
128 head = 0;
129 }
130 else
131 head = x;
132 } while (c);
133 }
134 if (sfvalue(ip))
135 error(ERROR_warn(1), "last line incomplete");
136 if (head)
137 error(ERROR_exit(1), "odd data");
138 if (pos = hashscan(tab, 0))
139 {
140 while (hashnext(pos))
141 visit((Node_t*)pos->bucket);
142 hashdone(pos);
143 }
144 else
145 error(ERROR_exit(1), "hash error");
146 hashfree(tab);
147 }
148
149 int
main(int argc,char ** argv)150 main(int argc, char** argv)
151 {
152 Sfio_t* ip;
153
154 NoP(argc);
155 error_info.id = "tsort";
156 for (;;)
157 {
158 switch (optget(argv, usage))
159 {
160 case ':':
161 error(2, "%s", opt_info.arg);
162 break;
163 case '?':
164 error(ERROR_usage(2), "%s", opt_info.arg);
165 break;
166 }
167 break;
168 }
169 argv += opt_info.index;
170 if (error_info.errors)
171 error(ERROR_usage(2), "%s", optusage(NiL));
172 if (!*argv || streq(*argv, "-") || streq(*argv, "/dev/stdin"))
173 ip = sfstdin;
174 else if (!(ip = sfopen(NiL, *argv, "r")))
175 error(ERROR_system(1), "%s cannot open", *argv);
176 tsort(ip);
177 if (ip != sfstdin)
178 sfclose(ip);
179 return error_info.errors != 0;
180 }
181