/misc/src/release/graphviz-2.18-1/src/graphviz-2.18/lib/graph/graphio.c

Go to the documentation of this file.
00001 /* $Id: graphio.c,v 1.2 2005/03/16 22:03:27 erg Exp $ $Revision: 1.2 $ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /**********************************************************
00005 *      This software is part of the graphviz package      *
00006 *                http://www.graphviz.org/                 *
00007 *                                                         *
00008 *            Copyright (c) 1994-2004 AT&T Corp.           *
00009 *                and is licensed under the                *
00010 *            Common Public License, Version 1.0           *
00011 *                      by AT&T Corp.                      *
00012 *                                                         *
00013 *        Information and Software Systems Research        *
00014 *              AT&T Research, Florham Park NJ             *
00015 **********************************************************/
00016 
00017 
00018 #include "libgraph.h"
00019 
00020 #ifdef DMALLOC
00021 #include "dmalloc.h"
00022 #endif
00023 
00024 typedef struct printdict_t {
00025     Dict_t *nodesleft, *edgesleft, *subgleft, *e_insubg, *n_insubg;
00026 } printdict_t;
00027 
00028 /*
00029  * memgets - same api as gets
00030  *
00031  * gets one line at a time from a memory buffer and places it in a user buffer
00032  *    up to a maximum of n characters
00033  *
00034  * returns pointer to obtained line in user buffer, or
00035  * returns NULL when last line read from memory buffer
00036  */
00037 static char *memgets(char *ubuf, int n, FILE * mbuf)
00038 {
00039     static char *mempos;
00040     char *to, *clp;             /* clp = current line pointer */
00041     int i;
00042 
00043     if (!n) {                   /* a call with n==0 (from aglexinit) resets */
00044         mempos = (char *) mbuf; /* cast from FILE* required by API */
00045     }
00046 
00047     clp = to = ubuf;
00048     for (i = 0; i < n - 1; i++) {       /* leave room for terminator */
00049         if (*mempos == '\0') {
00050             if (i) {            /* if mbuf doesn't end in \n, provide one */
00051                 *to++ = '\n';
00052             } else {            /* all done */
00053                 clp = NULL;
00054                 mempos = NULL;
00055             }
00056             break;              /* last line or end-of-buffer */
00057         }
00058         if (*mempos == '\n') {
00059             *to++ = *mempos++;
00060             break;              /* all done with this line */
00061         }
00062         *to++ = *mempos++;      /* copy character */
00063     }
00064     *to++ = '\0';               /* place terminator in ubuf */
00065     return clp;
00066 }
00067 
00068 Agraph_t *agread(FILE * fp)
00069 {
00070     aglexinit(fp, (fgets));     /* use system fgets */
00071     agparse();
00072     return AG.parsed_g;
00073 }
00074 
00075 Agraph_t *agmemread(char *cp)
00076 {
00077     /* cast into a file pointer, but flag that this is in-memory input */
00078     aglexinit((FILE *) cp, (memgets));  /* memgets defined above */
00079     agparse();
00080     return AG.parsed_g;
00081 }
00082 
00083 Agraph_t *agread_usergets(FILE * fp, gets_f usergets)
00084 {
00085     aglexinit(fp, (usergets));  /* usergets provided externally */
00086     agparse();
00087     return AG.parsed_g;
00088 }
00089 
00090 int agerrors(void)
00091 {
00092     return AG.syntax_errors;
00093 }
00094 
00095 /* _agstrcanon:
00096  * Canonicalize an ordinary string if necessary.
00097  */
00098 static char* 
00099 _agstrcanon (char* arg, char* buf)
00100 {
00101     char *s = arg;
00102     unsigned char uc;
00103     char *p = buf;
00104     int cnt = 0;
00105     int has_special = FALSE;
00106     int maybe_num;
00107 
00108     if (ISEMPTYSTR(arg))
00109         return "\"\"";
00110     *p++ = '\"';
00111     uc = *(unsigned char *) s++;
00112     maybe_num = (isdigit(uc) || (uc == '.'));
00113     while (uc) {
00114         if (uc == '\"') {
00115             *p++ = '\\';
00116             has_special = TRUE;
00117         } else {
00118             if (!ISALNUM(uc))
00119                 has_special = TRUE;
00120             else if (maybe_num && (!isdigit(uc) && (uc != '.')))
00121                 has_special = TRUE;
00122         }
00123         *p++ = (char) uc;
00124         uc = *(unsigned char *) s++;
00125         cnt++;
00126         if (cnt % SMALLBUF == 0) {
00127             *p++ = '\\';
00128             *p++ = '\n';
00129             has_special = TRUE;
00130         }
00131     }
00132     *p++ = '\"';
00133     *p = '\0';
00134     if (has_special)
00135         return buf;
00136 
00137     /* use quotes to protect tokens (example, a node named "node") */
00138     if (agtoken(arg) >= 0)
00139         return buf;
00140     return arg;
00141 }
00142 
00143 /* agstrcanon:
00144  * handles both html and ordinary strings.
00145  * canonicalize a string for printing.
00146  * changes to the semantics of this function
00147  * also involve the string scanner in lexer.c
00148  * Unsafe if buf is not large enough.
00149  */
00150 char *agstrcanon(char *arg, char *buf)
00151 {
00152     char *s = arg;
00153     char *p = buf;
00154 
00155     if (aghtmlstr(arg)) {
00156         *p++ = '<';
00157         while (*s)
00158             *p++ = *s++;
00159         *p++ = '>';
00160         *p = '\0';
00161         return buf;
00162     }
00163     else
00164         return (_agstrcanon(arg, buf));
00165 }
00166 
00167 
00168 static void tabover(FILE * fp, int tab)
00169 {
00170     while (tab--)
00171         putc('\t', fp);
00172 }
00173 
00174 static char *getoutputbuffer(char *str)
00175 {
00176     static char *rv;
00177     static int len;
00178     int req;
00179 
00180     req = MAX(2 * strlen(str) + 2, BUFSIZ);
00181     if (req > len) {
00182         if (rv)
00183             rv = realloc(rv, req);
00184         else
00185             rv = malloc(req);
00186         len = req;
00187     }
00188     return rv;
00189 }
00190 
00191 /* agcanonical:
00192  * Safe version of agstrcanon.
00193  */
00194 char*
00195 agcanonical(char *str)
00196 {
00197     return agstrcanon(str, getoutputbuffer(str));
00198 }
00199 
00200 static void write_dict(Agdict_t * dict, FILE * fp)
00201 {
00202     int i, cnt = 0;
00203     Agsym_t *a;
00204 
00205     for (i = 0; i < dtsize(dict->dict); i++) {
00206         a = dict->list[i];
00207         if (ISEMPTYSTR(a->value) == FALSE) {
00208             if (cnt++ == 0)
00209                 fprintf(fp, "\t%s [", dict->name);
00210             else
00211                 fprintf(fp, ", ");
00212             fprintf(fp, "%s=%s", a->name, agcanonical(a->value));
00213         }
00214     }
00215     if (cnt > 0)
00216         fprintf(fp, "];\n");
00217 }
00218 
00219 static void write_diffattr(FILE * fp, int indent, void *obj, void *par,
00220                            Agdict_t * dict)
00221 {
00222     Agsym_t *a;
00223     int i;
00224     char *p, *q;
00225     int cnt = 0;
00226 
00227     for (i = 0; i < dtsize(dict->dict); i++) {
00228         a = dict->list[i];
00229         if (a->printed == FALSE)
00230             continue;
00231         p = agxget(obj, a->index);
00232         if (par)
00233             q = agxget(par, a->index);
00234         else
00235             q = a->value;
00236         if (strcmp(p, q)) {
00237             if (cnt++ == 0) {
00238                 tabover(fp, indent);
00239                 fprintf(fp, "%s [", dict->name);
00240             } else {
00241                 fprintf(fp, ",\n");
00242                 tabover(fp, indent + 1);
00243             }
00244             fprintf(fp, "%s=", agcanonical(a->name));
00245             fprintf(fp, "%s", agcanonical(p));
00246         }
00247     }
00248     if (cnt > 0)
00249         fprintf(fp, "];\n");
00250 }
00251 
00252 static void writeattr(FILE * fp, int *npp, char *name, char *val)
00253 {
00254     fprintf(fp, ++(*npp) > 1 ? ", " : " [");
00255     fprintf(fp, "%s=", agcanonical(name));
00256     fprintf(fp, "%s", agcanonical(val));
00257 }
00258 
00259 void agwrnode(Agraph_t * g, FILE * fp, Agnode_t * n, int full, int indent)
00260 {
00261     char *myval, *defval;
00262     int i, didwrite = FALSE;
00263     int nprint = 0;
00264     Agdict_t *d = n->graph->univ->nodeattr;
00265     Agsym_t *a;
00266 
00267     if (full) {
00268         for (i = 0; i < dtsize(d->dict); i++) {
00269             a = d->list[i];
00270             if (a->printed == FALSE)
00271                 continue;
00272             myval = agget(n, a->name);
00273             if (g == n->graph)
00274                 defval = a->value;
00275             else
00276                 defval = agget(g->proto->n, a->name);
00277             if (strcmp(defval, myval)) {
00278                 if (didwrite == FALSE) {
00279                     tabover(fp, indent);
00280                     fprintf(fp, "%s", agcanonical(n->name));
00281                     didwrite = TRUE;
00282                 }
00283                 writeattr(fp, &nprint, a->name, myval);
00284             }
00285         }
00286         if (didwrite) {
00287             fprintf(fp, (nprint > 0 ? "];\n" : ";\n"));
00288             return;
00289         }
00290     }
00291     if ((agfstout(g, n) == NULL) && (agfstin(g, n) == NULL)) {
00292         tabover(fp, indent);
00293         fprintf(fp, "%s;\n", agcanonical(n->name));
00294     }
00295 }
00296 
00297 /* writenodeandport:
00298  */
00299 static void writenodeandport(FILE * fp, char *node, char *port)
00300 {
00301     char *ss;
00302     fprintf(fp, "%s", agcanonical(node));       /* slimey i know */
00303     if (port && *port) {
00304         if (aghtmlstr(port)) {
00305             fprintf(fp, ":%s", agstrcanon(port, getoutputbuffer(port)));
00306         }
00307         else {
00308             ss = strchr (port, ':');
00309             if (ss) {
00310                 *ss = '\0';
00311                 fprintf(fp, ":%s",
00312                     _agstrcanon(port, getoutputbuffer(port)));
00313                 fprintf(fp, ":%s",
00314                     _agstrcanon(ss+1, getoutputbuffer(ss+1)));
00315                 *ss = ':';
00316             }
00317             else {
00318                 fprintf(fp, ":%s", _agstrcanon(port, getoutputbuffer(port)));
00319             }
00320         }
00321     }
00322 }
00323 
00324 void agwredge(Agraph_t * g, FILE * fp, Agedge_t * e, int list_all)
00325 {
00326     char *myval, *defval, *edgeop, *tport, *hport;
00327     int i, nprint = 0;
00328     Agdict_t *d = e->tail->graph->univ->edgeattr;
00329     Agsym_t *a;
00330 
00331     if (e->attr) {
00332         tport = e->attr[TAILX];
00333         hport = e->attr[HEADX];
00334     }
00335     else tport = hport = "";
00336     if (g->kind & AGFLAG_DIRECTED)
00337         edgeop = "->";
00338     else
00339         edgeop = "--";
00340     writenodeandport(fp, e->tail->name, tport);
00341     fprintf(fp, " %s ", edgeop);
00342     writenodeandport(fp, e->head->name, hport);
00343     if (list_all) {
00344         for (i = 0; i < dtsize(d->dict); i++) {
00345             a = d->list[i];
00346             if ((a->printed == FALSE)
00347                 || ((i == KEYX) && (e->printkey != MUSTPRINT)))
00348                 continue;
00349             myval = agget(e, a->name);
00350             if (g == g->root)
00351                 defval = a->value;
00352             else
00353                 defval = agget(g->proto->e, a->name);
00354             if (strcmp(defval, myval))
00355                 writeattr(fp, &nprint, a->name, myval);
00356         }
00357     }
00358     fprintf(fp, (nprint > 0 ? "];\n" : ";\n"));
00359 }
00360 
00361 Dtdisc_t agEdgedisc = {
00362     offsetof(Agedge_t, id),
00363     sizeof(int),
00364     -1,
00365     NIL(Dtmake_f),
00366     NIL(Dtfree_f),
00367     (Dtcompar_f) agcmpid,
00368     NIL(Dthash_f),
00369     NIL(Dtmemory_f),
00370     NIL(Dtevent_f)
00371 };
00372 static void
00373 write_subg(Agraph_t * g, FILE * fp, Agraph_t * par, int indent,
00374            printdict_t * state)
00375 {
00376     Agraph_t *subg, *meta;
00377     Agnode_t *n, *pn;
00378     Agedge_t *e, *pe;
00379     Dict_t *save_e, *save_n;
00380 
00381     if (indent) {
00382         tabover(fp, indent++);
00383         if (dtsearch(state->subgleft, g->meta_node)) {
00384             if (strncmp(g->name, "_anonymous", 10))
00385                 fprintf(fp, "subgraph %s {\n", agcanonical(g->name));
00386             else
00387                 fprintf(fp, "{\n");     /* no name printed for anonymous subg */
00388             write_diffattr(fp, indent, g, par, g->univ->globattr);
00389             /* The root node and edge environment use the dictionaries,
00390              * not the proto node or edge, so the next level down must
00391              * record differences with the dictionaries.
00392              */
00393             if (par == g->root) {
00394                 pn = NULL;
00395                 pe = NULL;
00396             } else {
00397                 pn = par->proto->n;
00398                 pe = par->proto->e;
00399             }
00400             write_diffattr(fp, indent, g->proto->n, pn, g->univ->nodeattr);
00401             write_diffattr(fp, indent, g->proto->e, pe, g->univ->edgeattr);
00402             dtdelete(state->subgleft, g->meta_node);
00403         } else {
00404             fprintf(fp, "subgraph %s;\n", agcanonical(g->name));
00405             return;
00406         }
00407     } else
00408         write_diffattr(fp, ++indent, g, NULL, g->univ->globattr);
00409 
00410     save_n = state->n_insubg;
00411     save_e = state->e_insubg;
00412     meta = g->meta_node->graph;
00413     state->n_insubg = dtopen(&agNamedisc, Dttree);
00414     state->e_insubg = dtopen(&agOutdisc, Dttree);
00415     for (e = agfstout(meta, g->meta_node); e; e = agnxtout(meta, e)) {
00416         subg = agusergraph(e->head);
00417         write_subg(subg, fp, g, indent, state);
00418     }
00419     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00420         if (dtsearch(state->nodesleft, n)) {
00421             agwrnode(g, fp, n, TRUE, indent);
00422             dtdelete(state->nodesleft, n);
00423         } else {
00424             if (dtsearch(state->n_insubg, n) == NULL) {
00425                 agwrnode(g, fp, n, FALSE, indent);
00426             }
00427         }
00428         dtinsert(save_n, n);
00429     }
00430 
00431     dtdisc(g->outedges, &agEdgedisc, 0);        /* sort by id */
00432     for (e = (Agedge_t *) dtfirst(g->outedges); e;
00433          e = (Agedge_t *) dtnext(g->outedges, e)) {
00434         if (dtsearch(state->edgesleft, e)) {
00435             tabover(fp, indent);
00436             agwredge(g, fp, e, TRUE);
00437             dtdelete(state->edgesleft, e);
00438         } else {
00439             if (dtsearch(state->e_insubg, e) == NULL) {
00440                 tabover(fp, indent);
00441                 agwredge(g, fp, e, FALSE);
00442             }
00443         }
00444         dtinsert(save_e, e);
00445     }
00446     dtdisc(g->outedges, &agOutdisc, 0); /* sort by name */
00447     dtclose(state->n_insubg);
00448     state->n_insubg = save_n;
00449     dtclose(state->e_insubg);
00450     state->e_insubg = save_e;
00451 
00452     if (indent > 1) {
00453         tabover(fp, indent - 1);
00454         fprintf(fp, "}\n");
00455     }
00456 }
00457 
00458 static Dict_t *Copy;
00459 static int copydictf(Dict_t * d, void *a, void *ignored)
00460 {
00461     dtinsert(Copy, (Agsym_t *) a);
00462     return 0;
00463 }
00464 
00465 static void copydict(Dict_t * from, Dict_t * to)
00466 {
00467     Copy = to;
00468     dtwalk(from, copydictf, 0);
00469 }
00470 
00471 static printdict_t *new_printdict_t(Agraph_t * g)
00472 {
00473     printdict_t *rv = NEW(printdict_t);
00474     rv->nodesleft = dtopen(&agNodedisc, Dttree);
00475     copydict(g->nodes, rv->nodesleft);
00476     rv->edgesleft = dtopen(&agEdgedisc, Dttree);
00477     copydict(g->outedges, rv->edgesleft);
00478     rv->n_insubg = dtopen(&agNodedisc, Dttree);
00479     rv->e_insubg = dtopen(&agOutdisc, Dttree);
00480     rv->subgleft = dtopen(&agNodedisc, Dttree);
00481     copydict(g->meta_node->graph->nodes, rv->subgleft);
00482     return rv;
00483 }
00484 
00485 static void free_printdict_t(printdict_t * dict)
00486 {
00487     dtclose(dict->nodesleft);
00488     dtclose(dict->n_insubg);
00489     dtclose(dict->edgesleft);
00490     dtclose(dict->e_insubg);
00491     dtclose(dict->subgleft);
00492     free(dict);
00493 }
00494 
00495 int agwrite(Agraph_t * g, FILE * fp)
00496 {
00497     printdict_t *p;
00498     char *t0, *t1;
00499 
00500     /* write the graph header */
00501     t0 = (AG_IS_STRICT(g)) ? "strict " : "";
00502     t1 = (AG_IS_DIRECTED(g)) ? "digraph" : "graph";
00503     if (strncmp(g->name, "_anonymous", 10))
00504         fprintf(fp, "%s%s %s {\n", t0, t1, agcanonical(g->name));
00505     else
00506         fprintf(fp, "%s%s {\n", t0, t1);
00507 
00508     /* write the top level attribute defs */
00509     write_dict(g->univ->globattr, fp);
00510     write_dict(g->univ->nodeattr, fp);
00511     write_dict(g->univ->edgeattr, fp);
00512 
00513     /* write the graph contents */
00514     p = new_printdict_t(g);
00515     write_subg(g, fp, (Agraph_t *) 0, 0, p);
00516     fprintf(fp, "}\n");
00517     free_printdict_t(p);
00518     return ferror(fp);
00519 }

Generated on Mon Mar 31 19:03:26 2008 for Graphviz by  doxygen 1.5.1