123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726 |
- /**
- * @file
- * @brief DOT-GML converter
- */
- /**********************************************************
- * This software is part of the graphviz package *
- * https://graphviz.org *
- * *
- * Copyright (c) 1994-2004 AT&T Corp. *
- * and is licensed under the *
- * Common Public License, Version 1.0 *
- * by AT&T Corp. *
- * *
- * Information and Software Systems Research *
- * AT&T Research, Florham Park NJ *
- **********************************************************/
- #include <stdbool.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <inttypes.h>
- #include <getopt.h>
- #include <cgraph/cgraph.h>
- #include <cgraph/gv_ctype.h>
- #include <cgraph/ingraphs.h>
- #include <cgraph/strview.h>
- #include <cgraph/tokenize.h>
- #include <common/types.h>
- #include <common/utils.h>
- #include <util/exit.h>
- #include <util/streq.h>
- #include <util/unreachable.h>
- #include "openFile.h"
- static FILE *outFile;
- static char *CmdName;
- static char **Files;
- static uint64_t id;
- static bool yworks; ///< use yWorks.com variant of GML?
- #define POS_SET (1<<0)
- #define W_SET (1<<1)
- #define H_SET (1<<2)
- #define INVIS (1<<3)
- #define FILL (1<<4)
- #define LINE (1<<5)
- #define DASH (1<<6)
- #define DOT (1<<7)
- #define BOLD (1<<8)
- typedef struct {
- unsigned int flags;
- /* graphics */
- double x;
- double y;
- double w;
- double h;
- char* type; /* shape */
- char* image;
- char* fill; /* fillcolor */
- char* outline; /* pencolor */
- char* width; /* penwidth */
- char* outlineStyle;
- /* label graphics */
- char* fontColor;
- char* fontSize;
- char* fontName;
- } node_attrs;
- typedef struct {
- unsigned int flags;
- /* graphics */
- char* width; /* penwidth */
- char* fill; /* pencolor */
- char* arrow; /* dir */
- char* arrowhead; /* arrowhead */
- char* arrowtail; /* arrowtail */
- char* pos;
- /* label graphics */
- char* fontColor;
- char* fontSize;
- char* fontName;
- } edge_attrs;
- typedef struct {
- Agrec_t h;
- uint64_t id;
- } Local_Agnodeinfo_t;
- #define ID(n) (((Local_Agnodeinfo_t*)(n->base.data))->id)
- static void indent(int ix) {
- while (ix--)
- fprintf (outFile, " ");
- }
- /// Return true if input string is number
- static bool isNumber(char* s) {
- char* ep = s;
- strtod(s, &ep);
- if (s != ep) {
- while (gv_isspace(*ep)) ep++;
- if (*ep) return false;
- return true;
- }
- return false;
- }
- /// Simple implementation for parsing style attribute
- static int
- parseStyle (char* s)
- {
- int flags = 0;
- char* sep = " \t,";
- for (tok_t t = tok(s, sep); !tok_end(&t); tok_next(&t)) {
- strview_t ip = tok_get(&t);
- if (strview_str_eq(ip, "invis")) flags |= INVIS;
- else if (strview_str_eq(ip, "filled")) flags |= FILL;
- else if (strview_str_eq(ip, "dashed")) flags |= DASH;
- else if (strview_str_eq(ip, "dotted")) flags |= DOT;
- else if (strview_str_eq(ip, "solid")) flags |= LINE;
- else if (strview_str_eq(ip, "bold")) flags |= BOLD;
- }
- return flags;
- }
- static void emitInt(char *name, int value, int ix) {
- indent(ix);
- fprintf (outFile, "%s %d\n", name, value);
- }
- static void emitReal(char *name, double value, int ix) {
- indent(ix);
- fprintf (outFile, "%s %g\n", name, value);
- }
- static void emitPoint(double x, double y, int ix) {
- indent(ix);
- fprintf (outFile, "point [ x %g y %g ]\n", x, y);
- }
- static char*
- skipWS (char* s)
- {
- while (gv_isspace(*s)) s++;
- return s;
- }
- /* Return NULL if unsuccessful
- */
- static char*
- readPoint (char* s, double* xp, double* yp)
- {
- char* endp;
- s = skipWS(s);
- *xp = strtod (s, &endp);
- if (s == endp) return NULL;
- endp++; /* skip comma */
- s = endp;
- *yp = strtod (s, &endp);
- if (s == endp) return NULL;
- else return endp;
- }
- static char*
- arrowEnd (char* s0, char* pfx, int* fp, double* xp, double* yp)
- {
- char* s = skipWS(s0);
- if (strncmp(s,pfx,2)) return s;
- s += 2; /* skip prefix */
- s = readPoint (s, xp, yp);
- if (s == NULL) {
- fprintf (stderr, "Illegal spline end: %s\n", s0);
- graphviz_exit(1);
- }
- *fp = 1;
- return s;
- }
- static void emitSpline(char *s, int ix) {
- double sx, sy, ex, ey;
- int sarrow = 0;
- int earrow = 0;
- s = arrowEnd (s, "e,", &earrow, &ex, &ey);
- s = arrowEnd (s, "s,", &sarrow, &sx, &sy);
- indent(ix);
- fprintf (outFile, "Line [\n");
- if (sarrow)
- emitPoint(sx, sy, ix+1);
- while ((s = readPoint (s, &sx, &sy)))
- emitPoint(sx, sy, ix+1);
- if (earrow)
- emitPoint(ex, ey, ix+1);
- indent(ix);
- fprintf (outFile, "]\n");
- }
- // `fputs` wrapper to handle the difference in calling convention to what
- // `xml_escape`’s `cb` expects
- static inline int put(void *stream, const char *s) {
- return fputs(s, stream);
- }
- // write a string to the given file, XML-escaping the input
- static inline int xml_puts(FILE *stream, const char *s) {
- const xml_flags_t flags = {.dash = 1, .nbsp = 1};
- return xml_escape(s, flags, put, stream);
- }
- static void emitAttr(char *name, char *value, int ix) {
- indent(ix);
- if (isNumber (value))
- fprintf (outFile, "%s %s\n", name, value);
- else {
- fprintf(outFile, "%s \"", name);
- xml_puts(outFile, value);
- fputs("\"\n", outFile);
- }
- }
- /* node attributes:
- * label
- * graphics
- * LabelGraphics
- */
- static void emitNodeAttrs(Agraph_t *G, Agnode_t *np, int ix) {
- Agsym_t* s;
- char* v;
- node_attrs attrs;
- int doGraphics = 0;
- int doLabelGraphics = 0;
- char* label = 0;
- int style;
- double x, y;
- /* First, process the attributes, saving the graphics attributes */
- memset(&attrs,0, sizeof(attrs));
- for (s = agnxtattr (G, AGNODE, NULL); s; s = agnxtattr (G, AGNODE, s)) {
- if (streq(s->name, "style")) { /* hasFill outlineStyle invis */
- if (*(v = agxget (np, s))) {
- style = parseStyle (v);
- if (style & INVIS)
- attrs.flags |= INVIS;
- if (style & FILL)
- attrs.flags |= FILL;
- if (style & LINE)
- attrs.outlineStyle = "line";
- if (style & DASH)
- attrs.outlineStyle = "dashed";
- if (style & DOT)
- attrs.outlineStyle = "dotted";
- doGraphics = 1;
- }
- }
- else if (streq(s->name, "label")) {
- v = agxget (np, s);
- if (streq("\\N", v)) {
- label = agnameof(np);
- emitAttr(s->name, label, ix);
- doLabelGraphics = 1;
- }
- else if (*v) {
- label = v;
- emitAttr(s->name, label, ix);
- doLabelGraphics = 1;
- }
- }
- else if (streq(s->name, "penwidth")) {
- if (*(v = agxget (np, s))) {
- attrs.width = v;
- doGraphics = 1;
- }
- }
- else if (streq(s->name, "width")) {
- v = agxget (np, s);
- if (*v) {
- attrs.w = 72.0*atof (v);
- attrs.flags |= W_SET;
- doGraphics = 1;
- }
- }
- else if (streq(s->name, "height")) {
- v = agxget (np, s);
- if (*v) {
- attrs.h = 72.0*atof (v);
- attrs.flags |= H_SET;
- doGraphics = 1;
- }
- }
- else if (streq(s->name, "pos")) {
- v = agxget (np, s);
- if (sscanf (v, "%lf,%lf", &x, &y) == 2) {
- doGraphics = 1;
- attrs.x = x;
- attrs.y = y;
- attrs.flags |= POS_SET;
- }
- }
- else if (streq(s->name, "shape")) { /* type */
- if (*(v = agxget (np, s))) {
- attrs.type = v;
- doGraphics = 1;
- }
- }
- else if (streq(s->name, "color")) {
- if (*(v = agxget (np, s))) {
- attrs.fill = v;
- attrs.outline = v;
- doGraphics = 1;
- }
- }
- else if (streq(s->name, "fillcolor")) {
- if (*(v = agxget (np, s))) {
- attrs.fill = v;
- doGraphics = 1;
- }
- }
- else if (streq(s->name, "pencolor")) {
- if (*(v = agxget (np, s))) {
- attrs.outline = v;
- doGraphics = 1;
- }
- }
- else if (streq(s->name, "fontname")) { /* fontName */
- if (*(v = agxget (np, s))) {
- attrs.fontName = v;
- doLabelGraphics = 1;
- }
- }
- else if (streq(s->name, "fontsize")) { /* fontSize */
- if (*(v = agxget (np, s))) {
- attrs.fontSize = v;
- doLabelGraphics = 1;
- }
- }
- else if (streq(s->name, "fontcolor")) { /* fontColor */
- if (*(v = agxget (np, s))) {
- attrs.fontColor = v;
- doLabelGraphics = 1;
- }
- }
- else {
- v = agxget (np, s);
- emitAttr(s->name, v, ix);
- }
- }
- /* Then, print them, if any */
- if (doGraphics) {
- fprintf (outFile, " graphics [\n");
- if (attrs.flags & POS_SET) {
- emitReal("x", attrs.x, ix+1);
- emitReal("y", attrs.y, ix+1);
- }
- if (attrs.flags & W_SET) {
- emitReal("w", attrs.w, ix+1);
- }
- if (attrs.flags & H_SET) {
- emitReal("H", attrs.h, ix+1);
- }
- if (attrs.flags & INVIS) {
- emitInt("visible", 0, ix+1);
- }
- if (attrs.flags & FILL) {
- emitInt("hasFill", 1, ix+1);
- }
- if (attrs.type) {
- emitAttr("type", attrs.type, ix+1);
- }
- if (attrs.image) {
- emitAttr("image", attrs.image, ix+1);
- }
- if (attrs.fill) {
- emitAttr("fill", attrs.fill, ix+1);
- }
- if (attrs.outline) {
- emitAttr("outline", attrs.outline, ix+1);
- }
- if (attrs.width) {
- emitAttr("width", attrs.width, ix+1);
- }
- if (attrs.outlineStyle) {
- emitAttr("outlineStyle", attrs.outlineStyle, ix+1);
- }
- fprintf (outFile, " ]\n");
- }
- if (doLabelGraphics) {
- fprintf (outFile, " LabelGraphics [\n");
- if (label) emitAttr("text", label, ix+1);
- if (attrs.fontColor) {
- emitAttr(yworks ? "color" : "fontColor", attrs.fontColor, ix+1);
- }
- if (attrs.fontSize) {
- emitAttr("fontSize", attrs.fontSize, ix+1);
- }
- if (attrs.fontName) {
- emitAttr("fontName", attrs.fontName, ix+1);
- }
- fprintf (outFile, " ]\n");
- }
- }
- /* emitNode:
- * set node id
- * label, width height, x, y, type fillcolor
- */
- static void emitNode(Agraph_t *G, Agnode_t *n) {
- agbindrec(n, "nodeinfo", sizeof(Local_Agnodeinfo_t), true);
- fprintf(outFile, " node [\n id %" PRIu64 "\n name \"%s\"\n", id,
- agnameof(n));
- ID(n) = id++;
- emitNodeAttrs(G, n, 2);
- fprintf (outFile, " ]\n");
- }
- /* edge attributes:
- * label
- * graphics
- * LabelGraphics
- */
- static void emitEdgeAttrs(Agraph_t *G, Agedge_t *ep, int ix) {
- Agsym_t* s;
- char* v;
- edge_attrs attrs;
- int doGraphics = 0;
- int doLabelGraphics = 0;
- char* label = 0;
- int style;
- /* First, process the attributes, saving the graphics attributes */
- memset(&attrs,0, sizeof(attrs));
- for (s = agnxtattr (G, AGEDGE, NULL); s; s = agnxtattr (G, AGEDGE, s)) {
- if (streq(s->name, "style")) { /* hasFill outlineStyle invis */
- if (*(v = agxget (ep, s))) {
- style = parseStyle (v);
- if (style & INVIS)
- attrs.flags |= INVIS;
- if (style & LINE)
- attrs.flags |= LINE;
- if (style & DASH)
- attrs.flags |= DASH;
- if (style & DOT)
- attrs.flags |= DOT;
- if (style & BOLD)
- attrs.width = "2";
- doGraphics = 1;
- }
- }
- else if (streq(s->name, "label")) {
- if (*(v = agxget (ep, s))) {
- label = v;
- emitAttr(s->name, label, ix);
- doLabelGraphics = 1;
- }
- }
- else if (streq(s->name, "penwidth")) {
- if (*(v = agxget (ep, s))) {
- attrs.width = v;
- doGraphics = 1;
- }
- }
- else if (streq(s->name, "pos")) {
- if (*(v = agxget (ep, s))) {
- doGraphics = 1;
- attrs.pos = v;
- }
- }
- else if (streq(s->name, "dir")) {
- if (*(v = agxget (ep, s))) {
- doGraphics = 1;
- attrs.arrow = v;
- }
- }
- else if (streq(s->name, "color")) {
- if (*(v = agxget (ep, s))) {
- attrs.fill = v;
- doGraphics = 1;
- }
- }
- else if (streq(s->name, "pencolor")) {
- if (*(v = agxget (ep, s))) {
- attrs.fill = v;
- doGraphics = 1;
- }
- }
- else if (streq(s->name, "arrowhead")) {
- if (*(v = agxget (ep, s))) {
- attrs.arrowhead = v;
- doGraphics = 1;
- }
- }
- else if (streq(s->name, "arrowtail")) {
- if (*(v = agxget (ep, s))) {
- attrs.arrowtail = v;
- doGraphics = 1;
- }
- }
- else if (streq(s->name, "fontname")) { /* fontName */
- if (*(v = agxget (ep, s))) {
- attrs.fontName = v;
- doLabelGraphics = 1;
- }
- }
- else if (streq(s->name, "fontsize")) { /* fontSize */
- if (*(v = agxget (ep, s))) {
- attrs.fontSize = v;
- doLabelGraphics = 1;
- }
- }
- else if (streq(s->name, "fontcolor")) { /* fontColor */
- if (*(v = agxget (ep, s))) {
- attrs.fontColor = v;
- doLabelGraphics = 1;
- }
- }
- else {
- v = agxget (ep, s);
- emitAttr(s->name, v, ix);
- }
- }
- /* Then, print them, if any */
- if (doGraphics) {
- fprintf (outFile, " graphics [\n");
- if (attrs.pos) {
- emitSpline(attrs.pos, ix+1);
- }
- if (attrs.flags & INVIS) {
- emitInt("visible", 0, ix+1);
- }
- if (attrs.fill) {
- emitAttr("fill", attrs.fill, ix+1);
- }
- if (attrs.width) {
- emitAttr("width", attrs.width, ix+1);
- }
- if (attrs.arrowhead) {
- emitAttr("targetArrow", attrs.arrowhead, ix+1);
- }
- if (attrs.arrowtail) {
- emitAttr("sourceArrow", attrs.arrowtail, ix+1);
- }
- if (attrs.flags & DASH) {
- emitAttr("style", "dashed", ix+1);
- }
- else if (attrs.flags & DOT) {
- emitAttr("style", "dotted", ix+1);
- }
- else if (attrs.flags & LINE) {
- emitAttr("style", "line", ix+1);
- }
- if (attrs.arrow) {
- if (streq(attrs.arrow,"forward"))
- emitAttr("arrow", "first", ix+1);
- else if (streq(attrs.arrow,"back"))
- emitAttr("arrow", "last", ix+1);
- else if (streq(attrs.arrow,"both"))
- emitAttr("arrow", "both", ix+1);
- else if (streq(attrs.arrow,"none"))
- emitAttr("arrow", "none", ix+1);
- }
- fprintf (outFile, " ]\n");
- }
- if (doLabelGraphics) {
- fprintf (outFile, " LabelGraphics [\n");
- if (label) emitAttr("text", label, ix+1);
- if (attrs.fontColor) {
- emitAttr(yworks ? "color" : "fontColor", attrs.fontColor, ix+1);
- }
- if (attrs.fontSize) {
- emitAttr("fontSize", attrs.fontSize, ix+1);
- }
- if (attrs.fontName) {
- emitAttr("fontName", attrs.fontName, ix+1);
- }
- fprintf (outFile, " ]\n");
- }
- }
- static void emitEdge(Agraph_t *G, Agedge_t *e) {
- fprintf(outFile, " edge [\n id %" PRIu64 "\n", (uint64_t)AGSEQ(e));
- fprintf(outFile, " source %" PRIu64 "\n", ID(agtail(e)));
- fprintf(outFile, " target %" PRIu64 "\n", ID(aghead(e)));
- emitEdgeAttrs(G, e, 2);
- fprintf (outFile, " ]\n");
- }
- static void emitGraphAttrs(Agraph_t *G) {
- Agsym_t* s;
- char* v;
- for (s = agnxtattr (G, AGRAPH, NULL); s; s = agnxtattr (G, AGRAPH, s)) {
- if (*(v = agxget (G, s))) {
- emitAttr(s->name, v, 1);
- }
- }
- }
- static void gv_to_gml(Agraph_t *G) {
- Agnode_t* n;
- Agedge_t* e;
- fprintf (outFile, "graph [\n version 2\n");
- if (agisdirected(G))
- fprintf (outFile, " directed 1\n");
- else
- fprintf (outFile, " directed 0\n");
-
- emitGraphAttrs(G);
- /* FIX: Not sure how to handle default attributes or subgraphs */
- for (n = agfstnode(G); n; n = agnxtnode (G, n)) {
- emitNode(G, n);
- }
-
- for (n = agfstnode(G); n; n = agnxtnode (G, n)) {
- for (e = agfstout(G, n); e; e = agnxtout (G, e)) {
- emitEdge(G, e);
- }
- }
- fprintf (outFile, "]\n");
- }
- static char *useString = "Usage: %s [-y] [-?] <files>\n\
- -o<file> : output to <file> (stdout)\n\
- -y : output yWorks.com GML variant\n\
- -? - print usage\n\
- If no files are specified, stdin is used\n";
- static void usage(int v)
- {
- printf(useString, CmdName);
- graphviz_exit(v);
- }
- static char *cmdName(char *cmd)
- {
- char *sp;
- sp = strrchr(cmd, '/');
- if (sp)
- sp++;
- else
- sp = cmd;
- return sp;
- }
- static void initargs(int argc, char **argv)
- {
- int c;
- CmdName = cmdName(argv[0]);
- opterr = 0;
- while ((c = getopt(argc, argv, ":o:y")) != -1) {
- switch (c) {
- case 'o':
- if (outFile != NULL)
- fclose(outFile);
- outFile = openFile(CmdName, optarg, "w");
- break;
- case 'y':
- yworks = true;
- break;
- case ':':
- fprintf(stderr, "%s: option -%c missing parameter\n", CmdName, optopt);
- usage(1);
- break;
- case '?':
- if (optopt == '?')
- usage(0);
- else {
- fprintf(stderr, "%s: option -%c unrecognized\n", CmdName,
- optopt);
- usage(1);
- }
- break;
- default:
- UNREACHABLE();
- }
- }
- argv += optind;
- argc -= optind;
- if (argc)
- Files = argv;
- if (!outFile)
- outFile = stdout;
- }
- int main(int argc, char **argv)
- {
- Agraph_t *G;
- Agraph_t *prev = 0;
- int rv;
- ingraph_state ig;
- rv = 0;
- initargs(argc, argv);
- newIngraph(&ig, Files);
- while ((G = nextGraph(&ig))) {
- if (prev) {
- id = 0;
- agclose(prev);
- }
- prev = G;
- gv_to_gml(G);
- fflush(outFile);
- }
- graphviz_exit(rv);
- }
|