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