Graphviz  2.41.20170921.2350
tcldot.c
Go to the documentation of this file.
1 /* $Id$ $Revision$ */
2 /* vim:set shiftwidth=4 ts=8: */
3 
4 /*************************************************************************
5  * Copyright (c) 2011 AT&T Intellectual Property
6  * All rights reserved. This program and the accompanying materials
7  * are made available under the terms of the Eclipse Public License v1.0
8  * which accompanies this distribution, and is available at
9  * http://www.eclipse.org/legal/epl-v10.html
10  *
11  * Contributors: See CVS logs. Details at http://www.graphviz.org/
12  *************************************************************************/
13 
14 
15 #include "tcldot.h"
16 
17 static int dotnew(ClientData clientData, Tcl_Interp * interp,
18 #ifndef TCLOBJ
19  int argc, char *argv[]
20 #else /* TCLOBJ */
21  int argc, Tcl_Obj * CONST objv[]
22 #endif /* TCLOBJ */
23  )
24 {
25  ictx_t *ictx = (ictx_t *)clientData;
26  Agraph_t *g;
27  char c;
28  int i, length;
29  Agdesc_t kind;
30 
31  if ((argc < 2)) {
32  Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
33  " graphtype ?graphname? ?attributename attributevalue? ?...?\"",
34  NULL);
35  return TCL_ERROR;
36  }
37  c = argv[1][0];
38  length = strlen(argv[1]);
39  if ((c == 'd') && (strncmp(argv[1], "digraph", length) == 0)) {
40  kind = Agdirected;
41  } else if ((c == 'd')
42  && (strncmp(argv[1], "digraphstrict", length) == 0)) {
43  kind = Agstrictdirected;
44  } else if ((c == 'g') && (strncmp(argv[1], "graph", length) == 0)) {
45  kind = Agundirected;
46  } else if ((c == 'g')
47  && (strncmp(argv[1], "graphstrict", length) == 0)) {
48  kind = Agstrictundirected;
49  } else {
50  Tcl_AppendResult(interp, "bad graphtype \"", argv[1], "\": must be one of:",
51  "\n\tdigraph, digraphstrict, graph, graphstrict.", NULL);
52  return TCL_ERROR;
53  }
54  if (argc % 2) {
55  /* if odd number of args then argv[2] is name */
56  g = agopen(argv[2], kind, (Agdisc_t*)ictx);
57  i = 3;
58  } else {
59  /* else use handle as name */
60  g = agopen(Tcl_GetStringResult(interp), kind, (Agdisc_t*)ictx);
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], argc - i);
68  Tcl_AppendResult(interp, obj2cmd(g), NULL);
69 
70  return TCL_OK;
71 }
72 
73 static int dotread(ClientData clientData, Tcl_Interp * interp,
74 #ifndef TCLOBJ
75  int argc, char *argv[]
76 #else /* TCLOBJ */
77  int argc, Tcl_Obj * CONST objv[]
78 #endif /* TCLOBJ */
79  )
80 {
81  Agraph_t *g;
82  Tcl_Channel channel;
83  int mode;
84  ictx_t *ictx = (ictx_t *)clientData;
85 
86  ictx->myioDisc.afread = myiodisc_afread; /* replace afread to use Tcl Channels */
87 
88  if (argc < 2) {
89  Tcl_AppendResult(interp, "Wrong # args: should be \"", argv[0], " fileHandle\"", NULL);
90  return TCL_ERROR;
91  }
92  channel = Tcl_GetChannel(interp, argv[1], &mode);
93  if (channel == NULL || !(mode & TCL_READABLE)) {
94  Tcl_AppendResult(interp, "\nChannel \"", argv[1], "\"", "is unreadable.", NULL);
95  return TCL_ERROR;
96  }
97  /*
98  * read a graph from the channel, the channel is left open
99  * ready to read the first line after the last line of
100  * a properly parsed graph. If the graph doesn't parse
101  * during reading then the channel will be left at EOF
102  */
103  g = agread ((FILE*)channel, (Agdisc_t *)clientData);
104  if (!g) {
105  Tcl_AppendResult(interp, "\nFailure to read graph \"", argv[1], "\"", NULL);
106  if (agerrors()) {
107  Tcl_AppendResult(interp, " because of syntax errors.", NULL);
108  }
109  return TCL_ERROR;
110  }
111  if (agerrors()) {
112  Tcl_AppendResult(interp, "\nSyntax errors in file \"", argv[1], " \"", NULL);
113  return TCL_ERROR;
114  }
115  Tcl_AppendResult(interp, obj2cmd(g), NULL);
116  return TCL_OK;
117 }
118 
119 static int dotstring(ClientData clientData, Tcl_Interp * interp,
120 #ifndef TCLOBJ
121  int argc, char *argv[]
122 #else /* TCLOBJ */
123  int argc, Tcl_Obj * CONST objv[]
124 #endif /* TCLOBJ */
125  )
126 {
127  Agraph_t *g;
128  ictx_t *ictx = (ictx_t *)clientData;
129  rdr_t rdr;
130 
131  ictx->myioDisc.afread = myiodisc_memiofread; /* replace afread to use memory range */
132  rdr.data = argv[1];
133  rdr.len = strlen(rdr.data);
134  rdr.cur = 0;
135 
136  if (argc < 2) {
137  Tcl_AppendResult(interp, "Wrong # args: should be \"", argv[0], " string\"", NULL);
138  return TCL_ERROR;
139  }
140  /* agmemread() is broken for our use because it replaces the id disc */
141  g = agread(&rdr, (Agdisc_t *)clientData);
142  if (!g) {
143  Tcl_AppendResult(interp, "\nFailure to read string \"", argv[1], "\"", NULL);
144  if (agerrors()) {
145  Tcl_AppendResult(interp, " because of syntax errors.", NULL);
146  }
147  return TCL_ERROR;
148  }
149  if (agerrors()) {
150  Tcl_AppendResult(interp, "\nSyntax errors in string \"", argv[1], " \"", NULL);
151  return TCL_ERROR;
152  }
153  Tcl_AppendResult(interp, obj2cmd(g), NULL);
154  return TCL_OK;
155 }
156 
157 #if defined(_BLD_tcldot) && defined(_DLL)
158 __EXPORT__
159 #endif
160 int Tcldot_Init(Tcl_Interp * interp)
161 {
162  ictx_t *ictx;
163 
164  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 = NULL; /* set in dotread() or dotstring() according to need */
172  ictx->myioDisc.putstr = AgIoDisc.putstr; /* no change */
173  ictx->myioDisc.flush = AgIoDisc.flush; /* no change */
174 
175  ictx->mydisc.mem = &AgMemDisc; /* no change */
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  return TCL_ERROR;
184  }
185 #else
186  if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 0) == NULL) {
187  return TCL_ERROR;
188  }
189 #endif
190  if (Tcl_PkgProvide(interp, "Tcldot", PACKAGE_VERSION) != TCL_OK) {
191  return TCL_ERROR;
192  }
193 
194 #if HAVE_LIBGD
195  Gdtclft_Init(interp);
196 #endif
197 
198  /* create a GraphViz Context and pass a pointer to it in clientdata */
199  ictx->gvc = gvContextPlugins(lt_preloaded_symbols, DEMAND_LOADING);
200 
201 #ifndef TCLOBJ
202  Tcl_CreateCommand(interp, "dotnew", dotnew,
203  (ClientData) ictx, (Tcl_CmdDeleteProc *) NULL);
204  Tcl_CreateCommand(interp, "dotread", dotread,
205  (ClientData) ictx, (Tcl_CmdDeleteProc *) NULL);
206  Tcl_CreateCommand(interp, "dotstring", dotstring,
207  (ClientData) ictx, (Tcl_CmdDeleteProc *) NULL);
208 #else /* TCLOBJ */
209  Tcl_CreateObjCommand(interp, "dotnew", dotnew,
210  (ClientData) ictx, (Tcl_CmdDeleteProc *) NULL);
211  Tcl_CreateObjCommand(interp, "dotread", dotread,
212  (ClientData) ictx, (Tcl_CmdDeleteProc *) NULL);
213  Tcl_CreateObjCommand(interp, "dotstring", dotstring,
214  (ClientData) ictx, (Tcl_CmdDeleteProc *) NULL);
215 #endif /* TCLOBJ */
216 
217  return TCL_OK;
218 }
219 
220 int Tcldot_SafeInit(Tcl_Interp * interp)
221 {
222  return Tcldot_Init(interp);
223 }
224 
225 int Tcldot_builtin_Init(Tcl_Interp * interp)
226 {
227  return Tcldot_Init(interp);
228 }
GVC_t * gvc
Definition: tcldot.h:51
CGRAPH_API Agraph_t * agopen(char *name, Agdesc_t desc, Agdisc_t *disc)
Definition: graph.c:44
Agiddisc_t * id
Definition: cgraph.h:191
CGRAPH_API Agmemdisc_t AgMemDisc
Definition: cgraph.h:197
CGRAPH_API Agiodisc_t AgIoDisc
Definition: cgraph.h:199
int myiodisc_memiofread(void *chan, char *buf, int bufsize)
Definition: tcldot-io.c:86
Definition: tcldot.h:46
void setgraphattributes(Agraph_t *g, char *argv[], int argc)
Definition: tcldot-util.c:143
CGRAPH_API Agdesc_t Agstrictundirected
Definition: cgraph.h:421
Definition: io.c:93
char * obj2cmd(void *obj)
Definition: tcldot-util.c:55
CGRAPH_API Agraph_t * agread(void *chan, Agdisc_t *disc)
Definition: grammar.c:2349
int Tcldot_Init(Tcl_Interp *interp)
Definition: tcldot.c:160
Agiddisc_t myiddisc
Definition: tcldot-id.c:99
CGRAPH_API Agdesc_t Agundirected
Definition: cgraph.h:420
Agmemdisc_t * mem
Definition: cgraph.h:190
CGRAPH_API Agdesc_t Agstrictdirected
Definition: cgraph.h:419
CGRAPH_API Agdesc_t Agdirected
Definition: cgraph.h:418
int len
Definition: io.c:95
int Tcldot_builtin_Init(Tcl_Interp *interp)
Definition: tcldot.c:225
lt_symlist_t lt_preloaded_symbols[]
Definition: dot_builtins.c:36
int(* flush)(void *chan)
Definition: cgraph.h:185
int(* afread)(void *chan, char *buf, int bufsize)
Definition: cgraph.h:183
GVC_t * gvContextPlugins(const lt_symlist_t *builtins, int demand_loading)
Definition: gvc.c:36
int cur
Definition: io.c:96
const char * data
Definition: io.c:94
Tcl_Interp * interp
Definition: tcldot.h:50
int myiodisc_afread(void *channel, char *ubuf, int n)
Definition: tcldot-io.c:31
int Tcldot_SafeInit(Tcl_Interp *interp)
Definition: tcldot.c:220
#define NULL
Definition: logic.h:39
uint64_t ctr
Definition: tcldot.h:49
Agiodisc_t * io
Definition: cgraph.h:192
int(* putstr)(void *chan, const char *str)
Definition: cgraph.h:184
Agdisc_t mydisc
Definition: tcldot.h:47
int agerrors()
Definition: agerror.c:170
Agiodisc_t myioDisc
Definition: tcldot.h:48