00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
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
00030
00031
00032
00033
00034
00035
00036
00037 static char *memgets(char *ubuf, int n, FILE * mbuf)
00038 {
00039 static char *mempos;
00040 char *to, *clp;
00041 int i;
00042
00043 if (!n) {
00044 mempos = (char *) mbuf;
00045 }
00046
00047 clp = to = ubuf;
00048 for (i = 0; i < n - 1; i++) {
00049 if (*mempos == '\0') {
00050 if (i) {
00051 *to++ = '\n';
00052 } else {
00053 clp = NULL;
00054 mempos = NULL;
00055 }
00056 break;
00057 }
00058 if (*mempos == '\n') {
00059 *to++ = *mempos++;
00060 break;
00061 }
00062 *to++ = *mempos++;
00063 }
00064 *to++ = '\0';
00065 return clp;
00066 }
00067
00068 Agraph_t *agread(FILE * fp)
00069 {
00070 aglexinit(fp, (fgets));
00071 agparse();
00072 return AG.parsed_g;
00073 }
00074
00075 Agraph_t *agmemread(char *cp)
00076 {
00077
00078 aglexinit((FILE *) cp, (memgets));
00079 agparse();
00080 return AG.parsed_g;
00081 }
00082
00083 Agraph_t *agread_usergets(FILE * fp, gets_f usergets)
00084 {
00085 aglexinit(fp, (usergets));
00086 agparse();
00087 return AG.parsed_g;
00088 }
00089
00090 int agerrors(void)
00091 {
00092 return AG.syntax_errors;
00093 }
00094
00095
00096
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
00138 if (agtoken(arg) >= 0)
00139 return buf;
00140 return arg;
00141 }
00142
00143
00144
00145
00146
00147
00148
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
00192
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
00298
00299 static void writenodeandport(FILE * fp, char *node, char *port)
00300 {
00301 char *ss;
00302 fprintf(fp, "%s", agcanonical(node));
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");
00388 write_diffattr(fp, indent, g, par, g->univ->globattr);
00389
00390
00391
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);
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);
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
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
00509 write_dict(g->univ->globattr, fp);
00510 write_dict(g->univ->nodeattr, fp);
00511 write_dict(g->univ->edgeattr, fp);
00512
00513
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 }