Graphviz 12.0.1~dev.20240715.2254
Loading...
Searching...
No Matches
tcldot-graphcmd.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include <stdbool.h>
12#include <string.h>
13#include "tcldot.h"
14
15int graphcmd(ClientData clientData, Tcl_Interp * interp,
16#ifndef TCLOBJ
17 int argc, char *argv[]
18#else
19 int argc, Tcl_Obj * CONST objv[]
20#endif
21 )
22{
23
24 Agraph_t *g, *sg;
25 Agnode_t *n, *tail, *head;
26 Agedge_t *e;
27 gctx_t *gctx = (gctx_t *)clientData;
28 ictx_t *ictx = gctx->ictx;
29 Agsym_t *a;
30 char buf[12], **argv2;
31 int i, j, argc2;
32 GVC_t *gvc = ictx->gvc;
33
34 if (argc < 2) {
35 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " option ?arg arg ...?\"", NULL);
36 return TCL_ERROR;
37 }
38 g = cmd2g(argv[0]);
39 if (!g) {
40 Tcl_AppendResult(interp, "graph \"", argv[0], "\" not found", NULL);
41 return TCL_ERROR;
42 }
43
44 if (strcmp("addedge", argv[1]) == 0) {
45 if ((argc < 4) || (argc % 2)) {
46 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
47 " addedge tail head ?attributename attributevalue? ?...?\"",
48 NULL);
49 return TCL_ERROR;
50 }
51 tail = cmd2n(argv[2]);
52 if (!tail) {
53 if (!(tail = agfindnode(g, argv[2]))) {
54 Tcl_AppendResult(interp, "tail node \"", argv[2], "\" not found.", NULL);
55 return TCL_ERROR;
56 }
57 }
58 if (agroot(g) != agroot(agraphof(tail))) {
59 Tcl_AppendResult(interp, "tail node ", argv[2], " is not in the graph.", NULL);
60 return TCL_ERROR;
61 }
62 head = cmd2n(argv[3]);
63 if (!head) {
64 if (!(head = agfindnode(g, argv[3]))) {
65 Tcl_AppendResult(interp, "head node \"", argv[3], "\" not found.", NULL);
66 return TCL_ERROR;
67 }
68 }
69 if (agroot(g) != agroot(agraphof(head))) {
70 Tcl_AppendResult(interp, "head node ", argv[3], " is not in the graph.", NULL);
71 return TCL_ERROR;
72 }
73 e = agedge(g, tail, head, NULL, 1);
74 Tcl_AppendResult(interp, obj2cmd(e), NULL);
75 setedgeattributes(agroot(g), e, &argv[4], argc - 4);
76 return TCL_OK;
77
78 } else if (strcmp("addnode", argv[1]) == 0) {
79 if (argc % 2) {
80 /* if odd number of args then argv[2] is name */
81 n = agnode(g, argv[2], 1);
82 i = 3;
83 } else {
84 n = agnode(g, NULL, 1); /* anon node */
85 i = 2;
86 }
87 Tcl_AppendResult(interp, obj2cmd(n), NULL);
88 setnodeattributes(agroot(g), n, &argv[i], argc - i);
89 return TCL_OK;
90
91 } else if (strcmp("addsubgraph", argv[1]) == 0) {
92 if (argc < 2) {
93 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
94 "\" addsubgraph ?name? ?attributename attributevalue? ?...?",
95 NULL);
96 }
97 if (argc % 2) {
98 /* if odd number of args then argv[2] is name */
99 sg = agsubg(g, argv[2], 1);
100 Tcl_AppendResult(interp, obj2cmd(sg), NULL);
101 i = 3;
102 } else {
103 sg = agsubg(g, NULL, 1); /* anon subgraph */
104 i = 2;
105 }
106 setgraphattributes(sg, &argv[i], argc - i);
107 return TCL_OK;
108
109 } else if (strcmp("countnodes", argv[1]) == 0) {
110 snprintf(buf, sizeof(buf), "%d", agnnodes(g));
111 Tcl_AppendResult(interp, buf, NULL);
112 return TCL_OK;
113
114 } else if (strcmp("countedges", argv[1]) == 0) {
115 snprintf(buf, sizeof(buf), "%d", agnedges(g));
116 Tcl_AppendResult(interp, buf, NULL);
117 return TCL_OK;
118
119 } else if (strcmp("delete", argv[1]) == 0) {
120 deleteGraph(gctx, g);
121 return TCL_OK;
122
123 } else if (strcmp("findedge", argv[1]) == 0) {
124 if (argc < 4) {
125 Tcl_AppendResult(interp, "wrong # args: should be \"",
126 argv[0], " findedge tailnodename headnodename\"", NULL);
127 return TCL_ERROR;
128 }
129 if (!(tail = agfindnode(g, argv[2]))) {
130 Tcl_AppendResult(interp, "tail node \"", argv[2], "\" not found.", NULL);
131 return TCL_ERROR;
132 }
133 if (!(head = agfindnode(g, argv[3]))) {
134 Tcl_AppendResult(interp, "head node \"", argv[3], "\" not found.", NULL);
135 return TCL_ERROR;
136 }
137 if (!(e = agfindedge(g, tail, head))) {
138 Tcl_AppendResult(interp, "edge \"", argv[2], " - ", argv[3], "\" not found.", NULL);
139 return TCL_ERROR;
140 }
141 Tcl_AppendElement(interp, obj2cmd(e));
142 return TCL_OK;
143
144 } else if (strcmp("findnode", argv[1]) == 0) {
145 if (argc < 3) {
146 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " findnode nodename\"", NULL);
147 return TCL_ERROR;
148 }
149 if (!(n = agfindnode(g, argv[2]))) {
150 Tcl_AppendResult(interp, "node not found.", NULL);
151 return TCL_ERROR;
152 }
153 Tcl_AppendResult(interp, obj2cmd(n), NULL);
154 return TCL_OK;
155
156 } else if (strcmp("layoutedges", argv[1]) == 0) {
157 g = agroot(g);
158 if (!aggetrec (g, "Agraphinfo_t",0))
159 tcldot_layout(gvc, g, (argc > 2) ? argv[2] : NULL);
160 return TCL_OK;
161
162 } else if (strcmp("layoutnodes", argv[1]) == 0) {
163 g = agroot(g);
164 if (!aggetrec (g, "Agraphinfo_t",0))
165 tcldot_layout(gvc, g, (argc > 2) ? argv[2] : NULL);
166 return TCL_OK;
167
168 } else if (strcmp("listattributes", argv[1]) == 0) {
169 listGraphAttrs(interp, g);
170 return TCL_OK;
171
172 } else if (strcmp("listedgeattributes", argv[1]) == 0) {
173 listEdgeAttrs (interp, g);
174 return TCL_OK;
175
176 } else if (strcmp("listnodeattributes", argv[1]) == 0) {
177 listNodeAttrs (interp, g);
178 return TCL_OK;
179
180 } else if (strcmp("listedges", argv[1]) == 0) {
181 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
182 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
183 Tcl_AppendElement(interp, obj2cmd(e));
184 }
185 }
186 return TCL_OK;
187
188 } else if (strcmp("listnodes", argv[1]) == 0) {
189 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
190 Tcl_AppendElement(interp, obj2cmd(n));
191
192 }
193 return TCL_OK;
194
195 } else if (strcmp("listnodesrev", argv[1]) == 0) {
196 for (n = aglstnode(g); n; n = agprvnode(g, n)) {
197 Tcl_AppendElement(interp, obj2cmd(n));
198 }
199 return TCL_OK;
200
201 } else if (strcmp("listsubgraphs", argv[1]) == 0) {
202 for (sg = agfstsubg(g); sg; sg = agnxtsubg(sg)) {
203 Tcl_AppendElement(interp, obj2cmd(sg));
204 }
205 return TCL_OK;
206
207 } else if (strcmp("queryattributes", argv[1]) == 0) {
208 for (i = 2; i < argc; i++) {
209 if (Tcl_SplitList
210 (interp, argv[i], &argc2,
211 (CONST84 char ***) &argv2) != TCL_OK)
212 return TCL_ERROR;
213 for (j = 0; j < argc2; j++) {
214 if ((a = agfindgraphattr(g, argv2[j]))) {
215 Tcl_AppendElement(interp, agxget(g, a));
216 } else {
217 Tcl_AppendResult(interp, " No attribute named \"", argv2[j], "\"", NULL);
218 return TCL_ERROR;
219 }
220 }
221 Tcl_Free((char *) argv2);
222 }
223 return TCL_OK;
224
225 } else if (strcmp("queryattributevalues", argv[1]) == 0) {
226 for (i = 2; i < argc; i++) {
227 if (Tcl_SplitList
228 (interp, argv[i], &argc2,
229 (CONST84 char ***) &argv2) != TCL_OK)
230 return TCL_ERROR;
231 for (j = 0; j < argc2; j++) {
232 if ((a = agfindgraphattr(g, argv2[j]))) {
233 Tcl_AppendElement(interp, argv2[j]);
234 Tcl_AppendElement(interp, agxget(g, a));
235 } else {
236 Tcl_AppendResult(interp, " No attribute named \"", argv2[j], "\"", NULL);
237 return TCL_ERROR;
238 }
239 }
240 Tcl_Free((char *) argv2);
241 }
242 return TCL_OK;
243
244 } else if (strcmp("queryedgeattributes", argv[1]) == 0) {
245 for (i = 2; i < argc; i++) {
246 if (Tcl_SplitList
247 (interp, argv[i], &argc2,
248 (CONST84 char ***) &argv2) != TCL_OK)
249 return TCL_ERROR;
250 for (j = 0; j < argc2; j++) {
251 if ((a = agfindedgeattr(g, argv2[j]))) {
252 Tcl_AppendElement(interp, agxget(g, a));
253 } else {
254 Tcl_AppendResult(interp, " No attribute named \"", argv2[j], "\"", NULL);
255 return TCL_ERROR;
256 }
257 }
258 Tcl_Free((char *) argv2);
259 }
260 return TCL_OK;
261
262 } else if (strcmp("queryedgeattributevalues", argv[1]) == 0) {
263 for (i = 2; i < argc; i++) {
264 if (Tcl_SplitList
265 (interp, argv[i], &argc2,
266 (CONST84 char ***) &argv2) != TCL_OK)
267 return TCL_ERROR;
268 for (j = 0; j < argc2; j++) {
269 if ((a = agfindedgeattr(g, argv2[j]))) {
270 Tcl_AppendElement(interp, argv2[j]);
271 Tcl_AppendElement(interp, agxget(g, a));
272 } else {
273 Tcl_AppendResult(interp, " No attribute named \"",
274 argv2[j], "\"", NULL);
275 return TCL_ERROR;
276 }
277 }
278 Tcl_Free((char *) argv2);
279 }
280 return TCL_OK;
281
282 } else if (strcmp("querynodeattributes", argv[1]) == 0) {
283 for (i = 2; i < argc; i++) {
284 if (Tcl_SplitList
285 (interp, argv[i], &argc2,
286 (CONST84 char ***) &argv2) != TCL_OK)
287 return TCL_ERROR;
288 for (j = 0; j < argc2; j++) {
289 if ((a = agfindnodeattr(g, argv2[j]))) {
290 Tcl_AppendElement(interp, agxget(g, a));
291 } else {
292 Tcl_AppendResult(interp, " No attribute named \"",
293 argv2[j], "\"", NULL);
294 return TCL_ERROR;
295 }
296 }
297 Tcl_Free((char *) argv2);
298 }
299 return TCL_OK;
300
301 } else if (strcmp("querynodeattributevalues", argv[1]) == 0) {
302 for (i = 2; i < argc; i++) {
303 if (Tcl_SplitList
304 (interp, argv[i], &argc2,
305 (CONST84 char ***) &argv2) != TCL_OK)
306 return TCL_ERROR;
307 for (j = 0; j < argc2; j++) {
308 if ((a = agfindnodeattr(g, argv2[j]))) {
309 Tcl_AppendElement(interp, argv2[j]);
310 Tcl_AppendElement(interp, agxget(g, a));
311 } else {
312 Tcl_AppendResult(interp, " No attribute named \"", argv2[j], "\"", NULL);
313 return TCL_ERROR;
314 }
315 }
316 Tcl_Free((char *) argv2);
317 }
318 return TCL_OK;
319
320 } else if (strcmp("render", argv[1]) == 0) {
321 char *canvas;
322
323 if (argc < 3) {
324 canvas = "$c";
325 } else {
326 canvas = argv[2];
327 }
328
330 tcldot_context_t context = {.canvas = canvas, .interp = interp};
331
332 /* make sure that layout is done */
333 g = agroot(g);
334 if (!aggetrec (g, "Agraphinfo_t",0) || argc > 3)
335 tcldot_layout (gvc, g, (argc > 3) ? argv[3] : NULL);
336
337 /* render graph TK canvas commands */
338 gvc->common.viewNum = 0;
339 if (gvRenderContext(gvc, g, "tk", &context) != 0) {
340 return TCL_ERROR;
341 }
342 fflush(stdout);
343 return TCL_OK;
344
345 } else if (strcmp("setattributes", argv[1]) == 0) {
346 if (argc == 3) {
347 if (Tcl_SplitList
348 (interp, argv[2], &argc2,
349 (CONST84 char ***) &argv2) != TCL_OK)
350 return TCL_ERROR;
351 if ((argc2 == 0) || (argc2 % 2)) {
352 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
353 "\" setattributes attributename attributevalue ?attributename attributevalue? ?...?",
354 NULL);
355 Tcl_Free((char *) argv2);
356 return TCL_ERROR;
357 }
358 setgraphattributes(g, argv2, argc2);
359 Tcl_Free((char *) argv2);
360 }
361 if (argc == 4 && strcmp(argv[2], "viewport") == 0) {
362 /* special case to allow viewport to be set without resetting layout */
363 setgraphattributes(g, &argv[2], argc - 2);
364 } else {
365 if ((argc < 4) || (argc % 2)) {
366 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
367 "\" setattributes attributename attributevalue ?attributename attributevalue? ?...?",
368 NULL);
369 return TCL_ERROR;
370 }
371 setgraphattributes(g, &argv[2], argc - 2);
372 }
373 return TCL_OK;
374
375 } else if (strcmp("setedgeattributes", argv[1]) == 0) {
376 if (argc == 3) {
377 if (Tcl_SplitList
378 (interp, argv[2], &argc2,
379 (CONST84 char ***) &argv2) != TCL_OK)
380 return TCL_ERROR;
381 if ((argc2 == 0) || (argc2 % 2)) {
382 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
383 "\" setedgeattributes attributename attributevalue ?attributename attributevalue? ?...?",
384 NULL);
385 Tcl_Free((char *) argv2);
386 return TCL_ERROR;
387 }
388 setedgeattributes(g, NULL, argv2, argc2);
389 Tcl_Free((char *) argv2);
390 } else {
391 if ((argc < 4) || (argc % 2)) {
392 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
393 "\" setedgeattributes attributename attributevalue ?attributename attributevalue? ?...?",
394 NULL);
395 }
396 setedgeattributes(g, NULL, &argv[2], argc - 2);
397 }
398 return TCL_OK;
399
400 } else if (strcmp("setnodeattributes", argv[1]) == 0) {
401 if (argc == 3) {
402 if (Tcl_SplitList
403 (interp, argv[2], &argc2,
404 (CONST84 char ***) &argv2) != TCL_OK)
405 return TCL_ERROR;
406 if ((argc2 == 0) || (argc2 % 2)) {
407 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
408 "\" setnodeattributes attributename attributevalue ?attributename attributevalue? ?...?",
409 NULL);
410 Tcl_Free((char *) argv2);
411 return TCL_ERROR;
412 }
413 setnodeattributes(g, NULL, argv2, argc2);
414 Tcl_Free((char *) argv2);
415 } else {
416 if ((argc < 4) || (argc % 2)) {
417 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
418 "\" setnodeattributes attributename attributevalue ?attributename attributevalue? ?...?",
419 NULL);
420 }
421 setnodeattributes(g, NULL, &argv[2], argc - 2);
422 }
423 return TCL_OK;
424
425 } else if (strcmp("showname", argv[1]) == 0) {
426 Tcl_SetResult(interp, agnameof(g), TCL_STATIC);
427 return TCL_OK;
428 } else if (strcmp("write", argv[1]) == 0) {
429 g = agroot(g);
430 if (argc < 3) {
431 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
432 " write fileHandle ?language ?DOT|NEATO|TWOPI|FDP|CIRCO|NOP??\"",
433 NULL);
434 return TCL_ERROR;
435 }
436
438
439 Tcl_Channel chan;
440 {
441 int mode;
442
443 chan = Tcl_GetChannel(interp, argv[2], &mode);
444
445 if (!chan) {
446 Tcl_AppendResult(interp, "channel not open: \"", argv[2], NULL);
447 return TCL_ERROR;
448 }
449 if (!(mode & TCL_WRITABLE)) {
450 Tcl_AppendResult(interp, "channel not writable: \"", argv[2], NULL);
451 return TCL_ERROR;
452 }
453 }
454
455 /* make sure that layout is done - unless canonical output */
456 if (!aggetrec (g, "Agraphinfo_t",0) || argc > 4)
457 tcldot_layout(gvc, g, (argc > 4) ? argv[4] : NULL);
458
459 gvc->common.viewNum = 0;
460 if (gvRender(gvc, g, argc < 4 ? "dot" : argv[3], (FILE *)chan) != 0) {
461 return TCL_ERROR;
462 }
463 return TCL_OK;
464
465 } else {
466 Tcl_AppendResult(interp, "bad option \"", argv[1],
467 "\": must be one of:",
468 "\n\taddedge, addnode, addsubgraph, countedges, countnodes,",
469 "\n\tlayout, listattributes, listedgeattributes, listnodeattributes,",
470 "\n\tlistedges, listnodes, listsubgraphs, render, rendergd,",
471 "\n\tqueryattributes, queryedgeattributes, querynodeattributes,",
472 "\n\tqueryattributevalues, queryedgeattributevalues, querynodeattributevalues,",
473 "\n\tsetattributes, setedgeattributes, setnodeattributes,",
474 "\n\tshowname, write.", NULL);
475 return TCL_ERROR;
476 }
477} /* graphcmd */
mode
Definition cvtgxl.c:33
#define head
Definition dthdr.h:15
node NULL
Definition grammar.y:149
int agnedges(Agraph_t *g)
Definition graph.c:164
int agnnodes(Agraph_t *g)
Definition graph.c:158
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:458
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:260
#define agfindedgeattr(g, a)
Definition types.h:617
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:23
#define agfindedge(g, t, h)
Definition types.h:609
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:38
#define agfindgraphattr(g, a)
Definition types.h:613
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:147
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:47
Agnode_t * agprvnode(Agraph_t *g, Agnode_t *n)
Definition node.c:62
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:40
Agnode_t * aglstnode(Agraph_t *g)
Definition node.c:55
#define agfindnodeattr(g, a)
Definition types.h:615
#define agfindnode(g, n)
Definition types.h:611
Agraph_t * agraphof(void *obj)
Definition obj.c:184
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:158
Agraph_t * agroot(void *obj)
Definition obj.c:167
Agrec_t * aggetrec(void *obj, const char *name, int move_to_front)
find record in circular list and do optional move-to-front and lock
Definition rec.c:40
Agraph_t * agfstsubg(Agraph_t *g)
Definition subg.c:77
Agraph_t * agnxtsubg(Agraph_t *subg)
Definition subg.c:82
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
Definition subg.c:57
int gvRender(GVC_t *gvc, graph_t *g, const char *format, FILE *out)
Definition gvc.c:84
int gvRenderContext(GVC_t *gvc, graph_t *g, const char *format, void *context)
Definition gvc.c:143
GVC_t * gvc
Definition htmlparse.c:99
graph or subgraph
Definition cgraph.h:425
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:639
int viewNum
rendering state
Definition gvcommon.h:29
Definition gvcint.h:80
GVCOMMON_t common
Definition gvcint.h:81
size_t(* write_fn)(GVJ_t *job, const char *s, size_t len)
Definition gvcint.h:103
ictx_t * ictx
Definition tcldot.h:42
GVC_t * gvc
Definition tcldot.h:34
context used to convey information between commands and a renderer
Definition tcl_context.h:14
const char * canvas
TCL canvas to render to.
Definition tcl_context.h:15
int graphcmd(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
void setgraphattributes(Agraph_t *g, char *argv[], int argc)
void listNodeAttrs(Tcl_Interp *interp, Agraph_t *g)
Agraph_t * cmd2g(char *cmd)
Definition tcldot-util.c:40
void listGraphAttrs(Tcl_Interp *interp, Agraph_t *g)
size_t Tcldot_channel_writer(GVJ_t *job, const char *s, size_t len)
Definition tcldot-util.c:26
char * obj2cmd(void *obj)
Definition tcldot-util.c:65
void deleteGraph(gctx_t *gctx, Agraph_t *g)
size_t Tcldot_string_writer(GVJ_t *job, const char *s, size_t len)
Definition tcldot-util.c:19
void listEdgeAttrs(Tcl_Interp *interp, Agraph_t *g)
Agnode_t * cmd2n(char *cmd)
Definition tcldot-util.c:47
void setnodeattributes(Agraph_t *g, Agnode_t *n, char *argv[], int argc)
void setedgeattributes(Agraph_t *g, Agedge_t *e, char *argv[], int argc)
void tcldot_layout(GVC_t *gvc, Agraph_t *g, char *engine)
#define CONST84
Definition tcldot.h:23