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