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