2
* Tcl extension for Dynamic Graphs by John Ellson (ellson@lucent.com)
4
* Builds on libagraph by Stephen North (north@research.att.com)
14
graph_to_handle(Agraph_t * g, char * buf)
16
sprintf(buf, "dgG%lu", AGID(g));
21
edge_to_handle(Agedge_t * e, char * buf)
23
sprintf(buf, "dgE%lu", AGID(e));
28
node_to_handle(Agnode_t * n, char * buf)
30
sprintf(buf, "dgN%lu", AGID(n));
35
handle_to_graph(dgrInterp_t *dg, char *s)
40
if ((sscanf(s, "dgG%lu", &i)) != 1) {
43
if (!(g = (dgGraph_t *)tclhandleXlateIndex(dg->graphTable, i))) {
50
handle_to_edge(dgrInterp_t *dg, char *s)
55
if ((sscanf(s, "dgE%lu", &i)) != 1)
57
if (!(ep = (Agedge_t **)tclhandleXlateIndex(dg->edgeTable, i)))
63
handle_to_node(dgrInterp_t *dg, char *s)
68
if ((sscanf(s, "dgN%lu", &i)) != 1)
70
if (!(np = (Agnode_t **)tclhandleXlateIndex(dg->nodeTable, i)))
76
attributematch(Tcl_Interp *interp, Agobj_t *obj, int argc, char **argv)
83
Tcl_AppendResult(interp, "invalid attribute pattern, should be \"",
84
"?attributename attributevaluepattern? ...\"",
89
if (!(sym = (Agsym_t *) agattrsym(obj, argv[i]))) {
90
Tcl_AppendResult(interp, " No attribute named \"",
91
argv[i], "\"", (char *) 0);
95
val = agxget(obj, sym);
96
if (!val) val = sym->defval;
97
if (Tcl_StringMatch(val, argv[i])) {
104
switch (AGTYPE(obj)) {
105
case AGRAPH: graph_to_handle((Agraph_t *)obj, buf); break;
106
case AGNODE: node_to_handle((Agnode_t *)obj, buf); break;
107
case AGINEDGE: case AGOUTEDGE: edge_to_handle((Agedge_t *)obj, buf); break;
109
Tcl_AppendResult(interp,
110
"invalid object tag (internal error)", (char *)NULL);
114
Tcl_AppendElement(interp, buf);
119
/********************************************************************/
120
/* evaluate bindings after % substitutions */
123
dgrExpandPercentsEval(
124
Tcl_Interp * interp, /* interpreter context */
125
register char *before, /* Command with percent expressions */
126
char *g, /* graphHandle string to substitute for "%g" */
127
char *n, /* nodeHandle string to substitute for "%n"
128
or tailnode in insert_edge
129
or "node" in modify_graph */
130
char *e, /* edgeHandle string to substitute for "%e"
131
or "edge" in modify_graph */
132
char *A, /* attributeName/Value list for inserts or modifies
133
or headnode in insert_edge */
134
char *a /* attributeName for modify
135
or arglist string to substitute for "%a" */
138
register char *string;
141
Tcl_DStringInit(&scripts);
144
* Find everything up to the next % character and append it to the
148
for (string = before; (*string != 0) && (*string != '%'); string++) {
149
/* Empty loop body. */
151
if (string != before) {
152
Tcl_DStringAppend(&scripts, before, string - before);
159
* There's a percent sequence here. Process it.
164
Tcl_DStringAppend(&scripts, g, strlen(g)); /* graphHandle */
168
Tcl_DStringAppend(&scripts, n, strlen(n)); /* nodeHandle */
171
Tcl_DStringAppend(&scripts, e, strlen(e)); /* edgeHandle */
175
Tcl_DStringAppend(&scripts, A, strlen(A)); /* attributeName */
178
Tcl_DStringAppend(&scripts, a, strlen(a)); /* attributeValue */
181
Tcl_DStringAppend(&scripts, before+1, 1);
186
if (Tcl_GlobalEval(interp, Tcl_DStringValue(&scripts)) != TCL_OK)
187
fprintf(stderr, "%s while in binding: %s\n\n",
188
interp->result, Tcl_DStringValue(&scripts));
189
Tcl_DStringFree(&scripts);
190
return interp->result;
194
insert_graph_cb(Agobj_t *obj, void *arg)
196
dgrInterp_t *dg = (dgrInterp_t *)arg;
200
gp = (dgGraph_t *)tclhandleXlateIndex(dg->graphTable,
201
AGID(agroot((Agraph_t *)obj)));
202
if (gp->insert_graph_cmd) {
203
dgrExpandPercentsEval(dg->interp, gp->insert_graph_cmd,
204
graph_to_handle((Agraph_t *)obj, gbuf),
210
insert_node_cb(Agobj_t *obj, void *arg)
212
dgrInterp_t *dg = (dgrInterp_t *)arg;
213
char gbuf[16], nbuf[16];
217
g = agraphof((Agnode_t *)obj);
218
gp = (dgGraph_t *)tclhandleXlateIndex(dg->graphTable, AGID(agroot(g)));
219
if (gp->insert_node_cmd) {
220
dgrExpandPercentsEval(dg->interp, gp->insert_node_cmd,
221
graph_to_handle(g, gbuf),
222
node_to_handle((Agnode_t *)obj, nbuf),
228
insert_edge_cb(Agobj_t *obj, void *arg)
230
dgrInterp_t *dg = (dgrInterp_t *)arg;
231
char gbuf[16], ebuf[16], tbuf[16], hbuf[16];
235
g = agraphof((Agedge_t *)obj);
236
gp = (dgGraph_t *)tclhandleXlateIndex(dg->graphTable, AGID(agroot(g)));
237
if (gp->insert_edge_cmd) {
238
dgrExpandPercentsEval(dg->interp, gp->insert_edge_cmd,
239
graph_to_handle(g, gbuf),
240
node_to_handle(agtail((Agedge_t *)obj), tbuf),
241
edge_to_handle((Agedge_t *)obj, ebuf),
242
node_to_handle(aghead((Agedge_t *)obj), hbuf),
248
delete_graph_cb(Agobj_t *obj, void *arg)
250
dgrInterp_t *dg = (dgrInterp_t *)arg;
254
gp = (dgGraph_t *)tclhandleXlateIndex(dg->graphTable,
255
AGID(agroot((Agraph_t *)obj)));
256
if (gp->delete_graph_cmd) {
257
dgrExpandPercentsEval(dg->interp, gp->delete_graph_cmd,
258
graph_to_handle((Agraph_t *)obj, gbuf),
264
delete_node_cb(Agobj_t *obj, void *arg)
266
dgrInterp_t *dg = (dgrInterp_t *)arg;
267
char gbuf[16], nbuf[16];
271
g = agraphof((Agnode_t *)obj);
272
gp = (dgGraph_t *)tclhandleXlateIndex(dg->graphTable, AGID(agroot(g)));
273
if (gp->delete_node_cmd) {
274
dgrExpandPercentsEval(dg->interp, gp->delete_node_cmd,
275
graph_to_handle(g, gbuf),
276
node_to_handle((Agnode_t *)obj, nbuf),
282
delete_edge_cb(Agobj_t *obj, void *arg)
284
dgrInterp_t *dg = (dgrInterp_t *)arg;
285
char gbuf[16], ebuf[16];
289
g = agraphof((Agedge_t *)obj);
290
gp = (dgGraph_t *)tclhandleXlateIndex(dg->graphTable, AGID(agroot(g)));
291
if (gp->delete_edge_cmd) {
292
dgrExpandPercentsEval(dg->interp, gp->delete_edge_cmd,
293
graph_to_handle(g, gbuf),
294
"", edge_to_handle((Agedge_t *)obj, ebuf),
300
modify_graph_cb(Agobj_t *obj, void *arg, Agsym_t *sym)
302
dgrInterp_t *dg = (dgrInterp_t *)arg;
303
char gbuf[16], *val, *node="", *edge="";
306
gp = (dgGraph_t *)tclhandleXlateIndex(dg->graphTable,
307
AGID(agroot((Agraph_t *)obj)));
308
if (gp->modify_graph_cmd) {
310
val = agxget((Agraph_t *)obj, sym);
311
if (!val) val = sym->defval;
312
if (sym->kind == AGNODE) node = "node";
313
if (sym->kind == AGINEDGE || sym->kind == AGOUTEDGE) edge = "edge";
314
dgrExpandPercentsEval(dg->interp, gp->modify_graph_cmd,
315
graph_to_handle((Agraph_t *)obj, gbuf),
316
node, edge, sym->name, val);
321
modify_node_cb(Agobj_t *obj, void *arg, Agsym_t *sym)
323
dgrInterp_t *dg = (dgrInterp_t *)arg;
324
char gbuf[16], nbuf[16], *val;
328
g = agraphof((Agnode_t *)obj);
329
gp = (dgGraph_t *)tclhandleXlateIndex(dg->graphTable, AGID(agroot(g)));
330
if (gp->modify_node_cmd) {
331
val = agxget((Agnode_t *)obj, sym);
332
if (!val) val = sym->defval;
333
dgrExpandPercentsEval(dg->interp, gp->modify_node_cmd,
334
graph_to_handle(g, gbuf),
335
node_to_handle((Agnode_t *)obj, nbuf), "", sym->name, val);
340
modify_edge_cb(Agobj_t *obj, void *arg, Agsym_t *sym)
342
dgrInterp_t *dg = (dgrInterp_t *)arg;
343
char gbuf[16], ebuf[16], *val;
347
g = agraphof((Agedge_t *)obj);
348
gp = (dgGraph_t *)tclhandleXlateIndex(dg->graphTable, AGID(agroot(g)));
349
if (gp->modify_edge_cmd) {
350
val = agxget((Agedge_t *)obj, sym);
351
if (!val) val = sym->defval;
352
dgrExpandPercentsEval(dg->interp, gp->modify_edge_cmd,
353
graph_to_handle(g, gbuf),
354
"", edge_to_handle((Agedge_t *)obj, ebuf), sym->name, val);
359
deleteEdges(dgrInterp_t *dg, Agnode_t * n)
362
Agedge_t *e, *prev_e;
367
edge_to_handle(prev_e, ebuf);
368
e = agnxtedge(prev_e, n);
370
if (dg->object_commands) {
371
Tcl_DeleteCommand(dg->interp, ebuf);
377
deleteNodes(dgrInterp_t *dg, Agraph_t * g)
379
Agnode_t *n, *prev_n;
385
deleteEdges(dg, prev_n);
386
node_to_handle(prev_n, nbuf);
387
n = agnxtnode(prev_n);
389
if (dg->object_commands) {
390
Tcl_DeleteCommand(dg->interp, nbuf);
395
/* recursively delete subgraphs */
397
deleteSubgraphs(dgrInterp_t *dg, Agraph_t * g)
399
Agraph_t *sg, *prev_sg;
405
deleteSubgraphs(dg, sg);
406
graph_to_handle(sg, gbuf);
407
sg = agnxtsubg(prev_sg);
409
if (dg->object_commands) {
410
Tcl_DeleteCommand(dg->interp, gbuf);
415
/* recursively register all subgraphs as tcl commands */
417
registerSubgraphs(dgrInterp_t *dg, Agraph_t *g)
422
for (sg = agfstsubg(g); sg; sg = agnxtsubg(sg)) {
423
Tcl_CreateCommand(dg->interp, graph_to_handle(sg, gbuf),
424
graphcmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
425
registerSubgraphs(dg, sg);
429
void dg_gpstruct_init (dgrInterp_t *dg, Agraph_t *g) {
433
gp = (dgGraph_t *)tclhandleXlateIndex(dg->graphTable, AGID(g));
435
gp->callbacks_enabled = TRUE;
436
gp->batch_cmd = (char *) NULL;
437
gp->insert_graph_cmd = (char *) NULL;
438
gp->modify_graph_cmd = (char *) NULL;
439
gp->delete_graph_cmd = (char *) NULL;
440
gp->insert_node_cmd = (char *) NULL;
441
gp->modify_node_cmd = (char *) NULL;
442
gp->delete_node_cmd = (char *) NULL;
443
gp->insert_edge_cmd = (char *) NULL;
444
gp->modify_edge_cmd = (char *) NULL;
445
gp->delete_edge_cmd = (char *) NULL;
446
agpushdisc(g, &gcbdisc, (void *)dg);
447
graph_to_handle(g, gbuf);
448
if (dg->object_commands) {
449
Tcl_CreateCommand(dg->interp, gbuf, graphcmd, (ClientData) dg,
450
(Tcl_CmdDeleteProc *) NULL);
452
Tcl_AppendResult(dg->interp, gbuf, (char *) NULL);
455
void dg_gpfromdot_init (dgrInterp_t *dg, Agraph_t *g) {
460
if (dg->object_commands) {
461
for (n = agfstnode(g); n; n = agnxtnode(n)) {
462
np = (Agnode_t **)tclhandleXlateIndex(dg->nodeTable, AGID(n));
464
Tcl_CreateCommand(dg->interp, node_to_handle(n, buf),
465
nodecmd, (ClientData)dg, (Tcl_CmdDeleteProc *) NULL);
466
for (e = agfstout(n); e; e = agnxtout(e)) {
467
ep = (Agedge_t **)tclhandleXlateIndex(dg->edgeTable, AGID(e));
469
Tcl_CreateCommand(dg->interp, edge_to_handle(e, buf),
470
edgecmd, (ClientData)dg, (Tcl_CmdDeleteProc *) NULL);
473
registerSubgraphs(dg, g);
476
for (n = agfstnode(g); n; n = agnxtnode(n)) {
477
np = (Agnode_t **)tclhandleXlateIndex(dg->nodeTable, AGID(n));
479
for (e = agfstout(n); e; e = agnxtout(e)) {
480
ep = (Agedge_t **)tclhandleXlateIndex(dg->edgeTable, AGID(e));
487
/********************************************************************/
490
int dgrCallbacks (dgrInterp_t *dg, Agraph_t *g, int flag) {
494
gp = (dgGraph_t *)tclhandleXlateIndex(dg->graphTable,AGID(agroot(g)));
495
if (gp->callbacks_enabled) {
497
gp->callbacks_enabled = FALSE;
498
agcallbacks(g, FALSE);
502
if (flag) { /* && NOT gp->callbacks_enabled */
504
dgrExpandPercentsEval(dg->interp, gp->batch_cmd,
505
graph_to_handle(g, gbuf),"", "", "", "1");
507
agcallbacks(g, TRUE);
509
dgrExpandPercentsEval(dg->interp, gp->batch_cmd,
510
graph_to_handle(g, gbuf),"", "", "", "0");
512
gp->callbacks_enabled = TRUE;
517
/********************************************************************/
521
dgidopen(Agraph_t *g)
523
/* This is taken care of in Tcldg_Init */
524
return (void *) NULL;
528
dgidmap(void *state, int objtype, char *str, unsigned long *id, int createflag)
530
/* tcldg doesn't support string names in handle table */
531
if (str) return FALSE;
536
return (long)tclhandleAlloc(dgrInterp.graphTable, (char *)NULL, id);
538
return (long)tclhandleAlloc(dgrInterp.nodeTable, (char *)NULL, id);
541
return (long)tclhandleAlloc(dgrInterp.edgeTable, (char *)NULL, id);
547
return (long)tclhandleXlateIndex(dgrInterp.graphTable, *id);
549
return (long)tclhandleXlateIndex(dgrInterp.nodeTable, *id);
552
return (long)tclhandleXlateIndex(dgrInterp.edgeTable, *id);
559
dgidalloc(void *state, int objtype, unsigned long id)
561
/* id should already be allocated by map. check only. */
564
return (long)tclhandleXlateIndex(dgrInterp.graphTable, id);
566
return (long)tclhandleXlateIndex(dgrInterp.nodeTable, id);
569
return (long)tclhandleXlateIndex(dgrInterp.edgeTable, id);
575
dgidfree(void *state, int objtype, unsigned long id)
577
void *rv = (void *)NULL;
581
rv = tclhandleFreeIndex(dgrInterp.graphTable, id);
584
rv = tclhandleFreeIndex(dgrInterp.nodeTable, id);
588
rv = tclhandleFreeIndex(dgrInterp.edgeTable, id);
593
if (!rv) agerror(AGERROR_BADOBJ,"dgidfree");
597
dgidprint(void *state, int objtype, unsigned long id)
599
/* we don't support string names in handle table */
600
return (char *) NULL;
604
dgidclose(void *state)
606
/* Nothing to do in Tcldg */
609
Agiddisc_t iddisc = { dgidopen, dgidmap, dgidalloc,
610
dgidfree, dgidprint, dgidclose };
612
/********************************************************************/
613
/* io disciplines - files */
618
fileiofread(void *chan, char *buf, int bufsize)
620
return Tcl_Read((Tcl_Channel)chan, buf, bufsize);
624
fileioputstr(void *chan, char *str)
626
return Tcl_Write((Tcl_Channel)chan, str, -1);
630
fileioflush(void *chan)
632
return Tcl_Flush((Tcl_Channel)chan);
635
Agiodisc_t file_iodisc = {fileiofread, fileioputstr, fileioflush};
638
/* no TCL_CHANNELS so use libgraph default io routines */
640
Agiodisc_t file_iodisc = {NULL, NULL, NULL};
644
/********************************************************************/
645
/* io disciplines - strings */
648
stringiofread(void *chan, char *buf, int bufsize)
650
char *s = *(char **)chan;
653
strncpy(buf, s, bufsize);
655
*(char **)chan += bufsize;
663
stringioputstr(void *chan, char *str)
665
int len = strlen(str);
667
Tcl_DStringAppend((Tcl_DString *)chan, str, len);
672
stringioflush(void *chan)
677
Agiodisc_t string_iodisc = {stringiofread, stringioputstr, stringioflush};
679
/********************************************************************/
682
buildBindings(char *s1, char *s2)
684
* previous binding in s1 binding to be added in s2 result in s3
686
* if s2 begins with + then append (separated by \n) else s2 replaces if
687
* resultant string is null then bindings are deleted
697
s3 = Tcl_Alloc(strlen(s1) + l + 2);
709
s3 = Tcl_Alloc(l + 2);
721
s3 = Tcl_Alloc(l + 2);
731
Agcbdisc_t gcbdisc = {
732
{insert_graph_cb, modify_graph_cb, delete_graph_cb},
733
{insert_node_cb, modify_node_cb, delete_node_cb},
734
{insert_edge_cb, modify_edge_cb, delete_edge_cb}
737
Agdisc_t gdisc = {(Agmemdisc_t *)NULL, &iddisc, &file_iodisc};