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