00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include "render.h"
00018 #include "agxbuf.h"
00019 #include "htmltable.h"
00020 #include "entities.h"
00021 #include "ps.h"
00022
00023 #ifndef WIN32
00024 #include <unistd.h>
00025 #endif
00026
00027
00028
00029
00030 nodequeue *new_queue(int sz)
00031 {
00032 nodequeue *q = NEW(nodequeue);
00033
00034 if (sz <= 1)
00035 sz = 2;
00036 q->head = q->tail = q->store = N_NEW(sz, node_t *);
00037 q->limit = q->store + sz;
00038 return q;
00039 }
00040
00041 void free_queue(nodequeue * q)
00042 {
00043 free(q->store);
00044 free(q);
00045 }
00046
00047 void enqueue(nodequeue * q, node_t * n)
00048 {
00049 *(q->tail++) = n;
00050 if (q->tail >= q->limit)
00051 q->tail = q->store;
00052 }
00053
00054 node_t *dequeue(nodequeue * q)
00055 {
00056 node_t *n;
00057 if (q->head == q->tail)
00058 n = NULL;
00059 else {
00060 n = *(q->head++);
00061 if (q->head >= q->limit)
00062 q->head = q->store;
00063 }
00064 return n;
00065 }
00066
00067
00068 int late_attr(void *obj, char *name)
00069 {
00070 attrsym_t *a;
00071 if ((a = agfindattr(obj, name)) != 0)
00072 return a->index;
00073 else
00074 return -1;
00075 }
00076
00077 int late_int(void *obj, attrsym_t * attr, int def, int low)
00078 {
00079 char *p;
00080 int rv;
00081 if (attr == NULL)
00082 return def;
00083 p = agxget(obj, attr->index);
00084 if (p[0] == '\0')
00085 return def;
00086 if ((rv = atoi(p)) < low)
00087 rv = low;
00088 return rv;
00089 }
00090
00091 double late_double(void *obj, attrsym_t * attr, double def, double low)
00092 {
00093 char *p;
00094 double rv;
00095
00096 if (attr == NULL)
00097 return def;
00098 p = agxget(obj, attr->index);
00099 if (p[0] == '\0')
00100 return def;
00101 if ((rv = atof(p)) < low)
00102 rv = low;
00103 return rv;
00104 }
00105
00106 char *late_string(void *obj, attrsym_t * attr, char *def)
00107 {
00108 if (attr == NULL)
00109 return def;
00110 return agxget(obj, attr->index);
00111 }
00112
00113 char *late_nnstring(void *obj, attrsym_t * attr, char *def)
00114 {
00115 char *rv = late_string(obj, attr, def);
00116 if (rv[0] == '\0')
00117 rv = def;
00118 return rv;
00119 }
00120
00121 boolean late_bool(void *obj, attrsym_t * attr, int def)
00122 {
00123 if (attr == NULL)
00124 return def;
00125 return mapbool(agxget(obj, attr->index));
00126 }
00127
00128
00129 node_t *UF_find(node_t * n)
00130 {
00131 while (ND_UF_parent(n) && (ND_UF_parent(n) != n)) {
00132 if (ND_UF_parent(n)->u.UF_parent)
00133 ND_UF_parent(n) = ND_UF_parent(n)->u.UF_parent;
00134 n = ND_UF_parent(n);
00135 }
00136 return n;
00137 }
00138
00139 node_t *UF_union(node_t * u, node_t * v)
00140 {
00141 if (u == v)
00142 return u;
00143 if (ND_UF_parent(u) == NULL) {
00144 ND_UF_parent(u) = u;
00145 ND_UF_size(u) = 1;
00146 } else
00147 u = UF_find(u);
00148 if (ND_UF_parent(v) == NULL) {
00149 ND_UF_parent(v) = v;
00150 ND_UF_size(v) = 1;
00151 } else
00152 v = UF_find(v);
00153 if (u->id > v->id) {
00154 ND_UF_parent(u) = v;
00155 ND_UF_size(v) += ND_UF_size(u);
00156 } else {
00157 ND_UF_parent(v) = u;
00158 ND_UF_size(u) += ND_UF_size(v);
00159 v = u;
00160 }
00161 return v;
00162 }
00163
00164 void UF_remove(node_t * u, node_t * v)
00165 {
00166 assert(ND_UF_size(u) == 1);
00167 ND_UF_parent(u) = u;
00168 ND_UF_size(v) -= ND_UF_size(u);
00169 }
00170
00171 void UF_singleton(node_t * u)
00172 {
00173 ND_UF_size(u) = 1;
00174 ND_UF_parent(u) = NULL;
00175 ND_ranktype(u) = NORMAL;
00176 }
00177
00178 void UF_setname(node_t * u, node_t * v)
00179 {
00180 assert(u == UF_find(u));
00181 ND_UF_parent(u) = v;
00182 ND_UF_size(v) += ND_UF_size(u);
00183 }
00184
00185 point coord(node_t * n)
00186 {
00187 pointf pf;
00188 pf.x = ND_pos(n)[0];
00189 pf.y = ND_pos(n)[1];
00190 return cvt2pt(pf);
00191 }
00192
00193
00194 #define W_DEGREE 5
00195
00196
00197
00198
00199
00200
00201
00202
00203 pointf Bezier(pointf * V, int degree, double t, pointf * Left, pointf * Right)
00204 {
00205 int i, j;
00206 pointf Vtemp[W_DEGREE + 1][W_DEGREE + 1];
00207
00208
00209 for (j = 0; j <= degree; j++) {
00210 Vtemp[0][j] = V[j];
00211 }
00212
00213
00214 for (i = 1; i <= degree; i++) {
00215 for (j = 0; j <= degree - i; j++) {
00216 Vtemp[i][j].x =
00217 (1.0 - t) * Vtemp[i - 1][j].x + t * Vtemp[i - 1][j + 1].x;
00218 Vtemp[i][j].y =
00219 (1.0 - t) * Vtemp[i - 1][j].y + t * Vtemp[i - 1][j + 1].y;
00220 }
00221 }
00222
00223 if (Left != NULL)
00224 for (j = 0; j <= degree; j++)
00225 Left[j] = Vtemp[j][0];
00226 if (Right != NULL)
00227 for (j = 0; j <= degree; j++)
00228 Right[j] = Vtemp[degree - j][j];
00229
00230 return (Vtemp[degree][0]);
00231 }
00232
00233 #ifdef DEBUG
00234 edge_t *debug_getedge(graph_t * g, char *s0, char *s1)
00235 {
00236 node_t *n0, *n1;
00237 n0 = agfindnode(g, s0);
00238 n1 = agfindnode(g, s1);
00239 if (n0 && n1)
00240 return agfindedge(g, n0, n1);
00241 else
00242 return NULL;
00243 }
00244 #endif
00245
00246 #if !defined(MSWIN32) && !defined(WIN32)
00247 #include <pwd.h>
00248 static unsigned char userbuf[SMALLBUF];
00249 static agxbuf xb;
00250
00251 static void cleanup(void)
00252 {
00253 agxbfree(&xb);
00254 }
00255 #endif
00256
00257 char *gvUsername(void)
00258 {
00259 char *user = NULL;
00260 #if !defined(MSWIN32) && !defined(WIN32)
00261 static int first = 1;
00262 struct passwd *p;
00263 if (first) {
00264 agxbinit(&xb, SMALLBUF, userbuf);
00265 atexit(cleanup);
00266 first = 0;
00267 }
00268 p = (struct passwd *) getpwuid(getuid());
00269 if (p) {
00270 agxbputc(&xb, '(');
00271 agxbput(&xb, p->pw_name);
00272 agxbput(&xb, ") ");
00273 #ifdef SVR4
00274 agxbput(&xb, p->pw_comment);
00275 #else
00276 agxbput(&xb, p->pw_gecos);
00277 #endif
00278 user = agxbuse(&xb);
00279 }
00280 #endif
00281 if (user == NULL)
00282 user = getenv ("USERNAME");
00283 if (user == NULL)
00284 user = "Bill Gates";
00285 return user;
00286 }
00287
00288
00289
00290
00291
00292
00293 static char *Fgets(FILE * fp)
00294 {
00295 static int bsize = 0;
00296 static char *buf;
00297 char *lp;
00298 int len;
00299
00300 len = 0;
00301 do {
00302 if (bsize - len < BUFSIZ) {
00303 bsize += BUFSIZ;
00304 buf = grealloc(buf, bsize);
00305 }
00306 lp = fgets(buf + len, bsize - len, fp);
00307 if (lp == 0)
00308 break;
00309 len += strlen(lp);
00310 } while (buf[len - 1] != '\n');
00311
00312 if (len > 0)
00313 return buf;
00314 else
00315 return 0;
00316 }
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334 char *safefile(char *filename)
00335 {
00336 static boolean onetime = TRUE;
00337 static char *safefilename = NULL;
00338 char *str, *p;
00339
00340 if (!filename || !filename[0])
00341 return NULL;
00342 if (HTTPServerEnVar) {
00343
00344
00345
00346
00347
00348 if (!Gvfilepath) {
00349 if (onetime) {
00350 agerr(AGWARN,
00351 "file loading is disabled because the environment contains: %s\n"
00352 "and there is no GV_FILE_PATH variable.\n",
00353 HTTPServerEnVar);
00354 onetime = FALSE;
00355 }
00356 return NULL;
00357 }
00358
00359
00360
00361
00362 safefilename = realloc(safefilename,
00363 (strlen(Gvfilepath) + strlen(filename) + 1));
00364
00365 strcpy(safefilename, Gvfilepath);
00366 str = filename;
00367 if ((p = strrchr(str, '/')))
00368 str = ++p;
00369 if ((p = strrchr(str, '\\')))
00370 str = ++p;
00371 if ((p = strrchr(str, ':')))
00372 str = ++p;
00373 strcat(safefilename, str);
00374
00375 if (onetime && str != filename) {
00376 agerr(AGWARN, "Path provided to file: \"%s\" has been ignored"
00377 " because files are only permitted to be loaded from the \"%s\""
00378 " directory when running in an http server.\n", filename,
00379 Gvfilepath);
00380 onetime = FALSE;
00381 }
00382
00383 return safefilename;
00384 }
00385
00386 return filename;
00387 }
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399 void cat_libfile(FILE * ofp, char **arglib, char **stdlib)
00400 {
00401 FILE *fp;
00402 char *p, **s, *bp;
00403 int i;
00404 boolean use_stdlib = TRUE;
00405
00406
00407 if (arglib) {
00408 for (i = 0; use_stdlib && ((p = arglib[i])); i++) {
00409 if (*p == '\0')
00410 use_stdlib = FALSE;
00411 }
00412 }
00413 if (use_stdlib)
00414 for (s = stdlib; *s; s++) {
00415 fputs(*s, ofp);
00416 fputc('\n', ofp);
00417 }
00418 if (arglib) {
00419 for (i = 0; (p = arglib[i]) != 0; i++) {
00420 if (*p == '\0')
00421 continue;
00422 p = safefile(p);
00423 if ((fp = fopen(p, "r"))) {
00424 while ((bp = Fgets(fp)))
00425 fputs(bp, ofp);
00426 fputc('\n', ofp);
00427 } else
00428 agerr(AGWARN, "can't open library file %s\n", p);
00429 }
00430 }
00431 }
00432
00433 void cat_preamble(GVJ_t *job, char **arglib)
00434 {
00435 cat_libfile(job->output_file, arglib, ps_txt);
00436 }
00437
00438 int maptoken(char *p, char **name, int *val)
00439 {
00440 int i;
00441 char *q;
00442
00443 for (i = 0; (q = name[i]) != 0; i++)
00444 if (p && streq(p, q))
00445 break;
00446 return val[i];
00447 }
00448
00449 boolean mapbool(char *p)
00450 {
00451 if (p == NULL)
00452 return FALSE;
00453 if (!strcasecmp(p, "false"))
00454 return FALSE;
00455 if (!strcasecmp(p, "true"))
00456 return TRUE;
00457 return atoi(p);
00458 }
00459
00460 point dotneato_closest(splines * spl, point p)
00461 {
00462 int i, j, k, besti, bestj;
00463 double bestdist2, d2, dlow2, dhigh2;
00464 double low, high, t;
00465 pointf c[4], pt2, pt;
00466 point rv;
00467 bezier bz;
00468
00469 besti = bestj = -1;
00470 bestdist2 = 1e+38;
00471 P2PF(p, pt);
00472 for (i = 0; i < spl->size; i++) {
00473 bz = spl->list[i];
00474 for (j = 0; j < bz.size; j++) {
00475 pointf b;
00476
00477 b.x = bz.list[j].x;
00478 b.y = bz.list[j].y;
00479 d2 = DIST2(b, pt);
00480 if ((bestj == -1) || (d2 < bestdist2)) {
00481 besti = i;
00482 bestj = j;
00483 bestdist2 = d2;
00484 }
00485 }
00486 }
00487
00488 bz = spl->list[besti];
00489 j = bestj / 3;
00490 if (j >= spl->size)
00491 j--;
00492 for (k = 0; k < 4; k++) {
00493 c[k].x = bz.list[j + k].x;
00494 c[k].y = bz.list[j + k].y;
00495 }
00496 low = 0.0;
00497 high = 1.0;
00498 dlow2 = DIST2(c[0], pt);
00499 dhigh2 = DIST2(c[3], pt);
00500 do {
00501 t = (low + high) / 2.0;
00502 pt2 = Bezier(c, 3, t, NULL, NULL);
00503 if (fabs(dlow2 - dhigh2) < 1.0)
00504 break;
00505 if (fabs(high - low) < .00001)
00506 break;
00507 if (dlow2 < dhigh2) {
00508 high = t;
00509 dhigh2 = DIST2(pt2, pt);
00510 } else {
00511 low = t;
00512 dlow2 = DIST2(pt2, pt);
00513 }
00514 } while (1);
00515 PF2P(pt2, rv);
00516 return rv;
00517 }
00518
00519 point spline_at_y(splines * spl, int y)
00520 {
00521 int i, j;
00522 double low, high, d, t;
00523 pointf c[4], pt2;
00524 point pt;
00525 static bezier bz;
00526
00527
00528
00529
00530 #if 0
00531 static splines *mem = NULL;
00532
00533 if (mem != spl) {
00534 mem = spl;
00535 #endif
00536 for (i = 0; i < spl->size; i++) {
00537 bz = spl->list[i];
00538 if (BETWEEN(bz.list[bz.size - 1].y, y, bz.list[0].y))
00539 break;
00540 }
00541 #if 0
00542 }
00543 #endif
00544 if (y > bz.list[0].y)
00545 pt = bz.list[0];
00546 else if (y < bz.list[bz.size - 1].y)
00547 pt = bz.list[bz.size - 1];
00548 else {
00549 for (i = 0; i < bz.size; i += 3) {
00550 for (j = 0; j < 3; j++) {
00551 if ((bz.list[i + j].y <= y) && (y <= bz.list[i + j + 1].y))
00552 break;
00553 if ((bz.list[i + j].y >= y) && (y >= bz.list[i + j + 1].y))
00554 break;
00555 }
00556 if (j < 3)
00557 break;
00558 }
00559 assert(i < bz.size);
00560 for (j = 0; j < 4; j++) {
00561 c[j].x = bz.list[i + j].x;
00562 c[j].y = bz.list[i + j].y;
00563
00564 if ((j > 0) && (c[j].y > c[j - 1].y))
00565 c[j].y = c[j - 1].y;
00566 }
00567 low = 0.0;
00568 high = 1.0;
00569 do {
00570 t = (low + high) / 2.0;
00571 pt2 = Bezier(c, 3, t, NULL, NULL);
00572 d = pt2.y - y;
00573 if (ABS(d) <= 1)
00574 break;
00575 if (d < 0)
00576 high = t;
00577 else
00578 low = t;
00579 } while (1);
00580 pt.x = pt2.x;
00581 pt.y = pt2.y;
00582 }
00583 pt.y = y;
00584 return pt;
00585 }
00586
00587 point neato_closest(splines * spl, point p)
00588 {
00589
00590
00591 return spline_at_y(spl, p.y);
00592 }
00593
00594 static int Tflag;
00595 void gvToggle(int s)
00596 {
00597 Tflag = !Tflag;
00598 #if !defined(MSWIN32) && !defined(WIN32)
00599 signal(SIGUSR1, gvToggle);
00600 #endif
00601 }
00602
00603 int test_toggle()
00604 {
00605 return Tflag;
00606 }
00607
00608 void common_init_node(node_t * n)
00609 {
00610 char *str;
00611 int lbl_kind = LT_NONE;
00612 graph_t *sg = n->graph;
00613
00614 ND_width(n) =
00615 late_double(n, N_width, DEFAULT_NODEWIDTH, MIN_NODEWIDTH);
00616 ND_height(n) =
00617 late_double(n, N_height, DEFAULT_NODEHEIGHT, MIN_NODEHEIGHT);
00618 if (N_label == NULL)
00619 str = NODENAME_ESC;
00620 else {
00621 str = agxget(n, N_label->index);
00622 if (aghtmlstr(str)) lbl_kind = LT_HTML;
00623 }
00624 if (lbl_kind)
00625 str = strdup(str);
00626 else
00627 str = strdup_and_subst_obj(str, (void*)n);
00628 ND_shape(n) =
00629 bind_shape(late_nnstring(n, N_shape, DEFAULT_NODESHAPE), n);
00630 if (shapeOf(n) == SH_RECORD)
00631 lbl_kind |= LT_RECD;
00632 ND_label(n) = make_label(sg->root, lbl_kind, str,
00633 late_double(n, N_fontsize, DEFAULT_FONTSIZE, MIN_FONTSIZE),
00634 late_nnstring(n, N_fontname, DEFAULT_FONTNAME),
00635 late_nnstring(n, N_fontcolor, DEFAULT_COLOR));
00636 if (lbl_kind == LT_HTML) {
00637 if (make_html_label(sg->root, ND_label(n), n))
00638 agerr(AGPREV, "in label of node %s\n", n->name);
00639 }
00640 ND_showboxes(n) = late_int(n, N_showboxes, 0, 0);
00641 ND_shape(n)->fns->initfn(n);
00642 }
00643
00644 static void edgeError(edge_t * e, char *msg)
00645 {
00646 char *edgeop;
00647
00648 if (AG_IS_DIRECTED(e->tail->graph))
00649 edgeop = "->";
00650 else
00651 edgeop = "--";
00652 agerr(AGPREV, "for %s of edge %s %s %s\n",
00653 msg, e->tail->name, edgeop, e->head->name);
00654 }
00655
00656 struct fontinfo {
00657 double fontsize;
00658 char *fontname;
00659 char *fontcolor;
00660 };
00661
00662 static void initFontEdgeAttr(edge_t * e, struct fontinfo *fi)
00663 {
00664 fi->fontsize =
00665 late_double(e, E_fontsize, DEFAULT_FONTSIZE, MIN_FONTSIZE);
00666 fi->fontname = late_nnstring(e, E_fontname, DEFAULT_FONTNAME);
00667 fi->fontcolor = late_nnstring(e, E_fontcolor, DEFAULT_COLOR);
00668 }
00669
00670 static void
00671 initFontLabelEdgeAttr(edge_t * e, struct fontinfo *fi,
00672 struct fontinfo *lfi)
00673 {
00674 if (!fi->fontname)
00675 initFontEdgeAttr(e, fi);
00676 lfi->fontsize =
00677 late_double(e, E_labelfontsize, fi->fontsize, MIN_FONTSIZE);
00678 lfi->fontname = late_nnstring(e, E_labelfontname, fi->fontname);
00679 lfi->fontcolor = late_nnstring(e, E_labelfontcolor, fi->fontcolor);
00680 }
00681
00682
00683
00684
00685
00686 static boolean
00687 noClip(edge_t *e, attrsym_t* sym)
00688 {
00689 char *str;
00690 boolean rv = FALSE;
00691
00692 if (sym) {
00693 str = agxget(e,sym->index);
00694 if (str && str[0]) rv = !mapbool(str);
00695 else rv = FALSE;
00696 }
00697 return rv;
00698 }
00699
00700
00701
00702 static port
00703 chkPort (port (*pf)(node_t*, char*, char*), node_t* n, char* s)
00704 {
00705 port pt;
00706 char* cp = strchr(s,':');
00707
00708 if (cp) {
00709 *cp = '\0';
00710 pt = pf(n, s, cp+1);
00711 *cp = ':';
00712 }
00713 else
00714 pt = pf(n, s, NULL);
00715 return pt;
00716 }
00717
00718
00719 int common_init_edge(edge_t * e)
00720 {
00721 char *s;
00722 int r = 0;
00723 struct fontinfo fi;
00724 struct fontinfo lfi;
00725 graph_t *sg = e->tail->graph;
00726 int lbl_kind;
00727
00728 fi.fontname = NULL;
00729 lfi.fontname = NULL;
00730 if (E_label && (s = agxget(e, E_label->index)) && (s[0])) {
00731 r = 1;
00732 if (aghtmlstr(s)) lbl_kind = LT_HTML;
00733 else lbl_kind = LT_NONE;
00734 if (lbl_kind)
00735 s = strdup(s);
00736 else
00737 s = strdup_and_subst_obj(s, (void*)e);
00738 initFontEdgeAttr(e, &fi);
00739 ED_label(e) = make_label(sg->root, lbl_kind, s,
00740 fi.fontsize, fi.fontname, fi.fontcolor);
00741 if (lbl_kind == LT_HTML) {
00742 if (make_html_label(sg->root, ED_label(e), e) == 1)
00743 edgeError(e, "label");
00744 }
00745 GD_has_labels(sg) |= EDGE_LABEL;
00746 ED_label_ontop(e) =
00747 mapbool(late_string(e, E_label_float, "false"));
00748 }
00749
00750
00751
00752 if (E_headlabel && (s = agxget(e, E_headlabel->index)) && (s[0])) {
00753 if (aghtmlstr(s)) lbl_kind = LT_HTML;
00754 else lbl_kind = LT_NONE;
00755 if (lbl_kind)
00756 s = strdup(s);
00757 else
00758 s = strdup_and_subst_obj(s, (void*)e);
00759 initFontLabelEdgeAttr(e, &fi, &lfi);
00760 ED_head_label(e) = make_label(sg->root, lbl_kind, s,
00761 lfi.fontsize, lfi.fontname, lfi.fontcolor);
00762 if (lbl_kind) {
00763 if (make_html_label(sg->root, ED_head_label(e), e) == 1)
00764 edgeError(e, "head label");
00765 }
00766 GD_has_labels(sg) |= HEAD_LABEL;
00767 }
00768 if (E_taillabel && (s = agxget(e, E_taillabel->index)) && (s[0])) {
00769 if (aghtmlstr(s)) lbl_kind = LT_HTML;
00770 else lbl_kind = LT_NONE;
00771 if (lbl_kind)
00772 s = strdup(s);
00773 else
00774 s = strdup_and_subst_obj(s, (void*)e);
00775 if (!lfi.fontname)
00776 initFontLabelEdgeAttr(e, &fi, &lfi);
00777 ED_tail_label(e) = make_label(sg->root, lbl_kind, s,
00778 lfi.fontsize, lfi.fontname, lfi.fontcolor);
00779 if (lbl_kind) {
00780 if (make_html_label(sg->root, ED_tail_label(e), e) == 1)
00781 edgeError(e, "tail label");
00782 }
00783 GD_has_labels(sg) |= TAIL_LABEL;
00784 }
00785
00786
00787
00788 s = agget(e, TAIL_ID);
00789 if (s[0])
00790 ND_has_port(e->tail) = TRUE;
00791 ED_tail_port(e) = chkPort (ND_shape(e->tail)->fns->portfn,e->tail, s);
00792 if (noClip(e, E_tailclip))
00793 ED_tail_port(e).clip = FALSE;
00794 s = agget(e, HEAD_ID);
00795 if (s[0])
00796 ND_has_port(e->head) = TRUE;
00797 ED_head_port(e) = chkPort(ND_shape(e->head)->fns->portfn,e->head, s);
00798 if (noClip(e, E_headclip))
00799 ED_head_port(e).clip = FALSE;
00800
00801 return r;
00802 }
00803
00804
00805
00806 static box addLabelBB(box bb, textlabel_t * lp, boolean flipxy)
00807 {
00808 int width, height;
00809 point p = lp->p;
00810 int min, max;
00811
00812 if (flipxy) {
00813 height = ROUND(lp->dimen.x);
00814 width = ROUND(lp->dimen.y);
00815 }
00816 else {
00817 width = ROUND(lp->dimen.x);
00818 height = ROUND(lp->dimen.y);
00819 }
00820 min = p.x - width / 2;
00821 max = p.x + width / 2;
00822 if (min < bb.LL.x)
00823 bb.LL.x = min;
00824 if (max > bb.UR.x)
00825 bb.UR.x = max;
00826
00827 min = p.y - height / 2;
00828 max = p.y + height / 2;
00829 if (min < bb.LL.y)
00830 bb.LL.y = min;
00831 if (max > bb.UR.y)
00832 bb.UR.y = max;
00833
00834 return bb;
00835 }
00836
00837
00838
00839
00840
00841 void updateBB(graph_t * g, textlabel_t * lp)
00842 {
00843 GD_bb(g) = addLabelBB(GD_bb(g), lp, GD_flip(g));
00844 }
00845
00846
00847
00848
00849
00850
00851 void compute_bb(graph_t * g)
00852 {
00853 node_t *n;
00854 edge_t *e;
00855 box b, bb;
00856 point pt, s2;
00857 int i, j;
00858
00859 bb.LL = pointof(INT_MAX, INT_MAX);
00860 bb.UR = pointof(-INT_MAX, -INT_MAX);
00861 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00862 pt = coord(n);
00863 s2.x = ND_xsize(n) / 2 + 1;
00864 s2.y = ND_ysize(n) / 2 + 1;
00865 b.LL = sub_points(pt, s2);
00866 b.UR = add_points(pt, s2);
00867
00868 EXPANDBB(bb,b);
00869 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
00870 if (ED_spl(e) == 0)
00871 continue;
00872 for (i = 0; i < ED_spl(e)->size; i++) {
00873 for (j = 0; j < ED_spl(e)->list[i].size; j++) {
00874 pt = ED_spl(e)->list[i].list[j];
00875 EXPANDBP(bb,pt);
00876 }
00877 }
00878 if (ED_label(e) && ED_label(e)->set)
00879 bb = addLabelBB(bb, ED_label(e), GD_flip(g));
00880 }
00881 }
00882
00883 for (i = 1; i <= GD_n_cluster(g); i++) {
00884 EXPANDBB(bb,GD_clust(g)[i]->u.bb);
00885 }
00886
00887 GD_bb(g) = bb;
00888 }
00889
00890
00891
00892
00893
00894 static Agsym_t *setAttr(graph_t * g, void *obj, char *name, char *value,
00895 Agsym_t * ap)
00896 {
00897 if (ap == NULL) {
00898 switch (agobjkind(obj)) {
00899 case AGGRAPH:
00900 ap = agraphattr(g, name, "");
00901 break;
00902 case AGNODE:
00903 ap = agnodeattr(g, name, "");
00904 break;
00905 case AGEDGE:
00906 ap = agedgeattr(g, name, "");
00907 break;
00908 }
00909 }
00910 agxset(obj, ap->index, value);
00911 return ap;
00912 }
00913
00914
00915
00916
00917
00918
00919
00920 static node_t *clustNode(node_t * n, graph_t * cg, agxbuf * xb,
00921 graph_t * clg)
00922 {
00923 node_t *cn;
00924 static int idx = 0;
00925 char num[100];
00926
00927 agxbput(xb, "__");
00928 sprintf(num, "%d", idx++);
00929 agxbput(xb, num);
00930 agxbputc(xb, ':');
00931 agxbput(xb, cg->name);
00932
00933 cn = agnode(cg->root, agxbuse(xb));
00934 SET_CLUST_NODE(cn);
00935 aginsert(cg, cn);
00936 aginsert(clg, n);
00937
00938
00939 N_label = setAttr(cn->graph, cn, "label", "", N_label);
00940 N_style = setAttr(cn->graph, cn, "style", "invis", N_style);
00941 N_shape = setAttr(cn->graph, cn, "shape", "box", N_shape);
00942
00943
00944 return cn;
00945 }
00946
00947 typedef struct {
00948 Dtlink_t link;
00949 void *p[2];
00950 node_t *t;
00951 node_t *h;
00952 } item;
00953
00954 static int cmpItem(Dt_t * d, void *p1[], void *p2[], Dtdisc_t * disc)
00955 {
00956 NOTUSED(d);
00957 NOTUSED(disc);
00958
00959 if (p1[0] < p2[0])
00960 return -1;
00961 else if (p1[0] > p2[0])
00962 return 1;
00963 else if (p1[1] < p2[1])
00964 return -1;
00965 else if (p1[1] > p2[1])
00966 return 1;
00967 else
00968 return 0;
00969 }
00970
00971
00972
00973 static void *newItem(Dt_t * d, item * objp, Dtdisc_t * disc)
00974 {
00975 item *newp = NEW(item);
00976
00977 NOTUSED(disc);
00978 newp->p[0] = objp->p[0];
00979 newp->p[1] = objp->p[1];
00980 newp->t = objp->t;
00981 newp->h = objp->h;
00982
00983 return newp;
00984 }
00985
00986
00987
00988 static void freeItem(Dt_t * d, item * obj, Dtdisc_t * disc)
00989 {
00990 free(obj);
00991 }
00992
00993 static Dtdisc_t mapDisc = {
00994 offsetof(item, p),
00995 sizeof(2 * sizeof(void *)),
00996 offsetof(item, link),
00997 (Dtmake_f) newItem,
00998 (Dtfree_f) freeItem,
00999 (Dtcompar_f) cmpItem,
01000 NIL(Dthash_f),
01001 NIL(Dtmemory_f),
01002 NIL(Dtevent_f)
01003 };
01004
01005
01006
01007
01008 static edge_t *cloneEdge(edge_t * e, node_t * ct, node_t * ch)
01009 {
01010 graph_t *g = ct->graph;
01011 edge_t *ce = agedge(g, ct, ch);
01012 agcopyattr(e, ce);
01013
01014 return ce;
01015 }
01016
01017
01018
01019 static void insertEdge(Dt_t * map, void *t, void *h, edge_t * e)
01020 {
01021 item dummy;
01022
01023 dummy.p[0] = t;
01024 dummy.p[1] = h;
01025 dummy.t = e->tail;
01026 dummy.h = e->head;
01027 dtinsert(map, &dummy);
01028
01029 dummy.p[0] = h;
01030 dummy.p[1] = t;
01031 dummy.t = e->head;
01032 dummy.h = e->tail;
01033 dtinsert(map, &dummy);
01034 }
01035
01036
01037
01038
01039
01040 static item *mapEdge(Dt_t * map, edge_t * e)
01041 {
01042 void *key[2];
01043
01044 key[0] = e->tail;
01045 key[1] = e->head;
01046 return (item *) dtmatch(map, &key);
01047 }
01048
01049
01050
01051
01052
01053
01054
01055
01056
01057
01058
01059
01060
01061
01062
01063 #define MAPC(n) (strncmp((n)->name,"cluster",7)?NULL:agfindsubg((n)->graph, (n)->name))
01064
01065 static void
01066 checkCompound(edge_t * e, graph_t * clg, agxbuf * xb, Dt_t * map)
01067 {
01068 graph_t *tg;
01069 graph_t *hg;
01070 node_t *cn;
01071 node_t *cn1;
01072 node_t *t = e->tail;
01073 node_t *h = e->head;
01074 edge_t *ce;
01075 item *ip;
01076
01077 if (IS_CLUST_NODE(h)) return;
01078 tg = MAPC(t);
01079 hg = MAPC(h);
01080 if (!tg && !hg)
01081 return;
01082 if (tg == hg) {
01083 agerr(AGWARN, "cluster cycle %s -- %s not supported\n", t->name,
01084 t->name);
01085 return;
01086 }
01087 ip = mapEdge(map, e);
01088 if (ip) {
01089 cloneEdge(e, ip->t, ip->h);
01090 return;
01091 }
01092
01093 if (hg) {
01094 if (tg) {
01095 if (agcontains(hg, tg)) {
01096 agerr(AGWARN, "tail cluster %s inside head cluster %s\n",
01097 tg->name, hg->name);
01098 return;
01099 }
01100 if (agcontains(tg, hg)) {
01101 agerr(AGWARN, "head cluster %s inside tail cluster %s\n",
01102 hg->name, tg->name);
01103 return;
01104 }
01105 cn = clustNode(t, tg, xb, clg);
01106 cn1 = clustNode(h, hg, xb, clg);
01107 ce = cloneEdge(e, cn, cn1);
01108 insertEdge(map, t, h, ce);
01109 } else {
01110 if (agcontains(hg, t)) {
01111 agerr(AGWARN, "tail node %s inside head cluster %s\n",
01112 t->name, hg->name);
01113 return;
01114 }
01115 cn = clustNode(h, hg, xb, clg);
01116 ce = cloneEdge(e, t, cn);
01117 insertEdge(map, t, h, ce);
01118 }
01119 } else {
01120 if (agcontains(tg, h)) {
01121 agerr(AGWARN, "head node %s inside tail cluster %s\n", h->name,
01122 tg->name);
01123 return;
01124 }
01125 cn = clustNode(t, tg, xb, clg);
01126 ce = cloneEdge(e, cn, h);
01127 insertEdge(map, t, h, ce);
01128 }
01129 }
01130
01131
01132
01133
01134
01135
01136
01137 int processClusterEdges(graph_t * g)
01138 {
01139 int rv;
01140 node_t *n;
01141 node_t *nxt;
01142 edge_t *e;
01143 graph_t *clg;
01144 agxbuf xb;
01145 Dt_t *map;
01146 unsigned char buf[SMALLBUF];
01147
01148 map = dtopen(&mapDisc, Dtoset);
01149 clg = agsubg(g, "__clusternodes");
01150 agxbinit(&xb, SMALLBUF, buf);
01151 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
01152 if (IS_CLUST_NODE(n)) continue;
01153 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
01154 checkCompound(e, clg, &xb, map);
01155 }
01156 }
01157 agxbfree(&xb);
01158 dtclose(map);
01159 rv = agnnodes(clg);
01160 for (n = agfstnode(clg); n; n = nxt) {
01161 nxt = agnxtnode(clg, n);
01162 agdelete(g, n);
01163 }
01164 agclose(clg);
01165 if (rv)
01166 SET_CLUST_EDGE(g);
01167 return rv;
01168 }
01169
01170
01171
01172
01173
01174
01175
01176
01177
01178 static node_t *mapN(node_t * n, graph_t * clg)
01179 {
01180 extern Agdict_t *agdictof(void *);
01181 node_t *nn;
01182 char *name;
01183 graph_t *g = n->graph;
01184 Agdict_t *d;
01185 Agsym_t **list;
01186 Agsym_t *sym;
01187
01188 if (!(IS_CLUST_NODE(n)))
01189 return n;
01190 aginsert(clg, n);
01191
01192 name = strchr(n->name, ':');
01193 assert(name);
01194 name++;
01195 if ((nn = agfindnode(g, name)))
01196 return nn;
01197 nn = agnode(g, name);
01198
01199
01200 d = agdictof(n);
01201 list = d->list;
01202 while ((sym = *list++)) {
01203
01204 if (agxget(nn, sym->index) != sym->value)
01205 agxset(nn, sym->index, sym->value);
01206 }
01207
01208 return nn;
01209 }
01210
01211 static void undoCompound(edge_t * e, graph_t * clg)
01212 {
01213 node_t *t = e->tail;
01214 node_t *h = e->head;
01215 node_t *ntail;
01216 node_t *nhead;
01217
01218 if (!(IS_CLUST_NODE(t) || IS_CLUST_NODE(h)))
01219 return;
01220 ntail = mapN(t, clg);
01221 nhead = mapN(h, clg);
01222 cloneEdge(e, ntail, nhead);
01223 }
01224
01225
01226
01227
01228
01229
01230 void undoClusterEdges(graph_t * g)
01231 {
01232 node_t *n;
01233 edge_t *e;
01234 graph_t *clg;
01235
01236 clg = agsubg(g, "__clusternodes");
01237 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
01238 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
01239 undoCompound(e, clg);
01240 }
01241 }
01242 for (n = agfstnode(clg); n; n = agnxtnode(clg, n)) {
01243 agdelete(g, n);
01244 }
01245 agclose(clg);
01246 }
01247
01248
01249
01250
01251
01252
01253 attrsym_t*
01254 safe_dcl(graph_t * g, void *obj, char *name, char *def,
01255 attrsym_t * (*fun) (Agraph_t *, char *, char *))
01256 {
01257 attrsym_t *a = agfindattr(obj, name);
01258 if (a == NULL)
01259 a = fun(g, name, def);
01260 return a;
01261 }
01262
01263 static int comp_entities(const void *e1, const void *e2) {
01264 return strcmp(((struct entities_s *)e1)->name, ((struct entities_s *)e2)->name);
01265 }
01266
01267 #define MAXENTLEN 8
01268
01269
01270
01271
01272
01273
01274 char* scanEntity (char* t, agxbuf* xb)
01275 {
01276 char* endp = strchr (t, ';');
01277 struct entities_s key, *res;
01278 int len;
01279 char buf[MAXENTLEN+1];
01280
01281 agxbputc(xb, '&');
01282 if (!endp) return t;
01283 if (((len = endp-t) > MAXENTLEN) || (len < 2)) return t;
01284 strncpy (buf, t, len);
01285 buf[len] = '\0';
01286 key.name = buf;
01287 res = bsearch(&key, entities, NR_OF_ENTITIES,
01288 sizeof(entities[0]), comp_entities);
01289 if (!res) return t;
01290 sprintf (buf, "%d", res->value);
01291 agxbputc(xb, '#');
01292 agxbput(xb, buf);
01293 agxbputc(xb, ';');
01294 return (endp+1);
01295 }
01296
01297
01298
01299
01300
01301
01302
01303
01304
01305 static int
01306 htmlEntity (char** s)
01307 {
01308 char *p;
01309 struct entities_s key, *res;
01310 char entity_name_buf[ENTITY_NAME_LENGTH_MAX+1];
01311 unsigned char* str = *(unsigned char**)s;
01312 unsigned int byte;
01313 int i, n = 0;
01314
01315 byte = *str;
01316 if (byte == '#') {
01317 byte = *(str + 1);
01318 if (byte == 'x' || byte == 'X') {
01319 for (i = 2; i < 8; i++) {
01320 byte = *(str + i);
01321 if (byte >= 'A' && byte <= 'F')
01322 byte = byte - 'A' + 10;
01323 else if (byte >= 'a' && byte <= 'f')
01324 byte = byte - 'a' + 10;
01325 else if (byte >= '0' && byte <= '9')
01326 byte = byte - '0';
01327 else
01328 break;
01329 n = (n * 16) + byte;
01330 }
01331 }
01332 else {
01333 for (i = 1; i < 8; i++) {
01334 byte = *(str + i);
01335 if (byte >= '0' && byte <= '9')
01336 n = (n * 10) + (byte - '0');
01337 else
01338 break;
01339 }
01340 }
01341 if (byte == ';') {
01342 str += i+1;
01343 }
01344 else {
01345 n = 0;
01346 }
01347 }
01348 else {
01349 key.name = p = entity_name_buf;
01350 for (i = 0; i < ENTITY_NAME_LENGTH_MAX; i++) {
01351 byte = *(str + i);
01352 if (byte == '\0') break;
01353 if (byte == ';') {
01354 *p++ = '\0';
01355 res = bsearch(&key, entities, NR_OF_ENTITIES,
01356 sizeof(entities[0]), *comp_entities);
01357 if (res) {
01358 n = res->value;
01359 str += i+1;
01360 }
01361 break;
01362 }
01363 *p++ = byte;
01364 }
01365 }
01366 *s = (char*)str;
01367 return n;
01368 }
01369
01370
01371 char* htmlEntityUTF8 (char* s)
01372 {
01373 char* ns;
01374 agxbuf xb;
01375 unsigned char buf[BUFSIZ];
01376 unsigned char c;
01377 unsigned int v;
01378 int rc;
01379
01380 agxbinit(&xb, BUFSIZ, buf);
01381
01382 while ((c = *(unsigned char*)s++)) {
01383 if (c < 0xC0) {
01384
01385
01386
01387
01388
01389
01390 if (c == '&') {
01391
01392
01393 v = htmlEntity (&s);
01394 if (v) {
01395 if (v < 0x7F)
01396 c = v;
01397 else if (v < 0x07FF) {
01398 rc = agxbputc(&xb, (v >> 6) | 0xC0);
01399 c = (v & 0x3F) | 0x80;
01400 }
01401 else {
01402 rc = agxbputc(&xb, (v >> 12) | 0xE0);
01403 rc = agxbputc(&xb, ((v >> 6) & 0x3F) | 0x80);
01404 c = (v & 0x3F) | 0x80;
01405 }
01406 }
01407 }
01408 }
01409 else if (c < 0xE0) {
01410 if ((*s & 0xC0) == 0x80) {
01411 rc = agxbputc(&xb, c);
01412 c = *(unsigned char*)s++;
01413 }
01414 else {
01415 agerr(AGERR, "Invalid 2-byte UTF8 found in input. Perhaps \"-Gcharset=latin1\" is needed?\n");
01416 exit(EXIT_FAILURE);
01417 }
01418 }
01419 else if (c < 0xF0) {
01420 if (((*s & 0xC0) == 0x80) && ((s[1] & 0xC0) == 0x80)) {
01421 rc = agxbputc(&xb, c);
01422 c = *(unsigned char*)s++;
01423 rc = agxbputc(&xb, c);
01424 c = *(unsigned char*)s++;
01425 }
01426 else {
01427 agerr(AGERR, "Invalid 3-byte UTF8 found in input. Perhaps \"-Gcharset=latin1\" is needed?\n");
01428 exit(EXIT_FAILURE);
01429 }
01430 }
01431 else {
01432 agerr(AGERR, "UTF8 codes > 3 bytes are not currently supported. Or perhaps \"-Gcharset=latin1\" is needed?\n");
01433 exit(EXIT_FAILURE);
01434 }
01435 rc = agxbputc(&xb, c);
01436 }
01437 ns = strdup (agxbuse(&xb));
01438 agxbfree(&xb);
01439 return ns;
01440 }
01441
01442
01443
01444
01445
01446
01447 char* latin1ToUTF8 (char* s)
01448 {
01449 char* ns;
01450 agxbuf xb;
01451 unsigned char buf[BUFSIZ];
01452 unsigned int v;
01453 int rc;
01454
01455 agxbinit(&xb, BUFSIZ, buf);
01456
01457
01458
01459
01460 while ((v = *(unsigned char*)s++)) {
01461 if (v == '&') {
01462 v = htmlEntity (&s);
01463 if (!v) v = '&';
01464 }
01465 if (v < 0x7F)
01466 rc = agxbputc(&xb, v);
01467 else if (v < 0x07FF) {
01468 rc = agxbputc(&xb, (v >> 6) | 0xC0);
01469 rc = agxbputc(&xb, (v & 0x3F) | 0x80);
01470 }
01471 else {
01472 rc = agxbputc(&xb, (v >> 12) | 0xE0);
01473 rc = agxbputc(&xb, ((v >> 6) & 0x3F) | 0x80);
01474 rc = agxbputc(&xb, (v & 0x3F) | 0x80);
01475 }
01476 }
01477 ns = strdup (agxbuse(&xb));
01478 agxbfree(&xb);
01479 return ns;
01480 }
01481
01482
01483
01484
01485
01486
01487 char*
01488 utf8ToLatin1 (char* s)
01489 {
01490 char* ns;
01491 agxbuf xb;
01492 unsigned char buf[BUFSIZ];
01493 unsigned char c;
01494 unsigned char outc;
01495 int rc;
01496
01497 agxbinit(&xb, BUFSIZ, buf);
01498
01499 while ((c = *(unsigned char*)s++)) {
01500 if (c < 0x7F)
01501 rc = agxbputc(&xb, c);
01502 else {
01503 outc = (c & 0x03) << 6;
01504 c = *(unsigned char*)s++;
01505 outc = outc | (c & 0x3F);
01506 rc = agxbputc(&xb, outc);
01507 }
01508 }
01509 ns = strdup (agxbuse(&xb));
01510 agxbfree(&xb);
01511 return ns;
01512 }
01513
01514 boolean overlap_node(node_t *n, boxf b)
01515 {
01516 boxf bb;
01517 inside_t ictxt;
01518 pointf p;
01519
01520 bb = ND_bb(n);
01521 if (! OVERLAP(b, bb))
01522 return FALSE;
01523
01524 P2PF(ND_coord_i(n),p);
01525
01526
01527 p.x -= (b.UR.x + b.LL.x) / 2.;
01528 p.y -= (b.UR.y + b.LL.y) / 2.;
01529
01530 ictxt.s.n = n;
01531 ictxt.s.bp = NULL;
01532
01533 return ND_shape(n)->fns->insidefn(&ictxt, p);
01534 }
01535
01536 boolean overlap_label(textlabel_t *lp, boxf b)
01537 {
01538 double sx, sy;
01539 boxf bb;
01540
01541 sx = lp->dimen.x / 2.;
01542 sy = lp->dimen.y / 2.;
01543 bb.LL.x = lp->p.x - sx;
01544 bb.UR.x = lp->p.x + sx;
01545 bb.LL.y = lp->p.y - sy;
01546 bb.UR.y = lp->p.y + sy;
01547 return OVERLAP(b, bb);
01548 }
01549
01550 static boolean overlap_arrow(pointf p, pointf u, double scale, int flag, boxf b)
01551 {
01552 boxf bb;
01553
01554 bb = arrow_bb(p, u, scale, flag);
01555 if (OVERLAP(b, bb)) {
01556
01557 return TRUE;
01558 }
01559 return FALSE;
01560 }
01561
01562 static boolean overlap_bezier(bezier bz, boxf b)
01563 {
01564 int i;
01565 point pp;
01566 pointf p, u;
01567
01568 assert(bz.size);
01569 pp = bz.list[0];
01570 P2PF(pp, u);
01571 for (i = 1; i < bz.size; i++) {
01572 pp = bz.list[i];
01573 P2PF(pp, p);
01574 if (lineToBox(p, u, b) != -1)
01575 return TRUE;
01576 u = p;
01577 }
01578
01579
01580 if (bz.sflag) {
01581 P2PF(bz.sp, p);
01582 P2PF(bz.list[0], u);
01583 if (overlap_arrow(p, u, 1, bz.sflag, b))
01584 return TRUE;
01585 }
01586 if (bz.eflag) {
01587 P2PF(bz.ep, p);
01588 P2PF(bz.list[bz.size - 1], u);
01589 if (overlap_arrow(p, u, 1, bz.eflag, b))
01590 return TRUE;
01591 }
01592 return FALSE;
01593 }
01594
01595 boolean overlap_edge(edge_t *e, boxf b)
01596 {
01597 int i;
01598 splines *spl;
01599 textlabel_t *lp;
01600
01601 spl = ED_spl(e);
01602 if (spl && boxf_overlap(spl->bb, b))
01603 for (i = 0; i < spl->size; i++)
01604 if (overlap_bezier(spl->list[i], b))
01605 return TRUE;
01606
01607 lp = ED_label(e);
01608 if (lp && overlap_label(lp, b))
01609 return TRUE;
01610
01611 return FALSE;
01612 }
01613
01614
01615
01616
01617
01618
01619
01620
01621
01622
01623
01624
01625
01626
01627 void setEdgeType (graph_t* g, int dflt)
01628 {
01629 char* s = agget(g, "splines");
01630 int et;
01631
01632 if (!s) {
01633 GD_flags(g) |= dflt;
01634 return;
01635 }
01636 if (*s == '\0') {
01637 et = ET_NONE;
01638 return;
01639 }
01640 et = 0;
01641 switch (*s) {
01642 case '0' :
01643 et = ET_LINE;
01644 break;
01645 case '1' :
01646 case '2' :
01647 case '3' :
01648 case '4' :
01649 case '5' :
01650 case '6' :
01651 case '7' :
01652 case '8' :
01653 case '9' :
01654 et = ET_SPLINE;
01655 break;
01656 case 'c' :
01657 case 'C' :
01658 if (!strcasecmp (s+1, "ompound"))
01659 et = ET_COMPOUND;
01660 break;
01661 case 'f' :
01662 case 'F' :
01663 if (!strcasecmp (s+1, "alse"))
01664 et = ET_LINE;
01665 break;
01666 case 'l' :
01667 case 'L' :
01668 if (!strcasecmp (s+1, "ine"))
01669 et = ET_LINE;
01670 break;
01671 case 'n' :
01672 case 'N' :
01673 if (!strcasecmp (s+1, "one")) return;
01674 break;
01675 case 'o' :
01676 case 'O' :
01677 if (!strcasecmp (s+1, "rtho"))
01678 et = ET_ORTHO;
01679 break;
01680 case 'p' :
01681 case 'P' :
01682 if (!strcasecmp (s+1, "olyline"))
01683 et = ET_PLINE;
01684 break;
01685 case 's' :
01686 case 'S' :
01687 if (!strcasecmp (s+1, "pline"))
01688 et = ET_SPLINE;
01689 break;
01690 case 't' :
01691 case 'T' :
01692 if (!strcasecmp (s+1, "rue"))
01693 et = ET_SPLINE;
01694 break;
01695 }
01696 if (!et) {
01697 agerr(AGWARN, "Unknown \"splines\" value: \"%s\" - ignored\n", s);
01698 et = dflt;
01699 }
01700 GD_flags(g) |= et;
01701 }
01702
01703 #ifndef HAVE_STRCASECMP
01704
01705 #include <string.h>
01706 #include <ctype.h>
01707
01708
01709 int strcasecmp(const char *s1, const char *s2)
01710 {
01711 while ((*s1 != '\0')
01712 && (tolower(*(unsigned char *) s1) ==
01713 tolower(*(unsigned char *) s2))) {
01714 s1++;
01715 s2++;
01716 }
01717
01718 return tolower(*(unsigned char *) s1) - tolower(*(unsigned char *) s2);
01719 }
01720
01721 #endif
01722
01723 #ifndef HAVE_STRNCASECMP
01724
01725 #include <string.h>
01726 #include <ctype.h>
01727
01728 int strncasecmp(const char *s1, const char *s2, unsigned int n)
01729 {
01730 if (n == 0)
01731 return 0;
01732
01733 while ((n-- != 0)
01734 && (tolower(*(unsigned char *) s1) ==
01735 tolower(*(unsigned char *) s2))) {
01736 if (n == 0 || *s1 == '\0' || *s2 == '\0')
01737 return 0;
01738 s1++;
01739 s2++;
01740 }
01741
01742 return tolower(*(unsigned char *) s1) - tolower(*(unsigned char *) s2);
01743 }
01744
01745 #endif