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