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