1/* $Id$ $Revision$ */
2/* vim:set shiftwidth=4 ts=8: */
3
4/*************************************************************************
5 * Copyright (c) 2011 AT&T Intellectual Property
6 * All rights reserved. This program and the accompanying materials
7 * are made available under the terms of the Eclipse Public License v1.0
8 * which accompanies this distribution, and is available at
9 * http://www.eclipse.org/legal/epl-v10.html
10 *
11 * Contributors: See CVS logs. Details at http://www.graphviz.org/
12 *************************************************************************/
13
14
15/*
16 * Written by Stephen North
17 * Updated by Emden Gansner
18 */
19#include "config.h"
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#ifdef HAVE_UNISTD_H
25#include <unistd.h>
26#endif
27#include "cgraph.h"
28#include "ingraphs.h"
29
30#include <getopt.h>
31
32static int Do_fans = 0;
33static int MaxMinlen = 0;
34static int ChainLimit = 0;
35static int ChainSize = 0;
36static Agnode_t *ChainNode;
37static FILE *outFile;
38static char *cmd;
39
40static int myindegree(Agnode_t *n)
41{
42 return agdegree(n->root, n, TRUE, FALSE);
43}
44
45/* need outdegree without selfarcs */
46static int myoutdegree(Agnode_t *n)
47{
48 Agedge_t *e;
49 int rv = 0;
50
51 for (e = agfstout(n->root, n); e; e = agnxtout(n->root, e)) {
52 if (agtail(e) != aghead(e)) rv++;
53 }
54 return rv;
55}
56
57static int isleaf(Agnode_t * n)
58{
59 return ((myindegree(n) + myoutdegree(n)) == 1);
60}
61
62static int ischainnode(Agnode_t * n)
63{
64 return ((myindegree(n) == 1) && myoutdegree(n) == 1);
65}
66
67static void adjustlen(Agedge_t * e, Agsym_t * sym, int newlen)
68{
69 char buf[10];
70
71 sprintf(buf, "%d", newlen);
72 agxset(e, sym, buf);
73}
74
75static Agsym_t *bindedgeattr(Agraph_t * g, char *str)
76{
77 return agattr(g, AGEDGE, str, "");
78}
79
80static void transform(Agraph_t * g)
81{
82 Agnode_t *n;
83 Agedge_t *e;
84 char *str;
85 Agsym_t *m_ix, *s_ix;
86 int cnt, d;
87
88 m_ix = bindedgeattr(g, "minlen");
89 s_ix = bindedgeattr(g, "style");
90
91 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
92 d = myindegree(n) + myoutdegree(n);
93 if (d == 0) {
94 if (ChainLimit < 1)
95 continue;
96 if (ChainNode) {
97 e = agedge(g, ChainNode, n, "", TRUE);
98 agxset(e, s_ix, "invis");
99 ChainSize++;
100 if (ChainSize < ChainLimit)
101 ChainNode = n;
102 else {
103 ChainNode = NULL;
104 ChainSize = 0;
105 }
106 } else
107 ChainNode = n;
108 } else if (d > 1) {
109 if (MaxMinlen < 1)
110 continue;
111 cnt = 0;
112 for (e = agfstin(g, n); e; e = agnxtin(g, e)) {
113 if (isleaf(agtail(e))) {
114 str = agxget(e, m_ix);
115 if (str[0] == 0) {
116 adjustlen(e, m_ix, (cnt % MaxMinlen) + 1);
117 cnt++;
118 }
119 }
120 }
121
122 cnt = 0;
123 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
124 if (isleaf(e->node) || (Do_fans && ischainnode(e->node))) {
125 str = agxget(e, m_ix);
126 if (str[0] == 0)
127 adjustlen(e, m_ix, (cnt % MaxMinlen) + 1);
128 cnt++;
129 }
130 }
131 }
132 }
133}
134
135
136static char *useString =
137 "Usage: %s [-f?] [-l l] [-c l] [-o outfile] <files>\n\
138 -o <file> - put output in <file>\n\
139 -f - adjust immediate fanout chains\n\
140 -l <len> - stagger length of leaf edges between [1,l]\n\
141 -c <len> - put disconnected nodes in chains of length l\n\
142 -? - print usage\n";
143
144static void usage(int v)
145{
146 fprintf(stderr, useString, cmd);
147 exit(v);
148}
149
150static FILE *openFile(char *name, char *mode)
151{
152 FILE *fp;
153 char *modestr;
154
155 fp = fopen(name, mode);
156 if (!fp) {
157 if (*mode == 'r')
158 modestr = "reading";
159 else
160 modestr = "writing";
161 fprintf(stderr, "%s: could not open file %s for %s\n",
162 cmd, name, modestr);
163 exit(-1);
164 }
165 return (fp);
166}
167
168static char **scanargs(int argc, char **argv)
169{
170 int c, ival;
171
172 cmd = argv[0];
173 opterr = 0;
174 while ((c = getopt(argc, argv, ":fl:c:o:")) != -1) {
175 switch (c) {
176 case 'f':
177 Do_fans = 1;
178 break;
179 case 'l':
180 ival = atoi(optarg);
181 if (ival > 0)
182 MaxMinlen = ival;
183 break;
184 case 'c':
185 ival = atoi(optarg);
186 if (ival > 0)
187 ChainLimit = ival;
188 break;
189 case 'o':
190 outFile = openFile(optarg, "w");
191 break;
192 case '?':
193 if (optopt == '?')
194 usage(0);
195 else {
196 fprintf(stderr, "%s: option -%c unrecognized\n", cmd,
197 optopt);
198 usage(-1);
199 }
200 break;
201 case ':':
202 fprintf(stderr, "%s: missing argument for option -%c\n",
203 cmd, optopt);
204 usage(-1);
205 break;
206 }
207 }
208 if (Do_fans && (MaxMinlen < 1))
209 fprintf(stderr, "%s: Warning: -f requires -l flag\n", cmd);
210 argv += optind;
211 argc -= optind;
212
213 if (!outFile)
214 outFile = stdout;
215 if (argc)
216 return argv;
217 else
218 return 0;
219}
220
221static Agraph_t *gread(FILE * fp)
222{
223 return agread(fp, (Agdisc_t *) 0);
224}
225
226int main(int argc, char **argv)
227{
228 Agraph_t *g;
229 ingraph_state ig;
230 char **files;
231
232 files = scanargs(argc, argv);
233 newIngraph(&ig, files, gread);
234 while ((g = nextGraph(&ig))) {
235 transform(g);
236 agwrite(g, outFile);
237 }
238 return 0;
239}
240