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