32#ifndef XML_STATUS_ERROR
33#define XML_STATUS_ERROR 0
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_"
63 bool compositeReadState;
69static attr_t Current_class;
74static gv_stack_t Gstack;
93static void free_nitem(
void *name) {
102 .link = offsetof(
namev_t, link),
107static userdata_t genUserdata(
void) {
108 userdata_t user = {0};
110 user.closedElementType = TAG_NONE;
111 user.globalAttrType = TAG_NONE;
112 user.compositeReadState =
false;
113 user.edgeinverted =
false;
118static void freeUserdata(userdata_t ud) {
125static void addToMap(
Dt_t * map,
char *name,
char *uniqueName)
144static int isAnonGraph(
const char *name)
150 return (*name ==
'\0');
171 fprintf(stderr,
"gxl2gv: Gstack underflow in graph parser\n");
185static Agnode_t *bind_node(
const char *name)
191static Agedge_t *bind_edge(
const char *tail,
const char *
head)
196 tailNode =
agnode(
G, (
char *) tail, 1);
202static int get_xml_attr(
char *attrname,
const char **atts)
205 while (atts[count] !=
NULL) {
206 if (strcmp(attrname, atts[count]) == 0) {
214static void setName(
Dt_t * names,
Agobj_t * n,
char *value)
226static char *defval =
"";
229setNodeAttr(
Agnode_t * np,
char *name,
char *value, userdata_t * ud,
234 if (strcmp(name,
"name") == 0) {
235 setName(ud->nameMap, (
Agobj_t *) np, value);
250#define NODELBL "node:"
251#define NLBLLEN (sizeof(NODELBL)-1)
252#define EDGELBL "edge:"
253#define ELBLLEN (sizeof(EDGELBL)-1)
260setGlobalNodeAttr(
Agraph_t * g,
char *name,
char *value)
264 "Warning: global node attribute %s in graph %s does not begin with the prefix %s\n",
274setEdgeAttr(
Agedge_t * ep,
char *name,
char *value, userdata_t * ud,
280 if (strcmp(name,
"headport") == 0) {
281 if (ud->edgeinverted)
282 attrname =
"tailport";
284 attrname =
"headport";
288 }
else if (strcmp(name,
"tailport") == 0) {
289 if (ud->edgeinverted)
290 attrname =
"headport";
292 attrname =
"tailport";
316setGlobalEdgeAttr(
Agraph_t * g,
char *name,
char *value)
320 "Warning: global edge attribute %s in graph %s does not begin with the prefix %s\n",
330setGraphAttr(
Agraph_t * g,
char *name,
char *value, userdata_t * ud)
334 if ((g == root) && !strcmp(name,
"strict") && !strcmp(value,
"true")) {
336 }
else if (strcmp(name,
"name") == 0)
337 setName(ud->nameMap, (
Agobj_t *) g, value);
351static void setAttr(
char *name,
char *value, userdata_t * ud,
bool is_html)
353 switch (Current_class) {
355 setGraphAttr(
G, name, value, ud);
358 setNodeAttr(
N, name, value, ud, is_html);
361 setEdgeAttr(
E, name, value, ud, is_html);
371startElementHandler(
void *userData,
const char *name,
const char **atts)
374 userdata_t *ud = userData;
377 if (strcmp(name,
"gxl") == 0) {
379 }
else if (strcmp(name,
"graph") == 0) {
380 const char *edgeMode =
"";
383 Current_class = TAG_GRAPH;
384 if (ud->closedElementType == TAG_GRAPH) {
386 "Warning: Node contains more than one graph.\n");
388 pos = get_xml_attr(
"id", atts);
390 fprintf(stderr,
"Error: Graph has no ID attribute.\n");
393 const char *
id = atts[pos];
394 pos = get_xml_attr(
"edgemode", atts);
396 edgeMode = atts[pos];
400 if (strcmp(edgeMode,
"directed") == 0) {
402 }
else if (strcmp(edgeMode,
"undirected") == 0) {
406 "Warning: graph has no edgemode attribute");
407 fprintf(stderr,
" - assume directed\n");
413 if (isAnonGraph(
id)) {
414 static int anon_id = 1;
415 snprintf(buf,
sizeof(buf),
"%%%d", anon_id++);
418 subg =
agsubg(
G, (
char *)
id, 1);
422 pos = get_xml_attr(
"role", atts);
424 setGraphAttr(
G,
GXL_ROLE, (
char *) atts[pos], ud);
427 pos = get_xml_attr(
"hypergraph", atts);
429 setGraphAttr(
G,
GXL_HYPER, (
char *) atts[pos], ud);
432 }
else if (strcmp(name,
"node") == 0) {
434 pos = get_xml_attr(
"id", atts);
436 const char *attrname;
437 attrname = atts[pos];
439 if (attrname !=
NULL && strcmp(attrname,
"") != 0) {
444 }
else if (strcmp(name,
"edge") == 0) {
445 const char *
head =
"", *tail =
"";
449 Current_class = TAG_EDGE;
450 pos = get_xml_attr(
"from", atts);
453 pos = get_xml_attr(
"to", atts);
465 bind_edge(tail,
head);
470 if (strcmp(tname, tail) == 0) {
471 ud->edgeinverted =
false;
472 }
else if (strcmp(tname,
head) == 0) {
473 ud->edgeinverted =
true;
476 pos = get_xml_attr(
"fromorder", atts);
478 setEdgeAttr(
E,
GXL_FROM, (
char *) atts[pos], ud,
false);
481 pos = get_xml_attr(
"toorder", atts);
483 setEdgeAttr(
E,
GXL_TO, (
char *) atts[pos], ud,
false);
486 pos = get_xml_attr(
"id", atts);
488 setEdgeAttr(
E,
GXL_ID, (
char *) atts[pos], ud,
false);
490 }
else if (strcmp(name,
"attr") == 0) {
491 const char *attrname = atts[get_xml_attr(
"name", atts)];
493 agxbput(&ud->xml_attr_name, attrname);
494 pos = get_xml_attr(
"kind", atts);
497 if (strcmp(
"node", atts[pos]) == 0)
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;
506 ud->globalAttrType = TAG_NONE;
509 }
else if (strcmp(name,
"string") == 0
510 || strcmp(name,
"bool") == 0
511 || strcmp(name,
"int") == 0 || strcmp(name,
"float") == 0) {
514 if (ud->compositeReadState) {
515 agxbprint(&ud->composite_buffer,
"<%s>", name);
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);
524 }
else if (strcmp(name,
"locator") == 0) {
525 pos = get_xml_attr(
"xlink:href", atts);
527 const char *href = atts[pos];
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) {
535 ud->compositeReadState =
true;
536 agxbprint(&ud->composite_buffer,
"<%s>", name);
540 "Unknown node %s; DOT does not support extensions.\n",
545static void endElementHandler(
void *userData,
const char *name)
547 userdata_t *ud = userData;
549 if (strcmp(name,
"graph") == 0) {
551 ud->closedElementType = TAG_GRAPH;
552 }
else if (strcmp(name,
"node") == 0) {
553 Current_class = TAG_GRAPH;
556 }
else if (strcmp(name,
"edge") == 0) {
557 Current_class = TAG_GRAPH;
559 ud->closedElementType = TAG_EDGE;
560 ud->edgeinverted =
false;
561 }
else if (strcmp(name,
"attr") == 0) {
565 ud->closedElementType = TAG_NONE;
566 if (ud->compositeReadState) {
568 value =
agxbuse(&ud->composite_buffer);
570 ud->compositeReadState =
false;
573 value =
agxbuse(&ud->xml_attr_value);
576 switch (ud->globalAttrType) {
581 setGlobalNodeAttr(
G,
agxbuse(&new_name), value);
584 setGlobalEdgeAttr(
G,
agxbuse(&new_name), value);
587 setGraphAttr(
G,
agxbuse(&new_name), value, ud);
589 case TAG_HTML_LIKE_STRING:
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) {
601 if (ud->compositeReadState) {
602 agxbprint(&ud->composite_buffer,
"</%s>", name);
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);
612static void characterDataHandler(
void *userData,
const char *
s,
int length)
614 userdata_t *ud = userData;
616 assert(length >= 0 &&
"Expat returned negative length data");
617 size_t len = (size_t)length;
622 if (ud->compositeReadState) {
634 userdata_t udata = genUserdata();
635 XML_Parser parser = XML_ParserCreate(
NULL);
637 XML_SetUserData(parser, &udata);
638 XML_SetElementHandler(parser, startElementHandler, endElementHandler);
639 XML_SetCharacterDataHandler(parser, characterDataHandler);
641 Current_class = TAG_GRAPH;
644 size_t len = fread(buf, 1,
sizeof(buf), gxlFile);
647 done =
len <
sizeof(buf);
648 assert(
len <= (
size_t)INT_MAX &&
"too large data for Expat API");
652 XML_ErrorString(XML_GetErrorCode(parser)),
653 XML_GetCurrentLineNumber(parser));
657 XML_ParserFree(parser);
static void agxbfree(agxbuf *xb)
free any malloced resources
static size_t agxbput(agxbuf *xb, const char *s)
append string s into xb
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 char * agxbuse(agxbuf *xb)
Memory allocation wrappers that exit on failure.
static char * gv_strdup(const char *original)
void *(* Dtmake_f)(void *, Dtdisc_t *)
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)
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(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up attributes of a graph
int agxset(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
int agstrfree(Agraph_t *, const char *)
char * agstrdup_html(Agraph_t *, const char *)
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
static char * mapLookup(Dt_t *nm, char *name)
static namev_t * make_nitem(namev_t *objp, Dtdisc_t *disc)
static void addToMap(Dt_t *map, char *name, char *uniqueName)
replacements for ctype.h functions
static bool gv_isdigit(int c)
static void freef(void *ident)
Implementation of a dynamically expanding stack data structure.
static void stack_push(gv_stack_t *stack, void *item)
static size_t stack_size(const gv_stack_t *stack)
static void * stack_pop(gv_stack_t *stack)
static void * stack_top(gv_stack_t *stack)
static void stack_reset(gv_stack_t *stack)
static bool stack_is_empty(const gv_stack_t *stack)
static bool startswith(const char *s, const char *prefix)
does the string s begin with the string prefix?
a generic header of Agraph_s, Agnode_s and Agedge_s