/misc/src/release/graphviz-2.18-1/src/graphviz-2.18/lib/common/utils.c

Go to the documentation of this file.
00001 /* $Id: utils.c,v 1.67 2008/02/22 14:21:42 glenlow Exp $ $Revision: 1.67 $ */
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 #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  *  a queue of nodes
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 /* returns index of an attribute if bound, else -1 */
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 /* union-find */
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 /* from Glassner's Graphics Gems */
00194 #define W_DEGREE 5
00195 
00196 /*
00197  *  Bezier : 
00198  *      Evaluate a Bezier curve at a particular parameter value
00199  *      Fill in control points for resulting sub-curves if "Left" and
00200  *      "Right" are non-null.
00201  * 
00202  */
00203 pointf Bezier(pointf * V, int degree, double t, pointf * Left, pointf * Right)
00204 {
00205     int i, j;                   /* Index variables      */
00206     pointf Vtemp[W_DEGREE + 1][W_DEGREE + 1];
00207 
00208     /* Copy control points  */
00209     for (j = 0; j <= degree; j++) {
00210         Vtemp[0][j] = V[j];
00211     }
00212 
00213     /* Triangle computation */
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 /* Fgets:
00289  * Read a complete line.
00290  * Return pointer to line, 
00291  * or 0 on EOF
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);      /* since lp != NULL, len > 0 */
00310     } while (buf[len - 1] != '\n');
00311 
00312     if (len > 0)
00313         return buf;
00314     else
00315         return 0;
00316 }
00317 
00318 /* safefile:
00319  * Check to make sure it is okay to read in files.
00320  * For normal uses, it is a no-op.
00321  * If the application has set HTTPServerEnVar, this indicates
00322  * it is web-active. In this case, it requires that the Gvfilepath
00323  * variable be set. This gives the legal directory
00324  * from which files may be read. The filename returned is thus
00325  * Gvfilepath concatenated with the last component of filename,
00326  * where a component is determined by a slack, backslash or colon
00327  * character.  
00328  * If the argument contains one of these characters, the user is
00329  * warned, once, that everything to the left is ignored.
00330  * As coded now, Gvfilepath must end in a slash character, or
00331  * equivalent.
00332  * Returns NULL if the argument is trivial.
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          * If we are running in an http server we allow
00345          * files only from the directory specified in
00346          * the GV_FILE_PATH environment variable.
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         /* allocate a buffer that we are sure is big enough
00360          * +1 for null character.
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     /* else, not in server, use original filename without modification. */
00386     return filename;
00387 }
00388 
00389 /* cat_libfile:
00390  * Write library files onto the given file pointer.
00391  * arglib is an NULL-terminated array of char*
00392  * Each non-trivial entry should be the name of a file to be included.
00393  * stdlib is an NULL-terminated array of char*
00394  * Each of these is a line of a standard library to be included.
00395  * If any item in arglib is the empty string, the stdlib is not used.
00396  * The stdlib is printed first, if used, followed by the user libraries.
00397  * We check that for web-safe file usage.
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     /* check for empty string to turn off stdlib */
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;       /* ignore empty string */
00422             p = safefile(p);    /* make sure filename is okay */
00423             if ((fp = fopen(p, "r"))) {
00424                 while ((bp = Fgets(fp)))
00425                     fputs(bp, ofp);
00426                 fputc('\n', ofp); /* append a newline just in case */
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; /* squares of distances */
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 /* this caching seems to prevent pt.x from getting set from bz.list[0].x
00528         - optimizer problem ? */
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             /* make the spline be monotonic in Y, awful but it works for now */
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 /* this is a stub so that we can share a common emit.c between dot and neato */
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 /* noClip:
00683  * Return true if head/tail end of edge should not be clipped
00684  * to node.
00685  */
00686 static boolean 
00687 noClip(edge_t *e, attrsym_t* sym)
00688 {
00689     char                *str;
00690     boolean             rv = FALSE;
00691 
00692     if (sym) {  /* mapbool isn't a good fit, because we want "" to mean true */
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 /*chkPort:
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 /* return true if edge has label */
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     /* vladimir */
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     /* end vladimir */
00786 
00787     /* We still accept ports beginning with colons but this is deprecated */
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 /* addLabelBB:
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 /* updateBB:
00838  * Reset graph's bounding box to include bounding box of the given label.
00839  * Assume the label's position has been set.
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 /* compute_bb:
00847  * Compute bounding box of g using nodes, splines, and clusters.
00848  * Assumes bb of clusters already computed.
00849  * store in GD_bb.
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 /* setAttr:
00891  * Sets object's name attribute to the given value.
00892  * Creates the attribute if not already set.
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 /* clustNode:
00915  * Generate a special cluster node representing the end node
00916  * of an edge to the cluster cg. n is a node whose name is the same
00917  * as the cluster cg. clg is the subgraph of all of
00918  * the original nodes, which will be deleted later.
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     /* set attributes */
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     /* N_width = setAttr (cn->graph, cn, "width", "0.0001", N_width); */
00943 
00944     return cn;
00945 }
00946 
00947 typedef struct {
00948     Dtlink_t link;              /* cdt data */
00949     void *p[2];                 /* key */
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 /* newItem:
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 /* freeItem:
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 /* cloneEdge:
01006  * Make a copy of e in e's graph but using ct and ch as nodes
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 /* insertEdge:
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 /* mapEdge:
01037  * Check if we already have cluster edge corresponding to t->h,
01038  * and return it.
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 /* checkCompound:
01050  * If endpoint names a cluster, mark for temporary deletion and create 
01051  * special node and insert into cluster. Then clone the edge. Real edge
01052  * will be deleted when we delete the original node.
01053  * Invariant: new edge has same sense as old. That is, given t->h with
01054  * t and h mapped to ct and ch, the new edge is ct->ch.
01055  *
01056  * In the current model, we create a cluster node for each cluster edge
01057  * between the cluster and some other node or cluster, treating the
01058  * cluster node as a port on the cluster. This should help with better
01059  * routing to avoid edge crossings. At present, this is not implemented,
01060  * so we could use a simpler model in which we create a single cluster
01061  * node for each cluster used in a cluster edge.
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 /* processClusterEdges:
01132  * Look for cluster edges. Replace cluster edge endpoints
01133  * corresponding to a cluster with special cluster nodes.
01134  * Delete original nodes.
01135  * Return 0 if no cluster edges; 1 otherwise.
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 /* mapN:
01171  * Convert cluster nodes back to ordinary nodes
01172  * If n is already ordinary, return it.
01173  * Otherwise, we know node's name is "__i:xxx"
01174  * where i is some number and xxx is the nodes's original name.
01175  * Create new node of name xxx if it doesn't exist and add n to clg
01176  * for later deletion.
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     /* Set all attributes to default */
01200     d = agdictof(n);
01201     list = d->list;
01202     while ((sym = *list++)) {
01203         /* Can use pointer comparison because of ref strings. */
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 /* undoClusterEdges:
01226  * Replace cluster nodes with originals. Make sure original has
01227  * no attributes. Replace original edges. Delete cluster nodes,
01228  * which will also delete cluster edges.
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 /* safe_dcl:
01249  * Find the attribute belonging to graph g for objects like obj
01250  * with given name. If one does not exist, create it with the
01251  * supplied function fun with default value def.
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 /* scanEntity:
01270  * Scan non-numeric entity, convert to &#...; form and store in xbuf.
01271  * t points to first char after '&'. Return after final semicolon.
01272  * If unknown, we return t and let libexpat flag the error.
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 /* htmlEntity:
01299  * Check for an HTML entity for a special character.
01300  * Assume *s points to first byte after '&'. 
01301  * If successful, return the corresponding value and update s to
01302  * point after the terminating ';'.
01303  * On failure, return 0 and leave s unchanged.
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 /* substitute html entities like: &#123; and: &amp; for the UTF8 equivalents */
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              * Handles properly formed UTF-8 characters between
01386              * 0x01 and 0x7F.  Also treats \0 and naked trail
01387              * bytes 0x80 to 0xBF as valid characters representing
01388              * themselves.
01389              */
01390             if (c == '&') {
01391                 /* replace html entity sequences like: &amp;
01392                  * and: &#123; with their UTF8 equivalents */
01393                 v = htmlEntity (&s);
01394                 if (v) {
01395                     if (v < 0x7F) /* entity needs 1 byte in UTF8 */
01396                         c = v;
01397                     else if (v < 0x07FF) { /* entity needs 2 bytes in UTF8 */
01398                         rc = agxbputc(&xb, (v >> 6) | 0xC0);
01399                         c = (v & 0x3F) | 0x80;
01400                     }
01401                     else { /* entity needs 3 bytes in UTF8 */
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) { /* copy 2 byte UTF8 characters */
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) { /* copy 3 byte UTF8 characters */
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 /* latin1ToUTF8:
01443  * Converts string from Latin1 encoding to utf8
01444  * Also translates HTML entities.
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     /* Values are either a byte (<= 256) or come from htmlEntity, whose
01458      * values are all less than 0x07FF, so we need at most 3 bytes.
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 /* utf8ToLatin1:
01483  * Converts string from utf8 encoding to Latin1
01484  * Note that it does not attempt to reproduce HTML entities.
01485  * We assume the input string comes from latin1ToUTF8.
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 /*  FIXME - need to do something better about CLOSEENOUGH */
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         /* FIXME - check inside arrow shape */
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     /* check arrows */
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 /* setEdgeType:
01615  * Sets graph's edge type based on the "splines" attribute.
01616  * If the attribute is not defined, use default.
01617  * If the attribute is "", use NONE.
01618  * If attribute value matches (case indepedent), use match.
01619  *   ortho => ET_ORTHO
01620  *   none => ET_NONE
01621  *   line => ET_LINE
01622  *   polyline => ET_PLINE
01623  *   spline => ET_SPLINE
01624  * If attribute is boolean, true means ET_SPLINE, false means ET_LINE.
01625  * Else warn and use default.
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' :    /* false */
01643         et = ET_LINE;
01644         break;
01645     case '1' :    /* true */
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                          /* HAVE_STRCASECMP */
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                          /* HAVE_STRNCASECMP */

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