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 static int
00096 _is_number_char(char c)
00097 {
00098 return (isdigit(c) || c == '.' || c == '-');
00099 }
00100
00101
00102
00103
00104 static char*
00105 _agstrcanon (char* arg, char* buf)
00106 {
00107 char *s = arg;
00108 unsigned char uc;
00109 char *p = buf;
00110 int cnt = 0;
00111 int has_special = FALSE;
00112 int maybe_num;
00113 int backslash_pending = FALSE;
00114
00115 if (ISEMPTYSTR(arg))
00116 return "\"\"";
00117 *p++ = '\"';
00118 uc = *(unsigned char *) s++;
00119 maybe_num = _is_number_char(uc);
00120 while (uc) {
00121 if (uc == '\"') {
00122 *p++ = '\\';
00123 has_special = TRUE;
00124 } else {
00125 if (!ISALNUM(uc))
00126 has_special = TRUE;
00127 else if (maybe_num && !_is_number_char(uc))
00128 has_special = TRUE;
00129 }
00130 *p++ = (char) uc;
00131 uc = *(unsigned char *) s++;
00132 cnt++;
00133 if (uc && backslash_pending && !((_is_number_char(p[-1]) || isalpha(p[-1])) && (_is_number_char(uc) || isalpha(uc)))) {
00134 *p++ = '\\';
00135 *p++ = '\n';
00136 has_special = TRUE;
00137 backslash_pending = FALSE;
00138 } else if (uc && cnt % SMALLBUF == 0) {
00139 if (!((_is_number_char(p[-1]) || isalpha(p[-1])) && (_is_number_char(uc) || isalpha(uc)))) {
00140 *p++ = '\\';
00141 *p++ = '\n';
00142 has_special = TRUE;
00143 } else {
00144 backslash_pending = TRUE;
00145 }
00146 }
00147 }
00148 *p++ = '\"';
00149 *p = '\0';
00150 if (has_special)
00151 return buf;
00152
00153
00154 if (agtoken(arg) >= 0)
00155 return buf;
00156 return arg;
00157 }
00158
00159
00160
00161
00162
00163
00164
00165
00166 char *agstrcanon(char *arg, char *buf)
00167 {
00168 char *s = arg;
00169 char *p = buf;
00170
00171 if (aghtmlstr(arg)) {
00172 *p++ = '<';
00173 while (*s)
00174 *p++ = *s++;
00175 *p++ = '>';
00176 *p = '\0';
00177 return buf;
00178 }
00179 else
00180 return (_agstrcanon(arg, buf));
00181 }
00182
00183
00184 static void tabover(FILE * fp, int tab)
00185 {
00186 while (tab--)
00187 putc('\t', fp);
00188 }
00189
00190 static char *getoutputbuffer(char *str)
00191 {
00192 static char *rv;
00193 static int len;
00194 int req;
00195
00196 req = MAX(2 * strlen(str) + 2, BUFSIZ);
00197 if (req > len) {
00198 if (rv)
00199 rv = realloc(rv, req);
00200 else
00201 rv = malloc(req);
00202 len = req;
00203 }
00204 return rv;
00205 }
00206
00207
00208
00209
00210 char*
00211 agcanonical(char *str)
00212 {
00213 return agstrcanon(str, getoutputbuffer(str));
00214 }
00215
00216
00217
00218
00219
00220 char*
00221 agcanon(char *str)
00222 {
00223 return _agstrcanon(str, getoutputbuffer(str));
00224 }
00225
00226 static void write_dict(Agdict_t * dict, FILE * fp)
00227 {
00228 int i, cnt = 0;
00229 Agsym_t *a;
00230
00231 for (i = 0; i < dtsize(dict->dict); i++) {
00232 a = dict->list[i];
00233 if (ISEMPTYSTR(a->value) == FALSE) {
00234 if (cnt++ == 0)
00235 fprintf(fp, "\t%s [", dict->name);
00236 else
00237 fprintf(fp, ", ");
00238 fprintf(fp, "%s=%s", a->name, agcanonical(a->value));
00239 }
00240 }
00241 if (cnt > 0)
00242 fprintf(fp, "];\n");
00243 }
00244
00245 static void write_diffattr(FILE * fp, int indent, void *obj, void *par,
00246 Agdict_t * dict)
00247 {
00248 Agsym_t *a;
00249 int i;
00250 char *p, *q;
00251 int cnt = 0;
00252
00253 for (i = 0; i < dtsize(dict->dict); i++) {
00254 a = dict->list[i];
00255 if (a->printed == FALSE)
00256 continue;
00257 p = agxget(obj, a->index);
00258 if (par)
00259 q = agxget(par, a->index);
00260 else
00261 q = a->value;
00262 if (strcmp(p, q)) {
00263 if (cnt++ == 0) {
00264 tabover(fp, indent);
00265 fprintf(fp, "%s [", dict->name);
00266 } else {
00267 fprintf(fp, ",\n");
00268 tabover(fp, indent + 1);
00269 }
00270 fprintf(fp, "%s=", agcanonical(a->name));
00271 fprintf(fp, "%s", agcanonical(p));
00272 }
00273 }
00274 if (cnt > 0)
00275 fprintf(fp, "];\n");
00276 }
00277
00278 static void writeattr(FILE * fp, int *npp, char *name, char *val)
00279 {
00280 fprintf(fp, ++(*npp) > 1 ? ", " : " [");
00281 fprintf(fp, "%s=", agcanonical(name));
00282 fprintf(fp, "%s ", agcanonical(val));
00283 }
00284
00285 void agwrnode(Agraph_t * g, FILE * fp, Agnode_t * n, int full, int indent)
00286 {
00287 char *myval, *defval;
00288 int i, didwrite = FALSE;
00289 int nprint = 0;
00290 Agdict_t *d = n->graph->univ->nodeattr;
00291 Agsym_t *a;
00292
00293 if (full) {
00294 for (i = 0; i < dtsize(d->dict); i++) {
00295 a = d->list[i];
00296 if (a->printed == FALSE)
00297 continue;
00298 myval = agget(n, a->name);
00299 if (g == n->graph)
00300 defval = a->value;
00301 else
00302 defval = agget(g->proto->n, a->name);
00303 if (strcmp(defval, myval)) {
00304 if (didwrite == FALSE) {
00305 tabover(fp, indent);
00306 fprintf(fp, "%s", agcanonical(n->name));
00307 didwrite = TRUE;
00308 }
00309 writeattr(fp, &nprint, a->name, myval);
00310 }
00311 }
00312 if (didwrite) {
00313 fprintf(fp, (nprint > 0 ? "];\n" : ";\n"));
00314 return;
00315 }
00316 }
00317 if ((agfstout(g, n) == NULL) && (agfstin(g, n) == NULL)) {
00318 tabover(fp, indent);
00319 fprintf(fp, "%s;\n", agcanonical(n->name));
00320 }
00321 }
00322
00323
00324
00325 static void writenodeandport(FILE * fp, char *node, char *port)
00326 {
00327 char *ss;
00328 fprintf(fp, "%s", agcanonical(node));
00329 if (port && *port) {
00330 if (aghtmlstr(port)) {
00331 fprintf(fp, ":%s", agstrcanon(port, getoutputbuffer(port)));
00332 }
00333 else {
00334 ss = strchr (port, ':');
00335 if (ss) {
00336 *ss = '\0';
00337 fprintf(fp, ":%s",
00338 _agstrcanon(port, getoutputbuffer(port)));
00339 fprintf(fp, ":%s",
00340 _agstrcanon(ss+1, getoutputbuffer(ss+1)));
00341 *ss = ':';
00342 }
00343 else {
00344 fprintf(fp, ":%s", _agstrcanon(port, getoutputbuffer(port)));
00345 }
00346 }
00347 }
00348 }
00349
00350 void agwredge(Agraph_t * g, FILE * fp, Agedge_t * e, int list_all)
00351 {
00352 char *myval, *defval, *edgeop, *tport, *hport;
00353 int i, nprint = 0;
00354 Agdict_t *d = e->tail->graph->univ->edgeattr;
00355 Agsym_t *a;
00356
00357 if (e->attr) {
00358 tport = e->attr[TAILX];
00359 hport = e->attr[HEADX];
00360 }
00361 else tport = hport = "";
00362 if (g->kind & AGFLAG_DIRECTED)
00363 edgeop = "->";
00364 else
00365 edgeop = "--";
00366 writenodeandport(fp, e->tail->name, tport);
00367 fprintf(fp, " %s ", edgeop);
00368 writenodeandport(fp, e->head->name, hport);
00369 if (list_all) {
00370 for (i = 0; i < dtsize(d->dict); i++) {
00371 a = d->list[i];
00372 if ((a->printed == FALSE)
00373 || ((i == KEYX) && (e->printkey != MUSTPRINT)))
00374 continue;
00375 myval = agget(e, a->name);
00376 if (g == g->root)
00377 defval = a->value;
00378 else
00379 defval = agget(g->proto->e, a->name);
00380 if (strcmp(defval, myval))
00381 writeattr(fp, &nprint, a->name, myval);
00382 }
00383 }
00384 fprintf(fp, (nprint > 0 ? "];\n" : ";\n"));
00385 }
00386
00387 Dtdisc_t agEdgedisc = {
00388 offsetof(Agedge_t, id),
00389 sizeof(int),
00390 -1,
00391 NIL(Dtmake_f),
00392 NIL(Dtfree_f),
00393 (Dtcompar_f) agcmpid,
00394 NIL(Dthash_f),
00395 NIL(Dtmemory_f),
00396 NIL(Dtevent_f)
00397 };
00398 static void
00399 write_subg(Agraph_t * g, FILE * fp, Agraph_t * par, int indent,
00400 printdict_t * state)
00401 {
00402 Agraph_t *subg, *meta;
00403 Agnode_t *n, *pn;
00404 Agedge_t *e, *pe;
00405 Dict_t *save_e, *save_n;
00406
00407 if (indent) {
00408 tabover(fp, indent++);
00409 if (dtsearch(state->subgleft, g->meta_node)) {
00410 if (strncmp(g->name, "_anonymous", 10))
00411 fprintf(fp, "subgraph %s {\n", agcanonical(g->name));
00412 else
00413 fprintf(fp, "{\n");
00414 write_diffattr(fp, indent, g, par, g->univ->globattr);
00415
00416
00417
00418
00419 if (par == g->root) {
00420 pn = NULL;
00421 pe = NULL;
00422 } else {
00423 pn = par->proto->n;
00424 pe = par->proto->e;
00425 }
00426 write_diffattr(fp, indent, g->proto->n, pn, g->univ->nodeattr);
00427 write_diffattr(fp, indent, g->proto->e, pe, g->univ->edgeattr);
00428 dtdelete(state->subgleft, g->meta_node);
00429 } else {
00430 fprintf(fp, "subgraph %s;\n", agcanonical(g->name));
00431 return;
00432 }
00433 } else
00434 write_diffattr(fp, ++indent, g, NULL, g->univ->globattr);
00435
00436 save_n = state->n_insubg;
00437 save_e = state->e_insubg;
00438 meta = g->meta_node->graph;
00439 state->n_insubg = dtopen(&agNamedisc, Dttree);
00440 state->e_insubg = dtopen(&agOutdisc, Dttree);
00441 for (e = agfstout(meta, g->meta_node); e; e = agnxtout(meta, e)) {
00442 subg = agusergraph(e->head);
00443 write_subg(subg, fp, g, indent, state);
00444 }
00445 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00446 if (dtsearch(state->nodesleft, n)) {
00447 agwrnode(g, fp, n, TRUE, indent);
00448 dtdelete(state->nodesleft, n);
00449 } else {
00450 if (dtsearch(state->n_insubg, n) == NULL) {
00451 agwrnode(g, fp, n, FALSE, indent);
00452 }
00453 }
00454 dtinsert(save_n, n);
00455 }
00456
00457 dtdisc(g->outedges, &agEdgedisc, 0);
00458 for (e = (Agedge_t *) dtfirst(g->outedges); e;
00459 e = (Agedge_t *) dtnext(g->outedges, e)) {
00460 if (dtsearch(state->edgesleft, e)) {
00461 tabover(fp, indent);
00462 agwredge(g, fp, e, TRUE);
00463 dtdelete(state->edgesleft, e);
00464 } else {
00465 if (dtsearch(state->e_insubg, e) == NULL) {
00466 tabover(fp, indent);
00467 agwredge(g, fp, e, FALSE);
00468 }
00469 }
00470 dtinsert(save_e, e);
00471 }
00472 dtdisc(g->outedges, &agOutdisc, 0);
00473 dtclose(state->n_insubg);
00474 state->n_insubg = save_n;
00475 dtclose(state->e_insubg);
00476 state->e_insubg = save_e;
00477
00478 if (indent > 1) {
00479 tabover(fp, indent - 1);
00480 fprintf(fp, "}\n");
00481 }
00482 }
00483
00484 static Dict_t *Copy;
00485 static int copydictf(Dict_t * d, void *a, void *ignored)
00486 {
00487 dtinsert(Copy, (Agsym_t *) a);
00488 return 0;
00489 }
00490
00491 static void copydict(Dict_t * from, Dict_t * to)
00492 {
00493 Copy = to;
00494 dtwalk(from, copydictf, 0);
00495 }
00496
00497 static printdict_t *new_printdict_t(Agraph_t * g)
00498 {
00499 printdict_t *rv = NEW(printdict_t);
00500 rv->nodesleft = dtopen(&agNodedisc, Dttree);
00501 copydict(g->nodes, rv->nodesleft);
00502 rv->edgesleft = dtopen(&agEdgedisc, Dttree);
00503 copydict(g->outedges, rv->edgesleft);
00504 rv->n_insubg = dtopen(&agNodedisc, Dttree);
00505 rv->e_insubg = dtopen(&agOutdisc, Dttree);
00506 rv->subgleft = dtopen(&agNodedisc, Dttree);
00507 copydict(g->meta_node->graph->nodes, rv->subgleft);
00508 return rv;
00509 }
00510
00511 static void free_printdict_t(printdict_t * dict)
00512 {
00513 dtclose(dict->nodesleft);
00514 dtclose(dict->n_insubg);
00515 dtclose(dict->edgesleft);
00516 dtclose(dict->e_insubg);
00517 dtclose(dict->subgleft);
00518 free(dict);
00519 }
00520
00521 int agwrite(Agraph_t * g, FILE * fp)
00522 {
00523 printdict_t *p;
00524 char *t0, *t1;
00525
00526
00527 t0 = (AG_IS_STRICT(g)) ? "strict " : "";
00528 t1 = (AG_IS_DIRECTED(g)) ? "digraph" : "graph";
00529 if (strncmp(g->name, "_anonymous", 10))
00530 fprintf(fp, "%s%s %s {\n", t0, t1, agcanonical(g->name));
00531 else
00532 fprintf(fp, "%s%s {\n", t0, t1);
00533
00534
00535 write_dict(g->univ->globattr, fp);
00536 write_dict(g->univ->nodeattr, fp);
00537 write_dict(g->univ->edgeattr, fp);
00538
00539
00540 p = new_printdict_t(g);
00541 write_subg(g, fp, (Agraph_t *) 0, 0, p);
00542 fprintf(fp, "}\n");
00543 free_printdict_t(p);
00544 return ferror(fp);
00545 }