Add tsort(1)
Signed-off-by: Mattias Andrée <maandree@kth.se>
This commit is contained in:
parent
fad1d35357
commit
b44d4d8edd
1
Makefile
1
Makefile
|
@ -150,6 +150,7 @@ BIN =\
|
||||||
touch\
|
touch\
|
||||||
tr\
|
tr\
|
||||||
true\
|
true\
|
||||||
|
tsort\
|
||||||
tty\
|
tty\
|
||||||
uname\
|
uname\
|
||||||
unexpand\
|
unexpand\
|
||||||
|
|
1
README
1
README
|
@ -89,6 +89,7 @@ The following tools are implemented:
|
||||||
=*|o touch .
|
=*|o touch .
|
||||||
#*|o tr .
|
#*|o tr .
|
||||||
=*|o true .
|
=*|o true .
|
||||||
|
=* o tsort .
|
||||||
=*|o tty .
|
=*|o tty .
|
||||||
=*|o uname .
|
=*|o uname .
|
||||||
#*|o unexpand .
|
#*|o unexpand .
|
||||||
|
|
72
tsort.1
Normal file
72
tsort.1
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
.Dd 2016-02-16
|
||||||
|
.Dt TSORT 1
|
||||||
|
.Os sbase
|
||||||
|
.Sh NAME
|
||||||
|
.Nm tsort
|
||||||
|
.Nd topological sort
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Op Ar file
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
.Nm
|
||||||
|
topologically sorts a graph. The graph is read
|
||||||
|
either from
|
||||||
|
.Ar file
|
||||||
|
or from standard input. The result is not optimized
|
||||||
|
for any particular usage. Loops are detected and
|
||||||
|
reported to standard error, but does not stop the sort.
|
||||||
|
.Pp
|
||||||
|
The input is a list of edges (vertex pairs), where
|
||||||
|
the edge is directed from the first vertex to the
|
||||||
|
second vertex.
|
||||||
|
.Sh OPTIONS
|
||||||
|
None.
|
||||||
|
.Sh EXIT STATUS
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It 0
|
||||||
|
The graph as successfully sorted.
|
||||||
|
.It 1
|
||||||
|
The graph as successfully sorted, but contained loops.
|
||||||
|
.It > 1
|
||||||
|
An error occurred.
|
||||||
|
.El
|
||||||
|
.Sh EXAMPLES
|
||||||
|
The input
|
||||||
|
|
||||||
|
a a
|
||||||
|
a b
|
||||||
|
a c
|
||||||
|
a c
|
||||||
|
a d
|
||||||
|
b c
|
||||||
|
c b
|
||||||
|
e f
|
||||||
|
|
||||||
|
or equivalently
|
||||||
|
|
||||||
|
a a a b a c a c a d
|
||||||
|
b c c b e f
|
||||||
|
|
||||||
|
represents the graph
|
||||||
|
|
||||||
|
┌─┐
|
||||||
|
↓ │
|
||||||
|
┏━━━┓
|
||||||
|
┌──────┃ a ┃──────┐
|
||||||
|
│ ┗━━━┛ │
|
||||||
|
│ │ │ │
|
||||||
|
↓ ↓ ↓ ↓
|
||||||
|
┏━━━┓───→┏━━━┓ ┏━━━┓
|
||||||
|
┃ b ┃ ┃ c ┃ ┃ d ┃
|
||||||
|
┗━━━┛←───┗━━━┛ ┗━━━┛
|
||||||
|
|
||||||
|
┏━━━┓ ┏━━━┓
|
||||||
|
┃ e ┃───→┃ f ┃
|
||||||
|
┗━━━┛ ┗━━━┛
|
||||||
|
|
||||||
|
.Sh STANDARDS
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
utility is compliant with the
|
||||||
|
.St -p1003.1-2013
|
||||||
|
specification.
|
218
tsort.c
Normal file
218
tsort.c
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define eprintf(...) enprintf(2, __VA_ARGS__)
|
||||||
|
#define estrdup(...) enstrdup(2, __VA_ARGS__)
|
||||||
|
#define ecalloc(...) encalloc(2, __VA_ARGS__)
|
||||||
|
#define efshut(...) enfshut(2, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define WHITE 0
|
||||||
|
#define GREY 1
|
||||||
|
#define BLACK 2
|
||||||
|
|
||||||
|
struct vertex;
|
||||||
|
|
||||||
|
struct edge
|
||||||
|
{
|
||||||
|
struct vertex *to;
|
||||||
|
struct edge *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vertex
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
struct vertex *next;
|
||||||
|
struct edge edges;
|
||||||
|
size_t in_edges;
|
||||||
|
int colour;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct vertex graph;
|
||||||
|
|
||||||
|
static void
|
||||||
|
find_vertex(const char *name, struct vertex **it, struct vertex **prev)
|
||||||
|
{
|
||||||
|
for (*prev = &graph; (*it = (*prev)->next); *prev = *it) {
|
||||||
|
int cmp = strcmp(name, (*it)->name);
|
||||||
|
if (cmp > 0)
|
||||||
|
continue;
|
||||||
|
if (cmp < 0)
|
||||||
|
*it = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
find_edge(struct vertex* from, const char *to, struct edge **it, struct edge **prev)
|
||||||
|
{
|
||||||
|
for (*prev = &(from->edges); (*it = (*prev)->next); *prev = *it) {
|
||||||
|
int cmp = strcmp(to, (*it)->to->name);
|
||||||
|
if (cmp > 0)
|
||||||
|
continue;
|
||||||
|
if (cmp < 0)
|
||||||
|
*it = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct vertex *
|
||||||
|
add_vertex(char *name)
|
||||||
|
{
|
||||||
|
struct vertex *vertex;
|
||||||
|
struct vertex *prev;
|
||||||
|
|
||||||
|
find_vertex(name, &vertex, &prev);
|
||||||
|
if (vertex)
|
||||||
|
return vertex;
|
||||||
|
|
||||||
|
vertex = ecalloc(1, sizeof(*vertex));
|
||||||
|
vertex->name = name;
|
||||||
|
vertex->next = prev->next;
|
||||||
|
prev->next = vertex;
|
||||||
|
|
||||||
|
return vertex;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct edge *
|
||||||
|
add_edge(struct vertex* from, struct vertex* to)
|
||||||
|
{
|
||||||
|
struct edge *edge;
|
||||||
|
struct edge *prev;
|
||||||
|
|
||||||
|
find_edge(from, to->name, &edge, &prev);
|
||||||
|
if (edge)
|
||||||
|
return edge;
|
||||||
|
|
||||||
|
edge = ecalloc(1, sizeof(*edge));
|
||||||
|
edge->to = to;
|
||||||
|
edge->next = prev->next;
|
||||||
|
prev->next = edge;
|
||||||
|
to->in_edges += 1;
|
||||||
|
|
||||||
|
return edge;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
load_graph(FILE *fp)
|
||||||
|
{
|
||||||
|
#define SKIP(VAR, START, FUNC) for (VAR = START; FUNC(*VAR) && *VAR; VAR++)
|
||||||
|
#define TOKEN_END(P) do { if (*P) *P++ = 0; else P = 0; } while (0)
|
||||||
|
|
||||||
|
char *line = 0;
|
||||||
|
size_t size = 0;
|
||||||
|
ssize_t len;
|
||||||
|
char *p;
|
||||||
|
char *name;
|
||||||
|
struct vertex *from = 0;
|
||||||
|
|
||||||
|
while ((len = getline(&line, &size, fp)) != -1) {
|
||||||
|
if (len && line[len - 1] == '\n')
|
||||||
|
line[len - 1] = 0;
|
||||||
|
for (p = line; p;) {
|
||||||
|
SKIP(name, p, isspace);
|
||||||
|
if (!*name)
|
||||||
|
break;
|
||||||
|
SKIP(p, name, !isspace);
|
||||||
|
TOKEN_END(p);
|
||||||
|
if (!from) {
|
||||||
|
from = add_vertex(estrdup(name));
|
||||||
|
} else if (strcmp(from->name, name)) {
|
||||||
|
add_edge(from, add_vertex(estrdup(name)));
|
||||||
|
from = 0;
|
||||||
|
} else {
|
||||||
|
from = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(line);
|
||||||
|
|
||||||
|
if (from)
|
||||||
|
eprintf("odd number of tokens in input, did you intended to use -l?\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
sort_graph_visit(struct vertex *u)
|
||||||
|
{
|
||||||
|
struct edge *e = &(u->edges);
|
||||||
|
struct vertex *v;
|
||||||
|
int r = 0;
|
||||||
|
u->colour = GREY;
|
||||||
|
printf("%s\n", u->name);
|
||||||
|
while ((e = e->next)) {
|
||||||
|
v = e->to;
|
||||||
|
if (v->colour == WHITE) {
|
||||||
|
v->in_edges -= 1;
|
||||||
|
if (v->in_edges == 0)
|
||||||
|
r |= sort_graph_visit(v);
|
||||||
|
} else if (v->colour == GREY) {
|
||||||
|
r = 1;
|
||||||
|
fprintf(stderr, "%s: loop detected between %s and %s\n",
|
||||||
|
argv0, u->name, v->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u->colour = BLACK;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
sort_graph(void)
|
||||||
|
{
|
||||||
|
struct vertex *u, *prev;
|
||||||
|
int r = 0;
|
||||||
|
size_t in_edges;
|
||||||
|
for (in_edges = 0; graph.next; in_edges++) {
|
||||||
|
for (prev = &graph; (u = prev->next); prev = u) {
|
||||||
|
if (u->colour != WHITE)
|
||||||
|
goto unlist;
|
||||||
|
if (u->in_edges > in_edges)
|
||||||
|
continue;
|
||||||
|
r |= sort_graph_visit(u);
|
||||||
|
unlist:
|
||||||
|
prev->next = u->next;
|
||||||
|
u = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usage(void)
|
||||||
|
{
|
||||||
|
eprintf("usage: %s [file]\n", argv0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
FILE *fp = stdin;
|
||||||
|
const char *fn = "<stdin>";
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
ARGBEGIN {
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
} ARGEND;
|
||||||
|
|
||||||
|
if (argc > 1)
|
||||||
|
usage();
|
||||||
|
if (argc && strcmp(*argv, "-"))
|
||||||
|
if (!(fp = fopen(fn = *argv, "r")))
|
||||||
|
eprintf("fopen %s:", *argv);
|
||||||
|
|
||||||
|
memset(&graph, 0, sizeof(graph));
|
||||||
|
load_graph(fp);
|
||||||
|
efshut(fp, fn);
|
||||||
|
|
||||||
|
ret = sort_graph();
|
||||||
|
|
||||||
|
if (fshut(stdout, "<stdout>") | fshut(stderr, "<stderr>"))
|
||||||
|
ret = 2;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user