From: erg Date: Wed, 8 Jul 2009 21:00:58 +0000 (+0000) Subject: Add gml to gv converter X-Git-Tag: LAST_LIBGRAPH~32^2~1839 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=402f3993ce0ae4ddcd0c08d8448a993b210da4b5;p=graphviz Add gml to gv converter --- diff --git a/cmd/tools/Makefile.am b/cmd/tools/Makefile.am index 8d42f8b5c..368b52d6f 100644 --- a/cmd/tools/Makefile.am +++ b/cmd/tools/Makefile.am @@ -18,13 +18,13 @@ pdfdir = $(pkgdatadir)/doc/pdf noinst_HEADERS = colortbl.h convert.h mmio.h matrix_market.h graph_generator.h bin_PROGRAMS = gc gvcolor gxl2gv acyclic nop ccomps sccmap tred \ - unflatten gvpack dijkstra bcomps mm2gv gvgen + unflatten gvpack dijkstra bcomps mm2gv gvgen gml2gv man_MANS = gc.1 gvcolor.1 gxl2gv.1 acyclic.1 nop.1 ccomps.1 sccmap.1 \ - tred.1 unflatten.1 gvpack.1 dijkstra.1 bcomps.1 mm2gv.1 gvgen.1 + tred.1 unflatten.1 gvpack.1 dijkstra.1 bcomps.1 mm2gv.1 gvgen.1 gml2gv.1 pdf_DATA = gc.1.pdf gvcolor.1.pdf gxl2gv.1.pdf acyclic.1.pdf \ nop.1.pdf ccomps.1.pdf sccmap.1.pdf tred.1.pdf \ unflatten.1.pdf gvpack.1.pdf dijkstra.1.pdf \ - bcomps.1.pdf mm2gv.1.pdf gvgen.1.pdf + bcomps.1.pdf mm2gv.1.pdf gvgen.1.pdf gml2gv.1.pdf install-data-hook: (cd $(DESTDIR)$(man1dir); rm -f gv2gxl.1; $(LN_S) gxl2gv.1 gv2gxl.1;) @@ -153,6 +153,30 @@ mm2gv_LDADD = \ mm2gv.1.pdf: mm2gv.1 - @GROFF@ -Tps -man mm2gv.1 | @PS2PDF@ - - >mm2gv.1.pdf +gml2gv_SOURCES = gml2gv.c gmlparse.c gmlscan.c + +gmlparse.c: y.tab.c + @SED@ "s/yy/gml/g" < y.tab.c > gmlparse.c + +gmlparse.h: y.tab.h + @SED@ "s/yy/gml/g" < y.tab.h > gmlparse.h + +y.tab.c y.tab.h : $(top_srcdir)/cmd/tools/gmlparse.y + @YACC@ -dv $(top_srcdir)/cmd/tools/gmlparse.y + +gmlscan.o gmlscan.lo : gmlscan.c gmlparse.h + +gmlscan.c: $(top_srcdir)/cmd/tools/gmlscan.l + @LEX@ -i $(top_srcdir)/cmd/tools/gmlscan.l + @SED@ "s/yy/gml/g" < @LEX_OUTPUT_ROOT@.c > gmlscan.c + rm @LEX_OUTPUT_ROOT@.c + +gml2gv_LDADD = \ + $(top_builddir)/lib/cgraph/libcgraph.la @MATH_LIBS@ + +gml2gv.1.pdf: gml2gv.1 + - @GROFF@ -Tps -man gml2gv.1 | @PS2PDF@ - - >gml2gv.1.pdf + dijkstra_SOURCES = dijkstra.c dijkstra_LDADD = \ @@ -179,4 +203,4 @@ EXTRA_DIST = $(man_MANS) $(pdf_DATA) Makefile.old bcomps.vcproj \ CLEANFILES = stamp.h -DISTCLEANFILES = $(pdf_DATA) +DISTCLEANFILES = $(pdf_DATA) gmlparse.[ch] gmlscan.c y.output y.tab.[ch] diff --git a/cmd/tools/gml2gv.1 b/cmd/tools/gml2gv.1 new file mode 100644 index 000000000..14fa6a6ec --- /dev/null +++ b/cmd/tools/gml2gv.1 @@ -0,0 +1,52 @@ +.TH GML2GV 1 "9 July 2009" +.SH NAME +gml2gv \- GML-DOT converter +.SH SYNOPSIS +.B gml2gv +[ +.B \-? +] +[ +.BI -o outfile +] +[ +.I files +] +.br +.SH DESCRIPTION +.B gml2gv +converts a graph specified in the GML format to a graph in the GV (formerly DOT) format. +.SH OPTIONS +The following options are supported: +.TP +.B \-? +Prints usage information and exits. +.TP +.BI \-o "outfile" +Prints output to the file \fIoutfile\fP. If not given, \fBgml2gv\fP +uses stdout. +.TP +.SH OPERANDS +The following operand is supported: +.TP 8 +.I files +Names of files containing 1 or more graphs in GML. +If no +.I files +operand is specified, +the standard input will be used. +.SH RETURN CODES +Return \fB0\fP +if there were no problems during conversion; +and non-zero if any error occurred. +.SH "LIMITATIONS" +As both the graph and graphics models of GV and GML differ significantly, the +conversion is at best approximate. In particular, it is not clear how multiedges +are differentiated in GML, so multiedges are created in GV with no user-available +key. Also, no attribute information is lost, in that +any GML attributes that aren't converted to GV equivalents are retained as +attributes in the output graph. +.SH AUTHORS +Emden R. Gansner +.SH "SEE ALSO" +dot(1), libcgraph(3) diff --git a/cmd/tools/gml2gv.c b/cmd/tools/gml2gv.c new file mode 100644 index 000000000..3eb1b6e5b --- /dev/null +++ b/cmd/tools/gml2gv.c @@ -0,0 +1,155 @@ +/* $Id$Revision: */ +/* vim:set shiftwidth=4 ts=8: */ + +/********************************************************** +* This software is part of the graphviz package * +* http://www.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 * +**********************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gml2gv.h" +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_GETOPT_H +#include +#else +#include "compat_getopt.h" +#endif + +static FILE *outFile; +static char *CmdName; +static char **Files; + +static FILE *getFile(void) +{ + FILE *rv = NULL; + static FILE *savef = NULL; + static int cnt = 0; + + if (Files == NULL) { + if (cnt++ == 0) { + rv = stdin; + } + } else { + if (savef) + fclose(savef); + while (Files[cnt]) { + if ((rv = fopen(Files[cnt++], "r")) != 0) + break; + else + fprintf(stderr, "Can't open %s\n", Files[cnt - 1]); + } + } + savef = rv; + return rv; +} + +static FILE *openFile(char *name, char *mode) +{ + FILE *fp; + char *modestr; + + fp = fopen(name, mode); + if (!fp) { + if (*mode == 'r') + modestr = "reading"; + else + modestr = "writing"; + fprintf(stderr, "%s: could not open file %s for %s\n", + CmdName, name, modestr); + perror(name); + exit(1); + } + return fp; +} + + +static char *useString = "Usage: nop [-p?] \n\ + -o : output to (stdout)\n\ + -? - print usage\n\ +If no files are specified, stdin is used\n"; + +static void usage(int v) +{ + printf(useString); + exit(v); +} + +static char *cmdName(char *path) +{ + char *sp; + + sp = strrchr(path, '/'); + if (sp) + sp++; + else + sp = path; + return sp; +} + +static void initargs(int argc, char **argv) +{ + int c; + + CmdName = cmdName(argv[0]); + while ((c = getopt(argc, argv, ":gdo:?")) != -1) { + switch (c) { + case 'o': + outFile = openFile(optarg, "w"); + break; + case '?': + if (optopt == '?') + usage(0); + else { + fprintf(stderr, "%s: option -%c unrecognized\n", CmdName, + optopt); + exit(1); + } + } + } + + 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; + FILE *inFile; + int cnt, rv; + + rv = 0; + initargs(argc, argv); + while ((inFile = getFile())) { + cnt = 0; + while ((G = gml_to_gv(inFile, cnt, &rv))) { + cnt++; + if (prev) + agclose(prev); + prev = G; + agwrite(G, outFile); + fflush(outFile); + } + } + exit(rv); +} diff --git a/cmd/tools/gml2gv.h b/cmd/tools/gml2gv.h new file mode 100644 index 000000000..b7e5cce7a --- /dev/null +++ b/cmd/tools/gml2gv.h @@ -0,0 +1,46 @@ +/* $Id$Revision: */ +/* vim:set shiftwidth=4 ts=8: */ + +#include +#include + +typedef struct { + Dtlink_t link; + unsigned short kind; + unsigned short sort; + char* name; + union { + char* value; + Dt_t* lp; + }u; +} gmlattr; + +typedef struct { + Dtlink_t link; + char* id; + Dt_t* attrlist; +} gmlnode; + +typedef struct { + Dtlink_t link; + char* source; + char* target; + Dt_t* attrlist; +} gmledge; + +typedef struct gmlgraph { + Dtlink_t link; + struct gmlgraph* parent; + int directed; + Dt_t* attrlist; + Dt_t* nodelist; + Dt_t* edgelist; + Dt_t* graphlist; +} gmlgraph; + +extern int gmllex(void); +extern void gmllexeof(void); +extern void gmlerror(char *); +extern int gmlerrors(void); +extern void initgmlscan (FILE*); +extern Agraph_t* gml_to_gv (FILE*, int, int*); diff --git a/cmd/tools/gmlparse.y b/cmd/tools/gmlparse.y new file mode 100644 index 000000000..ba625393d --- /dev/null +++ b/cmd/tools/gmlparse.y @@ -0,0 +1,808 @@ +/* $Id$Revision: */ +/* vim:set shiftwidth=4 ts=8: */ + +/********************************************************** +* This software is part of the graphviz package * +* http://www.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 +#include +#include +#include +#include +#include +#define NEW(t) (t*)malloc(sizeof(t)) +#define N_NEW(n,t) (t*)malloc((n)*sizeof(t)) +#define RALLOC(size,ptr,type) ((type*)realloc(ptr,(size)*sizeof(type))) + +typedef unsigned short ushort; + +static gmlgraph* G; +static gmlnode* N; +static gmledge* E; +static Dt_t* L; +static Dt_t** liststk; +static int liststk_sz, liststk_cnt; + +static void +free_node (Dt_t*d, gmlnode* p, Dtdisc_t* ds) +{ + if (!p) return; + dtclose (p->attrlist); + free (p); +} + +static void +free_edge (Dt_t*d, gmledge* p, Dtdisc_t* ds) +{ + if (!p) return; + dtclose (p->attrlist); + free (p); +} + +static void +free_attr (Dt_t*d, gmlattr* p, Dtdisc_t* ds) +{ + if (!p) return; + if (p->kind == LIST) + dtclose (p->u.lp); + else + free (p->u.value); + free (p->name); + free (p); +} + +static void +free_graph (Dt_t*d, gmlgraph* p, Dtdisc_t* ds) +{ + if (!p) return; + if (p->nodelist) + dtclose (p->nodelist); + if (p->edgelist) + dtclose (p->edgelist); + if (p->attrlist) + dtclose (p->attrlist); + if (p->graphlist) + dtclose (p->graphlist); + free (p); +} + +static Dtdisc_t nodeDisc = { + offsetof(gmlnode,attrlist), + sizeof(Dt_t*), + offsetof(gmlnode,link), + NIL(Dtmake_f), + (Dtfree_f)free_node, + NIL(Dtcompar_f), + NIL(Dthash_f), + NIL(Dtmemory_f), + NIL(Dtevent_f) +}; + +static Dtdisc_t edgeDisc = { + offsetof(gmledge,attrlist), + sizeof(Dt_t*), + offsetof(gmledge,link), + NIL(Dtmake_f), + (Dtfree_f)free_edge, + NIL(Dtcompar_f), + NIL(Dthash_f), + NIL(Dtmemory_f), + NIL(Dtevent_f) +}; + +static Dtdisc_t attrDisc = { + offsetof(gmlattr,name), + sizeof(char*), + offsetof(gmlattr,link), + NIL(Dtmake_f), + (Dtfree_f)free_attr, + NIL(Dtcompar_f), + NIL(Dthash_f), + NIL(Dtmemory_f), + NIL(Dtevent_f) +}; + +static Dtdisc_t graphDisc = { + offsetof(gmlgraph,nodelist), + sizeof(Dt_t*), + offsetof(gmlgraph,link), + NIL(Dtmake_f), + (Dtfree_f)free_graph, + NIL(Dtcompar_f), + NIL(Dthash_f), + NIL(Dtmemory_f), + NIL(Dtevent_f) +}; + +static void +initstk () +{ + liststk_sz = 10; + liststk_cnt = 0; + liststk = N_NEW(liststk_sz, Dt_t*); +} + +static void +cleanup () +{ + int i; + + if (liststk) { + for (i = 0; i < liststk_cnt; i++) + dtclose (liststk[i]); + free (liststk); + liststk = NULL; + } + if (L) { + dtclose (L); + L = NULL; + } + if (N) { + free_node (0, N, 0); + N = NULL; + } + if (E) { + free_edge (0, E, 0); + E = NULL; + } + if (G) { + free_graph (0, G, 0); + G = NULL; + } +} + +static void +pushAlist () +{ + Dt_t* lp = dtopen (&attrDisc, Dtqueue); + + if (L) { + if (liststk_cnt == liststk_sz) { + liststk_sz *= 2; + liststk = RALLOC(liststk_sz, liststk, Dt_t*); + } + liststk[liststk_cnt++] = L; + } + L = lp; +} + +static Dt_t* +popAlist () +{ + Dt_t* lp = L; + + if (liststk_cnt) + L = liststk[--liststk_cnt]; + else + L = NULL; + + return lp; +} + +static void +popG () +{ + G = G->parent; +} + +static void +pushG () +{ + gmlgraph* g = NEW(gmlgraph); + + g->attrlist = dtopen(&attrDisc, Dtqueue); + g->nodelist = dtopen(&nodeDisc, Dtqueue); + g->edgelist = dtopen(&edgeDisc, Dtqueue); + g->graphlist = dtopen(&graphDisc, Dtqueue); + g->parent = G; + g->directed = -1; + + if (G) + dtinsert (G->graphlist, g); + + G = g; +} + +static gmlnode* +mkNode () +{ + gmlnode* np = NEW(gmlnode); + np->attrlist = dtopen (&attrDisc, Dtqueue); + np->id = NULL; + return np; +} + +static gmledge* +mkEdge () +{ + gmledge* ep = NEW(gmledge); + ep->attrlist = dtopen (&attrDisc, Dtqueue); + ep->source = NULL; + ep->target = NULL; + return ep; +} + +static gmlattr* +mkAttr (char* name, int sort, int kind, char* str, Dt_t* list) +{ + gmlattr* gp = NEW(gmlattr); + + assert (name || sort); + gp->sort = sort; + gp->kind = kind; + gp->name = name; + if (str) + gp->u.value = str; + else { + if (dtsize (list) == 0) { + dtclose (list); + list = 0; + } + gp->u.lp = list; + } +/* fprintf (stderr, "[%x] %d %d \"%s\" \"%s\" \n", gp, sort, kind, (name?name:""), (str?str:"")); */ + return gp; +} + +static int +setDir (char* d) +{ + gmlgraph* g; + int dir = atoi (d); + + free (d); + if (dir < 0) dir = -1; + else if (dir > 0) dir = 1; + else dir = 0; + G->directed = dir; + + if (dir >= 0) { + for (g = G->parent; g; g = g->parent) { + if (g->directed < 0) + g->directed = dir; + else if (g->directed != dir) + return 1; + } + } + + return 0; +} + +%} +%union { + int i; + char *str; + gmlnode* np; + gmledge* ep; + gmlattr* ap; + Dt_t* list; +} + +%token GRAPH NODE EDGE DIRECTED ID SOURCE TARGET +%token XVAL YVAL WVAL HVAL LABEL GRAPHICS LABELGRAPHICS TYPE FILL +%token OUTLINE OUTLINESTYLE OUTLINEWIDTH WIDTH +%token STYLE LINE POINT +%token TEXT FONTSIZE FONTNAME COLOR +%token INTEGER REAL STRING NAME +%token LIST + +%type node +%type edge +%type attrlist +%type alistitem + +%% +graph : optalist hdr body {gmllexeof(); if (G->parent) popG(); } + | error { cleanup(); YYABORT; } + | + ; + +hdr : GRAPH { pushG(); } + ; + +body : '[' optglist ']' + ; + +optglist : glist + | /* empty */ + ; + +glist : glist glistitem + | glistitem + ; + +glistitem : node { dtinsert (G->nodelist, $1); } + | edge { dtinsert (G->edgelist, $1); } + | hdr body + | DIRECTED INTEGER { + if (setDir($2)) { + yyerror("mixed directed and undirected graphs"); + cleanup (); + YYABORT; + } + } + | alistitem { dtinsert (G->attrlist, $1); } + ; + +node : NODE { N = mkNode(); } '[' nlist ']' { $$ = N; N = NULL; } + ; + +nlist : nlist nlistitem + | nlistitem + ; + +nlistitem : ID INTEGER { N->id = $2; } + | alistitem { dtinsert (N->attrlist, $1); } + ; + +edge : EDGE { E = mkEdge(); } '[' elist ']' { $$ = E; E = NULL; } + ; + +elist : elist elistitem + | elistitem + ; + +elistitem : SOURCE INTEGER { E->source = $2; } + | TARGET INTEGER { E->target = $2; } + | alistitem { dtinsert (E->attrlist, $1); } + ; + +attrlist : '[' {pushAlist(); } optalist ']' { $$ = popAlist(); } + ; + +optalist : alist + | /* empty */ + ; + +alist : alist alistitem { dtinsert (L, $2); } + | alistitem { dtinsert (L, $1); } + ; + +alistitem : NAME INTEGER { $$ = mkAttr ($1, 0, INTEGER, $2, 0); } + | NAME REAL { $$ = mkAttr ($1, 0, REAL, $2, 0); } + | NAME STRING { $$ = mkAttr ($1, 0, STRING, $2, 0); } + | NAME attrlist { $$ = mkAttr ($1, 0, LIST, 0, $2); } + | XVAL REAL { $$ = mkAttr (0, XVAL, REAL, $2, 0); } + | YVAL REAL { $$ = mkAttr (0, YVAL, REAL, $2, 0); } + | WVAL REAL { $$ = mkAttr (0, WVAL, REAL, $2, 0); } + | HVAL REAL { $$ = mkAttr (0, HVAL, REAL, $2, 0); } + | LABEL STRING { $$ = mkAttr (0, LABEL, STRING, $2, 0); } + | GRAPHICS attrlist { $$ = mkAttr (0, GRAPHICS, LIST, 0, $2); } + | LABELGRAPHICS attrlist { $$ = mkAttr (0, LABELGRAPHICS, LIST, 0, $2); } + | TYPE STRING { $$ = mkAttr (0, TYPE, STRING, $2, 0); } + | FILL STRING { $$ = mkAttr (0, FILL, STRING, $2, 0); } + | OUTLINE STRING { $$ = mkAttr (0, OUTLINE, STRING, $2, 0); } + | OUTLINESTYLE STRING { $$ = mkAttr (0, OUTLINESTYLE, STRING, $2, 0); } + | OUTLINEWIDTH INTEGER { $$ = mkAttr (0, OUTLINEWIDTH, INTEGER, $2, 0); } + | WIDTH INTEGER { $$ = mkAttr (0, OUTLINEWIDTH, INTEGER, $2, 0); } + | STYLE STRING { $$ = mkAttr (0, STYLE, STRING, $2, 0); } + | LINE attrlist { $$ = mkAttr (0, LINE, LIST, 0, $2); } + | POINT attrlist { $$ = mkAttr (0, POINT, LIST, 0, $2); } + | TEXT STRING { $$ = mkAttr (0, TEXT, STRING, $2, 0); } + | FONTNAME STRING { $$ = mkAttr (0, FONTNAME, STRING, $2, 0); } + | FONTSIZE INTEGER { $$ = mkAttr (0, FONTNAME, INTEGER, $2, 0); } + | COLOR STRING { $$ = mkAttr (0, COLOR, STRING, $2, 0); } + ; + +%% + +static void deparseList (Dt_t* alist, agxbuf* xb); /* forward declaration */ + +static void +deparseAttr (gmlattr* ap, agxbuf* xb) +{ + if (ap->kind == LIST) { + agxbput (xb, ap->name); + agxbputc (xb, ' '); + deparseList (ap->u.lp, xb); + } + else if (ap->kind == STRING) { + agxbput (xb, ap->name); + agxbput (xb, " \""); + agxbput (xb, ap->u.value); + agxbput (xb, "\""); + } + else { + agxbput (xb, ap->name); + agxbputc (xb, ' '); + agxbput (xb, ap->u.value); + } +} + +static void +deparseList (Dt_t* alist, agxbuf* xb) +{ + gmlattr* ap; + + agxbput (xb, "[ "); + for (ap = dtfirst(alist); ap; ap = dtnext (alist, ap)) { + deparseAttr (ap, xb); + agxbputc (xb, ' '); + } + agxbput (xb, "]"); + +} + +static void +unknown (Agobj_t* obj, gmlattr* ap, agxbuf* xb) +{ + char* str; + + if (ap->kind == LIST) { + deparseList (ap->u.lp, xb); + str = agxbuse (xb); + } + else + str = ap->u.value; + + agsafeset (obj, ap->name, str, ""); +} + +static void +addNodeLabelGraphics (Agnode_t* np, Dt_t* alist, agxbuf* xb, agxbuf* unk) +{ + gmlattr* ap; + int cnt = 0; + + for (ap = dtfirst(alist); ap; ap = dtnext (alist, ap)) { + if (ap->sort == TEXT) { + agsafeset (np, "label", ap->u.value, ""); + } + else if (ap->sort == COLOR) { + agsafeset (np, "fontcolor", ap->u.value, ""); + } + else if (ap->sort == FONTSIZE) { + agsafeset (np, "fontsize", ap->u.value, ""); + } + else if (ap->sort == FONTNAME) { + agsafeset (np, "fontname", ap->u.value, ""); + } + else { + if (cnt) + agxbputc (unk, ' '); + else { + agxbput (unk, "[ "); + } + deparseAttr (ap, unk); + cnt++; + } + } + + if (cnt) { + agxbput (unk, " ]"); + agsafeset (np, "LabelGraphics", agxbuse (unk), ""); + } + else + agxbclear (unk); +} + +static void +addEdgeLabelGraphics (Agedge_t* ep, Dt_t* alist, agxbuf* xb, agxbuf* unk) +{ + gmlattr* ap; + char* x = "0"; + char* y = "0"; + int cnt = 0; + + for (ap = dtfirst(alist); ap; ap = dtnext (alist, ap)) { + if (ap->sort == TEXT) { + agsafeset (ep, "label", ap->u.value, ""); + } + else if (ap->sort == COLOR) { + agsafeset (ep, "fontcolor", ap->u.value, ""); + } + else if (ap->sort == FONTSIZE) { + agsafeset (ep, "fontsize", ap->u.value, ""); + } + else if (ap->sort == FONTNAME) { + agsafeset (ep, "fontname", ap->u.value, ""); + } + else if (ap->sort == XVAL) { + x = ap->u.value; + } + else if (ap->sort == YVAL) { + y = ap->u.value; + } + else { + if (cnt) + agxbputc (unk, ' '); + else { + agxbput (unk, "[ "); + } + deparseAttr (ap, unk); + cnt++; + } + } + + agxbput (xb, x); + agxbputc (xb, ','); + agxbput (xb, y); + agsafeset (ep, "lp", agxbuse (xb), ""); + + if (cnt) { + agxbput (unk, " ]"); + agsafeset (ep, "LabelGraphics", agxbuse (unk), ""); + } + else + agxbclear (unk); +} + +static void +addNodeGraphics (Agnode_t* np, Dt_t* alist, agxbuf* xb, agxbuf* unk) +{ + gmlattr* ap; + char* x = "0"; + char* y = "0"; + char buf[BUFSIZ]; + double d; + int cnt = 0; + + for (ap = dtfirst(alist); ap; ap = dtnext (alist, ap)) { + if (ap->sort == XVAL) { + x = ap->u.value; + } + else if (ap->sort == YVAL) { + y = ap->u.value; + } + else if (ap->sort == WVAL) { + d = atof (ap->u.value); + sprintf (buf, "%.04f", d/72.0); + agsafeset (np, "width", buf, ""); + } + else if (ap->sort == HVAL) { + d = atof (ap->u.value); + sprintf (buf, "%.04f", d/72.0); + agsafeset (np, "height", buf, ""); + } + else if (ap->sort == TYPE) { + agsafeset (np, "shape", ap->u.value, ""); + } + else if (ap->sort == FILL) { + agsafeset (np, "color", ap->u.value, ""); + } + else if (ap->sort == OUTLINE) { + agsafeset (np, "pencolor", ap->u.value, ""); + } + else if ((ap->sort == WIDTH) && (ap->sort == OUTLINEWIDTH )) { + agsafeset (np, "penwidth", ap->u.value, ""); + } + else if ((ap->sort == OUTLINESTYLE) && (ap->sort == OUTLINEWIDTH )) { + agsafeset (np, "style", ap->u.value, ""); + } + else { + if (cnt) + agxbputc (unk, ' '); + else { + agxbput (unk, "[ "); + } + deparseAttr (ap, unk); + cnt++; + } + } + + agxbput (xb, x); + agxbputc (xb, ','); + agxbput (xb, y); + agsafeset (np, "pos", agxbuse (xb), ""); + + if (cnt) { + agxbput (unk, " ]"); + agsafeset (np, "graphics", agxbuse (unk), ""); + } + else + agxbclear (unk); +} + +static void +addEdgePoint (Agedge_t* ep, Dt_t* alist, agxbuf* xb) +{ + gmlattr* ap; + char* x = "0"; + char* y = "0"; + + for (ap = dtfirst(alist); ap; ap = dtnext (alist, ap)) { + if (ap->sort == XVAL) { + x = ap->u.value; + } + else if (ap->sort == YVAL) { + y = ap->u.value; + } + else { + fprintf (stderr, "non-X/Y field in point attribute"); + unknown ((Agobj_t*)ep, ap, xb); + } + } + + if (agxblen(xb)) agxbputc (xb, ' '); + agxbput (xb, x); + agxbputc (xb, ','); + agxbput (xb, y); +} + +static void +addEdgePos (Agedge_t* ep, Dt_t* alist, agxbuf* xb) +{ + gmlattr* ap; + + + for (ap = dtfirst(alist); ap; ap = dtnext (alist, ap)) { + if (ap->sort == POINT) { + addEdgePoint (ep, ap->u.lp, xb); + } + else { + fprintf (stderr, "non-point field in line attribute"); + unknown ((Agobj_t*)ep, ap, xb); + } + } + agsafeset (ep, "pos", agxbuse (xb), ""); +} + +static void +addEdgeGraphics (Agedge_t* ep, Dt_t* alist, agxbuf* xb, agxbuf* unk) +{ + gmlattr* ap; + int cnt = 0; + + for (ap = dtfirst(alist); ap; ap = dtnext (alist, ap)) { + if (ap->sort == WIDTH) { + agsafeset (ep, "penwidth", ap->u.value, ""); + } + else if (ap->sort == STYLE) { + agsafeset (ep, "style", ap->u.value, ""); + } + else if (ap->sort == FILL) { + agsafeset (ep, "color", ap->u.value, ""); + } + else if (ap->sort == LINE) { + addEdgePos (ep, ap->u.lp, xb); + } + else { + if (cnt) + agxbputc (unk, ' '); + else { + agxbput (unk, "[ "); + } + deparseAttr (ap, unk); + cnt++; + } + } + + if (cnt) { + agxbput (unk, " ]"); + agsafeset (ep, "graphics", agxbuse (unk), ""); + } + else + agxbclear(unk); +} + +static void +addAttrs (Agobj_t* obj, Dt_t* alist, agxbuf* xb, agxbuf* unk) +{ + gmlattr* ap; + + for (ap = dtfirst(alist); ap; ap = dtnext (alist, ap)) { + if (ap->sort == GRAPHICS) { + if (AGTYPE(obj) == AGNODE) + addNodeGraphics ((Agnode_t*)obj, ap->u.lp, xb, unk); + else if (AGTYPE(obj) == AGEDGE) + addEdgeGraphics ((Agedge_t*)obj, ap->u.lp, xb, unk); + else + unknown (obj, ap, xb); + } + else if (ap->sort == LABELGRAPHICS) { + if (AGTYPE(obj) == AGNODE) + addNodeLabelGraphics ((Agnode_t*)obj, ap->u.lp, xb, unk); + else if (AGTYPE(obj) == AGEDGE) + addEdgeLabelGraphics ((Agedge_t*)obj, ap->u.lp, xb, unk); + else + unknown (obj, ap, xb); + } + else if ((ap->sort == LABEL) && (AGTYPE(obj) != AGRAPH)) { + agsafeset (obj, "name", ap->u.value, ""); + } + else + unknown (obj, ap, xb); + } +} + +static Agraph_t* +mkGraph (gmlgraph* G, Agraph_t* parent, agxbuf* xb, agxbuf* unk) +{ + Agraph_t* g; + Agnode_t* n; + Agnode_t* h; + Agedge_t* e; + gmlnode* np; + gmledge* ep; + gmlgraph* gp; + + if (parent) { + g = agsubg (parent, NULL, 1); + } + else if (G->directed >= 1) + g = agopen ("", Agdirected, 0); + else + g = agopen ("", Agundirected, 0); + + if (!parent && L) { + addAttrs ((Agobj_t*)g, L, xb, unk); + } + for (np = dtfirst(G->nodelist); np; np = dtnext (G->nodelist, np)) { + if (!np->id) { + fprintf (stderr, "node without an id attribute"); + exit (1); + } + n = agnode (g, np->id, 1); + addAttrs ((Agobj_t*)n, np->attrlist, xb, unk); + } + + for (ep = dtfirst(G->edgelist); ep; ep = dtnext (G->edgelist, ep)) { + if (!ep->source) { + fprintf (stderr, "edge without an source attribute"); + exit (1); + } + if (!ep->target) { + fprintf (stderr, "node without an target attribute"); + exit (1); + } + n = agnode (g, ep->source, 1); + h = agnode (g, ep->target, 1); + e = agedge (g, n, h, NULL, 1); + addAttrs ((Agobj_t*)e, ep->attrlist, xb, unk); + } + for (gp = dtfirst(G->graphlist); gp; gp = dtnext (G->graphlist, gp)) { + mkGraph (gp, g, xb, unk); + } + + addAttrs ((Agobj_t*)g, G->attrlist, xb, unk); + + return g; +} + +Agraph_t* +gml_to_gv (FILE* fp, int cnt, int* errors) +{ + Agraph_t* g; + agxbuf xb; + unsigned char buf[BUFSIZ]; + agxbuf unk; + unsigned char unknownb[BUFSIZ]; + int error; + + if (cnt == 0) + initgmlscan(fp); + else + initgmlscan(0); + + initstk(); + L = NULL; + pushAlist (); + gmlparse (); + + error = gmlerrors(); + *errors |= error; + if (!G || error) + g = NULL; + else { + agxbinit (&xb, BUFSIZ, buf); + agxbinit (&unk, BUFSIZ, unknownb); + g = mkGraph (G, NULL, &xb, &unk); + agxbfree (&xb); + } + + cleanup (); + + return g; +} + diff --git a/cmd/tools/gmlscan.l b/cmd/tools/gmlscan.l new file mode 100644 index 000000000..44d0c0f6a --- /dev/null +++ b/cmd/tools/gmlscan.l @@ -0,0 +1,140 @@ +/* vim:set shiftwidth=4 ts=8: */ + +%{ +#include +#include + +#define GRAPH_EOF_TOKEN '@' /* lex class must be defined below */ + +static int line_num = 1; +static int errors; +static FILE* Ifile; + +void initgmlscan(FILE *ifile) +{ + if (ifile) { + Ifile = ifile; + line_num = 1; + } + errors = 0; +} + +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ((result = fread(buf, 1, max_size, Ifile)) < 0) \ + YY_FATAL_ERROR( "input in flex scanner failed" ) +#endif + +/* buffer for arbitrary length strings (longer than BUFSIZ) */ +static char *Sbuf,*Sptr,*Send; +static void beginstr(void) +{ + if (Sbuf == NULL) { + Sbuf = malloc(BUFSIZ); + Send = Sbuf + BUFSIZ; + } + Sptr = Sbuf; + *Sptr = 0; +} + +static void addstr(char *src) +{ + char c; + if (Sptr > Sbuf) Sptr--; + do { + do {c = *Sptr++ = *src++;} while (c && (Sptr < Send)); + if (c) { + long sz = Send - Sbuf; + long off = Sptr - Sbuf; + sz *= 2; + Sbuf = (char*)realloc(Sbuf,sz); + Send = Sbuf + sz; + Sptr = Sbuf + off; + } + } while (c); +} + +static void endstr(void) { + yylval.str = strdup(Sbuf); +} + +%} +GRAPH_EOF_TOKEN [@] +ASCII [ !#$%\047-\177] +DIGIT [0-9] +L_INT [-+]?{DIGIT}+ +MANTISSA E[-+]?{DIGIT} +L_REAL [-+]?{DIGIT}*\.{DIGIT}*{MANTISSA}? +L_ID [a-zA-Z][a-zA-Z0-9]* +%x qstring +%% +{GRAPH_EOF_TOKEN} return(EOF); +\n line_num++; +^"#".* /* ignore # line */ +[ \t\r] /* ignore whitespace */ + +"graph" return (GRAPH); +"node" return (NODE); +"edge" return (EDGE); +"directed" return (DIRECTED); +"id" return (ID); +"source" return (SOURCE); +"target" return (TARGET); +"x" return (XVAL); +"y" return (YVAL); +"w" return (WVAL); +"h" return (HVAL); +"label" return (LABEL); +"graphics" return (GRAPHICS);; +"LabelGraphics" return (LABELGRAPHICS); +"type" return (TYPE); +"fill" return (FILL); +"outline" return (OUTLINE); +"outlineStyle" return (OUTLINESTYLE); +"outlineWidth" return (OUTLINEWIDTH); +"width" return (WIDTH); +"style" return (STYLE); +"Line" return (LINE); +"point" return (POINT); +"text" return (TEXT); +"fontSize" return (FONTSIZE); +"fontName" return (FONTNAME); +"color" return (COLOR); +{L_INT} { yylval.str = strdup(yytext); return (INTEGER); } +{L_REAL} { yylval.str = strdup(yytext); return (REAL); } +{L_ID} { yylval.str = strdup(yytext); return (NAME); } +["] BEGIN(qstring); beginstr(); + +["] BEGIN(INITIAL); endstr(); return (STRING); +([^"]+) addstr(yytext); + +. return (yytext[0]); + +%% + +void yyerror(char *str) +{ + char buf[BUFSIZ]; + if (errors) + return; + errors = 1; + sprintf(buf," %s in line %d near '%s'\n", str,line_num,yytext); + agerr(AGWARN,buf); +} + +int gmlerrors() +{ + return errors; +} + +void gmllexeof() { unput(GRAPH_EOF_TOKEN); } + +#ifndef YY_CALL_ONLY_ARG +# define YY_CALL_ONLY_ARG void +#endif + +int yywrap(YY_CALL_ONLY_ARG) +{ + return 1; +} +