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_"
65 bool compositeReadState;
71static attr_t Current_class;
96static void free_nitem(
void *name) {
105 .link = offsetof(
namev_t, link),
110static userdata_t genUserdata(
void) {
111 userdata_t user = {0};
113 user.closedElementType = TAG_NONE;
114 user.globalAttrType = TAG_NONE;
115 user.compositeReadState =
false;
116 user.edgeinverted =
false;
121static void freeUserdata(userdata_t ud) {
128static void addToMap(
Dt_t *map,
char *name,
const char *uniqueName) {
146static int isAnonGraph(
const char *name) {
151 return (*name ==
'\0');
170 fprintf(stderr,
"gxl2gv: Gstack underflow in graph parser\n");
184static Agnode_t *bind_node(
const char *name) {
189static Agedge_t *bind_edge(
const char *tail,
const char *
head) {
193 tailNode =
agnode(
G, (
char *)tail, 1);
195 E =
agedge(
G, tailNode, headNode, key, 1);
199static int get_xml_attr(
char *attrname,
const char **atts) {
201 while (atts[count] !=
NULL) {
202 if (
streq(attrname, atts[count])) {
210static void setName(
Dt_t *names,
Agobj_t *n,
char *value) {
213 char *
const oldName =
agxget(n, ap);
218static char *defval =
"";
220static void setNodeAttr(
Agnode_t *np,
char *name,
char *value, userdata_t *ud,
222 if (
streq(name,
"name")) {
223 setName(ud->nameMap, &np->
base, value);
236#define NODELBL "node:"
237#define NLBLLEN (sizeof(NODELBL) - 1)
238#define EDGELBL "edge:"
239#define ELBLLEN (sizeof(EDGELBL) - 1)
245static void setGlobalNodeAttr(
Agraph_t *g,
char *name,
char *value) {
248 "Warning: global node attribute %s in graph %s does not begin with "
258static void setEdgeAttr(
Agedge_t *ep,
char *name,
const char *value,
259 userdata_t *ud,
bool is_html) {
263 if (
streq(name,
"headport")) {
264 if (ud->edgeinverted)
265 attrname =
"tailport";
267 attrname =
"headport";
271 }
else if (
streq(name,
"tailport")) {
272 if (ud->edgeinverted)
273 attrname =
"headport";
275 attrname =
"tailport";
296static void setGlobalEdgeAttr(
Agraph_t *g,
char *name,
char *value) {
299 "Warning: global edge attribute %s in graph %s does not begin with "
309static void setGraphAttr(
Agraph_t *g,
char *name,
char *value, userdata_t *ud) {
312 if ((g == root) &&
streq(name,
"strict") &&
streq(value,
"true")) {
314 }
else if (
streq(name,
"name"))
315 setName(ud->nameMap, &g->
base, value);
329static void setAttr(
char *name,
char *value, userdata_t *ud,
bool is_html) {
330 switch (Current_class) {
332 setGraphAttr(
G, name, value, ud);
335 setNodeAttr(
N, name, value, ud, is_html);
338 setEdgeAttr(
E, name, value, ud, is_html);
347static void startElementHandler(
void *userData,
const char *name,
350 userdata_t *ud = userData;
353 if (
streq(name,
"gxl")) {
355 }
else if (
streq(name,
"graph")) {
356 const char *edgeMode =
"";
359 Current_class = TAG_GRAPH;
360 if (ud->closedElementType == TAG_GRAPH) {
361 fprintf(stderr,
"Warning: Node contains more than one graph.\n");
363 pos = get_xml_attr(
"id", atts);
365 fprintf(stderr,
"Error: Graph has no ID attribute.\n");
368 const char *
id = atts[pos];
369 pos = get_xml_attr(
"edgemode", atts);
371 edgeMode = atts[pos];
375 if (
streq(edgeMode,
"directed")) {
377 }
else if (
streq(edgeMode,
"undirected")) {
380 fprintf(stderr,
"Warning: graph has no edgemode attribute");
381 fprintf(stderr,
" - assume directed\n");
387 if (isAnonGraph(
id)) {
388 static int anon_id = 1;
389 snprintf(buf,
sizeof(buf),
"%%%d", anon_id++);
392 subg =
agsubg(
G, (
char *)
id, 1);
396 pos = get_xml_attr(
"role", atts);
398 setGraphAttr(
G,
GXL_ROLE, (
char *)atts[pos], ud);
401 pos = get_xml_attr(
"hypergraph", atts);
403 setGraphAttr(
G,
GXL_HYPER, (
char *)atts[pos], ud);
406 }
else if (
streq(name,
"node")) {
407 Current_class = TAG_NODE;
408 pos = get_xml_attr(
"id", atts);
410 const char *attrname;
411 attrname = atts[pos];
413 if (attrname !=
NULL && !
streq(attrname,
"")) {
418 }
else if (
streq(name,
"edge")) {
419 const char *
head =
"", *tail =
"";
423 Current_class = TAG_EDGE;
424 pos = get_xml_attr(
"from", atts);
427 pos = get_xml_attr(
"to", atts);
439 bind_edge(tail,
head);
444 if (
streq(tname, tail)) {
445 ud->edgeinverted =
false;
447 ud->edgeinverted =
true;
450 pos = get_xml_attr(
"fromorder", atts);
452 setEdgeAttr(
E,
GXL_FROM, atts[pos], ud,
false);
455 pos = get_xml_attr(
"toorder", atts);
457 setEdgeAttr(
E,
GXL_TO, atts[pos], ud,
false);
460 pos = get_xml_attr(
"id", atts);
462 setEdgeAttr(
E,
GXL_ID, atts[pos], ud,
false);
464 }
else if (
streq(name,
"attr")) {
465 const char *attrname = atts[get_xml_attr(
"name", atts)];
467 agxbput(&ud->xml_attr_name, attrname);
468 pos = get_xml_attr(
"kind", atts);
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;
480 ud->globalAttrType = TAG_NONE;
483 }
else if (
streq(name,
"string") ||
streq(name,
"bool") ||
487 if (ud->compositeReadState) {
488 agxbprint(&ud->composite_buffer,
"<%s>", name);
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);
497 }
else if (
streq(name,
"locator")) {
498 pos = get_xml_attr(
"xlink:href", atts);
500 const char *href = atts[pos];
503 }
else if (
streq(name,
"seq") ||
streq(name,
"set") ||
streq(name,
"bag") ||
506 ud->compositeReadState =
true;
507 agxbprint(&ud->composite_buffer,
"<%s>", name);
510 fprintf(stderr,
"Unknown node %s; DOT does not support extensions.\n",
515static void endElementHandler(
void *userData,
const char *name) {
516 userdata_t *ud = userData;
518 if (
streq(name,
"graph")) {
520 ud->closedElementType = TAG_GRAPH;
521 }
else if (
streq(name,
"node")) {
522 Current_class = TAG_GRAPH;
524 ud->closedElementType = TAG_NODE;
525 }
else if (
streq(name,
"edge")) {
526 Current_class = TAG_GRAPH;
528 ud->closedElementType = TAG_EDGE;
529 ud->edgeinverted =
false;
530 }
else if (
streq(name,
"attr")) {
534 ud->closedElementType = TAG_NONE;
535 if (ud->compositeReadState) {
537 value =
agxbuse(&ud->composite_buffer);
539 ud->compositeReadState =
false;
542 value =
agxbuse(&ud->xml_attr_value);
545 switch (ud->globalAttrType) {
550 setGlobalNodeAttr(
G,
agxbuse(&new_name), value);
553 setGlobalEdgeAttr(
G,
agxbuse(&new_name), value);
556 setGraphAttr(
G,
agxbuse(&new_name), value, ud);
558 case TAG_HTML_LIKE_STRING:
565 ud->globalAttrType = TAG_NONE;
566 }
else if (
streq(name,
"string") ||
streq(name,
"bool") ||
569 if (ud->compositeReadState) {
570 agxbprint(&ud->composite_buffer,
"</%s>", name);
572 }
else if (
streq(name,
"seq") ||
streq(name,
"set") ||
streq(name,
"bag") ||
574 agxbprint(&ud->composite_buffer,
"</%s>", name);
578static void characterDataHandler(
void *userData,
const char *
s,
int length) {
579 userdata_t *ud = userData;
581 assert(
length >= 0 &&
"Expat returned negative length data");
587 if (ud->compositeReadState) {
595static uint64_t expat_get_lineno(XML_Parser parser) {
598#if XML_MAJOR_VERSION > 2 || (XML_MAJOR_VERSION == 2 && XML_MINOR_VERSION >= 9)
599 return XML_GetCurrentLineNumber64(parser);
601 return XML_GetCurrentLineNumber(parser);
608 userdata_t udata = genUserdata();
609 XML_Parser parser = XML_ParserCreate(
NULL);
611 XML_SetUserData(parser, &udata);
612 XML_SetElementHandler(parser, startElementHandler, endElementHandler);
613 XML_SetCharacterDataHandler(parser, characterDataHandler);
615 Current_class = TAG_GRAPH;
618 size_t len = fread(buf, 1,
sizeof(buf), gxlFile);
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));
630 XML_ParserFree(parser);
Dynamically expanding string buffers.
static void agxbfree(agxbuf *xb)
free any malloced resources
static size_t agxbput_n(agxbuf *xb, const char *s, size_t ssz)
append string s of length ssz into xb
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
static void agxbclear(agxbuf *xb)
resets pointer to data
static WUR char * agxbuse(agxbuf *xb)
Memory allocation wrappers that exit on failure.
static char * gv_strdup(const char *original)
CDT_API int dtclose(Dt_t *)
CDT_API Dtmethod_t * Dtoset
ordered set (self-adjusting tree)
CDT_API Dt_t * dtopen(Dtdisc_t *, Dtmethod_t *)
int agrename(Agobj_t *obj, char *newname)
Agsym_t * setAttr(graph_t *g, void *obj, char *name, char *value, Agsym_t *ap)
static Extype_t length(Exid_t *rhs, Exdisc_t *disc)
DOT-GXL converter API for gxl2gv.c and gv2gxl.c.
static NORETURN void graphviz_exit(int status)
static double len(glCompPoint p)
Agsym_t * agattr_text(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up text attributes of a graph
int agxset(void *obj, Agsym_t *sym, const char *value)
int agxset_html(void *obj, Agsym_t *sym, const char *value)
char * agxget(void *obj, Agsym_t *sym)
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Agdesc_t Agundirected
undirected
Agraph_t * agopen(char *name, Agdesc_t desc, Agdisc_t *disc)
creates a new graph with the given name and kind
Agdesc_t Agdirected
directed
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
char * agnameof(void *)
returns a string descriptor for the object.
#define AGTYPE(obj)
returns AGRAPH, AGNODE, or AGEDGE depending on the type of the object
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
static char * mapLookup(Dt_t *nm, char *name)
static void * make_nitem(void *p, Dtdisc_t *disc)
static void addToMap(Dt_t *map, char *name, char *uniqueName)
replacements for ctype.h functions
static bool gv_isdigit(int c)
type-generic dynamically expanding list
#define LIST_PUSH_BACK(list,...)
#define LIST_POP_BACK(list)
#define LIST_IS_EMPTY(list)
static bool startswith(const char *s, const char *prefix)
does the string s begin with the string prefix?
static bool streq(const char *a, const char *b)
are a and b equal?
a generic header of Agraph_s, Agnode_s and Agedge_s