Graphviz 13.0.0~dev.20241220.2304
Loading...
Searching...
No Matches
gxl2gv.c
Go to the documentation of this file.
1
6/*************************************************************************
7 * Copyright (c) 2011 AT&T Intellectual Property
8 * All rights reserved. This program and the accompanying materials
9 * are made available under the terms of the Eclipse Public License v1.0
10 * which accompanies this distribution, and is available at
11 * https://www.eclipse.org/legal/epl-v10.html
12 *
13 * Contributors: Details at https://graphviz.org
14 *************************************************************************/
15
16#include <assert.h>
17#include "convert.h"
18#include <cgraph/list.h>
19#include <stdbool.h>
20#include <stdio.h>
21#include <util/agxbuf.h>
22#include <util/alloc.h>
23#include <util/exit.h>
24#include <util/gv_ctype.h>
25#include <util/startswith.h>
26#include <util/unreachable.h>
27#ifdef HAVE_EXPAT
28#include <expat.h>
29#include <limits.h>
30#include <stdlib.h>
31
32#ifndef XML_STATUS_ERROR
33#define XML_STATUS_ERROR 0
34#endif
35
36#define NAMEBUF 100
37
38#define GXL_ATTR "_gxl_"
39#define GXL_ID "_gxl_id"
40#define GXL_ROLE "_gxl_role"
41#define GXL_HYPER "_gxl_hypergraph"
42#define GXL_FROM "_gxl_fromorder"
43#define GXL_TO "_gxl_toorder"
44#define GXL_TYPE "_gxl_type"
45#define GXL_COMP "_gxl_composite_"
46#define GXL_LOC "_gxl_locator_"
47
48typedef enum {
49 TAG_NONE,
50 TAG_GRAPH,
51 TAG_NODE,
52 TAG_EDGE,
53 TAG_HTML_LIKE_STRING,
54} attr_t;
55
56typedef struct {
57 agxbuf xml_attr_name;
58 agxbuf xml_attr_value;
59 agxbuf composite_buffer;
60 bool listen;
61 attr_t closedElementType;
62 attr_t globalAttrType;
63 bool compositeReadState;
64 bool edgeinverted;
65 Dt_t *nameMap;
66} userdata_t;
67
68static Agraph_t *root; /* root graph */
69static attr_t Current_class; /* Current element type */
70static Agraph_t *G; /* Current graph */
71static Agnode_t *N; /* Set if Current_class == TAG_NODE */
72static Agedge_t *E; /* Set if Current_class == TAG_EDGE */
73
74DEFINE_LIST(graph_stack, Agraph_t *)
75static graph_stack_t Gstack;
76
77typedef struct {
78 Dtlink_t link;
79 char *name;
80 char *unique_name;
81} namev_t;
82
83static void *make_nitem(void *p, Dtdisc_t *disc){
84 (void)disc;
85 namev_t *objp = p;
86
87 namev_t *np = malloc(sizeof(namev_t));
88 if (np == NULL)
89 return NULL;
90 np->name = objp->name;
91 np->unique_name = 0;
92 return np;
93}
94
95static void free_nitem(void *name) {
96 namev_t *np = name;
97 free(np->unique_name);
98 free(np);
99}
100
101static Dtdisc_t nameDisc = {
102 .key = offsetof(namev_t, name),
103 .size = -1,
104 .link = offsetof(namev_t, link),
105 .makef = make_nitem,
106 .freef = free_nitem,
107};
108
109static userdata_t genUserdata(void) {
110 userdata_t user = {0};
111 user.listen = false;
112 user.closedElementType = TAG_NONE;
113 user.globalAttrType = TAG_NONE;
114 user.compositeReadState = false;
115 user.edgeinverted = false;
116 user.nameMap = dtopen(&nameDisc, Dtoset);
117 return user;
118}
119
120static void freeUserdata(userdata_t ud) {
121 dtclose(ud.nameMap);
122 agxbfree(&ud.xml_attr_name);
123 agxbfree(&ud.xml_attr_value);
124 agxbfree(&ud.composite_buffer);
125}
126
127static void addToMap(Dt_t * map, char *name, char *uniqueName)
128{
129 namev_t obj;
130 namev_t *objp;
131
132 obj.name = name;
133 objp = dtinsert(map, &obj);
134 assert(objp->unique_name == 0);
135 objp->unique_name = gv_strdup(uniqueName);
136}
137
138static char *mapLookup(Dt_t *nm, const char *name) {
139 namev_t *objp = dtmatch(nm, name);
140 if (objp)
141 return objp->unique_name;
142 else
143 return 0;
144}
145
146static int isAnonGraph(const char *name)
147{
148 if (*name++ != '%')
149 return 0;
150 while (gv_isdigit(*name))
151 name++; /* skip over digits */
152 return (*name == '\0');
153}
154
155static void push_subg(Agraph_t * g)
156{
157 // insert the new graph
158 graph_stack_push_back(&Gstack, g);
159
160 // save the root if this is the first graph
161 if (graph_stack_size(&Gstack) == 1) {
162 root = g;
163 }
164
165 // update the top graph
166 G = g;
167}
168
169static Agraph_t *pop_subg(void)
170{
171 // is the stack empty?
172 if (graph_stack_is_empty(&Gstack)) {
173 fprintf(stderr, "gxl2gv: Gstack underflow in graph parser\n");
174 graphviz_exit(EXIT_FAILURE);
175 }
176
177 // pop the top graph
178 Agraph_t *g = graph_stack_pop_back(&Gstack);
179
180 // update the top graph
181 if (!graph_stack_is_empty(&Gstack))
182 G = *graph_stack_back(&Gstack);
183
184 return g;
185}
186
187static Agnode_t *bind_node(const char *name)
188{
189 N = agnode(G, (char *) name, 1);
190 return N;
191}
192
193static Agedge_t *bind_edge(const char *tail, const char *head)
194{
195 Agnode_t *tailNode, *headNode;
196 char *key = 0;
197
198 tailNode = agnode(G, (char *) tail, 1);
199 headNode = agnode(G, (char *) head, 1);
200 E = agedge(G, tailNode, headNode, key, 1);
201 return E;
202}
203
204static int get_xml_attr(char *attrname, const char **atts)
205{
206 int count = 0;
207 while (atts[count] != NULL) {
208 if (strcmp(attrname, atts[count]) == 0) {
209 return count + 1;
210 }
211 count += 2;
212 }
213 return -1;
214}
215
216static void setName(Dt_t * names, Agobj_t * n, char *value)
217{
218 Agsym_t *ap;
219 char *oldName;
220
221 ap = agattr(root, AGTYPE(n), GXL_ID, "");
222 agxset(n, ap, agnameof(n));
223 oldName = agxget(n, ap); /* set/get gives us new copy */
224 addToMap(names, oldName, value);
225 agrename(n, value);
226}
227
228static char *defval = "";
229
230static void
231setNodeAttr(Agnode_t * np, char *name, char *value, userdata_t * ud,
232 bool is_html)
233{
234 Agsym_t *ap;
235
236 if (strcmp(name, "name") == 0) {
237 setName(ud->nameMap, (Agobj_t *) np, value);
238 } else {
239 ap = agattr(root, AGNODE, name, 0);
240 if (!ap)
241 ap = agattr(root, AGNODE, name, defval);
242 if (is_html) {
243 char *val = agstrdup_html(root, value);
244 agxset(np, ap, val);
245 agstrfree(root, val); // drop the extra reference count we bumped for val
246 } else {
247 agxset(np, ap, value);
248 }
249 }
250}
251
252#define NODELBL "node:"
253#define NLBLLEN (sizeof(NODELBL)-1)
254#define EDGELBL "edge:"
255#define ELBLLEN (sizeof(EDGELBL)-1)
256
257/* setGlobalNodeAttr:
258 * Set global node attribute.
259 * The names must always begin with "node:".
260 */
261static void
262setGlobalNodeAttr(Agraph_t * g, char *name, char *value)
263{
264 if (!startswith(name, NODELBL))
265 fprintf(stderr,
266 "Warning: global node attribute %s in graph %s does not begin with the prefix %s\n",
267 name, agnameof(g), NODELBL);
268 else
269 name += NLBLLEN;
270 if ((g != root) && !agattr(root, AGNODE, name, 0))
271 agattr(root, AGNODE, name, defval);
272 agattr(G, AGNODE, name, value);
273}
274
275static void
276setEdgeAttr(Agedge_t * ep, char *name, char *value, userdata_t * ud,
277 bool is_html)
278{
279 Agsym_t *ap;
280 char *attrname;
281
282 if (strcmp(name, "headport") == 0) {
283 if (ud->edgeinverted)
284 attrname = "tailport";
285 else
286 attrname = "headport";
287 ap = agattr(root, AGEDGE, attrname, 0);
288 if (!ap)
289 ap = agattr(root, AGEDGE, attrname, defval);
290 } else if (strcmp(name, "tailport") == 0) {
291 if (ud->edgeinverted)
292 attrname = "headport";
293 else
294 attrname = "tailport";
295 ap = agattr(root, AGEDGE, attrname, 0);
296 if (!ap)
297 ap = agattr(root, AGEDGE, attrname, defval);
298 } else {
299 ap = agattr(root, AGEDGE, name, 0);
300 if (!ap)
301 ap = agattr(root, AGEDGE, name, defval);
302 }
303
304 if (is_html) {
305 char *val = agstrdup_html(root, value);
306 agxset(ep, ap, val);
307 agstrfree(root, val); // drop the extra reference count we bumped for val
308 } else {
309 agxset(ep, ap, value);
310 }
311}
312
313/* setGlobalEdgeAttr:
314 * Set global edge attribute.
315 * The names always begin with "edge:".
316 */
317static void
318setGlobalEdgeAttr(Agraph_t * g, char *name, char *value)
319{
320 if (!startswith(name, EDGELBL))
321 fprintf(stderr,
322 "Warning: global edge attribute %s in graph %s does not begin with the prefix %s\n",
323 name, agnameof(g), EDGELBL);
324 else
325 name += ELBLLEN;
326 if ((g != root) && !agattr(root, AGEDGE, name, 0))
327 agattr(root, AGEDGE, name, defval);
328 agattr(g, AGEDGE, name, value);
329}
330
331static void
332setGraphAttr(Agraph_t * g, char *name, char *value, userdata_t * ud)
333{
334 Agsym_t *ap;
335
336 if ((g == root) && !strcmp(name, "strict") && !strcmp(value, "true")) {
337 g->desc.strict = true;
338 } else if (strcmp(name, "name") == 0)
339 setName(ud->nameMap, (Agobj_t *) g, value);
340 else {
341 ap = agattr(root, AGRAPH, name, 0);
342 if (ap)
343 agxset(g, ap, value);
344 else if (g == root)
345 agattr(root, AGRAPH, name, value);
346 else {
347 ap = agattr(root, AGRAPH, name, defval);
348 agxset(g, ap, value);
349 }
350 }
351}
352
353static void setAttr(char *name, char *value, userdata_t * ud, bool is_html)
354{
355 switch (Current_class) {
356 case TAG_GRAPH:
357 setGraphAttr(G, name, value, ud);
358 break;
359 case TAG_NODE:
360 setNodeAttr(N, name, value, ud, is_html);
361 break;
362 case TAG_EDGE:
363 setEdgeAttr(E, name, value, ud, is_html);
364 break;
365 default:
366 break;
367 }
368}
369
370/*------------- expat handlers ----------------------------------*/
371
372static void
373startElementHandler(void *userData, const char *name, const char **atts)
374{
375 int pos;
376 userdata_t *ud = userData;
377 Agraph_t *g = NULL;
378
379 if (strcmp(name, "gxl") == 0) {
380 /* do nothing */
381 } else if (strcmp(name, "graph") == 0) {
382 const char *edgeMode = "";
383 char buf[NAMEBUF]; /* holds % + number */
384
385 Current_class = TAG_GRAPH;
386 if (ud->closedElementType == TAG_GRAPH) {
387 fprintf(stderr,
388 "Warning: Node contains more than one graph.\n");
389 }
390 pos = get_xml_attr("id", atts);
391 if (pos <= 0) {
392 fprintf(stderr, "Error: Graph has no ID attribute.\n");
393 graphviz_exit(EXIT_FAILURE);
394 }
395 const char *id = atts[pos];
396 pos = get_xml_attr("edgemode", atts);
397 if (pos > 0) {
398 edgeMode = atts[pos];
399 }
400
401 if (graph_stack_is_empty(&Gstack)) {
402 if (strcmp(edgeMode, "directed") == 0) {
403 g = agopen((char *) id, Agdirected, &AgDefaultDisc);
404 } else if (strcmp(edgeMode, "undirected") == 0) {
405 g = agopen((char *) id, Agundirected, &AgDefaultDisc);
406 } else {
407 fprintf(stderr,
408 "Warning: graph has no edgemode attribute");
409 fprintf(stderr, " - assume directed\n");
410 g = agopen((char *) id, Agdirected, &AgDefaultDisc);
411 }
412 push_subg(g);
413 } else {
414 Agraph_t *subg;
415 if (isAnonGraph(id)) {
416 static int anon_id = 1;
417 snprintf(buf, sizeof(buf), "%%%d", anon_id++);
418 id = buf;
419 }
420 subg = agsubg(G, (char *) id, 1);
421 push_subg(subg);
422 }
423
424 pos = get_xml_attr("role", atts);
425 if (pos > 0) {
426 setGraphAttr(G, GXL_ROLE, (char *) atts[pos], ud);
427 }
428
429 pos = get_xml_attr("hypergraph", atts);
430 if (pos > 0) {
431 setGraphAttr(G, GXL_HYPER, (char *) atts[pos], ud);
432 }
433
434 } else if (strcmp(name, "node") == 0) {
435 Current_class = TAG_NODE;
436 pos = get_xml_attr("id", atts);
437 if (pos > 0) {
438 const char *attrname;
439 attrname = atts[pos];
440
441 if (attrname != NULL && strcmp(attrname, "") != 0) {
442 bind_node(attrname);
443 }
444 }
445
446 } else if (strcmp(name, "edge") == 0) {
447 const char *head = "", *tail = "";
448 char *tname;
449 Agnode_t *t;
450
451 Current_class = TAG_EDGE;
452 pos = get_xml_attr("from", atts);
453 if (pos > 0)
454 tail = atts[pos];
455 pos = get_xml_attr("to", atts);
456 if (pos > 0)
457 head = atts[pos];
458
459 tname = mapLookup(ud->nameMap, tail);
460 if (tname)
461 tail = tname;
462
463 tname = mapLookup(ud->nameMap, head);
464 if (tname)
465 head = tname;
466
467 bind_edge(tail, head);
468
469 t = AGTAIL(E);
470 tname = agnameof(t);
471
472 if (strcmp(tname, tail) == 0) {
473 ud->edgeinverted = false;
474 } else if (strcmp(tname, head) == 0) {
475 ud->edgeinverted = true;
476 }
477
478 pos = get_xml_attr("fromorder", atts);
479 if (pos > 0) {
480 setEdgeAttr(E, GXL_FROM, (char *) atts[pos], ud, false);
481 }
482
483 pos = get_xml_attr("toorder", atts);
484 if (pos > 0) {
485 setEdgeAttr(E, GXL_TO, (char *) atts[pos], ud, false);
486 }
487
488 pos = get_xml_attr("id", atts);
489 if (pos > 0) {
490 setEdgeAttr(E, GXL_ID, (char *) atts[pos], ud, false);
491 }
492 } else if (strcmp(name, "attr") == 0) {
493 const char *attrname = atts[get_xml_attr("name", atts)];
494
495 agxbput(&ud->xml_attr_name, attrname);
496 pos = get_xml_attr("kind", atts);
497
498 if (pos > 0) {
499 if (strcmp("node", atts[pos]) == 0)
500 ud->globalAttrType = TAG_NODE;
501 else if (strcmp("edge", atts[pos]) == 0)
502 ud->globalAttrType = TAG_EDGE;
503 else if (strcmp("graph", atts[pos]) == 0)
504 ud->globalAttrType = TAG_GRAPH;
505 else if (strcmp("HTML-like string", atts[pos]) == 0)
506 ud->globalAttrType = TAG_HTML_LIKE_STRING;
507 } else {
508 ud->globalAttrType = TAG_NONE;
509 }
510
511 } else if (strcmp(name, "string") == 0
512 || strcmp(name, "bool") == 0
513 || strcmp(name, "int") == 0 || strcmp(name, "float") == 0) {
514
515 ud->listen = true;
516 if (ud->compositeReadState) {
517 agxbprint(&ud->composite_buffer, "<%s>", name);
518 }
519 } else if (strcmp(name, "rel") == 0 || strcmp(name, "relend") == 0) {
520 fprintf(stderr, "%s element is ignored by DOT\n", name);
521 } else if (strcmp(name, "type") == 0) {
522 pos = get_xml_attr("xlink:href", atts);
523 if (pos > 0) {
524 setAttr(GXL_TYPE, (char *) atts[pos], ud, false);
525 }
526 } else if (strcmp(name, "locator") == 0) {
527 pos = get_xml_attr("xlink:href", atts);
528 if (pos > 0) {
529 const char *href = atts[pos];
530 agxbprint(&ud->xml_attr_value, "%s%s", GXL_LOC, href);
531 }
532 } else if (strcmp(name, "seq") == 0
533 || strcmp(name, "set") == 0
534 || strcmp(name, "bag") == 0
535 || strcmp(name, "tup") == 0 || strcmp(name, "enum") == 0) {
536
537 ud->compositeReadState = true;
538 agxbprint(&ud->composite_buffer, "<%s>", name);
539 } else {
540 /* must be some extension */
541 fprintf(stderr,
542 "Unknown node %s; DOT does not support extensions.\n",
543 name);
544 }
545}
546
547static void endElementHandler(void *userData, const char *name)
548{
549 userdata_t *ud = userData;
550
551 if (strcmp(name, "graph") == 0) {
552 pop_subg();
553 ud->closedElementType = TAG_GRAPH;
554 } else if (strcmp(name, "node") == 0) {
555 Current_class = TAG_GRAPH;
556 N = 0;
557 ud->closedElementType = TAG_NODE;
558 } else if (strcmp(name, "edge") == 0) {
559 Current_class = TAG_GRAPH;
560 E = 0;
561 ud->closedElementType = TAG_EDGE;
562 ud->edgeinverted = false;
563 } else if (strcmp(name, "attr") == 0) {
564 agxbuf new_name = {0};
565 char *value;
566
567 ud->closedElementType = TAG_NONE;
568 if (ud->compositeReadState) {
569 agxbprint(&new_name, "%s%s", GXL_COMP, agxbuse(&ud->xml_attr_name));
570 value = agxbuse(&ud->composite_buffer);
571 agxbclear(&ud->xml_attr_value);
572 ud->compositeReadState = false;
573 } else {
574 agxbput(&new_name, agxbuse(&ud->xml_attr_name));
575 value = agxbuse(&ud->xml_attr_value);
576 }
577
578 switch (ud->globalAttrType) {
579 case TAG_NONE:
580 setAttr(agxbuse(&new_name), value, ud, false);
581 break;
582 case TAG_NODE:
583 setGlobalNodeAttr(G, agxbuse(&new_name), value);
584 break;
585 case TAG_EDGE:
586 setGlobalEdgeAttr(G, agxbuse(&new_name), value);
587 break;
588 case TAG_GRAPH:
589 setGraphAttr(G, agxbuse(&new_name), value, ud);
590 break;
591 case TAG_HTML_LIKE_STRING:
592 setAttr(agxbuse(&new_name), value, ud, true);
593 break;
594 default:
595 UNREACHABLE();
596 }
597 agxbfree(&new_name);
598 ud->globalAttrType = TAG_NONE;
599 } else if (strcmp(name, "string") == 0
600 || strcmp(name, "bool") == 0
601 || strcmp(name, "int") == 0 || strcmp(name, "float") == 0) {
602 ud->listen = false;
603 if (ud->compositeReadState) {
604 agxbprint(&ud->composite_buffer, "</%s>", name);
605 }
606 } else if (strcmp(name, "seq") == 0
607 || strcmp(name, "set") == 0
608 || strcmp(name, "bag") == 0
609 || strcmp(name, "tup") == 0 || strcmp(name, "enum") == 0) {
610 agxbprint(&ud->composite_buffer, "</%s>", name);
611 }
612}
613
614static void characterDataHandler(void *userData, const char *s, int length)
615{
616 userdata_t *ud = userData;
617
618 assert(length >= 0 && "Expat returned negative length data");
619 size_t len = (size_t)length;
620
621 if (!ud->listen)
622 return;
623
624 if (ud->compositeReadState) {
625 agxbput_n(&ud->composite_buffer, s, len);
626 return;
627 }
628
629 agxbput_n(&ud->xml_attr_value, s, len);
630}
631
632Agraph_t *gxl_to_gv(FILE * gxlFile)
633{
634 char buf[BUFSIZ];
635 int done;
636 userdata_t udata = genUserdata();
637 XML_Parser parser = XML_ParserCreate(NULL);
638
639 XML_SetUserData(parser, &udata);
640 XML_SetElementHandler(parser, startElementHandler, endElementHandler);
641 XML_SetCharacterDataHandler(parser, characterDataHandler);
642
643 Current_class = TAG_GRAPH;
644 root = 0;
645 do {
646 size_t len = fread(buf, 1, sizeof(buf), gxlFile);
647 if (len == 0)
648 break;
649 done = len < sizeof(buf);
650 assert(len <= (size_t)INT_MAX && "too large data for Expat API");
651 if (XML_Parse(parser, buf, (int)len, done) == XML_STATUS_ERROR) {
652 fprintf(stderr,
653 "%s at line %lu\n",
654 XML_ErrorString(XML_GetErrorCode(parser)),
655 XML_GetCurrentLineNumber(parser));
656 graphviz_exit(1);
657 }
658 } while (!done);
659 XML_ParserFree(parser);
660 freeUserdata(udata);
661 graph_stack_free(&Gstack);
662
663 return root;
664}
665
666#endif
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:78
static size_t agxbput_n(agxbuf *xb, const char *s, size_t ssz)
append string s of length ssz into xb
Definition agxbuf.h:250
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:234
static void agxbclear(agxbuf *xb)
resets pointer to data
Definition agxbuf.h:294
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:307
Memory allocation wrappers that exit on failure.
static char * gv_strdup(const char *original)
Definition alloc.h:101
#define N(n)
Definition bcomps.c:58
#define dtmatch(d, o)
Definition cdt.h:184
#define dtinsert(d, o)
Definition cdt.h:185
CDT_API int dtclose(Dt_t *)
Definition dtclose.c:8
CDT_API Dtmethod_t * Dtoset
ordered set (self-adjusting tree)
Definition dttree.c:304
CDT_API Dt_t * dtopen(Dtdisc_t *, Dtmethod_t *)
Definition dtopen.c:9
int agrename(Agobj_t *obj, char *newname)
Definition obj.c:41
Agsym_t * setAttr(graph_t *g, void *obj, char *name, char *value, Agsym_t *ap)
Definition utils.c:699
DOT-GXL converter API for gxl2gv.c and gv2gxl.c.
#define head
Definition dthdr.h:15
static NORETURN void graphviz_exit(int status)
Definition exit.h:23
static Dtdisc_t disc
Definition exparse.y:209
#define E
Definition gdefs.h:6
#define G
Definition gdefs.h:7
static double len(glCompPoint p)
Definition glutils.c:150
void * malloc(YYSIZE_T)
void free(void *)
node NULL
Definition grammar.y:163
Agsym_t * agattr(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up attributes of a graph
Definition attr.c:338
int agxset(void *obj, Agsym_t *sym, const char *value)
Definition attr.c:478
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:455
Agdisc_t AgDefaultDisc
Definition graph.c:289
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:260
#define AGTAIL(e)
Definition cgraph.h:876
Agdesc_t Agundirected
undirected
Definition graph.c:286
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
Agdesc_t Agdirected
directed
Definition graph.c:284
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:145
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:158
#define AGTYPE(obj)
returns AGRAPH, AGNODE, or AGEDGE depending on the type of the object
Definition cgraph.h:216
@ AGEDGE
Definition cgraph.h:207
@ AGNODE
Definition cgraph.h:207
@ AGRAPH
Definition cgraph.h:207
int agstrfree(Agraph_t *, const char *)
Definition refstr.c:139
char * agstrdup_html(Agraph_t *, const char *)
Definition refstr.c:135
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
Definition subg.c:58
#define GXL_FROM
Definition gv2gxl.c:34
static Dtdisc_t nameDisc
Definition gv2gxl.c:66
static char * mapLookup(Dt_t *nm, char *name)
Definition gv2gxl.c:202
static void * make_nitem(void *p, Dtdisc_t *disc)
Definition gv2gxl.c:56
#define GXL_COMP
Definition gv2gxl.c:37
#define GXL_LOC
Definition gv2gxl.c:38
#define GXL_ID
Definition gv2gxl.c:33
#define GXL_TYPE
Definition gv2gxl.c:36
static void addToMap(Dt_t *map, char *name, char *uniqueName)
Definition gv2gxl.c:243
#define GXL_TO
Definition gv2gxl.c:35
#define GXL_HYPER
Definition gv2gxl.c:32
#define GXL_ROLE
Definition gv2gxl.c:31
replacements for ctype.h functions
static bool gv_isdigit(int c)
Definition gv_ctype.h:41
agxbput(xb, staging)
#define XML_STATUS_ERROR
Definition htmllex.c:41
#define DEFINE_LIST(name, type)
Definition list.h:26
static bool startswith(const char *s, const char *prefix)
does the string s begin with the string prefix?
Definition startswith.h:11
unsigned strict
Definition cgraph.h:286
a generic header of Agraph_s, Agnode_s and Agedge_s
Definition cgraph.h:210
graph or subgraph
Definition cgraph.h:425
Agdesc_t desc
Definition cgraph.h:427
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:637
Definition cdt.h:100
int key
Definition cdt.h:85
char * unique_name
Definition gv2gxl.c:53
char * name
Definition gv2gxl.c:52
Definition grammar.c:93
#define UNREACHABLE()
Definition unreachable.h:30