From 93795c0fd2bf58e3fda1fb3bad6693e6b41d2af5 Mon Sep 17 00:00:00 2001 From: ellson Date: Thu, 26 Feb 2009 03:10:13 +0000 Subject: [PATCH] Fixes a bug (# unknown) where tcldot or any gv language binding couldn't write dot,xdot,plain,canon to memory or to Tcl_Channels. Problem was that agwrite() used fprintf() & putc() directly to an output file. Fix is to add an output discipline to libgraph so that we can provide our own fwrite() equiv. The discipline defaults to regular fwrite() and ferror() for backward compat. Adds new functions to libgraph: extern void agsetodisc( size_t (*fwrite) (FILE *fp, const char *s, size_t len), int (*ferror) (FILE *fp)); extern void agfprintf(FILE *fp, const char *format, ...); extern int agputs(const char *s, FILE *fp); extern int agputc(int c, FILE *fp); --- cmd/dot/dot.c | 5 +- cmd/tools/gvpack.c | 2 + lib/common/output.c | 28 ++++---- lib/graph/attribs.c | 13 ++++ lib/graph/graph.h | 4 ++ lib/graph/graphio.c | 117 ++++++++++++++++++++++---------- lib/graph/libgraph.h | 5 +- lib/gvc/gvc.c | 3 + lib/gvc/gvdevice.c | 10 +++ lib/gvc/gvio.h | 4 +- lib/gvc/gvplugin.c | 2 + plugin/core/gvrender_core_dot.c | 8 +-- tclpkg/tcldot/tcldot.c | 2 + 13 files changed, 147 insertions(+), 56 deletions(-) diff --git a/cmd/dot/dot.c b/cmd/dot/dot.c index 39c584241..7bf38f94e 100644 --- a/cmd/dot/dot.c +++ b/cmd/dot/dot.c @@ -23,6 +23,7 @@ #endif #include "gvc.h" +#include "gvio.h" #ifndef WIN32_DLL #ifdef GVDLL @@ -127,6 +128,8 @@ static graph_t *create_test_graph(void) /* Create a new graph */ #ifndef WITH_CGRAPH + aginit(); + agsetodisc(gvfwrite, gvferror); g = agopen("new_graph", AGDIGRAPH); #else /* WITH_CGRAPH */ g = agopen("new_graph", Agdirected,NIL(Agdisc_t *)); @@ -167,7 +170,7 @@ int main(int argc, char **argv) #endif /* WITH_CGRAPH */ - Gvc = gvNEWcontext(lt_preloaded_symbols, DEMAND_LOADING); + Gvc = gvContextPlugins(lt_preloaded_symbols, DEMAND_LOADING); gvParseArgs(Gvc, argc, argv); #ifndef WIN32 diff --git a/cmd/tools/gvpack.c b/cmd/tools/gvpack.c index ded297adf..590ec7e8f 100644 --- a/cmd/tools/gvpack.c +++ b/cmd/tools/gvpack.c @@ -31,6 +31,7 @@ #include #include "gvc.h" +#include "gvio.h" #include "render.h" #include "neatoprocs.h" #include "ingraphs.h" @@ -162,6 +163,7 @@ static void init(int argc, char *argv[]) int c; aginit(); + agsetodisc(gvfwrite, gvferror); while ((c = getopt(argc, argv, ":ngvum:o:G:?")) != -1) { switch (c) { case 'n': diff --git a/lib/common/output.c b/lib/common/output.c index a576de9d0..8b0930ec4 100644 --- a/lib/common/output.c +++ b/lib/common/output.c @@ -25,7 +25,7 @@ double YF_off; /* Y_off in inches */ static void printptf(FILE * f, pointf pt) { - fprintf(f, " %.5g %.5g", PS2INCH(pt.x), PS2INCH(YDIR(pt.y))); + agfprintf(f, " %.5g %.5g", PS2INCH(pt.x), PS2INCH(YDIR(pt.y))); } /* setYInvert: @@ -70,12 +70,12 @@ static void writenodeandport(FILE * fp, node_t * node, char *port) #else name = agcanonStr (agnameof(node)); #endif - fprintf(fp, "%s", name); /* slimey i know */ + agfprintf(fp, "%s", name); /* slimey i know */ if (port && *port) #ifndef WITH_CGRAPH - fprintf(fp, ":%s", agcanonical(port)); + agfprintf(fp, ":%s", agcanonical(port)); #else - fprintf(fp, ":%s", agcanonStr(port)); + agfprintf(fp, ":%s", agcanonStr(port)); #endif } @@ -94,14 +94,14 @@ void write_plain(GVJ_t * job, graph_t * g, FILE * f, boolean extend) // setup_graph(job, g); setYInvert(g); pt = GD_bb(g).UR; - fprintf(f, "graph %.5g %.5g %.5g\n", job->zoom, PS2INCH(pt.x), PS2INCH(pt.y)); + agfprintf(f, "graph %.5g %.5g %.5g\n", job->zoom, PS2INCH(pt.x), PS2INCH(pt.y)); for (n = agfstnode(g); n; n = agnxtnode(g, n)) { if (IS_CLUST_NODE(n)) continue; #ifndef WITH_CGRAPH - fprintf(f, "node %s ", agcanonical(agnameof(n))); + agfprintf(f, "node %s ", agcanonical(agnameof(n))); #else - fprintf(f, "node %s ", agcanonStr(agnameof(n))); + agfprintf(f, "node %s ", agcanonStr(agnameof(n))); #endif printptf(f, ND_coord(n)); if (ND_label(n)->html) /* if html, get original text */ @@ -112,7 +112,7 @@ void write_plain(GVJ_t * job, graph_t * g, FILE * f, boolean extend) #endif else lbl = canon(agraphof(n),ND_label(n)->text); - fprintf(f, " %.5g %.5g %s %s %s %s %s\n", + agfprintf(f, " %.5g %.5g %s %s %s %s %s\n", ND_width(n), ND_height(n), lbl, late_nnstring(n, N_style, "solid"), ND_shape(n)->name, @@ -148,11 +148,11 @@ void write_plain(GVJ_t * job, graph_t * g, FILE * f, boolean extend) bz = ED_spl(e)->list[i]; splinePoints += bz.size; } - fprintf(f, "edge "); + agfprintf(f, "edge "); writenodeandport(f, agtail(e), tport); - fprintf(f, " "); + agfprintf(f, " "); writenodeandport(f, aghead(e), hport); - fprintf(f, " %d", splinePoints); + agfprintf(f, " %d", splinePoints); for (i = 0; i < ED_spl(e)->size; i++) { bz = ED_spl(e)->list[i]; for (j = 0; j < bz.size; j++) @@ -160,14 +160,14 @@ void write_plain(GVJ_t * job, graph_t * g, FILE * f, boolean extend) } } if (ED_label(e)) { - fprintf(f, " %s", canon(agraphof(agtail(e)),ED_label(e)->text)); + agfprintf(f, " %s", canon(agraphof(agtail(e)),ED_label(e)->text)); printptf(f, ED_label(e)->pos); } - fprintf(f, " %s %s\n", late_nnstring(e, E_style, "solid"), + agfprintf(f, " %s %s\n", late_nnstring(e, E_style, "solid"), late_nnstring(e, E_color, DEFAULT_COLOR)); } } - fprintf(f, "stop\n"); + agfprintf(f, "stop\n"); } static void set_record_rects(node_t * n, field_t * f, agxbuf * xb) diff --git a/lib/graph/attribs.c b/lib/graph/attribs.c index 8e223376c..35fe9948b 100644 --- a/lib/graph/attribs.c +++ b/lib/graph/attribs.c @@ -324,6 +324,17 @@ Agsym_t *agprvattr(void *obj, Agsym_t *a) return (Agsym_t *)dtprev(dict->dict, a); } +static size_t agfwrite(FILE *fp, const char *s, size_t len) +{ + return fwrite(s, sizeof(char), len, fp); +} + +static int agferror(FILE *fp) +{ + return ferror(fp); +} + + /* this is normally called by the aginit() macro */ void aginitlib(int gs, int ns, int es) { @@ -332,6 +343,8 @@ void aginitlib(int gs, int ns, int es) AG.node_nbytes = ns; AG.edge_nbytes = es; AG.init_called = TRUE; + AG.fwrite = agfwrite; + AG.ferror = agferror; initproto(); } else if ((AG.graph_nbytes != gs) || (AG.node_nbytes != ns) diff --git a/lib/graph/graph.h b/lib/graph/graph.h index f8300f012..824493e83 100644 --- a/lib/graph/graph.h +++ b/lib/graph/graph.h @@ -163,6 +163,10 @@ extern "C" { extern void agreadline(int); extern void agsetfile(char *); extern Agraph_t *agmemread(char *); + extern void agsetodisc(size_t (*fwrite) (FILE *fp, const char *s, size_t len), int (*ferror) (FILE *fp)); + extern void agfprintf(FILE *fp, const char *format, ...); + extern int agputs(const char *s, FILE *fp); + extern int agputc(int c, FILE *fp); extern int agwrite(Agraph_t *, FILE *); extern int agerrors(void); extern Agraph_t *agprotograph(void); diff --git a/lib/graph/graphio.c b/lib/graph/graphio.c index c3e2cf895..4edef00a2 100644 --- a/lib/graph/graphio.c +++ b/lib/graph/graphio.c @@ -16,10 +16,7 @@ #include "libgraph.h" - -#ifdef DMALLOC -#include "dmalloc.h" -#endif +#include typedef struct printdict_t { Dict_t *nodesleft, *edgesleft, *subgleft, *e_insubg, *n_insubg; @@ -180,11 +177,61 @@ char *agstrcanon(char *arg, char *buf) return (_agstrcanon(arg, buf)); } +void agsetodisc(size_t (*fwrite) (FILE *fp, const char *s, size_t len), int (*ferror) (FILE *fp)) +{ + AG.fwrite = fwrite; + AG.ferror = ferror; +} + +/* agfprintf: + * Note that this function is unsafe due to the fixed buffer size. + * It should only be used when the caller is sure the input will not + * overflow the buffer. In particular, it should be avoided for + * input coming from users. Also, if vsnprintf is available, the + * code should check for return values to use it safely. + */ +void agfprintf(FILE *fp, const char *format, ...) +{ + char buf[BUFSIZ]; + size_t len; + va_list argp; + + va_start(argp, format); +#ifdef HAVE_VSNPRINTF + len = vsnprintf((char *)buf, sizeof(buf), format, argp); +#else + len = vsprintf((char *)buf, format, argp); +#endif + va_end(argp); + + AG.fwrite(fp, buf, len); +} + +int agputs(const char *s, FILE *fp) +{ + size_t len = strlen(s); + + if (AG.fwrite(fp, s, len) != len) { + return EOF; + } + return +1; +} + + +int agputc(int c, FILE *fp) +{ + const char cc = c; + + if (AG.fwrite (fp, &cc, 1) != 1) { + return EOF; + } + return c; +} static void tabover(FILE * fp, int tab) { while (tab--) - putc('\t', fp); + agputc('\t', fp); } static char *getoutputbuffer(char *str) @@ -232,14 +279,14 @@ static void write_dict(Agdict_t * dict, FILE * fp) a = dict->list[i]; if (ISEMPTYSTR(a->value) == FALSE) { if (cnt++ == 0) - fprintf(fp, "\t%s [", dict->name); + agfprintf(fp, "\t%s [", dict->name); else - fprintf(fp, ", "); - fprintf(fp, "%s=%s", a->name, agcanonical(a->value)); + agfprintf(fp, ", "); + agfprintf(fp, "%s=%s", a->name, agcanonical(a->value)); } } if (cnt > 0) - fprintf(fp, "];\n"); + agfprintf(fp, "];\n"); } static void write_diffattr(FILE * fp, int indent, void *obj, void *par, @@ -262,24 +309,24 @@ static void write_diffattr(FILE * fp, int indent, void *obj, void *par, if (strcmp(p, q)) { if (cnt++ == 0) { tabover(fp, indent); - fprintf(fp, "%s [", dict->name); + agfprintf(fp, "%s [", dict->name); } else { - fprintf(fp, ",\n"); + agfprintf(fp, ",\n"); tabover(fp, indent + 1); } - fprintf(fp, "%s=", agcanonical(a->name)); - fprintf(fp, "%s", agcanonical(p)); + agfprintf(fp, "%s=", agcanonical(a->name)); + agfprintf(fp, "%s", agcanonical(p)); } } if (cnt > 0) - fprintf(fp, "];\n"); + agfprintf(fp, "];\n"); } static void writeattr(FILE * fp, int *npp, char *name, char *val) { - fprintf(fp, ++(*npp) > 1 ? ", " : " ["); - fprintf(fp, "%s=", agcanonical(name)); - fprintf(fp, "%s", agcanonical(val)); + agfprintf(fp, ++(*npp) > 1 ? ", " : " ["); + agfprintf(fp, "%s=", agcanonical(name)); + agfprintf(fp, "%s", agcanonical(val)); } void agwrnode(Agraph_t * g, FILE * fp, Agnode_t * n, int full, int indent) @@ -303,20 +350,20 @@ void agwrnode(Agraph_t * g, FILE * fp, Agnode_t * n, int full, int indent) if (strcmp(defval, myval)) { if (didwrite == FALSE) { tabover(fp, indent); - fprintf(fp, "%s", agcanonical(n->name)); + agfprintf(fp, "%s", agcanonical(n->name)); didwrite = TRUE; } writeattr(fp, &nprint, a->name, myval); } } if (didwrite) { - fprintf(fp, (nprint > 0 ? "];\n" : ";\n")); + agfprintf(fp, (nprint > 0 ? "];\n" : ";\n")); return; } } if ((agfstout(g, n) == NULL) && (agfstin(g, n) == NULL)) { tabover(fp, indent); - fprintf(fp, "%s;\n", agcanonical(n->name)); + agfprintf(fp, "%s;\n", agcanonical(n->name)); } } @@ -325,23 +372,23 @@ void agwrnode(Agraph_t * g, FILE * fp, Agnode_t * n, int full, int indent) static void writenodeandport(FILE * fp, char *node, char *port) { char *ss; - fprintf(fp, "%s", agcanonical(node)); /* slimey i know */ + agfprintf(fp, "%s", agcanonical(node)); /* slimey i know */ if (port && *port) { if (aghtmlstr(port)) { - fprintf(fp, ":%s", agstrcanon(port, getoutputbuffer(port))); + agfprintf(fp, ":%s", agstrcanon(port, getoutputbuffer(port))); } else { ss = strchr (port, ':'); if (ss) { *ss = '\0'; - fprintf(fp, ":%s", + agfprintf(fp, ":%s", _agstrcanon(port, getoutputbuffer(port))); - fprintf(fp, ":%s", + agfprintf(fp, ":%s", _agstrcanon(ss+1, getoutputbuffer(ss+1))); *ss = ':'; } else { - fprintf(fp, ":%s", _agstrcanon(port, getoutputbuffer(port))); + agfprintf(fp, ":%s", _agstrcanon(port, getoutputbuffer(port))); } } } @@ -364,7 +411,7 @@ void agwredge(Agraph_t * g, FILE * fp, Agedge_t * e, int list_all) else edgeop = "--"; writenodeandport(fp, e->tail->name, tport); - fprintf(fp, " %s ", edgeop); + agfprintf(fp, " %s ", edgeop); writenodeandport(fp, e->head->name, hport); if (list_all) { for (i = 0; i < dtsize(d->dict); i++) { @@ -381,7 +428,7 @@ void agwredge(Agraph_t * g, FILE * fp, Agedge_t * e, int list_all) writeattr(fp, &nprint, a->name, myval); } } - fprintf(fp, (nprint > 0 ? "];\n" : ";\n")); + agfprintf(fp, (nprint > 0 ? "];\n" : ";\n")); } Dtdisc_t agEdgedisc = { @@ -408,9 +455,9 @@ write_subg(Agraph_t * g, FILE * fp, Agraph_t * par, int indent, tabover(fp, indent++); if (dtsearch(state->subgleft, g->meta_node)) { if (strncmp(g->name, "_anonymous", 10)) - fprintf(fp, "subgraph %s {\n", agcanonical(g->name)); + agfprintf(fp, "subgraph %s {\n", agcanonical(g->name)); else - fprintf(fp, "{\n"); /* no name printed for anonymous subg */ + agfprintf(fp, "{\n"); /* no name printed for anonymous subg */ write_diffattr(fp, indent, g, par, g->univ->globattr); /* The root node and edge environment use the dictionaries, * not the proto node or edge, so the next level down must @@ -427,7 +474,7 @@ write_subg(Agraph_t * g, FILE * fp, Agraph_t * par, int indent, write_diffattr(fp, indent, g->proto->e, pe, g->univ->edgeattr); dtdelete(state->subgleft, g->meta_node); } else { - fprintf(fp, "subgraph %s;\n", agcanonical(g->name)); + agfprintf(fp, "subgraph %s;\n", agcanonical(g->name)); return; } } else @@ -477,7 +524,7 @@ write_subg(Agraph_t * g, FILE * fp, Agraph_t * par, int indent, if (indent > 1) { tabover(fp, indent - 1); - fprintf(fp, "}\n"); + agfprintf(fp, "}\n"); } } @@ -527,9 +574,9 @@ int agwrite(Agraph_t * g, FILE * fp) t0 = (AG_IS_STRICT(g)) ? "strict " : ""; t1 = (AG_IS_DIRECTED(g)) ? "digraph" : "graph"; if (strncmp(g->name, "_anonymous", 10)) - fprintf(fp, "%s%s %s {\n", t0, t1, agcanonical(g->name)); + agfprintf(fp, "%s%s %s {\n", t0, t1, agcanonical(g->name)); else - fprintf(fp, "%s%s {\n", t0, t1); + agfprintf(fp, "%s%s {\n", t0, t1); /* write the top level attribute defs */ write_dict(g->univ->globattr, fp); @@ -539,7 +586,7 @@ int agwrite(Agraph_t * g, FILE * fp) /* write the graph contents */ p = new_printdict_t(g); write_subg(g, fp, (Agraph_t *) 0, 0, p); - fprintf(fp, "}\n"); + agfprintf(fp, "}\n"); free_printdict_t(p); - return ferror(fp); + return AG.ferror(fp); } diff --git a/lib/graph/libgraph.h b/lib/graph/libgraph.h index cd61a1d18..ec1ce104e 100644 --- a/lib/graph/libgraph.h +++ b/lib/graph/libgraph.h @@ -25,8 +25,9 @@ extern "C" { #if _PACKAGE_ast #include #else -#include +#include #include +#include #ifndef MSWIN32 #include #endif @@ -128,6 +129,8 @@ extern "C" { char *linebuf; short syntax_errors; unsigned char accepting_state, init_called; + size_t (*fwrite) (FILE *fp, const char *s, size_t len); + int (*ferror) (FILE *fp); } AG; /* follow structs used in graph parser */ diff --git a/lib/gvc/gvc.c b/lib/gvc/gvc.c index a6b2b6837..45b02bd09 100644 --- a/lib/gvc/gvc.c +++ b/lib/gvc/gvc.c @@ -24,6 +24,7 @@ #include "gvcint.h" #include "gvcproc.h" #include "gvconfig.h" +#include "gvio.h" GVC_t *gvContext(void) { @@ -31,6 +32,7 @@ GVC_t *gvContext(void) #ifndef WITH_CGRAPH aginit(); + agsetodisc(gvfwrite, gvferror); agnodeattr(NULL, "label", NODENAME_ESC); #else agattr(NULL, AGNODE, "label", NODENAME_ESC); @@ -47,6 +49,7 @@ GVC_t *gvContextPlugins(const lt_symlist_t *builtins, int demand_loading) #ifndef WITH_CGRAPH aginit(); + agsetodisc(gvfwrite, gvferror); agnodeattr(NULL, "label", NODENAME_ESC); #else agattr(NULL, AGNODE, "label", NODENAME_ESC); diff --git a/lib/gvc/gvdevice.c b/lib/gvc/gvdevice.c index 9b07a1999..1b39e3d80 100644 --- a/lib/gvc/gvdevice.c +++ b/lib/gvc/gvdevice.c @@ -252,6 +252,16 @@ size_t gvwrite (GVJ_t * job, const char *s, size_t len) return len; } +int gvferror (FILE* fp) +{ + return 0; +} + +size_t gvfwrite (FILE* fp, const char *s, size_t len) +{ + return gvwrite((GVJ_t*)fp, s, len); +} + int gvputs(GVJ_t * job, const char *s) { size_t len = strlen(s); diff --git a/lib/gvc/gvio.h b/lib/gvc/gvio.h index 50694460b..f54d7c407 100644 --- a/lib/gvc/gvio.h +++ b/lib/gvc/gvio.h @@ -37,7 +37,9 @@ extern "C" { #endif /*end visual studio*/ - extern size_t gvwrite (GVJ_t * job, const char *s, size_t len); + extern size_t gvwrite (GVJ_t * job, const char *s, size_t len); + extern size_t gvfwrite (FILE * job, const char *s, size_t len); + extern int gvferror (FILE * job); extern int gvputc(GVJ_t * job, int c); extern int gvputs(GVJ_t * job, const char *s); extern void gvprintf(GVJ_t * job, const char *format, ...); diff --git a/lib/gvc/gvplugin.c b/lib/gvc/gvplugin.c index ade0cc3fd..70b429c95 100644 --- a/lib/gvc/gvplugin.c +++ b/lib/gvc/gvplugin.c @@ -29,6 +29,7 @@ #include "gvcjob.h" #include "gvcint.h" #include "gvcproc.h" +#include "gvio.h" #include "const.h" @@ -466,6 +467,7 @@ Agraph_t * gvplugin_graph(GVC_t * gvc) #ifndef WITH_CGRAPH aginit(); + agsetodisc(gvfwrite, gvferror); /* set persistent attributes here */ agraphattr(NULL, "label", ""); agraphattr(NULL, "rankdir", ""); diff --git a/plugin/core/gvrender_core_dot.c b/plugin/core/gvrender_core_dot.c index c4234c7cd..c77e8f97e 100644 --- a/plugin/core/gvrender_core_dot.c +++ b/plugin/core/gvrender_core_dot.c @@ -415,20 +415,20 @@ static void dot_end_graph(GVJ_t *job) switch (job->render.id) { case FORMAT_PLAIN: - write_plain(job, g, job->output_file, FALSE); + write_plain(job, g, (FILE*)job, FALSE); break; case FORMAT_PLAIN_EXT: - write_plain(job, g, job->output_file, TRUE); + write_plain(job, g, (FILE*)job, TRUE); break; case FORMAT_DOT: case FORMAT_CANON: if (!(job->flags & OUTPUT_NOT_REQUIRED)) - agwrite(g, job->output_file); + agwrite(g, (FILE*)job); break; case FORMAT_XDOT: xdot_end_graph(g); if (!(job->flags & OUTPUT_NOT_REQUIRED)) - agwrite(g, job->output_file); + agwrite(g, (FILE*)job); break; } } diff --git a/tclpkg/tcldot/tcldot.c b/tclpkg/tcldot/tcldot.c index 5017e665f..b8e192e21 100644 --- a/tclpkg/tcldot/tcldot.c +++ b/tclpkg/tcldot/tcldot.c @@ -21,6 +21,7 @@ #include #include "render.h" #include "gvc.h" +#include "gvio.h" #include "tclhandle.h" #ifndef CONST84 @@ -1721,6 +1722,7 @@ int Tcldot_Init(Tcl_Interp * interp) #endif aginit(); + agsetodisc(gvfwrite, gvferror); /* set persistent attributes here */ agnodeattr(NULL, "label", NODENAME_ESC); -- 2.40.0