Graphviz 14.1.2~dev.20260119.0928
Loading...
Searching...
No Matches
tcldot.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 "config.h"
12
13#include "../tcl-compat.h"
14#include "tcldot.h"
15#include <cgraph/rdr.h>
16#include <stdlib.h>
17#include <string.h>
18#include <tcl.h>
19#include <util/alloc.h>
20#include <util/streq.h>
21
22static int dotnew_internal(ClientData clientData, Tcl_Interp *interp, int argc,
23 char *argv[]) {
24 ictx_t *ictx = (ictx_t *)clientData;
25 Agraph_t *g;
26 int i;
27 Agdesc_t kind;
28
29 if (argc < 2) {
30 Tcl_AppendResult(
31 interp, "wrong # args: should be \"", argv[0],
32 " graphtype ?graphname? ?attributename attributevalue? ?...?\"", NULL);
33 return TCL_ERROR;
34 }
35 if (streq("digraph", argv[1])) {
36 kind = Agdirected;
37 } else if (streq("digraphstrict", argv[1])) {
38 kind = Agstrictdirected;
39 } else if (streq("graph", argv[1])) {
40 kind = Agundirected;
41 } else if (streq("graphstrict", argv[1])) {
42 kind = Agstrictundirected;
43 } else {
44 Tcl_AppendResult(interp, "bad graphtype \"", argv[1], "\": must be one of:",
45 "\n\tdigraph, digraphstrict, graph, graphstrict.", NULL);
46 return TCL_ERROR;
47 }
48 if (argc % 2) {
49 /* if odd number of args then argv[2] is name */
50 g = agopen(argv[2], kind, &ictx->mydisc);
51 i = 3;
52 } else {
53 /* else use handle as name */
54#if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 4
55 char *name = gv_strdup(Tcl_GetStringResult(interp));
56 g = agopen(name, kind, &ictx->mydisc);
57 free(name);
58#else
59 g = agopen(Tcl_GetStringResult(interp), kind, &ictx->mydisc);
60#endif
61 i = 2;
62 }
63 if (!g) {
64 Tcl_AppendResult(interp, "\nFailure to open graph.", NULL);
65 return TCL_ERROR;
66 }
67 setgraphattributes(g, &argv[i], (Tcl_Size)(argc - i));
68 Tcl_AppendResult(interp, obj2cmd(g), NULL);
69
70 return TCL_OK;
71}
72
73static int dotnew(ClientData clientData, Tcl_Interp *interp, int argc,
74 const char *argv[]) {
75 char **argv_copy = tcldot_argv_dup((Tcl_Size)argc, argv);
76 int rc = dotnew_internal(clientData, interp, argc, argv_copy);
77 tcldot_argv_free((Tcl_Size)argc, argv_copy);
78 return rc;
79}
80
81static int dotread(ClientData clientData, Tcl_Interp *interp, int argc,
82 const char *argv[]) {
83 Agraph_t *g;
84 Tcl_Channel channel;
85 int mode;
86 ictx_t *ictx = (ictx_t *)clientData;
87
88 ictx->myioDisc.afread =
89 myiodisc_afread; /* replace afread to use Tcl Channels */
90
91 if (argc < 2) {
92 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
93 " fileHandle\"", NULL);
94 return TCL_ERROR;
95 }
96 channel = Tcl_GetChannel(interp, argv[1], &mode);
97 if (channel == NULL || !(mode & TCL_READABLE)) {
98 Tcl_AppendResult(interp, "\nChannel \"", argv[1], "\"", "is unreadable.",
99 NULL);
100 return TCL_ERROR;
101 }
102 /*
103 * read a graph from the channel, the channel is left open
104 * ready to read the first line after the last line of
105 * a properly parsed graph. If the graph doesn't parse
106 * during reading then the channel will be left at EOF
107 */
108 g = agread((FILE *)channel, (Agdisc_t *)clientData);
109 if (!g) {
110 Tcl_AppendResult(interp, "\nFailure to read graph \"", argv[1], "\"", NULL);
111 if (agerrors()) {
112 Tcl_AppendResult(interp, " because of syntax errors.", NULL);
113 }
114 return TCL_ERROR;
115 }
116 if (agerrors()) {
117 Tcl_AppendResult(interp, "\nSyntax errors in file \"", argv[1], " \"",
118 NULL);
119 return TCL_ERROR;
120 }
121 Tcl_AppendResult(interp, obj2cmd(g), NULL);
122 return TCL_OK;
123}
124
125static int dotstring(ClientData clientData, Tcl_Interp *interp, int argc,
126 const char *argv[]) {
127 Agraph_t *g;
128 ictx_t *ictx = (ictx_t *)clientData;
129 rdr_t rdr;
130
131 if (argc < 2) {
132 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " string\"",
133 NULL);
134 return TCL_ERROR;
135 }
136
137 ictx->myioDisc.afread =
138 myiodisc_memiofread; /* replace afread to use memory range */
139 rdr.data = argv[1];
140 rdr.len = strlen(rdr.data);
141 rdr.cur = 0;
142
143 /* agmemread() is broken for our use because it replaces the id disc */
144 g = agread(&rdr, (Agdisc_t *)clientData);
145 if (!g) {
146 Tcl_AppendResult(interp, "\nFailure to read string \"", argv[1], "\"",
147 NULL);
148 if (agerrors()) {
149 Tcl_AppendResult(interp, " because of syntax errors.", NULL);
150 }
151 return TCL_ERROR;
152 }
153 if (agerrors()) {
154 Tcl_AppendResult(interp, "\nSyntax errors in string \"", argv[1], " \"",
155 NULL);
156 return TCL_ERROR;
157 }
158 Tcl_AppendResult(interp, obj2cmd(g), NULL);
159 return TCL_OK;
160}
161
162int Tcldot_Init(Tcl_Interp *interp);
163int Tcldot_Init(Tcl_Interp *interp) {
164 ictx_t *ictx = calloc(1, sizeof(ictx_t));
165 if (!ictx)
166 return TCL_ERROR;
167
168 ictx->interp = interp;
169 /* build disciplines dynamically so we can selectively replace functions */
170
171 ictx->myioDisc.afread =
172 NULL; /* set in dotread() or dotstring() according to need */
173 ictx->myioDisc.putstr = AgIoDisc.putstr; /* no change */
174 ictx->myioDisc.flush = AgIoDisc.flush; /* no change */
175
176 ictx->mydisc.id = &myiddisc; /* complete replacement */
177 ictx->mydisc.io = &ictx->myioDisc; /* change parts */
178
179 ictx->ctr = 1; /* init to first odd number, increment by 2 */
180
181#ifdef USE_TCL_STUBS
182 if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
183 free(ictx);
184 return TCL_ERROR;
185 }
186#else
187 if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 0) == NULL) {
188 free(ictx);
189 return TCL_ERROR;
190 }
191#endif
192 // inter-release Graphviz versions have a number including '~dev.' that does
193 // not comply with TCL version number rules, so replace this with 'b'
194 char adjusted_version[sizeof(PACKAGE_VERSION)] = PACKAGE_VERSION;
195 char *tilde_dev = strstr(adjusted_version, "~dev.");
196 if (tilde_dev != NULL) {
197 *tilde_dev = 'b';
198 memmove(tilde_dev + 1, tilde_dev + strlen("~dev."),
199 strlen(tilde_dev + strlen("~dev.")) + 1);
200 }
201 if (Tcl_PkgProvide(interp, "Tcldot", adjusted_version) != TCL_OK) {
202 free(ictx);
203 return TCL_ERROR;
204 }
205
206#ifdef HAVE_LIBGD
207 Gdtclft_Init(interp);
208#endif
209
210 /* create a GraphViz Context and pass a pointer to it in clientdata */
211 ictx->gvc = gvContextPlugins(lt_preloaded_symbols, DEMAND_LOADING);
212
213 Tcl_CreateCommand(interp, "dotnew", dotnew, ictx, free);
214 Tcl_CreateCommand(interp, "dotread", dotread, ictx, NULL);
215 Tcl_CreateCommand(interp, "dotstring", dotstring, ictx, NULL);
216
217 return TCL_OK;
218}
219
220int Tcldot_SafeInit(Tcl_Interp *interp);
221int Tcldot_SafeInit(Tcl_Interp *interp) { return Tcldot_Init(interp); }
222
223int Tcldot_builtin_Init(Tcl_Interp *interp);
224int Tcldot_builtin_Init(Tcl_Interp *interp) { return Tcldot_Init(interp); }
Memory allocation wrappers that exit on failure.
static char * gv_strdup(const char *original)
Definition alloc.h:101
mode
Definition cvtgxl.c:33
lt_symlist_t lt_preloaded_symbols[]
Tcl_AppInitProc Gdtclft_Init
Definition gdtclft.c:42
void free(void *)
node NULL
Definition grammar.y:181
Agiodisc_t AgIoDisc
Definition io.c:41
int agerrors(void)
Definition agerror.c:183
Agdesc_t Agundirected
undirected
Definition graph.c:274
Agdesc_t Agstrictundirected
strict undirected
Definition graph.c:275
Agdesc_t Agstrictdirected
strict directed. A strict graph cannot have multi-edges or self-arcs.
Definition graph.c:273
Agraph_t * agopen(char *name, Agdesc_t desc, Agdisc_t *disc)
creates a new graph with the given name and kind
Definition graph.c:44
Agraph_t * agread(void *chan, Agdisc_t *disc)
constructs a new graph
Definition grammar.c:2052
Agdesc_t Agdirected
directed
Definition graph.c:272
GVC_t * gvContextPlugins(const lt_symlist_t *builtins, int demand_loading)
Definition gvc.c:35
static bool streq(const char *a, const char *b)
are a and b equal?
Definition streq.h:11
graph descriptor
Definition cgraph.h:284
user's discipline
Definition cgraph.h:336
Agiddisc_t * id
Definition cgraph.h:337
Agiodisc_t * io
Definition cgraph.h:338
int(* afread)(void *chan, char *buf, int bufsize)
Definition cgraph.h:327
int(* flush)(void *chan)
Definition cgraph.h:329
int(* putstr)(void *chan, const char *str)
Definition cgraph.h:328
graph or subgraph
Definition cgraph.h:424
Tcl_Interp * interp
Definition tcldot.h:29
uint64_t ctr
Definition tcldot.h:28
Agdisc_t mydisc
Definition tcldot.h:26
Agiodisc_t myioDisc
Definition tcldot.h:27
GVC_t * gvc
Definition tcldot.h:30
Definition rdr.h:10
size_t cur
Definition rdr.h:13
const char * data
Definition rdr.h:11
size_t len
Definition rdr.h:12
#define Tcl_Size
Definition tcl-compat.h:33
Agiddisc_t myiddisc
Definition tcldot-id.c:93
int myiodisc_memiofread(void *chan, char *buf, int bufsize)
Definition tcldot-io.c:91
int myiodisc_afread(void *channel, char *ubuf, int n)
Definition tcldot-io.c:34
void tcldot_argv_free(Tcl_Size argc, char *argv[])
free the strings pointed to by argv
void setgraphattributes(Agraph_t *g, char *argv[], Tcl_Size argc)
char * obj2cmd(void *obj)
Definition tcldot-util.c:67
char ** tcldot_argv_dup(Tcl_Size argc, const char *argv[])
duplicate the strings pointed to by argv as non-const strings
int Tcldot_SafeInit(Tcl_Interp *interp)
Definition tcldot.c:221
int Tcldot_Init(Tcl_Interp *interp)
Definition tcldot.c:163
int Tcldot_builtin_Init(Tcl_Interp *interp)
Definition tcldot.c:224
static int dotstring(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
Definition tcldot.c:125
static int dotnew_internal(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
Definition tcldot.c:22
static int dotread(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
Definition tcldot.c:81
static int dotnew(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
Definition tcldot.c:73
#define Tcl_GetStringResult(interp)
Definition tclpathplan.c:45