2
This software may only be used by you under license from AT&T Corp.
3
("AT&T"). A copy of AT&T's Source Code Agreement is available at
4
AT&T's Internet website having the URL:
5
<http://www.research.att.com/sw/tools/graphviz/license/source.html>
6
If you received this software without first entering into a license
7
with AT&T, you have an infringing copy of this software and cannot use
8
it without violating AT&T's intellectual property rights.
21
#define SEQ_ORDER FALSE
23
static Agtag_t Tag; /* to silence warnings about initialization */
25
Agedge_t *agfstout(Agnode_t *n)
28
Agedge_t *e = NILedge;
31
if (agisflattened(g)) e = AGFSTOUT(n);
33
dtrestore(g->e_seq,&(n->out->base.seq_link));
34
e = (Agedge_t*) dtfirst(g->e_seq);
35
n->out = (Agedge_t*)dtextract(g->e_seq);
40
Agedge_t *agnxtout(Agedge_t *e)
47
if (agisflattened(g)) f = AGNXTE(e);
50
dtrestore(g->e_seq,&(n->out->base.seq_link));
51
f = (Agedge_t*) dtnext(g->e_seq,e);
52
n->out = (Agedge_t*)dtextract(g->e_seq);
57
Agedge_t *agfstin(Agnode_t *n)
60
Agedge_t *e = NILedge;
63
if (agisflattened(g)) e = AGFSTIN(n);
65
dtrestore(g->e_seq,&(n->in->base.seq_link));
66
e = (Agedge_t*) dtfirst(g->e_seq);
67
n->in = (Agedge_t*)dtextract(g->e_seq);
72
Agedge_t *agnxtin(Agedge_t *e)
79
if (agisflattened(g)) f = AGNXTE(e);
82
dtrestore(g->e_seq,&(n->in->base.seq_link));
83
f = (Agedge_t*) dtnext(g->e_seq,e);
84
n->in = (Agedge_t*)dtextract(g->e_seq);
89
Agedge_t *agfstedge(Agnode_t *n)
93
if (rv == NILedge) rv = agfstin(n);
97
Agedge_t *agnxtedge(Agedge_t *e, Agnode_t *n)
100
if (AGID(n) == AGID(agtail(e))) {
102
if (rv == NILedge) rv = agfstin(n);
104
else rv = agnxtin(e);
108
static Agedge_t *agfindedge(Agnode_t *t, Agnode_t *h, Agtag_t key)
111
Agedge_t *e, template;
113
assert(agraphof(t) == agraphof(h));
116
template.base.tag = key; /* guess that fan-in < fan-out */
118
if (t != h) { /* guess that fan-in < fan-out */
119
dtrestore(g->e_id,h->inid);
120
e = (Agedge_t*) dtsearch(g->e_id,&template);
121
h->inid = dtextract(g->e_id);
124
dtrestore(g->e_id,t->outid);
125
e = (Agedge_t*) dtsearch(g->e_id,&template);
126
t->outid = dtextract(g->e_id);
131
static Agedge_t *agfindedge_by_id(Agnode_t *t, Agnode_t *h, unsigned long id)
135
assert(agraphof(t) == agraphof(h));
137
tag.objtype = AGEDGE;
139
return agfindedge(t,h,tag);
142
void agedgesetop(Agraph_t *g, Agedge_t *e, int ins)
144
Dtlink_t **seq_set, **id_set;
145
Agnode_t *n; /* node where <e> is referenced */
147
if (AGTYPE(e) == AGOUTEDGE) {
148
n = AGOUT2IN(e)->node;
149
seq_set = (Dtlink_t**)&(n->out);
150
id_set = &(n->outid);
153
n = AGIN2OUT(e)->node;
154
seq_set = (Dtlink_t**)&(n->in);
158
dtrestore(g->e_seq,*seq_set);
159
if (ins) dtinsert(g->e_seq,e);
160
else dtdelete(g->e_seq,e);
161
*seq_set = dtextract(g->e_seq);
163
dtrestore(g->e_id,*id_set);
164
if (ins) dtinsert(g->e_id,e);
165
else dtdelete(g->e_id,e);
166
*id_set = dtextract(g->e_id);
169
/* creates new edge pair and returns outedge */
170
static Agedgepair_t *newedgepair(Agraph_t *g, Agnode_t *t, Agnode_t *h, unsigned long id,
176
e2 = (Agedgepair_t*)agalloc(g,sizeof(Agedgepair_t));
177
in = &(e2->in); out = &(e2->out);
178
AGTYPE(in) = AGINEDGE;
179
AGTYPE(out) = AGOUTEDGE;
180
AGID(in) = AGID(out) = id;
181
AGSEQ(in) = AGSEQ(out) = seq;
184
agedgesetop(g,out,TRUE);
185
if (t != h) agedgesetop(g,in,TRUE);
189
static Agedge_t *localedge(Agraph_t *g, Agnode_t *arg_t, Agnode_t *arg_h, unsigned long id)
199
t = agsubnode(g,arg_t,TRUE);
200
h = agsubnode(g,arg_h,TRUE);
201
/* protect against multi-edges */
203
if (agisstrict(g)) key.objtype = 0;
204
else key.objtype = AGEDGE;
206
if ((e = agfindedge(t,h,key))) return e;
208
if ((par = agparent(g))) {
209
epar = localedge(par, t, h, id);
215
e2 = newedgepair(g,t,h,id,epar?AGSEQ(epar):agnextseq(g,AGEDGE));
217
if (epar) AGDATA(&(e2->in)) = AGDATA(&(e2->out)) = AGDATA(epar);
219
if (g->desc.has_attrs) agedgeattr_init(e, TRUE);
225
static int ok_to_make_edge(Agnode_t *t, Agnode_t *h)
232
/* protect against endpoints in different graphs */
234
if (g != agraphof(h)) return FALSE;
236
/* protect against self, multi-edges in strict graphs */
238
if (AGID(t) == AGID(h)) return FALSE;
240
key.objtype = 0; /* wild card */
241
if (agfindedge(t,h,key)) return FALSE;
246
Agedge_t *agidedge(Agnode_t *t, Agnode_t *h, unsigned long id, int cflag)
252
if ((g = agraphof(t)) != agraphof(h)) return NILedge;
253
if (agisundirected(g) && (AGID(t) > AGID(h)))
254
{ Agnode_t *temp; temp = h; h = t; t = temp; }
256
e = agfindedge_by_id(t,h,id);
257
if ((e == NILedge) && cflag && ok_to_make_edge(t,h)) {
259
if (((g != root) && ((tr = agsubnode(root,t,FALSE))) /* old */
260
&& ((hr = agsubnode(root,h,FALSE))) && agfindedge_by_id(tr,hr,id))
261
|| agallocid(g,AGEDGE,id)) /* new */
262
e = localedge(g, t, h, id);
267
Agedge_t *agedge(Agnode_t *t, Agnode_t *h, char *name, int cflag)
274
if ((g = agraphof(t)) != agraphof(h)) return NILedge;
275
if (agisundirected(g) && (AGID(t) > AGID(h)))
276
{ Agnode_t *temp; temp = h; h = t; t = temp; }
278
have_id = agmapnametoid(g, AGEDGE, name, &id, FALSE);
279
if (have_id || ((name == NILstr) && (NOT(cflag) || agisstrict(g)))) {
280
/* probe for pre-existing edge */
283
if (have_id) {key.id = id; key.objtype = AGEDGE;}
284
else {key.id = key.objtype = 0;}
286
/* might already exist locally */
287
if ((e = agfindedge(t,h,key))) return e;
290
if (cflag && ok_to_make_edge(t,h)
291
&& agmapnametoid(g, AGEDGE, name, &id, TRUE)) /* reserve id */
292
e = localedge(g,t,h,id);
297
void agdeledgepair(Agedge_t *e, void *ignored)
306
if (AGTYPE(e) == AGINEDGE) {in = e; out = AGIN2OUT(e);}
307
else {out = e; in = AGOUT2IN(e);}
308
t = in->node; h = out->node;
309
agedgesetop(g,out,FALSE);
310
if (t != h) agedgesetop(g,in,FALSE);
312
for (e = agfstin(h); e; e = agnxtin(e)) assert(e != in);
313
for (e = agfstout(t); e; e = agnxtout(e)) assert(e != out);
316
int agdeledge(Agedge_t *e)
323
if (agfindedge(agtail(e),aghead(e),AGTAG(e)) == NILedge)
326
if (agisarootobj(e)) {
327
if (g->desc.has_attrs) agedgeattr_delete(e);
328
agmethod_delete(g,e);
329
agrecclose((Agobj_t*)e);
330
agfreeid(g, AGEDGE, AGID(e));
332
return agapply(g,(Agobj_t*)e,(agobjfn_t)agdeledgepair,NILedge,FALSE);
336
Agedge_t *agsubedge(Agraph_t *g, Agedge_t *arg_e, int cflag)
341
if (agraphof(arg_e) == g) return arg_e;
344
t = agsubnode(g,AGTAIL(arg_e),cflag);
345
h = agsubnode(g,AGHEAD(arg_e),cflag);
346
if ((t == NILnode) || (h == NILnode)) e = NILedge;
348
e = agfindedge(t,h,AGTAG(arg_e));
349
if (cflag && (e == NILedge)) {
350
e = localedge(g,t,h,AGID(arg_e));
353
if (e && (AGTYPE(e) != AGTYPE(arg_e))) e = AGOPP(e);
358
/* edge comparison. OBJTYPE(e) == 0 means ID is a wildcard. */
359
int agedgecmpf(Dict_t *d, void *arg_e0, void *arg_e1, Dtdisc_t *disc)
365
e0 = arg_e0; e1 = arg_e1;
367
rv = AGID(e0->node) - AGID(e1->node);
368
if (rv == 0) { /* same node */
369
if ((AGTYPE(e0) == 0) || (AGTYPE(e1) == 0))
372
rv = AGID(e0) - AGID(e1);
377
/* discipline for edges that compares endpoints + ID */
378
Dtdisc_t Ag_edge_disc = {
379
0, /* obj is passed as key */
380
0, /* key size (ignored) */
381
offsetof(Agedge_t,base.id_link), /* link offset */
388
/* debug functions */
392
Agnode_t *agtail(Agedge_t *e) {return AGTAIL(e);}
397
Agnode_t *aghead(Agedge_t *e) {return AGHEAD(e);}
402
Agedge_t *agopp(Agedge_t *e) {return AGOPP(e);}
405
/* could be useful if we write relabel_edge */
406
static Agedge_t *agfindedge_by_name(Agnode_t *t, Agnode_t *h, char *name)
410
if (agmapnametoid(agraphof(t), AGEDGE, name, &id, FALSE))
411
return agfindedge_by_id(t, h, id);