Graphviz 14.1.3~dev.20260124.0732
Loading...
Searching...
No Matches
gv2gxl.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#include "config.h"
17
18#include "convert.h"
19#include <stdbool.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <util/agxbuf.h>
23#include <util/alloc.h>
24#include <util/gv_ctype.h>
25#include <util/startswith.h>
26#include <util/xml.h>
27
28#define EMPTY(s) ((s == 0) || (*s == '\0'))
29#define SLEN(s) (sizeof(s)-1)
30
31#define GXL_ATTR "_gxl_"
32#define GXL_ROLE "_gxl_role"
33#define GXL_HYPER "_gxl_hypergraph"
34#define GXL_ID "_gxl_id"
35#define GXL_FROM "_gxl_fromorder"
36#define GXL_TO "_gxl_toorder"
37#define GXL_TYPE "_gxl_type"
38#define GXL_COMP "_gxl_composite_"
39#define GXL_LOC "_gxl_locator_"
40
41#define GXL_COMP_LEN (SLEN(GXL_COMP))
42
43typedef struct {
44 Agrec_t h;
47
48static int Level; /* level of tabs */
50
51typedef struct {
53 char *name;
55} namev_t;
56
57static void *make_nitem(void *p, Dtdisc_t *disc) {
58 (void)disc;
59 namev_t *objp = p;
60
61 namev_t *np = gv_alloc(sizeof(*np));
62 np->name = objp->name;
63 np->unique_name = 0;
64 return np;
65}
66
68 offsetof(namev_t, name),
69 -1,
70 offsetof(namev_t, link),
72 free,
73 NULL,
74};
75
76typedef struct {
78 char *name;
79} idv_t;
80
81static void free_iditem(void *idv) {
82 idv_t *idp = idv;
83 free(idp->name);
84 free(idp);
85}
86
87static Dtdisc_t idDisc = {
88 offsetof(idv_t, name),
89 -1,
90 offsetof(idv_t, link),
91 NULL,
93 NULL,
94};
95
105
106static void writeBody(gxlstate_t *, Agraph_t * g, FILE * gxlFile);
107static void iterateBody(gxlstate_t * stp, Agraph_t * g);
108
109static void tabover(FILE * gxlFile)
110{
111 int temp = Level;
112 while (temp--)
113 putc('\t', gxlFile);
114}
115
116/* legalGXLName:
117 * By XML spec,
118 * ID := (alpha|'_'|':')(NameChar)*
119 * NameChar := alpha|digit|'.'|':'|'-'|'_'
120 */
121static bool legalGXLName(const char *id) {
122 char c = *id++;
123 if (!gv_isalpha(c) && c != '_' && c != ':')
124 return false;
125 while ((c = *id++)) {
126 if (!gv_isalnum(c) && c != '_' && c != ':' && c != '-' && c != '.')
127 return false;
128 }
129 return true;
130}
131
132// `fputs` wrapper to handle the difference in calling convention to what
133// `gv_xml_escape`’s `cb` expects
134static inline int put(void *stream, const char *s) {
135 return fputs(s, stream);
136}
137
138// write a string to the given file, XML-escaping the input
139static inline int xml_puts(FILE *stream, const char *s) {
140 const xml_flags_t flags = {.dash = 1, .nbsp = 1};
141 return gv_xml_escape(s, flags, put, stream);
142}
143
144// wrapper around `gv_xml_escape` to set flags for URL escaping
145static int xml_url_puts(FILE *f, const char *s) {
146 const xml_flags_t flags = {0};
147 return gv_xml_escape(s, flags, put, f);
148}
149
150static bool isGxlGrammar(const char *name) {
151 return startswith(name, GXL_ATTR);
152}
153
154static bool isLocatorType(const char *name) {
155 return startswith(name, GXL_LOC);
156}
157
158static bool idexists(Dt_t * ids, char *id) {
159 return dtmatch(ids, id) != NULL;
160}
161
162/* addid:
163 * assume id is not in ids.
164 */
165static char *addid(Dt_t *ids, const char *id) {
166 idv_t *idp = gv_alloc(sizeof(*idp));
167
168 idp->name = gv_strdup(id);
169 dtinsert(ids, idp);
170 return idp->name;
171}
172
173static char *createGraphId(Dt_t * ids)
174{
175 static int graphIdCounter = 0;
176 agxbuf buf = {0};
177 char *name;
178
179 do {
180 agxbprint(&buf, "G_%d", graphIdCounter++);
181 name = agxbuse(&buf);
182 } while (idexists(ids, name));
183 char *rv = addid(ids, name);
184 agxbfree(&buf);
185 return rv;
186}
187
188static char *createNodeId(Dt_t * ids)
189{
190 static int nodeIdCounter = 0;
191 agxbuf buf = {0};
192 char *name;
193
194 do {
195 agxbprint(&buf, "N_%d", nodeIdCounter++);
196 name = agxbuse(&buf);
197 } while (idexists(ids, name));
198 char *rv = addid(ids, name);
199 agxbfree(&buf);
200 return rv;
201}
202
203static char *mapLookup(Dt_t * nm, char *name)
204{
205 namev_t *objp = dtmatch(nm, name);
206 if (objp)
207 return objp->unique_name;
208 return NULL;
209}
210
211/* nodeID:
212 * Return id associated with the given node.
213 */
214static char *nodeID(gxlstate_t * stp, Agnode_t * n)
215{
216 char *name = agnameof(n);
217 char *uniqueName = mapLookup(stp->nodeMap, name);
218 assert(uniqueName);
219 return uniqueName;
220}
221
222#define EDGEOP "--" /* cannot use '>'; illegal in ID in GXL */
223
224static char *createEdgeId(gxlstate_t * stp, Agedge_t * e)
225{
226 int edgeIdCounter = 1;
227 char *hname = nodeID(stp, AGHEAD(e));
228 char *tname = nodeID(stp, AGTAIL(e));
229
230 agxbuf bp = {0};
231
232 agxbprint(&bp, "%s%s%s", tname, EDGEOP, hname);
233 char *id_name = agxbuse(&bp);
234 while (idexists(stp->idList, id_name)) {
235 agxbprint(&bp, "%s%s%s:%d", tname, EDGEOP, hname, edgeIdCounter++);
236 id_name = agxbuse(&bp);
237 }
238
239 char *rv = addid(stp->idList, id_name);
240 agxbfree(&bp);
241 return rv;
242}
243
244static void addToMap(Dt_t * map, char *name, char *uniqueName)
245{
246 namev_t obj = {.name = name};
247 namev_t *objp = dtinsert(map, &obj);
248 assert(objp->unique_name == NULL);
249 objp->unique_name = uniqueName;
250}
251
252static void graphAttrs(FILE * gxlFile, Agraph_t * g)
253{
254 char *val = agget(g, GXL_ROLE);
255 if (!EMPTY(val)) {
256 fprintf(gxlFile, " role=\"");
257 xml_puts(gxlFile, val);
258 fprintf(gxlFile, "\"");
259 }
260 val = agget(g, GXL_HYPER);
261 if (!EMPTY(val)) {
262 fprintf(gxlFile, " hypergraph=\"");
263 xml_puts(gxlFile, val);
264 fprintf(gxlFile, "\"");
265 }
266}
267
268static void edgeAttrs(FILE * gxlFile, Agedge_t * e)
269{
270 char *val = agget(e, GXL_ID);
271 if (!EMPTY(val)) {
272 fprintf(gxlFile, " id=\"");
273 xml_puts(gxlFile, val);
274 fprintf(gxlFile, "\"");
275 }
276 val = agget(e, GXL_FROM);
277 if (!EMPTY(val)) {
278 fprintf(gxlFile, " fromorder=\"");
279 xml_puts(gxlFile, val);
280 fprintf(gxlFile, "\"");
281 }
282 val = agget(e, GXL_TO);
283 if (!EMPTY(val)) {
284 fprintf(gxlFile, " toorder=\"");
285 xml_puts(gxlFile, val);
286 fprintf(gxlFile, "\"");
287 }
288}
289
290
291static void printHref(FILE * gxlFile, void *n)
292{
293 char *val = agget(n, GXL_TYPE);
294 if (!EMPTY(val)) {
295 tabover(gxlFile);
296 fprintf(gxlFile, "\t<type xlink:href=\"");
297 xml_url_puts(gxlFile, val);
298 fprintf(gxlFile, "\">\n");
299 tabover(gxlFile);
300 fprintf(gxlFile, "\t</type>\n");
301 }
302}
303
304
305static void
306writeDict(FILE *gxlFile, const char *name, Dict_t *dict, bool isGraph) {
307 Dict_t *view = dtview(dict, NULL);
308 for (Agsym_t *sym = dtfirst(dict); sym; sym = dtnext(dict, sym)) {
309 if (!isGxlGrammar(sym->name)) {
310 if (EMPTY(sym->defval)) { /* try to skip empty str (default) */
311 if (view == NULL)
312 continue; /* no parent */
313 Agsym_t *psym = dtsearch(view, sym);
314 /* assert(psym); */
315 if (EMPTY(psym->defval))
316 continue; /* also empty in parent */
317 }
318
319 if (isLocatorType(sym->defval)) {
320 char *locatorVal = sym->defval + strlen(GXL_LOC);
321
322 tabover(gxlFile);
323 fprintf(gxlFile, "\t<attr name=\"");
324 xml_puts(gxlFile, sym->name);
325 fprintf(gxlFile, "\">\n");
326 tabover(gxlFile);
327 fprintf(gxlFile, "\t\t<locator xlink:href=\"");
328 xml_url_puts(gxlFile, locatorVal);
329 fprintf(gxlFile, "\"/>\n");
330 tabover(gxlFile);
331 fprintf(gxlFile, "\t</attr>\n");
332 } else {
333 tabover(gxlFile);
334 if (isGraph) {
335 fprintf(gxlFile, "\t<attr name=\"");
336 xml_puts(gxlFile, sym->name);
337 fprintf(gxlFile, "\" ");
338 fprintf(gxlFile, "kind=\"");
339 xml_puts(gxlFile, name);
340 fprintf(gxlFile, "\">\n");
341 }
342 else {
343 fprintf(gxlFile, "\t<attr name=\"");
344 xml_puts(gxlFile, name);
345 fprintf(gxlFile, ":");
346 xml_puts(gxlFile, sym->name);
347 fprintf(gxlFile, "\" kind=\"");
348 xml_puts(gxlFile, name);
349 fprintf(gxlFile, "\">\n");
350 }
351 tabover(gxlFile);
352 fprintf(gxlFile, "\t\t<string>");
353 xml_puts(gxlFile, sym->defval);
354 fprintf(gxlFile, "</string>\n");
355 tabover(gxlFile);
356 fprintf(gxlFile, "\t</attr>\n");
357 }
358 } else {
359 /* gxl attr; check for special cases like composites */
360 if (startswith(sym->name, GXL_COMP)) {
361 if (EMPTY(sym->defval)) {
362 if (view == NULL)
363 continue;
364 Agsym_t *psym = dtsearch(view, sym);
365 if (EMPTY(psym->defval))
366 continue;
367 }
368
369 tabover(gxlFile);
370 fprintf(gxlFile, "\t<attr name=\"");
371 xml_puts(gxlFile, sym->name + GXL_COMP_LEN);
372 fprintf(gxlFile, "\" ");
373 fprintf(gxlFile, "kind=\"");
374 xml_puts(gxlFile, name);
375 fprintf(gxlFile, "\">\n");
376 tabover(gxlFile);
377 fprintf(gxlFile, "\t\t");
378 xml_puts(gxlFile, sym->defval);
379 fprintf(gxlFile, "\n");
380 tabover(gxlFile);
381 fprintf(gxlFile, "\t</attr>\n");
382 }
383 }
384 }
385 dtview(dict, view); /* restore previous view */
386}
387
388static void writeDicts(Agraph_t * g, FILE * gxlFile)
389{
390 Agdatadict_t *def;
391 if ((def = agdatadict(g, false))) {
392 writeDict(gxlFile, "graph", def->dict.g, true);
393 writeDict(gxlFile, "node", def->dict.n, false);
394 writeDict(gxlFile, "edge", def->dict.e, false);
395 }
396}
397
398static void writeHdr(gxlstate_t *stp, Agraph_t *g, FILE *gxlFile, bool top) {
399 char *kind;
400
401 Level++;
402 stp->attrsNotWritten = AGATTRWF(g);
403
404 char *name = agnameof(g);
405 if (g->desc.directed)
406 kind = "directed";
407 else
408 kind = "undirected";
409 if (!top && agparent(g)) {
410 /* this must be anonymous graph */
411
412 agxbuf buf = {0};
413 agxbprint(&buf, "N_%s", name);
414 char *bp = agxbuse(&buf);
415 if (idexists(stp->idList, bp) || !legalGXLName(bp)) {
416 bp = createNodeId(stp->idList);
417 } else {
418 bp = addid(stp->idList, bp);
419 }
420 addToMap(stp->synNodeMap, name, bp);
421
422 tabover(gxlFile);
423 fprintf(gxlFile, "<node id=\"%s\">\n", bp);
424 agxbfree(&buf);
425 Level++;
426 } else {
427 Tailport = agattr_text(g, AGEDGE, "tailport", NULL);
428 Headport = agattr_text(g, AGEDGE, "headport", NULL);
429 }
430
431 char *uniqueName = mapLookup(stp->graphMap, name);
432 tabover(gxlFile);
433 fprintf(gxlFile, "<graph id=\"%s\" edgeids=\"true\" edgemode=\"%s\"",
434 uniqueName, kind);
435 graphAttrs(gxlFile, g);
436 fprintf(gxlFile, ">\n");
437
438 if (uniqueName && (strcmp(name, uniqueName) != 0)) {
439 tabover(gxlFile);
440 fprintf(gxlFile, "\t<attr name=\"name\">\n");
441 tabover(gxlFile);
442 fprintf(gxlFile, "\t\t<string>");
443 xml_puts(gxlFile, name);
444 fprintf(gxlFile, "</string>\n");
445 tabover(gxlFile);
446 fprintf(gxlFile, "\t</attr>\n");
447 }
448
449 if (agisstrict(g)) {
450 tabover(gxlFile);
451 fprintf(gxlFile, "\t<attr name=\"strict\">\n");
452 tabover(gxlFile);
453 fprintf(gxlFile, "\t\t<string>true</string>\n");
454 tabover(gxlFile);
455 fprintf(gxlFile, "\t</attr>\n");
456 }
457
458 writeDicts(g, gxlFile);
459 printHref(gxlFile, g);
460 AGATTRWF(g) = !(AGATTRWF(g));
461}
462
463static void writeTrl(Agraph_t *g, FILE *gxlFile, bool top) {
464 tabover(gxlFile);
465 fprintf(gxlFile, "</graph>\n");
466 Level--;
467 if (!top && agparent(g)) {
468 tabover(gxlFile);
469 fprintf(gxlFile, "</node>\n");
470 Level--;
471 }
472}
473
474
475static void writeSubgs(gxlstate_t * stp, Agraph_t * g, FILE * gxlFile)
476{
477 for (Agraph_t *subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
478 writeHdr(stp, subg, gxlFile, false);
479 writeBody(stp, subg, gxlFile);
480 writeTrl(subg, gxlFile, false);
481 }
482}
483
484static void writeEdgeName(Agedge_t *e, FILE *gxlFile) {
485 char *p = agnameof(e);
486 if (!(EMPTY(p))) {
487 tabover(gxlFile);
488 fprintf(gxlFile, "\t<attr name=\"key\">\n");
489 tabover(gxlFile);
490 fprintf(gxlFile, "\t\t<string>");
491 xml_puts(gxlFile, p);
492 fprintf(gxlFile, "</string>\n");
493 tabover(gxlFile);
494 fprintf(gxlFile, "\t</attr>\n");
495 }
496}
497
498
499static void
500writeNondefaultAttr(void *obj, FILE * gxlFile, Dict_t * defdict)
501{
502 if (AGTYPE(obj) == AGINEDGE || AGTYPE(obj) == AGOUTEDGE) {
503 writeEdgeName(obj, gxlFile);
504 }
505 Agattr_t *data = agattrrec(obj);
506 if (data) {
507 for (Agsym_t *sym = dtfirst(defdict); sym; sym = dtnext(defdict, sym)) {
508 if (!isGxlGrammar(sym->name)) {
509 if (AGTYPE(obj) == AGINEDGE || AGTYPE(obj) == AGOUTEDGE) {
510 if (Tailport && sym->id == Tailport->id)
511 continue;
512 if (Headport && sym->id == Headport->id)
513 continue;
514 }
515 if (data->str[sym->id] != sym->defval) {
516
517 if (strcmp(data->str[sym->id], "") == 0)
518 continue;
519
520 if (isLocatorType(data->str[sym->id])) {
521 char *locatorVal = data->str[sym->id] + strlen(GXL_LOC);
522
523 tabover(gxlFile);
524 fprintf(gxlFile, "\t<attr name=\"");
525 xml_puts(gxlFile, sym->name);
526 fprintf(gxlFile, "\">\n");
527 tabover(gxlFile);
528 fprintf(gxlFile, "\t\t<locator xlink:href=\"");
529 xml_url_puts(gxlFile, locatorVal);
530 fprintf(gxlFile, "\"/>\n");
531 tabover(gxlFile);
532 fprintf(gxlFile, "\t</attr>\n");
533 } else {
534 tabover(gxlFile);
535 fprintf(gxlFile, "\t<attr name=\"");
536 xml_puts(gxlFile, sym->name);
537 fprintf(gxlFile, "\"");
538 if (aghtmlstr(data->str[sym->id])) {
539 // This is a <…> string. Note this in the kind.
540 fprintf(gxlFile, " kind=\"HTML-like string\"");
541 }
542 fprintf(gxlFile, ">\n");
543 tabover(gxlFile);
544 fprintf(gxlFile, "\t\t<string>");
545 xml_puts(gxlFile, data->str[sym->id]);
546 fprintf(gxlFile, "</string>\n");
547 tabover(gxlFile);
548 fprintf(gxlFile, "\t</attr>\n");
549 }
550 }
551 } else {
552 /* gxl attr; check for special cases like composites */
553 if (startswith(sym->name, GXL_COMP)) {
554 if (data->str[sym->id] != sym->defval) {
555
556 tabover(gxlFile);
557 fprintf(gxlFile, "\t<attr name=\"");
558 xml_puts(gxlFile, sym->name + GXL_COMP_LEN);
559 fprintf(gxlFile, "\">\n");
560 tabover(gxlFile);
561 fprintf(gxlFile, "\t\t");
562 xml_puts(gxlFile, data->str[sym->id]);
563 fprintf(gxlFile, "\n");
564 tabover(gxlFile);
565 fprintf(gxlFile, "\t</attr>\n");
566 }
567 }
568 }
569 }
570 }
571 AGATTRWF(obj) = !(AGATTRWF(obj));
572}
573
574static bool attrs_written(gxlstate_t * stp, void *obj) {
575 return AGATTRWF(obj) != stp->attrsNotWritten;
576}
577
578static void
579writeNode(gxlstate_t * stp, Agnode_t * n, FILE * gxlFile, Dict_t * d)
580{
581 char *name = agnameof(n);
582 char *uniqueName = nodeID(stp, n);
583 Level++;
584 tabover(gxlFile);
585 fprintf(gxlFile, "<node id=\"%s\">\n", uniqueName);
586
587 printHref(gxlFile, n);
588
589 if (strcmp(name, uniqueName)) {
590 tabover(gxlFile);
591 fprintf(gxlFile, "\t<attr name=\"name\">\n");
592 tabover(gxlFile);
593 fprintf(gxlFile, "\t\t<string>");
594 xml_puts(gxlFile, name);
595 fprintf(gxlFile, "</string>\n");
596 tabover(gxlFile);
597 fprintf(gxlFile, "\t</attr>\n");
598 }
599
600 if (!attrs_written(stp, n))
601 writeNondefaultAttr(n, gxlFile, d);
602 tabover(gxlFile);
603 fprintf(gxlFile, "</node>\n");
604 Level--;
605}
606
607static void writePort(Agedge_t * e, FILE * gxlFile, char *name)
608{
609 char *val = agget(e, name);
610 if (val && val[0]) {
611 tabover(gxlFile);
612 fprintf(gxlFile, "\t<attr name=\"");
613 xml_puts(gxlFile, name);
614 fprintf(gxlFile, "\">\n");
615 tabover(gxlFile);
616 fprintf(gxlFile, "\t\t<string>");
617 xml_puts(gxlFile, val);
618 fprintf(gxlFile, "</string>\n");
619 tabover(gxlFile);
620 fprintf(gxlFile, "\t</attr>\n");
621 }
622}
623
624static bool writeEdgeTest(Agraph_t *g, Agedge_t *e) {
625 /* can use agedge() because we subverted the dict compar_f */
626 for (Agraph_t *subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
627 if (agsubedge(subg, e, 0))
628 return false;
629 }
630 return true;
631}
632
633static void
634writeEdge(gxlstate_t * stp, Agedge_t * e, FILE * gxlFile, Dict_t * d)
635{
636 Agnode_t *t = AGTAIL(e);
637 Agnode_t *h = AGHEAD(e);
638
639 Level++;
640 tabover(gxlFile);
641 fprintf(gxlFile, "<edge from=\"%s\" ", nodeID(stp, t));
642 fprintf(gxlFile, "to=\"%s\"", nodeID(stp, h));
643 edgeAttrs(gxlFile, e);
644
645 if (stp->directed) {
646 fprintf(gxlFile, " isdirected=\"true\"");
647 } else {
648 fprintf(gxlFile, " isdirected=\"false\"");
649 }
650
651 char *edge_id = agget(e, GXL_ID);
652 if (!EMPTY(edge_id)) {
653 fprintf(gxlFile, ">\n");
654 } else {
655 char *bp = createEdgeId(stp, e);
656 fprintf(gxlFile, " id=\"%s\">\n", bp);
657 }
658
659 printHref(gxlFile, e);
660
661 writePort(e, gxlFile, "tailport");
662 writePort(e, gxlFile, "headport");
663 if (!(attrs_written(stp, e)))
664 writeNondefaultAttr(e, gxlFile, d);
665 else
666 writeEdgeName(e, gxlFile);
667 tabover(gxlFile);
668 fprintf(gxlFile, "</edge>\n");
669 Level--;
670}
671
672
673#define writeval(n) (((Local_Agnodeinfo_t*)((n)->base.data))->written)
674
675static void writeBody(gxlstate_t * stp, Agraph_t * g, FILE * gxlFile)
676{
677 writeSubgs(stp, g, gxlFile);
678 Agdatadict_t *dd = agdatadict(g, false);
679 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
680 Agnode_t *realn = agidnode(stp->root, AGID(n), 0);
681 if (!writeval(realn)) {
682 writeval(realn) = 1;
683 writeNode(stp, n, gxlFile, dd->dict.n);
684 }
685
686 for (Agedge_t *e = agfstout(g, n); e; e = agnxtout(g, e)) {
687 if (writeEdgeTest(g, e))
688 writeEdge(stp, e, gxlFile, dd->dict.e);
689 }
690 }
691}
692
693static void iterateHdr(gxlstate_t * stp, Agraph_t * g)
694{
695 char *name = agnameof(g);
696 char *gxlId = agget(g, GXL_ID);
697 if (EMPTY(gxlId))
698 gxlId = name;
699
700 if (idexists(stp->idList, gxlId) || !legalGXLName(gxlId))
701 gxlId = createGraphId(stp->idList);
702 else
703 gxlId = addid(stp->idList, gxlId);
704 addToMap(stp->graphMap, name, gxlId);
705}
706
707static void iterate_subgs(gxlstate_t * stp, Agraph_t * g)
708{
709 for (Agraph_t *subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
710 iterateHdr(stp, subg);
711 iterateBody(stp, subg);
712 }
713}
714
715
716static void iterateBody(gxlstate_t * stp, Agraph_t * g)
717{
718 iterate_subgs(stp, g);
719 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
720 char *nodename = agnameof(n);
721
722 if (!mapLookup(stp->nodeMap, nodename)) {
723 char *gxlId = agget(n, GXL_ID);
724 if (EMPTY(gxlId))
725 gxlId = nodename;
726 if (idexists(stp->idList, gxlId) || !legalGXLName(gxlId))
727 gxlId = createNodeId(stp->idList);
728 else
729 gxlId = addid(stp->idList, gxlId);
730 addToMap(stp->nodeMap, nodename, gxlId);
731 }
732
733 for (Agedge_t *e = agfstout(g, n); e; e = agnxtout(g, e)) {
734 if (writeEdgeTest(g, e)) {
735 char *edge_id = agget(e, GXL_ID);
736 if (!EMPTY(edge_id))
737 addid(stp->idList, edge_id);
738 }
739 }
740 }
741}
742
744 gxlstate_t stp = {0};
745 stp.nodeMap = dtopen(&nameDisc, Dtoset);
748 stp.idList = dtopen(&idDisc, Dtoset);
749 stp.attrsNotWritten = 0;
750 stp.root = g;
751 stp.directed = agisdirected(g) != 0;
752 return stp;
753}
754
755static void freeState(gxlstate_t stp) {
756 dtclose(stp.nodeMap);
757 dtclose(stp.graphMap);
758 dtclose(stp.synNodeMap);
759 dtclose(stp.idList);
760}
761
762void gv_to_gxl(Agraph_t * g, FILE * gxlFile)
763{
764 gxlstate_t stp = initState(g);
765 aginit(g, AGNODE, "node", sizeof(Local_Agnodeinfo_t), true);
766
767 iterateHdr(&stp, g);
768 iterateBody(&stp, g);
769
770 Level = 0;
771
772 fprintf(gxlFile, "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n");
773 fprintf(gxlFile, "<gxl>\n");
774
775 writeHdr(&stp, g, gxlFile, true);
776 writeBody(&stp, g, gxlFile);
777 writeTrl(g, gxlFile, true);
778
779 fprintf(gxlFile, "</gxl>\n");
780
781 freeState(stp);
782}
Dynamically expanding string buffers.
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:97
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:252
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
static void * gv_alloc(size_t size)
Definition alloc.h:47
#define dtmatch(d, o)
Definition cdt.h:185
#define dtsearch(d, o)
Definition cdt.h:184
#define dtinsert(d, o)
Definition cdt.h:186
CDT_API Dt_t * dtview(Dt_t *, Dt_t *)
Definition dtview.c:92
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
#define dtnext(d, o)
Definition cdt.h:181
#define dtfirst(d)
Definition cdt.h:180
DOT-GXL converter API for gxl2gv.c and gv2gxl.c.
static Dtdisc_t disc
Definition exparse.y:209
static int flags
Definition gc.c:63
void free(void *)
node NULL
Definition grammar.y:181
Agattr_t * agattrrec(void *obj)
Definition attr.c:208
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:336
char * agget(void *obj, char *name)
Definition attr.c:450
Agdatadict_t * agdatadict(Agraph_t *g, bool cflag)
Definition attr.c:49
Agedge_t * agsubedge(Agraph_t *g, Agedge_t *e, int createflag)
Definition edge.c:350
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:28
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:43
#define AGTAIL(e)
Definition cgraph.h:973
#define AGHEAD(e)
Definition cgraph.h:974
int agisdirected(Agraph_t *g)
Definition graph.c:178
int agisstrict(Agraph_t *g)
Definition graph.c:188
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:50
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:43
Agnode_t * agidnode(Agraph_t *g, IDTYPE id, int createflag)
Definition node.c:125
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:145
#define AGID(obj)
returns the unique integer ID associated with the object
Definition cgraph.h:221
#define AGTYPE(obj)
returns AGRAPH, AGNODE, or AGEDGE depending on the type of the object
Definition cgraph.h:216
#define AGATTRWF(obj)
Definition cgraph.h:226
@ AGOUTEDGE
Definition cgraph.h:207
@ AGEDGE
Definition cgraph.h:207
@ AGNODE
Definition cgraph.h:207
@ AGINEDGE
Definition cgraph.h:207
void aginit(Agraph_t *g, int kind, const char *rec_name, int rec_size, int move_to_front)
attach new records to objects of specified kind
Definition rec.c:172
int aghtmlstr(const char *)
Definition refstr.c:440
Agraph_t * agparent(Agraph_t *g)
Definition subg.c:88
Agraph_t * agfstsubg(Agraph_t *g)
Definition subg.c:75
Agraph_t * agnxtsubg(Agraph_t *subg)
Definition subg.c:80
static Agsym_t * Headport
Definition gv2gxl.c:49
#define GXL_FROM
Definition gv2gxl.c:35
static void writeNondefaultAttr(void *obj, FILE *gxlFile, Dict_t *defdict)
Definition gv2gxl.c:500
static void writeTrl(Agraph_t *g, FILE *gxlFile, bool top)
Definition gv2gxl.c:463
static void writeDicts(Agraph_t *g, FILE *gxlFile)
Definition gv2gxl.c:388
static void writeNode(gxlstate_t *stp, Agnode_t *n, FILE *gxlFile, Dict_t *d)
Definition gv2gxl.c:579
static char * nodeID(gxlstate_t *stp, Agnode_t *n)
Definition gv2gxl.c:214
static Dtdisc_t nameDisc
Definition gv2gxl.c:67
static char * mapLookup(Dt_t *nm, char *name)
Definition gv2gxl.c:203
static void printHref(FILE *gxlFile, void *n)
Definition gv2gxl.c:291
static bool isGxlGrammar(const char *name)
Definition gv2gxl.c:150
static char * createEdgeId(gxlstate_t *stp, Agedge_t *e)
Definition gv2gxl.c:224
static void * make_nitem(void *p, Dtdisc_t *disc)
Definition gv2gxl.c:57
static void writePort(Agedge_t *e, FILE *gxlFile, char *name)
Definition gv2gxl.c:607
#define GXL_COMP
Definition gv2gxl.c:38
static bool idexists(Dt_t *ids, char *id)
Definition gv2gxl.c:158
static bool attrs_written(gxlstate_t *stp, void *obj)
Definition gv2gxl.c:574
#define GXL_LOC
Definition gv2gxl.c:39
static void graphAttrs(FILE *gxlFile, Agraph_t *g)
Definition gv2gxl.c:252
static Dtdisc_t idDisc
Definition gv2gxl.c:87
#define GXL_COMP_LEN
Definition gv2gxl.c:41
static bool legalGXLName(const char *id)
Definition gv2gxl.c:121
static int xml_puts(FILE *stream, const char *s)
Definition gv2gxl.c:139
#define GXL_ID
Definition gv2gxl.c:34
#define GXL_TYPE
Definition gv2gxl.c:37
static int put(void *stream, const char *s)
Definition gv2gxl.c:134
static void writeBody(gxlstate_t *, Agraph_t *g, FILE *gxlFile)
Definition gv2gxl.c:675
static void writeHdr(gxlstate_t *stp, Agraph_t *g, FILE *gxlFile, bool top)
Definition gv2gxl.c:398
static void addToMap(Dt_t *map, char *name, char *uniqueName)
Definition gv2gxl.c:244
static int Level
Definition gv2gxl.c:48
#define GXL_TO
Definition gv2gxl.c:36
static void freeState(gxlstate_t stp)
Definition gv2gxl.c:755
void gv_to_gxl(Agraph_t *g, FILE *gxlFile)
Definition gv2gxl.c:762
static char * createGraphId(Dt_t *ids)
Definition gv2gxl.c:173
static void writeSubgs(gxlstate_t *stp, Agraph_t *g, FILE *gxlFile)
Definition gv2gxl.c:475
static bool isLocatorType(const char *name)
Definition gv2gxl.c:154
static void free_iditem(void *idv)
Definition gv2gxl.c:81
static void iterateHdr(gxlstate_t *stp, Agraph_t *g)
Definition gv2gxl.c:693
static void edgeAttrs(FILE *gxlFile, Agedge_t *e)
Definition gv2gxl.c:268
#define GXL_ATTR
Definition gv2gxl.c:31
static int xml_url_puts(FILE *f, const char *s)
Definition gv2gxl.c:145
static void writeEdgeName(Agedge_t *e, FILE *gxlFile)
Definition gv2gxl.c:484
static void writeEdge(gxlstate_t *stp, Agedge_t *e, FILE *gxlFile, Dict_t *d)
Definition gv2gxl.c:634
static void writeDict(FILE *gxlFile, const char *name, Dict_t *dict, bool isGraph)
Definition gv2gxl.c:306
static Agsym_t * Tailport
Definition gv2gxl.c:49
#define GXL_HYPER
Definition gv2gxl.c:33
#define EMPTY(s)
Definition gv2gxl.c:28
static char * createNodeId(Dt_t *ids)
Definition gv2gxl.c:188
static gxlstate_t initState(Agraph_t *g)
Definition gv2gxl.c:743
static bool writeEdgeTest(Agraph_t *g, Agedge_t *e)
Definition gv2gxl.c:624
#define GXL_ROLE
Definition gv2gxl.c:32
#define EDGEOP
Definition gv2gxl.c:222
static void tabover(FILE *gxlFile)
Definition gv2gxl.c:109
static char * addid(Dt_t *ids, const char *id)
Definition gv2gxl.c:165
#define writeval(n)
Definition gv2gxl.c:673
static void iterateBody(gxlstate_t *stp, Agraph_t *g)
Definition gv2gxl.c:716
static void iterate_subgs(gxlstate_t *stp, Agraph_t *g)
Definition gv2gxl.c:707
replacements for ctype.h functions
static bool gv_isalnum(int c)
Definition gv_ctype.h:43
static bool gv_isalpha(int c)
Definition gv_ctype.h:29
static Agedge_t * top(edge_stack_t *sp)
Definition tred.c:75
ViewInfo * view
Definition viewport.c:40
static bool startswith(const char *s, const char *prefix)
does the string s begin with the string prefix?
Definition startswith.h:11
string attribute container
Definition cgraph.h:632
char ** str
the attribute string values indexed by Agsym_s.id
Definition cgraph.h:635
Dict_t * n
Definition cgraph.h:655
struct Agdatadict_s::@32 dict
Dict_t * e
Definition cgraph.h:655
Dict_t * g
Definition cgraph.h:655
unsigned directed
Definition cgraph.h:285
graph or subgraph
Definition cgraph.h:424
Agdesc_t desc
Definition cgraph.h:426
implementation of Agrec_t
Definition cgraph.h:172
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:640
int id
index in Agattr_s.str
Definition cgraph.h:644
char * defval
Definition cgraph.h:643
Definition cdt.h:98
Agraph_t * root
Definition gv2gxl.c:101
Dt_t * nodeMap
Definition gv2gxl.c:97
char directed
Definition gv2gxl.c:103
Dt_t * synNodeMap
Definition gv2gxl.c:99
Dt_t * idList
Definition gv2gxl.c:100
Dt_t * graphMap
Definition gv2gxl.c:98
char attrsNotWritten
Definition gv2gxl.c:102
Definition gv2gxl.c:76
Dtlink_t link
Definition gv2gxl.c:77
char * name
Definition gv2gxl.c:78
char * unique_name
Definition gv2gxl.c:54
char * name
Definition gv2gxl.c:53
Dtlink_t link
Definition gv2gxl.c:52
options to tweak the behavior of XML escaping
Definition xml.h:13
unsigned dash
escape '-'
Definition xml.h:17
Definition grammar.c:90
int gv_xml_escape(const char *s, xml_flags_t flags, int(*cb)(void *state, const char *s), void *state)
Definition xml.c:185
XML escaping functionality.