#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#else
#include "compat_getopt.h"
#endif
#include <agraph.h>
#include <ingraphs.h>
#include <generic_list.h>
typedef struct strattr_s
{
char* n;
char* v;
} strattr_t;
void remove_child(Agraph_t *graph, Agnode_t* node);
void help_message(const char* progname);
generic_list_t* addattr(generic_list_t* l, char* a);
generic_list_t* addnode(generic_list_t* l, char* n);
int verbose = 0;
Agraph_t* gread (FILE* fp);
#define NDNAME "mk"
typedef struct {
Agrec_t h;
int mark;
} ndata;
#define NDATA(n) ((ndata*)(AGDATA(n)))
#define MARKED(n) (((NDATA(n))->mark)&1)
#define MARK(n) (((NDATA(n))->mark) = 1)
#define UNMARK(n) (((NDATA(n))->mark) = 0)
int
main(int argc, char **argv)
{
int c;
char *progname;
ingraph_state ig;
Agraph_t *graph;
Agnode_t *node;
Agedge_t *edge;
Agedge_t *nexte;
Agsym_t *attr;
char **files;
generic_list_t *attr_list;
generic_list_t *node_list;
unsigned long i,j;
opterr = 0;
progname = strrchr(argv[0], '/');
if (progname == NULL)
{
progname = argv[0];
}
else
{
progname++;
}
attr_list = new_generic_list(16);
node_list = new_generic_list(16);
while ((c = getopt(argc, argv, "hvn:N:")) != -1)
{
switch(c)
{
case 'N':
{
attr_list = addattr(attr_list, optarg);
break;
}
case 'n':
{
node_list = addnode(node_list, optarg);
break;
}
case 'h':
{
help_message(progname);
exit(EXIT_SUCCESS);
break;
}
case 'v':
{
verbose = 1;
break;
}
case '?':
if (isprint (optopt))
{
fprintf (stderr, "Unknown option `-%c'.\n", optopt);
}
else
{
fprintf (stderr, "Unknown option character `\\x%X'.\n", optopt);
}
exit(EXIT_FAILURE);
break;
default:
help_message(progname);
exit(EXIT_FAILURE);
break;
}
}
if (optind < argc)
{
files = &argv[optind];
}
else
{
files = NULL;
}
newIngraph (&ig, files, gread);
while ((graph = nextGraph(&ig)) != NULL) {
if (agisdirected(graph) == 0) {
fprintf(stderr, "*** Error: Graph is undirected! Pruning works only with directed graphs!\n");
exit(EXIT_FAILURE);
}
aginit(graph,AGNODE,NDNAME,sizeof(ndata),1);
for (i=0; i < node_list->used; i++) {
if (verbose == 1) fprintf(stderr, "Pruning node %s\n", (char*)node_list->data[i]);
node = agnode(graph, (char*)node_list->data[i], 0);
if (node == NULL) {
fprintf(stderr, "*** Warning: No such node: %s -- gracefully skipping this one\n", (char*)node_list->data[i]);
}
else {
MARK(node);
for (edge = agfstout(node); edge; edge = nexte) {
nexte = agnxtout (edge);
if (aghead(edge) != node) {
if (verbose == 1) fprintf(stderr, "Processing descendant: %s\n", agnameof(aghead(edge)));
remove_child(graph, aghead(edge));
agdelete(graph, edge);
}
}
UNMARK(node);
for (j=0; j < attr_list->used; j++) {
attr = agattr(graph, AGNODE, ((strattr_t*)attr_list->data[j])->n, "");
if (attr == NULL) {
fprintf(stderr, "Couldn't create attribute: %s\n", ((strattr_t*)attr_list->data[j])->n);
exit(EXIT_FAILURE);
}
agxset(node, attr, ((strattr_t*)attr_list->data[j])->v);
}
}
}
agwrite(graph, stdout);
agclose(graph);
}
free(attr_list);
free(node_list);
exit(EXIT_SUCCESS);
}
void
remove_child(Agraph_t *graph, Agnode_t* node) {
Agedge_t *edge;
Agedge_t *nexte;
if MARKED(node) return;
MARK(node);
edge = agfstin(node);
if (edge && (agnxtin(edge) != NULL)) {
UNMARK(node);
return;
}
for (edge = agfstout(node); edge; edge = nexte) {
nexte = agnxtout(edge);
if (aghead(edge) != node) {
if (verbose) fprintf(stderr, "Processing descendant: %s\n", agnameof(aghead(edge)));
remove_child(graph, aghead(edge));
agdeledge(edge);
}
}
agdelnode(node);
return;
}
void
help_message(const char* progname) {
fprintf (stderr, "\
Usage: %s [options] [<files>]\n\
\n\
Options:\n\
-h : Print this message\n\
-n<node> : Name node to prune.\n\
-N<attrspec> : Attribute specification to apply to pruned nodes\n\
\n\
Both options `-n' and `-N' can be used multiple times on the command line.\n", progname);
}
Agraph_t*
gread (FILE* fp) {
return agread(fp,(Agdisc_t*)0);
}
generic_list_t*
addattr(generic_list_t *l, char *a) {
char *p;
strattr_t *sp;
sp = (strattr_t *)malloc(sizeof(strattr_t));
if (sp == NULL) {
perror("[addattr()->malloc()]");
exit(EXIT_FAILURE);
}
p = strchr(a, '=');
if (p == NULL) {
fprintf(stderr, "Invalid argument specification: %s\n", a);
exit(EXIT_FAILURE);
}
*(p++) = '\0';
sp->n = strdup(a);
if (sp->n == NULL) {
perror("[addattr()->strdup()]");
exit(EXIT_FAILURE);
}
sp->v=strdup(p);
if (sp->v == NULL) {
perror("[addattr()->strdup()]");
exit(EXIT_FAILURE);
}
return add_to_generic_list(l, (gl_data)sp);
}
generic_list_t*
addnode(generic_list_t *l, char *n) {
char *sp;
sp = strdup(n);
if (sp == NULL) {
perror("[addnode()->strdup()]");
exit(EXIT_FAILURE);
}
return add_to_generic_list(l, (gl_data)sp);
}