Graphviz 12.0.1~dev.20240716.0800
Loading...
Searching...
No Matches
graphml2gv.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
17#include "convert.h"
18#include <assert.h>
19#include <cgraph/agxbuf.h>
20#include <cgraph/alloc.h>
21#include <cgraph/exit.h>
22#include <cgraph/gv_ctype.h>
23#include <cgraph/stack.h>
24#include <cgraph/unreachable.h>
25#include <getopt.h>
26#include <limits.h>
27#include <stdbool.h>
28#include <stdio.h>
29#include <string.h>
30#include "openFile.h"
31#ifdef HAVE_EXPAT
32#include <expat.h>
33
34#ifndef XML_STATUS_ERROR
35#define XML_STATUS_ERROR 0
36#endif
37
38#define NAMEBUF 100
39
40#define GRAPHML_ID "_graphml_id"
41
42#define TAG_NONE -1
43#define TAG_GRAPH 0
44#define TAG_NODE 1
45#define TAG_EDGE 2
46
47static FILE *outFile;
48static char *CmdName;
49static char **Files;
50static int Verbose;
51static char* gname = "";
52
53static void pushString(gv_stack_t *stk, const char *s) {
54
55 // duplicate the string we will push
56 char *copy = gv_strdup(s);
57
58 // push this onto the stack
59 stack_push(stk, copy);
60}
61
62static void popString(gv_stack_t *stk) {
63
64 if (stack_is_empty(stk)) {
65 fprintf(stderr, "PANIC: graphml2gv: empty element stack\n");
66 graphviz_exit(EXIT_FAILURE);
67 }
68
69 char *s = stack_pop(stk);
70 free(s);
71}
72
73static char *topString(gv_stack_t *stk) {
74
75 if (stack_is_empty(stk)) {
76 fprintf(stderr, "PANIC: graphml2gv: empty element stack\n");
77 graphviz_exit(EXIT_FAILURE);
78 }
79
80 return stack_top(stk);
81}
82
83static void freeString(gv_stack_t *stk) {
84 while (!stack_is_empty(stk)) {
85 char *s = stack_pop(stk);
86 free(s);
87 }
88 stack_reset(stk);
89}
90
91typedef struct {
92 char* gname;
93 gv_stack_t elements;
94 int closedElementType;
95 bool edgeinverted;
96} userdata_t;
97
98static Agraph_t *root; /* root graph */
99static Agraph_t *G; /* Current graph */
100static Agedge_t *E; // current edge
101
102static gv_stack_t Gstack;
103
104static userdata_t genUserdata(char *dfltname) {
105 userdata_t user = {0};
106 user.elements = (gv_stack_t){0};
107 user.closedElementType = TAG_NONE;
108 user.edgeinverted = false;
109 user.gname = dfltname;
110 return user;
111}
112
113static void freeUserdata(userdata_t ud) {
114 freeString(&ud.elements);
115}
116
117static int isAnonGraph(const char *name) {
118 if (*name++ != '%')
119 return 0;
120 while (gv_isdigit(*name))
121 name++; /* skip over digits */
122 return (*name == '\0');
123}
124
125static void push_subg(Agraph_t * g)
126{
127 // save the root if this is the first graph
128 if (stack_is_empty(&Gstack)) {
129 root = g;
130 }
131
132 // insert the new graph
133 stack_push(&Gstack, g);
134
135 // update the top graph
136 G = g;
137}
138
139static Agraph_t *pop_subg(void)
140{
141 if (stack_is_empty(&Gstack)) {
142 fprintf(stderr, "graphml2gv: Gstack underflow in graph parser\n");
143 graphviz_exit(EXIT_FAILURE);
144 }
145
146 // pop the top graph
147 Agraph_t *g = stack_pop(&Gstack);
148
149 // update the top graph
150 if (!stack_is_empty(&Gstack)) {
151 G = stack_top(&Gstack);
152 }
153
154 return g;
155}
156
157static Agnode_t *bind_node(const char *name)
158{
159 return agnode(G, (char *)name, 1);
160}
161
162static Agedge_t *bind_edge(const char *tail, const char *head)
163{
164 Agnode_t *tailNode, *headNode;
165 char *key = 0;
166
167 tailNode = agnode(G, (char *) tail, 1);
168 headNode = agnode(G, (char *) head, 1);
169 E = agedge(G, tailNode, headNode, key, 1);
170 return E;
171}
172
173static int get_xml_attr(char *attrname, const char **atts)
174{
175 int count = 0;
176 while (atts[count] != NULL) {
177 if (strcmp(attrname, atts[count]) == 0) {
178 return count + 1;
179 }
180 count += 2;
181 }
182 return -1;
183}
184
185static char *defval = "";
186
187static void setEdgeAttr(Agedge_t *ep, char *name, const char *value,
188 userdata_t *ud) {
189 Agsym_t *ap;
190 char *attrname;
191
192 if (strcmp(name, "headport") == 0) {
193 if (ud->edgeinverted)
194 attrname = "tailport";
195 else
196 attrname = "headport";
197 ap = agattr(root, AGEDGE, attrname, 0);
198 if (!ap)
199 ap = agattr(root, AGEDGE, attrname, defval);
200 agxset(ep, ap, value);
201 } else if (strcmp(name, "tailport") == 0) {
202 if (ud->edgeinverted)
203 attrname = "headport";
204 else
205 attrname = "tailport";
206 ap = agattr(root, AGEDGE, attrname, 0);
207 if (!ap)
208 ap = agattr(root, AGEDGE, attrname, defval);
209 agxset(ep, ap, value);
210 } else {
211 ap = agattr(root, AGEDGE, name, 0);
212 if (!ap)
213 ap = agattr(root, AGEDGE, name, defval);
214 agxset(ep, ap, value);
215 }
216}
217
218/*------------- expat handlers ----------------------------------*/
219
220static void
221startElementHandler(void *userData, const char *name, const char **atts)
222{
223 int pos;
224 userdata_t *ud = userData;
225 Agraph_t *g = NULL;
226
227 if (strcmp(name, "graphml") == 0) {
228 /* do nothing */
229 } else if (strcmp(name, "graph") == 0) {
230 const char *edgeMode = "";
231 const char *id;
232 Agdesc_t dir;
233 char buf[NAMEBUF]; /* holds % + number */
234
235 if (ud->closedElementType == TAG_GRAPH) {
236 fprintf(stderr,
237 "Warning: Node contains more than one graph.\n");
238 }
239 pos = get_xml_attr("id", atts);
240 if (pos > 0) {
241 id = atts[pos];
242 }
243 else
244 id = ud->gname;
245 pos = get_xml_attr("edgedefault", atts);
246 if (pos > 0) {
247 edgeMode = atts[pos];
248 }
249
250 if (stack_is_empty(&Gstack)) {
251 if (strcmp(edgeMode, "directed") == 0) {
252 dir = Agdirected;
253 } else if (strcmp(edgeMode, "undirected") == 0) {
254 dir = Agundirected;
255 } else {
256 if (Verbose) {
257 fprintf(stderr,
258 "Warning: graph has no edgedefault attribute - assume directed\n");
259 }
260 dir = Agdirected;
261 }
262 g = agopen((char *) id, dir, &AgDefaultDisc);
263 push_subg(g);
264 } else {
265 Agraph_t *subg;
266 if (isAnonGraph(id)) {
267 static int anon_id = 1;
268 snprintf(buf, sizeof(buf), "%%%d", anon_id++);
269 id = buf;
270 }
271 subg = agsubg(G, (char *) id, 1);
272 push_subg(subg);
273 }
274
275 pushString(&ud->elements, id);
276 } else if (strcmp(name, "node") == 0) {
277 pos = get_xml_attr("id", atts);
278 if (pos > 0) {
279 const char *attrname;
280 attrname = atts[pos];
281 if (G == 0)
282 fprintf(stderr,"node %s outside graph, ignored\n",attrname);
283 else
284 bind_node(attrname);
285
286 pushString(&ud->elements, attrname);
287 }
288
289 } else if (strcmp(name, "edge") == 0) {
290 const char *head = "", *tail = "";
291 char *tname;
292 Agnode_t *t;
293
294 pos = get_xml_attr("source", atts);
295 if (pos > 0)
296 tail = atts[pos];
297 pos = get_xml_attr("target", atts);
298 if (pos > 0)
299 head = atts[pos];
300
301 if (G == 0)
302 fprintf(stderr,"edge source %s target %s outside graph, ignored\n",tail,head);
303 else {
304 bind_edge(tail, head);
305
306 t = AGTAIL(E);
307 tname = agnameof(t);
308
309 if (strcmp(tname, tail) == 0) {
310 ud->edgeinverted = false;
311 } else if (strcmp(tname, head) == 0) {
312 ud->edgeinverted = true;
313 }
314
315 pos = get_xml_attr("id", atts);
316 if (pos > 0) {
317 setEdgeAttr(E, GRAPHML_ID, atts[pos], ud);
318 }
319 }
320 } else {
321 /* must be some extension */
322 fprintf(stderr,
323 "Unknown node %s - ignoring.\n",
324 name);
325 }
326}
327
328static void endElementHandler(void *userData, const char *name)
329{
330 userdata_t *ud = userData;
331
332 if (strcmp(name, "graph") == 0) {
333 pop_subg();
334 popString(&ud->elements);
335 ud->closedElementType = TAG_GRAPH;
336 } else if (strcmp(name, "node") == 0) {
337 char *ele_name = topString(&ud->elements);
338 if (ud->closedElementType == TAG_GRAPH) {
339 Agnode_t *node = agnode(root, ele_name, 0);
340 if (node) agdelete(root, node);
341 }
342 popString(&ud->elements);
343 ud->closedElementType = TAG_NODE;
344 } else if (strcmp(name, "edge") == 0) {
345 E = 0;
346 ud->closedElementType = TAG_EDGE;
347 ud->edgeinverted = false;
348 }
349}
350
351static Agraph_t *graphml_to_gv(char *graphname, FILE *graphmlFile, int *rv) {
352 char buf[BUFSIZ];
353 int done;
354 userdata_t udata = genUserdata(graphname);
355 XML_Parser parser = XML_ParserCreate(NULL);
356
357 *rv = 0;
358 XML_SetUserData(parser, &udata);
359 XML_SetElementHandler(parser, startElementHandler, endElementHandler);
360
361 root = 0;
362 do {
363 size_t len = fread(buf, 1, sizeof(buf), graphmlFile);
364 if (len == 0)
365 break;
366 done = len < sizeof(buf);
367 assert(len <= INT_MAX);
368 if (XML_Parse(parser, buf, (int)len, done) == XML_STATUS_ERROR) {
369 fprintf(stderr,
370 "%s at line %lu\n",
371 XML_ErrorString(XML_GetErrorCode(parser)),
372 XML_GetCurrentLineNumber(parser));
373 *rv = 1;
374 break;
375 }
376 } while (!done);
377 XML_ParserFree(parser);
378 freeUserdata(udata);
379
380 return root;
381}
382
383static FILE *getFile(void)
384{
385 FILE *rv = NULL;
386 static FILE *savef = NULL;
387 static int cnt = 0;
388
389 if (Files == NULL) {
390 if (cnt++ == 0) {
391 rv = stdin;
392 }
393 } else {
394 if (savef)
395 fclose(savef);
396 while (Files[cnt]) {
397 if ((rv = fopen(Files[cnt++], "r")) != 0)
398 break;
399 else
400 fprintf(stderr, "Can't open %s\n", Files[cnt - 1]);
401 }
402 }
403 savef = rv;
404 return rv;
405}
406
407static const char *use = "Usage: %s [-gd?] [-o<file>] [<graphs>]\n\
408 -g<name> : use <name> as template for graph names\n\
409 -o<file> : output to <file> (stdout)\n\
410 -v : verbose mode\n\
411 -? : usage\n";
412
413static void usage(int v)
414{
415 fprintf(stderr, use, CmdName);
416 graphviz_exit(v);
417}
418
419static char *cmdName(char *path)
420{
421 char *sp;
422
423 sp = strrchr(path, '/');
424 if (sp)
425 sp++;
426 else
427 sp = path;
428 return sp;
429}
430
431static void initargs(int argc, char **argv)
432{
433 int c;
434
435 CmdName = cmdName(argv[0]);
436 opterr = 0;
437 while ((c = getopt(argc, argv, ":vg:o:")) != -1) {
438 switch (c) {
439 case 'g':
440 gname = optarg;
441 break;
442 case 'v':
443 Verbose = 1;
444 break;
445 case 'o':
446 if (outFile != NULL)
447 fclose(outFile);
448 outFile = openFile(CmdName, optarg, "w");
449 break;
450 case ':':
451 fprintf(stderr, "%s: option -%c missing argument\n", CmdName, optopt);
452 usage(1);
453 break;
454 case '?':
455 if (optopt == '?')
456 usage(0);
457 else {
458 fprintf(stderr, "%s: option -%c unrecognized\n", CmdName,
459 optopt);
460 usage(1);
461 }
462 break;
463 default:
464 UNREACHABLE();
465 }
466 }
467
468 argv += optind;
469 argc -= optind;
470
471 if (argc)
472 Files = argv;
473 if (!outFile)
474 outFile = stdout;
475}
476
477static char *nameOf(agxbuf *buf, char *name, int cnt) {
478 if (*name == '\0')
479 return name;
480 if (cnt) {
481 agxbprint(buf, "%s%d", name, cnt);
482 return agxbuse(buf);
483 }
484 else
485 return name;
486}
487
488#endif
489
490int main(int argc, char **argv)
491{
493 Agraph_t *prev = 0;
494 FILE *inFile;
495 int rv = 0, gcnt = 0;
496
497#ifdef HAVE_EXPAT
498 agxbuf buf = {0};
499 initargs(argc, argv);
500 while ((inFile = getFile())) {
501 while ((graph = graphml_to_gv(nameOf(&buf, gname, gcnt), inFile, &rv))) {
502 gcnt++;
503 if (prev)
504 agclose(prev);
505 prev = graph;
506 if (Verbose)
507 fprintf (stderr, "%s: %d nodes %d edges\n",
510 fflush(outFile);
511 }
512 }
513
514 stack_reset(&Gstack);
515
516 agxbfree(&buf);
517 graphviz_exit(rv);
518#else
519 fputs("graphml2gv: not configured for conversion from GXL to GV\n", stderr);
520 graphviz_exit(1);
521#endif
522}
Agobj_t * copy(Agraph_t *g, Agobj_t *obj)
Definition actions.c:156
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:77
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:213
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
char ** Files
Definition bcomps.c:61
static FILE * inFile
Definition acyclic.c:36
#define TAG_NODE
Definition ccomps.c:59
DOT-GXL converter API for gxl2gv.c and gv2gxl.c.
static const char * use
Definition cvtgxl.c:66
static FILE * outFile
Definition cvtgxl.c:35
static void initargs(int argc, char **argv)
Definition cvtgxl.c:130
static char * cmdName(char *path)
Definition cvtgxl.c:78
static char * CmdName
Definition cvtgxl.c:36
#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
static FILE * getFile(void)
Definition gml2gv.c:28
static char * gname
Definition gml2gv.c:23
static int Verbose
Definition gml2gv.c:22
static char * nameOf(agxbuf *buf, char *name, int cnt)
Definition gml2gv.c:123
void free(void *)
node NULL
Definition grammar.y:149
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:199
int agnedges(Agraph_t *g)
Definition graph.c:164
int agnnodes(Agraph_t *g)
Definition graph.c:158
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
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
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:96
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
int agwrite(Agraph_t *g, void *chan)
Return 0 on success, EOF on failure.
Definition write.c:708
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
int agdelete(Agraph_t *g, void *obj)
deletes object. Equivalent to agclose, agdelnode, and agdeledge for obj being a graph,...
Definition obj.c:19
@ AGEDGE
Definition cgraph.h:207
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
Definition subg.c:57
static uint64_t id
Definition gv2gml.c:42
Agraph_t * graph(char *name)
Definition gv.cpp:31
replacements for ctype.h functions
static bool gv_isdigit(int c)
Definition gv_ctype.h:41
static const char * usage
Definition gvpr.c:53
#define XML_STATUS_ERROR
Definition htmllex.c:41
$2 u p prev
Definition htmlparse.y:495
static FILE * openFile(const char *argv0, const char *name, const char *mode)
Definition openFile.h:8
Implementation of a dynamically expanding stack data structure.
static void stack_push(gv_stack_t *stack, void *item)
Definition stack.h:21
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
graph descriptor
Definition cgraph.h:284
graph or subgraph
Definition cgraph.h:425
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:639
Definition types.h:81
int main()
Definition grammar.c:93
#define UNREACHABLE()
Definition unreachable.h:30